04 【ArcGIS JS API + eCharts系列】實現二、三維網絡路徑圖的繪製

概述

前面兩篇文章通過擴展EchartsLayer.js這個圖層類,實現了使用ArcGIS JS API和eCharts,在二維和三維場景下繪製遷徙圖和散點圖。這篇文章繼續通過繪製網絡路徑圖的例子,再來驗證下我們擴展的這個圖層類是否可用,先來看下最終效果:

 

 

實現思路

遷徙圖、散點圖和網絡路徑圖這種圖表跟地理座標關係緊密,所以僅僅通過第一篇二維普通圖表繪製的方式是無法實現這類圖表繪製的,所以就需要我們來擴展eCharts的相關功能,使其能夠夠結合最新版的ArcGIS JS API來完成地圖上這類圖表的繪製,eCharts官網也提供了相應的擴展插件,但這種插件並不能很好地支持我們ArcGIS JS API的高版本,所以我們在這篇文章裏直接擴展了一個圖層類,下面是具體的實現思路:

實現ArcGIS JS API和eCharts的結合,最最關鍵的是要實現兩個插件庫裏的座標系轉換,這是重點,只要搞清楚了這一點,我們完全可以脫離地圖API庫的束縛,理論上可以實現eCharts跟任意地圖庫的結合。在此處轉換座標時我們使用了eCharts提供的registerCoordinateSystem方法,通過這個方法我們註冊了一個名爲"arcgis"的座標系,裏面對eCharts中的dataToPoint、pointToData等方法進行了重寫,然後將這些所有內容封裝爲了一個EchartsLayer圖層類。至於這個文件的源碼,文章結尾會提供,接下來我們看一下具體的實現步驟。

 

實現步驟

1、本文所用的demo同樣是基於React框架搭建的,所以我們首先基於React框架搭建一個初始化項目,然後改寫src目錄下的App.js這個主文件,實例化出一張二維地圖,這中間用到了esri-loader插件,具體的實現過程可查看我的這篇文章【【番外】 React中使用ArcGIS JS API 4.14開發】,裏面有具體的實現步驟。

