テレコム資源管理システム:ベースH5重畳OpenLayers3のGIS

序文

すべての領域は、等、効果は、鉄道線を描く、部屋を見つけるために、通信リソース管理システム、食品場所共有ソフトウェアパッチの形態として使用することができ、通信ネットワークトポロジーマップの大きなアプリケーションにHTML5とOpenLayersをを組み合わせて合成することができますこれは、アプリケーションに関与することができます。これはOpenLayers3デモの組み合わせですが、実際には、またのArcGIS、BaiduのマップとGoogleマップや他の多くのGISマップエンジンとの統合に拡張することができます。

http://www.hightopo.com/demo/openlayers/

コード生成

マップを作成します。

OpenLayersをは型WebGISのクライアント側のJavaScriptパッケージを開発するために使用されます。マップソースをサポートOpenLayersを、Googleマップ、ヤフー、地図、Microsoft仮想地球とここで使用されるマップ他のオフラインは比較的人気のGoogleマップGoogleマップの地図で、ちょうどOpenLayersをを使用して、関連するライブラリの導入前と含まCSSファイル:

<リンクのrel = "スタイルシート"のhref = "CSS / ol.css"タイプ= "テキスト/ cssの"> 
<スクリプトSRC = "libに/ ol.js"> </ SCRIPT>

地図の初期化操作がdiv要素に吸い込まマップ、全体の通信リソース管理システムに不可欠であるol.Mapマップクラスを初期化し、このクラスのパラメータを設定します。