2、通過上述操作實例化完一張二維地圖後,我們接下來就要進行網絡路徑圖的繪製操作了,在開始之前我們需要數據,就是關於網絡路徑的座標數據和相應權重值的數據,我將它存在了一份JS文件裏,此文件比較大,所以並不打算在此處粘貼出來,有需要的可以到此地址下載(https://download.csdn.net/download/qq_35117024/12279946)。

3、下載到數據後,將數據拷貝至demo代碼目錄下,然後在組件代碼中引入,此處我們是將它引入到我們新建的TrafficNetwork組件目錄下,然後編寫數據處理的函數,對下載下來的數據進行初始化處理,代碼如下:

    //初始化數據
    _initData=() => {
        const _self = this;
        let data = trafficData;
        let hStep = 300 / (data.length - 1);
        _self.state.busLines = [].concat.apply([], data.map(function (busLine, idx) {
            let prevPt;
            let points = [];
            for (let i = 0; i < busLine.length; i += 2) {
                let pt = [busLine[i], busLine[i + 1]];
                if (i > 0) {
                    pt = [
                        prevPt[0] + pt[0],
                        prevPt[1] + pt[1]
                    ];
                }
                prevPt = pt;

                points.push([pt[0] / 1e4, pt[1] / 1e4]);
            }
            return {
                coords: points,
                lineStyle: {
                    normal: {
                        color: echarts.color.modifyHSL('#5A94DF', Math.round(hStep * idx))
                    }
                }
            }
        }))
    }

4、數據初始完成後,我們進行網絡路徑圖表的繪製信息配置,也就是初始化series屬性,代碼如下:

    //初始化圖表配置
    _initCharts=() => {
        const _self = this;
        _self.state.series = [{
            type: 'lines',
            coordinateSystem: 'arcgis',
            polyline: true,
            data: _self.state.busLines,
            silent: true,
            lineStyle: {
                // color: '#c23531',
                // color: 'rgb(200, 35, 45)',
                opacity: 0.2,
                width: 1
            },
            progressiveThreshold: 500,
            progressive: 200
        }, {
            type: 'lines',
            coordinateSystem: 'arcgis',
            polyline: true,
            data: _self.state.busLines,
            lineStyle: {
                width: 0
            },
            effect: {
                constantSpeed: 20,
                show: true,
                trailLength: 0.1,
                symbolSize: 1.5
            },
            zlevel: 1
        }]
    }

5、接下來和之前文章的流程一樣,監聽地圖的繪製完成事件,然後調用圖表繪製函數來繪製網絡路徑圖,代碼如下:

view.when(function() {
	_self.state.mapview = view;
	_self._drawCharts();
});
    //繪製圖表
    _drawCharts=() => {
        const _self = this;
        const options = {
            url: 'https://js.arcgis.com/4.14/dojo/dojo.js',
        };

        loadModules([
            'http://localhost/test/EchartsLayer.min.js'
        ], options).then(([
            echartsLayer
        ]) => {
            console.log(_self.state.mapview)
            //_self.state.mapview.when(function(){
                let chart = new echartsLayer(_self.state.mapview);
                let option = {
                    title: {
                        text: 'ArcGIS API for Javascript4.14擴展Echarts4之網絡路徑圖',
                        subtext: 'Develop By X北辰北',
                        left: 'center',
                        textStyle: {
                            color: '#fff'
                        }
                    },
                    series: _self.state.series
                };
                chart.setChartOption(option);
            //});
        }
        ).catch((err) => {
            console.log('圖表繪製失敗,' + err);
        });
    }

6、通過以上步驟就完成了網絡路徑的二維繪製,三維繪製的話很簡單,只需要將MapView替換爲SceneView即可,代碼如下:

let view = new SceneView({
	container: "mapview", 
	map: map, 
	scale: 50000000, 
	center: [107.246152,34.414465] 
});

 

總結

我們通過三篇文章,介紹了關於在eCharts中有關地理座標的圖表如何在ArcGIS的底圖上繪製的過程,這篇文章中的實現流程跟之前兩篇一樣,都是通過擴展EchartsLayer.js這個圖層類來實現的,所以後續大家有需要將eCharts中有關地理座標的圖表繪製到ArcGIS地圖上時,可用此擴展類來實現。關於將eCharts中普通的二維圖表繪製到ArcGIS底圖上的操作,因爲不涉及地理座標的問題,所以我們通過純前端的方式即可解決,具體實現請看這篇文章【01 【ArcGIS JS API + eCharts系列】實現地圖上二維圖表的繪製】。

 

附:

TrafficNetwork組件代碼:

import React,{Component} from 'react';
import {loadModules} from 'esri-loader';
import echarts from 'echarts';
import './trafficNetwork.css';

import trafficData from './data/trafficData';

class TrafficNetwork extends Component {

    state = {
        busLines: null,
        series: null,
    }

    componentDidMount=() => {
        this._initMapView();
        this._initData();
        this._initCharts();
    }

    //實例化地圖
    _initMapView=() => {
        const _self = this;
        const options = {
            url: 'https://js.arcgis.com/4.14/',
            css: 'https://js.arcgis.com/4.14/esri/themes/light/main.css'
        };

        loadModules(['esri/Map',
            'esri/Basemap',
            'esri/layers/TileLayer',
            'esri/views/MapView',
            'esri/views/SceneView',
        ], options).then(([
            Map, 
            Basemap,
            TileLayer,
            MapView,
            SceneView,
        ]) => {
                    let basemap = new Basemap({
                        baseLayers: [
                            new TileLayer({
                                url: "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer",
                                title: "Basemap"
                            })
                        ],
                        title: "basemap",
                        id: "basemap"
                    });
                    let map = new Map({
                        basemap: basemap
                    });
                    let view = new MapView({
                        container: "mapview", 
                        map: map, 
                        zoom: 10, 
                        center: [116.403406,39.91582]         //北京 116.403406,39.91582  成都 107.246152,34.414465
                    });
                    // let view = new SceneView({
                    //     container: "mapview", 
                    //     map: map, 
                    //     scale: 50000000, 
                    //     center: [107.246152,34.414465] 
                    // });
                    
                    view.when(function() {
                        _self.state.mapview = view;
                        _self._drawCharts();
                    });
            }
        ).catch((err) => {
            console.log('底圖創建失敗,' + err);
        });
    }

    //初始化數據
    _initData=() => {
        const _self = this;
        let data = trafficData;
        let hStep = 300 / (data.length - 1);
        _self.state.busLines = [].concat.apply([], data.map(function (busLine, idx) {
            let prevPt;
            let points = [];
            for (let i = 0; i < busLine.length; i += 2) {
                let pt = [busLine[i], busLine[i + 1]];
                if (i > 0) {
                    pt = [
                        prevPt[0] + pt[0],
                        prevPt[1] + pt[1]
                    ];
                }
                prevPt = pt;

                points.push([pt[0] / 1e4, pt[1] / 1e4]);
            }
            return {
                coords: points,
                lineStyle: {
                    normal: {
                        color: echarts.color.modifyHSL('#5A94DF', Math.round(hStep * idx))
                    }
                }
            }
        }))
    }

    //初始化圖表配置
    _initCharts=() => {
        const _self = this;
        _self.state.series = [{
            type: 'lines',
            coordinateSystem: 'arcgis',
            polyline: true,
            data: _self.state.busLines,
            silent: true,
            lineStyle: {
                // color: '#c23531',
                // color: 'rgb(200, 35, 45)',
                opacity: 0.2,
                width: 1
            },
            progressiveThreshold: 500,
            progressive: 200
        }, {
            type: 'lines',
            coordinateSystem: 'arcgis',
            polyline: true,
            data: _self.state.busLines,
            lineStyle: {
                width: 0
            },
            effect: {
                constantSpeed: 20,
                show: true,
                trailLength: 0.1,
                symbolSize: 1.5
            },
            zlevel: 1
        }]
    }

    //繪製圖表
    _drawCharts=() => {
        const _self = this;
        const options = {
            url: 'https://js.arcgis.com/4.14/dojo/dojo.js',
        };

        loadModules([
            'http://localhost/test/EchartsLayer.min.js'
        ], options).then(([
            echartsLayer
        ]) => {
            console.log(_self.state.mapview)
            //_self.state.mapview.when(function(){
                let chart = new echartsLayer(_self.state.mapview);
                let option = {
                    title: {
                        text: 'ArcGIS API for Javascript4.14擴展Echarts4之網絡路徑圖',
                        subtext: 'Develop By X北辰北',
                        left: 'center',
                        textStyle: {
                            color: '#fff'
                        }
                    },
                    series: _self.state.series
                };
                chart.setChartOption(option);
            //});
        }
        ).catch((err) => {
            console.log('圖表繪製失敗,' + err);
        });
    }

    render() {
        return (
            <div id="mapview"></div>
        )
    }
}

export default TrafficNetwork;

EchartsLayer.min.js文件代碼:

var _0x4564=['prototype','setMapOffset','dataToPoint','point','toScreen','pointToData','toMap','getViewRect','BoundingRect','getRoamTransform','dojo/_base/declare','dojo/_base/lang','esri/geometry/Point','esri/geometry/SpatialReference','EchartsglLayer','registerCoordinateSystem','arcgis','getE3CoordinateSystem','init','setBaseMap','createLayer','view','chartOption','setCharts','box','visible','hidden','chart','off','undefined','extent','xAxis','xmin','xmax','yAxis','ymin','ymax','setOption','animation','createElement','div','setAttribute','echartsData','name','style','width','height','position','absolute','top','left','getElementsByClassName','esri-view-surface','appendChild','startMapEventListeners','outerHTML','originLyr','features','screenData','map_DragStart_Listener','remove','map_DragEnd_Listener','map_ZoomStart_Listener','map_ZoomEnd_Listener','map_ExtentChange_Listener','watch','hitch','resize','rotation','map','_mapOffset','create','eachSeries','get','coordinateSystem','getDimensionsInfo','dimensions'];(function(_0x4ea369,_0x173297){var _0x432a1a=function(_0x3b4d7a){while(--_0x3b4d7a){_0x4ea369['push'](_0x4ea369['shift']());}};_0x432a1a(++_0x173297);}(_0x4564,0xf1));var _0x1824=function(_0x20e690,_0x5f0396){_0x20e690=_0x20e690-0x0;var _0x841fe2=_0x4564[_0x20e690];return _0x841fe2;};define([_0x1824('0x0'),_0x1824('0x1'),_0x1824('0x2'),_0x1824('0x3')],function(_0x4156fb,_0x59c3eb,_0x275378,_0x4d54b1){return _0x4156fb(_0x1824('0x4'),null,{'name':_0x1824('0x4'),'view':null,'box':null,'chart':null,'chartOption':null,'visible':!![],'constructor':function(_0x27b7d3,_0x649a95){echarts[_0x1824('0x5')](_0x1824('0x6'),this[_0x1824('0x7')](_0x27b7d3));this[_0x1824('0x8')](_0x27b7d3,_0x649a95);},'init':function(_0x3a80a9,_0x5617d3){this[_0x1824('0x9')](_0x3a80a9);this[_0x1824('0xa')]();},'setBaseMap':function(_0x3ddf37){this[_0x1824('0xb')]=_0x3ddf37;},'setChartOption':function(_0x497153){this[_0x1824('0xc')]=_0x497153;this[_0x1824('0xd')]();},'setVisible':function(_0x36aa18){if(!this[_0x1824('0xe')]||this[_0x1824('0xf')]===_0x36aa18)return;this[_0x1824('0xe')][_0x1824('0x10')]=!_0x36aa18;this[_0x1824('0xf')]=_0x36aa18;_0x36aa18===!![]&&setCharts();},'refreshBegin':function(){this[_0x1824('0xe')][_0x1824('0x10')]=!![];},'refreshing':function(){setCharts();},'refreshEnd':function(){this[_0x1824('0xe')][_0x1824('0x10')]=![];},'on':function(_0x5dd691,_0x472109,_0x4b90b9){this[_0x1824('0x11')]['on'](_0x5dd691,_0x472109,_0x4b90b9);},'off':function(_0x25e82f,_0x44fdf2,_0x3cd39d){this[_0x1824('0x11')][_0x1824('0x12')](_0x25e82f,_0x44fdf2,_0x3cd39d);},'map_DragStart_Listener':null,'map_DragEnd_Listener':null,'map_ZoomStart_Listener':null,'map_ZoomEnd_Listener':null,'map_ExtentChange_Listener':null,'map_click_Listener':null,'setCharts':function(){if(!this[_0x1824('0xf')])return;if(this[_0x1824('0xc')]==null||this[_0x1824('0xc')]==_0x1824('0x13'))return;let _0x50f53f=this[_0x1824('0xb')][_0x1824('0x14')];this[_0x1824('0xc')][_0x1824('0x15')]={'show':![],'min':_0x50f53f[_0x1824('0x16')],'max':_0x50f53f[_0x1824('0x17')]};this[_0x1824('0xc')][_0x1824('0x18')]={'show':![],'min':_0x50f53f[_0x1824('0x19')],'max':_0x50f53f[_0x1824('0x1a')]};this[_0x1824('0x11')][_0x1824('0x1b')](this[_0x1824('0xc')]);this[_0x1824('0xc')][_0x1824('0x1c')]=![];},'createLayer':function(){let _0x56973d=this[_0x1824('0xe')]=document[_0x1824('0x1d')](_0x1824('0x1e'));_0x56973d[_0x1824('0x1f')]('id',_0x1824('0x20'));_0x56973d[_0x1824('0x1f')](_0x1824('0x21'),_0x1824('0x20'));_0x56973d[_0x1824('0x22')][_0x1824('0x23')]=this[_0x1824('0xb')][_0x1824('0x23')]+'px';_0x56973d[_0x1824('0x22')][_0x1824('0x24')]=this[_0x1824('0xb')][_0x1824('0x24')]+'px';_0x56973d[_0x1824('0x22')][_0x1824('0x25')]=_0x1824('0x26');_0x56973d[_0x1824('0x22')][_0x1824('0x27')]=0x0;_0x56973d[_0x1824('0x22')][_0x1824('0x28')]=0x0;let _0x22f992=document[_0x1824('0x29')](_0x1824('0x2a'))[0x0];_0x22f992[_0x1824('0x2b')](_0x56973d);this[_0x1824('0x11')]=echarts[_0x1824('0x8')](_0x56973d);this[_0x1824('0x2c')]();},'removeLayer':function(){this[_0x1824('0xe')][_0x1824('0x2d')]='';this[_0x1824('0xb')]=null;this[_0x1824('0xe')]=null;this[_0x1824('0x2e')]=null;this[_0x1824('0x2f')]=null;this[_0x1824('0x30')]=[];this[_0x1824('0x11')]=null;this[_0x1824('0xc')]=null;this[_0x1824('0x31')][_0x1824('0x32')]();this[_0x1824('0x33')][_0x1824('0x32')]();this[_0x1824('0x34')][_0x1824('0x32')]();this[_0x1824('0x35')][_0x1824('0x32')]();this[_0x1824('0x36')][_0x1824('0x32')]();},'startMapEventListeners':function(){let _0x576d14=this[_0x1824('0xb')];_0x576d14[_0x1824('0x37')](_0x1824('0x14'),_0x59c3eb[_0x1824('0x38')](this,function(){if(!this[_0x1824('0xf')])return;this[_0x1824('0xd')]();this[_0x1824('0x11')][_0x1824('0x39')]();this[_0x1824('0xe')][_0x1824('0x10')]=![];}));_0x576d14[_0x1824('0x37')](_0x1824('0x3a'),_0x59c3eb[_0x1824('0x38')](this,function(){if(!this[_0x1824('0xf')])return;this[_0x1824('0xd')]();this[_0x1824('0x11')][_0x1824('0x39')]();this[_0x1824('0xe')][_0x1824('0x10')]=![];}));},'getE3CoordinateSystem':function(_0x56f41a){var _0x4504c9=function _0x4504c9(_0x180267){this[_0x1824('0x3b')]=_0x180267;this[_0x1824('0x3c')]=[0x0,0x0];};_0x4504c9[_0x1824('0x3d')]=function(_0x1a4547){_0x1a4547[_0x1824('0x3e')](function(_0x17e9bb){if(_0x17e9bb[_0x1824('0x3f')](_0x1824('0x40'))===_0x1824('0x6')){_0x17e9bb[_0x1824('0x40')]=new _0x4504c9(_0x56f41a);}});};_0x4504c9[_0x1824('0x41')]=function(){return['x','y'];};_0x4504c9[_0x1824('0x42')]=['x','y'];_0x4504c9[_0x1824('0x43')][_0x1824('0x42')]=['x','y'];_0x4504c9[_0x1824('0x43')][_0x1824('0x44')]=function setMapOffset(_0xeffdb8){this[_0x1824('0x3c')]=_0xeffdb8;};_0x4504c9[_0x1824('0x43')][_0x1824('0x45')]=function dataToPoint(_0x209327){var _0x2755d4={'type':_0x1824('0x46'),'x':_0x209327[0x0],'y':_0x209327[0x1],'spatialReference':new _0x4d54b1(0x10e6)};var _0x3676a6=_0x56f41a[_0x1824('0x47')](_0x2755d4);var _0x52b765=this[_0x1824('0x3c')];return[_0x3676a6['x']-_0x52b765[0x0],_0x3676a6['y']-_0x52b765[0x1]];};_0x4504c9[_0x1824('0x43')][_0x1824('0x48')]=function pointToData(_0x5d9368){var _0x4282c5=this[_0x1824('0x3c')];var _0x3a367d={'x':_0x5d9368[0x0]+_0x4282c5[0x0],'y':_0x5d9368[0x1]+_0x4282c5[0x1]};var _0x3a9399=_0x56f41a[_0x1824('0x49')](_0x3a367d);return[_0x3a9399['x'],_0x3a9399['y']];};_0x4504c9[_0x1824('0x43')][_0x1824('0x4a')]=function getViewRect(){return new graphic[(_0x1824('0x4b'))](0x0,0x0,this[_0x1824('0x3b')][_0x1824('0x23')],this[_0x1824('0x3b')][_0x1824('0x24')]);};_0x4504c9[_0x1824('0x43')][_0x1824('0x4c')]=function getRoamTransform(){return matrix[_0x1824('0x3d')]();};return _0x4504c9;}});});

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章