コードをコピー
document.getElementById mapDiv = VAR( 'mapDiv'); 
地図ol.Map新しい新=({ 
    対象: 'mapDiv'、//マップコンテナ
    。コントロール:ol.control.defaults()拡張([ 
        graphViewControl、//カスタム・トポロジーコントロール
        の新しいol.control.OverviewMapは、(グローバルビューは、制御マップ//、) 新しいol.control.ScaleLine(、//スケール制御
        ズーム倍率を制御する//新しいol.control.ZoomSlider()、
        新ol.control.ZoomToExtentは( )//ズームグローバルコントロールへ
    ])、
    レイヤー:[//層
        新新ol.layer.Tile({ 
            出典:新新ol.source.XYZ({// Googleマップ
                のURL:「のhttp://www.google。 CN /マップ/ VT / PB = !1m4!1立方メートル!1I {Z}!2I {X}!3I {Y}!2立方メートル!の1E0!2SM!3i345013117!3m8!2szh-CN!3scn!5e1105!12m4!1e68! 2M2!1sset!2sRoadmap!4e0 "    
            })  
        })
    ]、
    ビュー:新しいol.View({//マップビュー
        投影: 'EPSG:3857'、 // 投影
        中心:ol.proj.fromLonLat([106、35 ])、 初期の中心は//ビュー投影により座標系オプションが指定     
        ズーム:初期ズームビューを計算するために使用される4 //解像度レベルを
    })
})。
コードをコピー

行あたりのコードのコメントに基づいて、上記のコードに加えての説明は支障ないはず公式API。慎重な友人が非公式コントロールを気づいているかもしれません:graphViewControl制御を、この制御は、セクションGraphViewControl.jsファイルで、この制御、宣言と定義上のグラフィックスを描画するために使用されるトポロジアウト私の習慣です。

カスタムコントロール

クラスはol.control.Controlクラスから継承し、さまざまなニーズが増加のために、親クラスのメソッドやメソッドをオーバーライドするよりも、OpenLayersをカスタムコントロールは、より多くの何もできなくなります。

制御の時間設定と制御によるクラスの定義におけるコンテナエレメントが、GISマップビューポートの外にレンダリングされるとき、私は、クラス宣言のパラメータオプションを渡します。

VARビュー= graphView.getView(); //取得トポロジーアセンブリDIV 
ol.control.Control.call(この、{ 
要素:ビュー、//コンテナ素子制御
対象:制御マップをレンダリングするoptions.target//ビューポートの外側
)}。

上記新たに親クラスのメソッドで添加方法GraphViewControl graphViewを初期化ht.graph.GraphView、HTトポロジグラフィカルコンポーネントです。

// 获取GraphView对象
GraphViewControl.prototype.getGraphView = function() { return this._graphView; };
var graphView = this._graphView = new ht.graph.GraphView();// 拓扑图组件

我在控件中还给 graphView 拓扑组件添加了一些事件的监听,由于 OpenLayers 和 HT 是两款不同的 js 库,有着各自的交互系统和坐标系,首先我们将某些我们需要获取在 HT 上做的交互事件并停止事件传播到 OpenLayers 上:

コードをコピー
// 拖拽 node 时不移动地图
var stopGraphPropagation = function(e) {
    var data = graphView.getDataAt(e);// 获取 graphView 事件下的节点
    var interaction = graphView.getEditInteractor();// 获取编辑交互器
    if (data || e.metaKey || e.ctrlKey || interaction && interaction.gvEditing) {
        e.stopPropagation();// 不再派发事件 该方法将停止事件的传播,阻止它被分派到其他 Document 节点
    }
}

/** pointerdown 当指针变为活动事件
*    对于鼠标,当设备从按下的按钮转换到至少一个按钮被按下时,它会被触发。
*    对于触摸,当与数字化仪进行物理接触时会被触发。
*    对于笔,当触笔与数字化仪进行物理接触时会被触发。
**/
view.addEventListener('pointerdown', stopGraphPropagation, false);
view.addEventListener('touchstart', stopGraphPropagation, false);// 当触摸点被放置在触控面板上事件
view.addEventListener('mousedown', stopGraphPropagation, false);// 鼠标点下事件
コードをコピー

GraphViewControl 类定义部分还添加了一些关于移动和编辑节点的交互事件,主要是将节点的像素坐标转为 OpenLayers 的 ol.Cordinate 地图视图投影中的坐标并存储到节点的业务属性(HT 的一个可以存储任意值的对象)中,这样我们只需要通过获取或设置节点的业务属性 coord 就可以自由获取和设置节点在 map 上的像素坐标。

var position = data.getPosition(),// 获取选中节点的坐标
    x = position.x + graphView.tx(),// 节点横坐标+graphView水平平移值
    y = position.y + graphView.ty();// 节点纵坐标+graphView垂直平移值

var coord = map.getCoordinateFromPixel([x, y]);// 根据坐标的像素获取地图视图投影中的坐标
data.a('coord', coord);

这里我就提一些基础的功能,其他的就不作解释了,只是一些扩展。

值得注意的一点是,我们在上面对节点在电信 GIS 地图视图投影中的坐标进行了数据存储,但是这个方法对于 Shape 类型的节点来说不太合适,因为地图上一般都是用点围成区域面,勾勒出某个国家或者某个城市的轮廓,缩放的时候并不实时保持大小,而是根据地图的缩放来缩放,实时保持在电信 GIS 地图的某个位置,所以我对 Shape 类型的节点中所有的点遍历了一遍,都设置了业务属性 pointCoord,获取地图视图投影中的坐标:

コードをコピー
// 给 shape 类型的节点的每个点位置都设置为经纬度
if (e.kind === 'endEditPoint' || e.kind === 'endEditPoints' || e.kind === 'endEditResize' || e.kind === 'endMove') {
    if (data instanceof ht.Shape) {// Shape 类型的节点
        data.getPoints().forEach(function(point, index) {
            var pointCoord = map.getCoordinateFromPixel([point.x, point.y]);// 获取给定像素的坐标
            data.a('pointCoord['+index+']', pointCoord);
        });
    }
}
コードをコピー

图层叠加

OpenLayers 的结构比较复杂,而 HT 相对来说简单很多,所以我将 HT 叠加到 OpenLayers Map 的 viewport 中。这里我在子类 GraphViewControl 中重载了父类 ol.control.Control 的 setMap 方法,在此方法中将 HT 的拓扑组件 graphView 添加到 OpenLayers 的视图 viewport 中,我们知道,HT 的组件一般都是绝对定位的,所以我们要设置 css 中的位置和宽高属性:

コードをコピー
var graphView = self._graphView;// = GraphViewControl.getGraphView()
var view = graphView.getView();// 获取 graphView 组件的 div
var dataModel = graphView.getDataModel();// 获取 graphView 的数据容器
view.style.top = '0';
view.style.left = '0';
view.style.width = '100%';
view.style.height = '100%';

map.getViewport().insertBefore(view, map.getViewport().firstChild);// getViewPort 获取用作地图视口的元素 insertBefore 在指定的已有子节点(参数二)之前插入新的子节点(参数一)
コードをコピー

并对数据容器增删变化事件进行监听,通过监听当前加入数据容器的节点类型,将当前节点的像素坐标转为地图视图投影中的坐标存储在节点的业务属性 coord 上:

コードをコピー
dataModel.addDataModelChangeListener(function(e) {// 数据容器增删改查变化监听
    if (e.kind === 'add' && !(e.data instanceof ht.Edge)) {// 添加事件&&事件对象不是 ht.Edge 类型
        if (e.data instanceof ht.Node) {
            var position = e.data.getPosition();
            var coordPosition = map.getCoordinateFromPixel([position.x, position.y]);// 获取给定像素的坐标
            e.data.a('coord', coordPosition);
        }

        if (e.data instanceof ht.Shape) {// 给 shape 类型的节点上的每个点都设置经纬度
            e.data.getPoints().forEach(function(point, index) {// 对 shape 类型的节点则将所有点的坐标都转为经纬度
                var pointCoord = map.getCoordinateFromPixel([point.x, point.y]);// 获取给定像素的坐标
                e.data.a('pointCoord['+index+']', pointCoord);
            });
        }
    }
});
コードをコピー

最后监听地图更新事件,重设拓扑:

map.on('postrender', function() { self.resetGraphView(); });

坐标转换

重设拓扑在这边的意思就是将拓扑图中节点坐标从我们一开始设置在 HT 中的像素坐标重新通过地图的缩放或者移动将地图视图投影中的坐标转为像素坐标设置到节点上,这时候前面存储的业务属性 coord 就派上用场了,记住,Shape 类型的节点是例外的,还是要对其中的每个点都重新设置坐标:

コードをコピー
GraphViewControl.prototype.resetGraphView = function() {// 重置 graphView 组件的状态
    var graphView = this._graphView;
    graphView.tx(0);// grpahView 水平平移值
    graphView.ty(0);// graphView 垂直平移值

    graphView.dm().each(function(data) {// 遍历 graphView 中的数据容器 
        var coord = data.a('coord');// 获取节点的业务属性 coord
        if (coord) {
            var position = map.getPixelFromCoordinate(coord);// 获取给定坐标的像素
            data.setPosition(position[0], position[1]);// 重新给节点设置像素坐标
        }
        if (data instanceof ht.Shape) {
            var points = data.toPoints();// 构建一个新的Shape点集合并返回
            data.getPoints().clear();// 清空点集合
            data._points = new ht.List();

            points.forEach(function(point, index) {// 给 shape 重新设置每一个点的像素坐标
                point.x = map.getPixelFromCoordinate(data.a('pointCoord['+ index +']'))[0];
                point.y = map.getPixelFromCoordinate(data.a('pointCoord['+ index +']'))[1];
                data._points.add(point);
            });

            data.setPoints(data._points);
        }
    });

    graphView.validate();//刷新拓扑组件
}
コードをコピー

场景搭建

OpenLayers 的 Map 部分做好了,接下来就是将它放进场景中了~但是从上面的截图中能看到,除了地图,顶部有工具条(但是我是用 formPane 表单组件做的),左侧有一个可供拖拽的 Palette 面板组件,通过 HT 的 borderPane 边框面板组件将整个场景布局好:

コードをコピー
raphViewControl = new GraphViewControl();// 自定义控件,作为 openlayers 地图上自定义控件
graphView = graphViewControl.getGraphView();// 获取拓扑图组件
dm = graphView.getDataModel();// 获取拓扑图中的数据容器

palette = new ht.widget.Palette();// 创建一个组件面板
formPane = createFormPane();// 工具条的 form 表单

borderPane = new ht.widget.BorderPane();// 边框面板组件
borderPane.setTopView(formPane);// 设置顶部组件为 formPane
borderPane.setLeftView(palette, 260);// 设置左边组件为 palette 参数二为设置 该view的宽度
borderPane.setCenterView(mapDiv);// 设置中间组件为 mapDiv

borderPane.addToDOM();// 将面板组件添加到 body 中
コードをコピー

这样整个场景的布局和显示就完成了,非常轻松~

工具条

本身 HT 有自带的工具条,但是因为 form 表单在排布以及样式上面可以更灵活,所以采用这个。

コードをコピー
var fp = new ht.widget.FormPane();
fp.setVGap(0);// 设置表单组件水平间距 默认值为6
fp.setHGap(0);// 设置表单的行垂直间距 默认值为6
fp.setHPadding(4);// 设置表单左边和右边与组件内容的间距,默认值为8
fp.setVPadding(4);// 设置表单顶部和顶部与组件内容的间距,默认值为8
fp.setHeight(40);// 设置表单高度

var btBgColor = '#fff',
    btnIconColor = 'rgb(159, 159, 159)',
    btnSelectColor = 'rgb(231, 231, 231)';

fp.addRow([// 添加行 首尾各加了一个'',并且占的宽度均为相对值0.1,就会将中间部分居中
    '', {
        id: 'select',// id 唯一标示属性,可通过 formPane.getItemById(id) 获取添加到对应的 item 对象
        button: {// ht.widget.Button 为按钮类
            background: btBgColor,// 设置背景颜色
            icon: './symbols/icon/select.json',// 设置图标
            iconColor: btnIconColor,// 设置图标颜色
            selectBackground: btnSelectColor,// 设置选中背景颜色
            togglable: true,// 设置按钮是否处于开关状态
            groupId: 't',// 设置组编号,属于同组的togglable按钮具有互斥功能
            toolTip: '编辑',// 设置文字提示,可通过 enableToolTip() 和 disableToolTip() 启动和关闭文字提示
            onClicked: function() {// 按钮点击触发函数
                editableFunc();
            }
        }
    }, {
        id: 'pointLine',
        button: {
            background: btBgColor,
            icon: './symbols/icon/line.json',
            iconColor: btnIconColor,
            selectBackground: btnSelectColor,
            togglable: true,
            groupId: 't',
            toolTip: '连线',
            onClicked: function () {
                /** 通过 setInteractors 组合交互器
                * DefaultInteractor实现Group、Edge和SubGraph图元的默认双击响应,手抓图平移,滚轮缩放,键盘响应等功能
                * TouchInteractor实现移动设备上的Touch交互功能
                * CreateEdgeInteractor 为 CreateEdgeInteractor.js 文件中自定义的连线交互器
                * CreateShapeInteractor 为 CreateShapeInteractor.js 文件中自定义的多边形交互器
                **/
                graphView.setInteractors([new ht.graph.DefaultInteractor(graphView), new ht.graph.TouchInteractor(graphView, {
                    selectable: false
                }), new CreateEdgeInteractor(graphView)]);
            }
        }
    },''
], [0.1, 36, 36, 0.1]);
コードをコピー

上面的 form 表单中添加行我只列出了两个功能,一个编辑的功能,另一个绘制连线的功能。formPane.addRow 为添加一行元素,参数一为元素数组,元素可为字符串、json 格式描述的组件参数信息、html 元素或者为 null 的空,参数二为为每个元素宽度信息数组,宽度值大于1代表固定绝对值,小于等于1代表相对值,也可为 80+0.3 的组合。

为了让我想显示的部分显示在工具栏的正中央,所以我在第一项和最后一项都设置了一个空,占 0.1 的相对宽度,并且比例相同,所以中间的部分才会显示在正中央。

上面代码通过 setInteractors 组合我们所需要的交互器。DefaultInteractor 实现 Group、Edge 和 SubGraph 图元的默认双击响应,手抓图平移,滚轮缩放,键盘响应等功能;TouchInteractor 实现移动设备上的 Touch 交互功能。至于最后面的 CreateEdgeInteractor 则是继承于 ht.graph.Interactor 交互器的创建连线的交互器。这里细细地分析一下这个部分,以后就可以修改或者自定义新的交互器。

自定义交互器

 我们通过 ht.Default.def(className, superClass, methods) 定义类,并在 methods 对象中对方法和变量进行声明。

setUp 方法在对象被创建的时候被调用,根据需求在这里设置一些功能,我设置的是清除所有的选中的节点:

setUp: function () {// CreateEdgeInteractor 对象被创建的时候调用的函数
    CreateEdgeInteractor.superClass.setUp.call(this);this._graphView.sm().cs();// 清除所有选中
}

 tearDown 方法在对象结束调用的时候被调用,绘制连线的时候,如果未结束绘制怎么办?下一次绘制不可能连着上一次继续绘制,所以我们得在结束调用这个类的时候将之前的绘制的点都清除:

コードをコピー
tearDown: function () {// CreateEdgeInteractor 对象结束调用的时候调用的函数
    CreateEdgeInteractor.superClass.tearDown.call(this);

    // 清除连线起点、终点以及连线中间的各个点  
    this._source = null;
    this._target = null;
    this._logicalPoint = null;
}
コードをコピー

关于鼠标事件以及 touch 事件,我希望这两者在操作上相同,所以直接在鼠标事件中调用的 touch 事件的方法。

绘制连线需要鼠标左键先选中一个节点,然后拖动鼠标左键不放,移动鼠标到连线的终点节点上,此时一条连线创建完毕。

首先是 touchstart 选中一个节点:

コードをコピー
handle_mousedown: function (e) {// 鼠标点下事件
    this.handle_touchstart(e);
},
handle_touchstart: function (e) {// 开始 touch
    this._sourceNode = this.getNodeAt(e);// 获取事件下的节点
    if (this._sourceNode) {
        this._targetNode = null;// 初始化 targetNode
        this.startDragging(e);
        this._graphView.addTopPainter(this);// 增加顶层Painter 使用Canvas的画笔对象自由绘制任意形状,顶层Painter绘制在拓扑最上面
        this._graphView.sm().ss(this._sourceNode);// 设置选中
    }
},
getNodeAt: function(e){// 获取事件下的节点
    if (ht.Default.isLeftButton(e) && ht.Default.getTouchCount(e) === 1) {// 鼠标左键被按下 && 当前Touch手指个数为1
        var data = this._graphView.getDataAt(e);// 获取事件下的节点

        if(data instanceof ht.Node) return data;// 为 ht.Node 类型的节点
    } 
    return null;
}
コードをコピー

 

然后手指滑动 touchmove :

コードをコピー
handleWindowMouseMove: function (e) {
    this.handleWindowTouchMove(e);
},
handleWindowTouchMove: function (e) {// 手指滑动
    var graphView = this._graphView;// 拓扑组件
    this.redraw();// 如果不重新绘制矩形区域,那么容易造成脏矩形
    this._logicalPoint = graphView.getLogicalPoint(e);// 获取事件下的逻辑坐标
    this._targetNode = this.getNodeAt(e);// 获取事件下的 edge 的终点

    if (this._targetNode) graphView.sm().ss([this._sourceNode, this._targetNode]);// 设置起始和终止节点都被选中
    else graphView.sm().ss([this._sourceNode]);// 只选中起始节点
},
redraw: function () {
    var p1 = this._sourceNode.getPosition(),// 获取连线起始端的节点的坐标
        p2 = this._logicalPoint;

    if (p1 && p2) {
        var rect = ht.Default.unionPoint(p1, p2);// 将点组合成矩形
        ht.Default.grow(rect, 1);// 改变rect大小,上下左右分别扩展 extend 的大小
        this._graphView.redraw(rect);// 重绘拓扑,rect参数为空时重绘拓扑中的所有图元,否则重绘矩形范围内的图元
    }
}
コードをコピー

最后 touchend 创建连线:

コードをコピー
handleWindowMouseUp: function (e) {
    this.handleWindowTouchEnd(e);
},      
handleWindowTouchEnd: function (e) { 
    if (this._targetNode) {
        var edge = new ht.Edge(this._sourceNode, this._targetNode);// 创建新的连线节点
        if (this._edgeType) edge.s('edge.type', this._edgeType);// 设置连线的类型

        this._graphView.dm().add(edge);// 将节点添加进数据容器
        this._graphView.sm().ss(edge);// 设置选中您当前连线
    }
    editableFunc();// 绘制结束后 工具条选中“编辑”项
    this._graphView.removeTopPainter(this);// 移除顶层画笔
}
コードをコピー

至于还未创建连线之前(也就是说为选中终止节点),鼠标在拖动的过程中会创建一条连线,这里是直接用 canvas 绘制的:

コードをコピー
draw: function (g) {// 绘制起点与鼠标移动位置的连线
    var p1 = this._sourceNode.getPosition(),
        p2 = this._logicalPoint;    

    if(p1 && p2){
        g.lineWidth = 1;
        g.strokeStyle = '#1ABC9C';
        g.beginPath();
        g.moveTo(p1.x, p1.y);
        g.lineTo(p2.x, p2.y);
        g.stroke();              
    }
}    
コードをコピー

这样,自定义连线类结束!

面板组件

左侧面板组件 ht.widget.Palette 支持自定义样式及单选、拖拽操作,由 ht.DataModel 驱动,用 ht.Group 展示分组,ht.Node 展示按钮元素。

展示分组,首先得创建分组和组中的按钮元素:

コードをコピー
function initPalette(palette) {// 加载palette面板组件中的图元
    var nodeArray = ['city', 'equipment'];
    var nameArray = ['城市', '大型'];// arrNode中的index与nameArr中的一一对应

    for (var i = 0; i < nodeArray.length; i++) {
        var name = nameArray[i];

        nodeArray[i] = new ht.Group();// palette面板是将图元都分在“组”里面,然后向“组”中添加图元即可
        palette.dm().add(nodeArray[i]);// 向palette面板组件中添加group图元
        nodeArray[i].setExpanded(true);// 设置分组为打开的状态
        nodeArray[i].setName(name);// 设置组的名字

        var imageArray = [];
        switch(i){
            case 0:
                imageArray = ['symbols/5.json', 'symbols/6.json', 'symbols/叉车.json', 'symbols/公交车.json', 'symbols/人1.json', 'symbols/人2.json', 'symbols/人3.json', 'symbols/树.json', 'symbols/树2.json'];
                break;
            case 1: 
                imageArray = ['symbols/飞机.json', 'symbols/吊机.json', 'symbols/卡车.json', 'symbols/货轮.json', 'symbols/龙门吊.json', 'symbols/公园.json'];
                break;
            default: 
                break;
        }
        setPaletteNode(imageArray, nodeArray[i], palette);
    }
}

function setPaletteNode(imageArray, array, palette) {// 创建 palette 上 节点及设置名称、显示图片、父子关系
    for (var i = 0; i < imageArray.length; i++) {
        var imageName = imageArray[i],
            name = imageName.slice(imageName.lastIndexOf('/')+1, imageName.lastIndexOf('.'));// 获取最后一个 / 和最后一个.中间的文本,作为节点的 name
        
        createNode(imageName, name, array, palette);// 创建节点,显示在 palette 面板上
    }
}

function createNode(image, name, parent, palette) {// 创建palette面板组件上的节点
    var node = new ht.Node();
    palette.dm().add(node);// 将节点添加进 palette 的数据容器中
    node.setImage(image);// 设置节点的图片
    node.setName(name);// 设置节点名称
    node.setParent(parent);// 设置节点的父亲
    node.s({// 设置节点的属性
        'draggable': true,// 如果Node的draggable设为true,Palette可以自动处理dragstart,但是dragover和drop事件需要我们处理
        'image.stretch': 'centerUniform',// 图片的绘制方式为非失真方式
    });
    return node;
}
コードをコピー

创建完后,我们就要启用模拟的拖拽事件 handleDragAndDrop(e, state): 

コードをコピー
palette = new ht.widget.Palette();// 创建一个组件面板

var data;
palette.handleDragAndDrop = function(e, state) {// 左侧面板组件拖拽功能
    if ( state === 'prepare' ) data = palette.getDataAt(e);
    else if( state === 'begin' || state === 'between' ) {}
    else {
        if (!ht.Default.containedInView(e, graphView)) return; // 判断交互事件所处位置是否在graphView组件之上

        var node = new ht.Node();// 拖拽到graphView中就创建一个新的节点显示在graphView上
        node.setImage(data.getImage());// 设置节点上贴图
        node.setName(data.getName());// 设置名称(为了显示在属性栏中)
        node.s('label', '');// 在graphView中节点下方不会出现setName中的值,label优先级高于name
        node.p(graphView.lp(e));// 将节点的位置设置为graphView事件下的拓扑图中的逻辑坐标,即设置鼠标点下的位置为节点坐标

        graphView.dm()(ノード)を追加; // でgraphViewにノードを追加
        graphView.sm()SS(ノード); // デフォルトのノードが選択され
        graphView.setFocus(ノード); //フォーカス集合を設定しますノード
        
        editableFunc(); //選択するために編集可能に設定され、ノードナビゲーションツリー「編集」
    } 
}
コードをコピー

さて、まず、あなたはドラッグすることができ、右側にgraphViewノードトポロジマップに直接左サイドパネルのコンポーネントにパレットから直接ドロップします。

私たちは、graphView上のノードを描画する線を描く、直角接続を描画し、ポリゴンを描画するために編集することができます。

遂に

「地下鉄マップ」を追加すると、ナビゲーションバーの上にある間に、私は、機能性を高めるためにマップを切り替えしようとした上記基底GIS通信リソース管理システムに基づいて、それを達成するために地下鉄のルートマップは非常に強力で、次回I'LL私が追加した後、最終的な結果を見るために、ここで説明するより多くをしない、地下鉄マップのためにもう一度詳細:



http://www.hightopo.com/demo/openlayers/

あなたが何か提案やコメントがあれば、私にメッセージやプライベートの手紙を残してください、あなたは、Web(直接のためにHTに行くことができますhttps://hightopo.com/)関連情報への公式ウェブサイトへのアクセス。

おすすめ

転載: www.cnblogs.com/htdaydayup/p/11666654.html