0%

【ArcGIS JS API + eCharts系列】实现二、三维散点图的绘制

本文主要介绍使用ArcGIS JS API 4.14和eCharts 4.7.0来实现在地图上绘制散点图的实现步骤,包括二维和三维。

概述

上一篇文章中我们介绍了如何使用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文件,源文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
let geoCoordMap = {
   '海门':[121.15,31.89],
   '鄂尔多斯':[109.781327,39.608266],
   '招远':[120.38,37.35],
   '舟山':[122.207216,29.985295],
   '齐齐哈尔':[123.97,47.33],
   '盐城':[120.13,33.38],
   '赤峰':[118.87,42.28],
   '青岛':[120.33,36.07],
   '乳山':[121.52,36.89],
   '金昌':[102.188043,38.520089],
   '泉州':[118.58,24.93],
   '莱西':[120.53,36.86],
   '日照':[119.46,35.42],
   '胶南':[119.97,35.88],
   '南通':[121.05,32.08],
   '拉萨':[91.11,29.97],
   '云浮':[112.02,22.93],
   '梅州':[116.1,24.55],
   '文登':[122.05,37.2],
   '上海':[121.48,31.22],
   '攀枝花':[101.718637,26.582347],
   '威海':[122.1,37.5],
   '承德':[117.93,40.97],
   '厦门':[118.1,24.46],
   '汕尾':[115.375279,22.786211],
   '潮州':[116.63,23.68],
   '丹东':[124.37,40.13],
   '太仓':[121.1,31.45],
   '曲靖':[103.79,25.51],
   '烟台':[121.39,37.52],
   '福州':[119.3,26.08],
   '瓦房店':[121.979603,39.627114],
   '即墨':[120.45,36.38],
   '抚顺':[123.97,41.97],
   '玉溪':[102.52,24.35],
   '张家口':[114.87,40.82],
   '阳泉':[113.57,37.85],
   '莱州':[119.942327,37.177017],
   '湖州':[120.1,30.86],
   '汕头':[116.69,23.39],
   '昆山':[120.95,31.39],
   '宁波':[121.56,29.86],
   '湛江':[110.359377,21.270708],
   '揭阳':[116.35,23.55],
   '荣成':[122.41,37.16],
   '连云港':[119.16,34.59],
   '葫芦岛':[120.836932,40.711052],
   '常熟':[120.74,31.64],
   '东莞':[113.75,23.04],
   '河源':[114.68,23.73],
   '淮安':[119.15,33.5],
   '泰州':[119.9,32.49],
   '南宁':[108.33,22.84],
   '营口':[122.18,40.65],
   '惠州':[114.4,23.09],
   '江阴':[120.26,31.91],
   '蓬莱':[120.75,37.8],
   '韶关':[113.62,24.84],
   '嘉峪关':[98.289152,39.77313],
   '广州':[113.23,23.16],
   '延安':[109.47,36.6],
   '太原':[112.53,37.87],
   '清远':[113.01,23.7],
   '中山':[113.38,22.52],
   '昆明':[102.73,25.04],
   '寿光':[118.73,36.86],
   '盘锦':[122.070714,41.119997],
   '长治':[113.08,36.18],
   '深圳':[114.07,22.62],
   '珠海':[113.52,22.3],
   '宿迁':[118.3,33.96],
   '咸阳':[108.72,34.36],
   '铜川':[109.11,35.09],
   '平度':[119.97,36.77],
   '佛山':[113.11,23.05],
   '海口':[110.35,20.02],
   '江门':[113.06,22.61],
   '章丘':[117.53,36.72],
   '肇庆':[112.44,23.05],
   '大连':[121.62,38.92],
   '临汾':[111.5,36.08],
   '吴江':[120.63,31.16],
   '石嘴山':[106.39,39.04],
   '沈阳':[123.38,41.8],
   '苏州':[120.62,31.32],
   '茂名':[110.88,21.68],
   '嘉兴':[120.76,30.77],
   '长春':[125.35,43.88],
   '胶州':[120.03336,36.264622],
   '银川':[106.27,38.47],
   '张家港':[120.555821,31.875428],
   '三门峡':[111.19,34.76],
   '锦州':[121.15,41.13],
   '南昌':[115.89,28.68],
   '柳州':[109.4,24.33],
   '三亚':[109.511909,18.252847],
   '自贡':[104.778442,29.33903],
   '吉林':[126.57,43.87],
   '阳江':[111.95,21.85],
   '泸州':[105.39,28.91],
   '西宁':[101.74,36.56],
   '宜宾':[104.56,29.77],
   '呼和浩特':[111.65,40.82],
   '成都':[104.06,30.67],
   '大同':[113.3,40.12],
   '镇江':[119.44,32.2],
   '桂林':[110.28,25.29],
   '张家界':[110.479191,29.117096],
   '宜兴':[119.82,31.36],
   '北海':[109.12,21.49],
   '西安':[108.95,34.27],
   '金坛':[119.56,31.74],
   '东营':[118.49,37.46],
   '牡丹江':[129.58,44.6],
   '遵义':[106.9,27.7],
   '绍兴':[120.58,30.01],
   '扬州':[119.42,32.39],
   '常州':[119.95,31.79],
   '潍坊':[119.1,36.62],
   '重庆':[106.54,29.59],
   '台州':[121.420757,28.656386],
   '南京':[118.78,32.04],
   '滨州':[118.03,37.36],
   '贵阳':[106.71,26.57],
   '无锡':[120.29,31.59],
   '本溪':[123.73,41.3],
   '克拉玛依':[84.77,45.59],
   '渭南':[109.5,34.52],
   '马鞍山':[118.48,31.56],
   '宝鸡':[107.15,34.38],
   '焦作':[113.21,35.24],
   '句容':[119.16,31.95],
   '北京':[116.46,39.92],
   '徐州':[117.2,34.26],
   '衡水':[115.72,37.72],
   '包头':[110,40.58],
   '绵阳':[104.73,31.48],
   '乌鲁木齐':[87.68,43.77],
   '枣庄':[117.57,34.86],
   '杭州':[120.19,30.26],
   '淄博':[118.05,36.78],
   '鞍山':[122.85,41.12],
   '溧阳':[119.48,31.43],
   '库尔勒':[86.06,41.68],
   '安阳':[114.35,36.1],
   '开封':[114.35,34.79],
   '济南':[117,36.65],
   '德阳':[104.37,31.13],
   '温州':[120.65,28.01],
   '九江':[115.97,29.71],
   '邯郸':[114.47,36.6],
   '临安':[119.72,30.23],
   '兰州':[103.73,36.03],
   '沧州':[116.83,38.33],
   '临沂':[118.35,35.05],
   '南充':[106.110698,30.837793],
   '天津':[117.2,39.13],
   '富阳':[119.95,30.07],
   '泰安':[117.13,36.18],
   '诸暨':[120.23,29.71],
   '郑州':[113.65,34.76],
   '哈尔滨':[126.63,45.75],
   '聊城':[115.97,36.45],
   '芜湖':[118.38,31.33],
   '唐山':[118.02,39.63],
   '平顶山':[113.29,33.75],
   '邢台':[114.48,37.05],
   '德州':[116.29,37.45],
   '济宁':[116.59,35.38],
   '荆州':[112.239741,30.335165],
   '宜昌':[111.3,30.7],
   '义乌':[120.06,29.32],
   '丽水':[119.92,28.45],
   '洛阳':[112.44,34.7],
   '秦皇岛':[119.57,39.95],
   '株洲':[113.16,27.83],
   '石家庄':[114.48,38.03],
   '莱芜':[117.67,36.19],
   '常德':[111.69,29.05],
   '保定':[115.48,38.85],
   '湘潭':[112.91,27.87],
   '金华':[119.64,29.12],
   '岳阳':[113.09,29.37],
   '长沙':[113,28.21],
   '衢州':[118.88,28.97],
   '廊坊':[116.7,39.53],
   '菏泽':[115.480656,35.23375],
   '合肥':[117.27,31.86],
   '武汉':[114.31,30.52],
   '大庆':[125.03,46.58]
};
export default geoCoordMap;

除了上述的城市坐标之外,我们还需要一份跟城市坐标相对应的权重数据,用来表示某个指标在各个城市的值,同样的是一份单独的js文件,源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
let dataValue = [
    {name: '海门', value: 9},
    {name: '鄂尔多斯', value: 12},
    {name: '招远', value: 12},
    {name: '舟山', value: 12},
    {name: '齐齐哈尔', value: 14},
    {name: '盐城', value: 15},
    {name: '赤峰', value: 16},
    {name: '青岛', value: 18},
    {name: '乳山', value: 18},
    {name: '金昌', value: 19},
    {name: '泉州', value: 21},
    {name: '莱西', value: 21},
    {name: '日照', value: 21},
    {name: '胶南', value: 22},
    {name: '南通', value: 23},
    {name: '拉萨', value: 24},
    {name: '云浮', value: 24},
    {name: '梅州', value: 25},
    {name: '文登', value: 25},
    {name: '上海', value: 25},
    {name: '攀枝花', value: 25},
    {name: '威海', value: 25},
    {name: '承德', value: 25},
    {name: '厦门', value: 26},
    {name: '汕尾', value: 26},
    {name: '潮州', value: 26},
    {name: '丹东', value: 27},
    {name: '太仓', value: 27},
    {name: '曲靖', value: 27},
    {name: '烟台', value: 28},
    {name: '福州', value: 29},
    {name: '瓦房店', value: 30},
    {name: '即墨', value: 30},
    {name: '抚顺', value: 31},
    {name: '玉溪', value: 31},
    {name: '张家口', value: 31},
    {name: '阳泉', value: 31},
    {name: '莱州', value: 32},
    {name: '湖州', value: 32},
    {name: '汕头', value: 32},
    {name: '昆山', value: 33},
    {name: '宁波', value: 33},
    {name: '湛江', value: 33},
    {name: '揭阳', value: 34},
    {name: '荣成', value: 34},
    {name: '连云港', value: 35},
    {name: '葫芦岛', value: 35},
    {name: '常熟', value: 36},
    {name: '东莞', value: 36},
    {name: '河源', value: 36},
    {name: '淮安', value: 36},
    {name: '泰州', value: 36},
    {name: '南宁', value: 37},
    {name: '营口', value: 37},
    {name: '惠州', value: 37},
    {name: '江阴', value: 37},
    {name: '蓬莱', value: 37},
    {name: '韶关', value: 38},
    {name: '嘉峪关', value: 38},
    {name: '广州', value: 38},
    {name: '延安', value: 38},
    {name: '太原', value: 39},
    {name: '清远', value: 39},
    {name: '中山', value: 39},
    {name: '昆明', value: 39},
    {name: '寿光', value: 40},
    {name: '盘锦', value: 40},
    {name: '长治', value: 41},
    {name: '深圳', value: 41},
    {name: '珠海', value: 42},
    {name: '宿迁', value: 43},
    {name: '咸阳', value: 43},
    {name: '铜川', value: 44},
    {name: '平度', value: 44},
    {name: '佛山', value: 44},
    {name: '海口', value: 44},
    {name: '江门', value: 45},
    {name: '章丘', value: 45},
    {name: '肇庆', value: 46},
    {name: '大连', value: 47},
    {name: '临汾', value: 47},
    {name: '吴江', value: 47},
    {name: '石嘴山', value: 49},
    {name: '沈阳', value: 50},
    {name: '苏州', value: 50},
    {name: '茂名', value: 50},
    {name: '嘉兴', value: 51},
    {name: '长春', value: 51},
    {name: '胶州', value: 52},
    {name: '银川', value: 52},
    {name: '张家港', value: 52},
    {name: '三门峡', value: 53},
    {name: '锦州', value: 54},
    {name: '南昌', value: 54},
    {name: '柳州', value: 54},
    {name: '三亚', value: 54},
    {name: '自贡', value: 56},
    {name: '吉林', value: 56},
    {name: '阳江', value: 57},
    {name: '泸州', value: 57},
    {name: '西宁', value: 57},
    {name: '宜宾', value: 58},
    {name: '呼和浩特', value: 58},
    {name: '成都', value: 58},
    {name: '大同', value: 58},
    {name: '镇江', value: 59},
    {name: '桂林', value: 59},
    {name: '张家界', value: 59},
    {name: '宜兴', value: 59},
    {name: '北海', value: 60},
    {name: '西安', value: 61},
    {name: '金坛', value: 62},
    {name: '东营', value: 62},
    {name: '牡丹江', value: 63},
    {name: '遵义', value: 63},
    {name: '绍兴', value: 63},
    {name: '扬州', value: 64},
    {name: '常州', value: 64},
    {name: '潍坊', value: 65},
    {name: '重庆', value: 66},
    {name: '台州', value: 67},
    {name: '南京', value: 67},
    {name: '滨州', value: 70},
    {name: '贵阳', value: 71},
    {name: '无锡', value: 71},
    {name: '本溪', value: 71},
    {name: '克拉玛依', value: 72},
    {name: '渭南', value: 72},
    {name: '马鞍山', value: 72},
    {name: '宝鸡', value: 72},
    {name: '焦作', value: 75},
    {name: '句容', value: 75},
    {name: '北京', value: 79},
    {name: '徐州', value: 79},
    {name: '衡水', value: 80},
    {name: '包头', value: 80},
    {name: '绵阳', value: 80},
    {name: '乌鲁木齐', value: 84},
    {name: '枣庄', value: 84},
    {name: '杭州', value: 84},
    {name: '淄博', value: 85},
    {name: '鞍山', value: 86},
    {name: '溧阳', value: 86},
    {name: '库尔勒', value: 86},
    {name: '安阳', value: 90},
    {name: '开封', value: 90},
    {name: '济南', value: 92},
    {name: '德阳', value: 93},
    {name: '温州', value: 95},
    {name: '九江', value: 96},
    {name: '邯郸', value: 98},
    {name: '临安', value: 99},
    {name: '兰州', value: 99},
    {name: '沧州', value: 100},
    {name: '临沂', value: 103},
    {name: '南充', value: 104},
    {name: '天津', value: 105},
    {name: '富阳', value: 106},
    {name: '泰安', value: 112},
    {name: '诸暨', value: 112},
    {name: '郑州', value: 113},
    {name: '哈尔滨', value: 114},
    {name: '聊城', value: 116},
    {name: '芜湖', value: 117},
    {name: '唐山', value: 119},
    {name: '平顶山', value: 119},
    {name: '邢台', value: 119},
    {name: '德州', value: 120},
    {name: '济宁', value: 120},
    {name: '荆州', value: 127},
    {name: '宜昌', value: 130},
    {name: '义乌', value: 132},
    {name: '丽水', value: 133},
    {name: '洛阳', value: 134},
    {name: '秦皇岛', value: 136},
    {name: '株洲', value: 143},
    {name: '石家庄', value: 147},
    {name: '莱芜', value: 148},
    {name: '常德', value: 152},
    {name: '保定', value: 153},
    {name: '湘潭', value: 154},
    {name: '金华', value: 157},
    {name: '岳阳', value: 169},
    {name: '长沙', value: 175},
    {name: '衢州', value: 177},
    {name: '廊坊', value: 193},
    {name: '菏泽', value: 194},
    {name: '合肥', value: 229},
    {name: '武汉', value: 273},
    {name: '大庆', value: 279}
];
export default dataValue;

3、完成了以上操作之后,我们接下来就要进行散点图的配置信息初始化,在此处其实就是实例化series这个属性,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    //初始化图表参数
   _initCharts=() => {
       const _self = this;
       _self.state.series = [
          {
               name: 'pm2.5',
               type: 'scatter',
               coordinateSystem: 'arcgis',
               data: _self._convertData(dataValue),
               symbolSize: function (val) {
                   return val[2] / 10;
              },
               label: {
                   formatter: '{b}',
                   position: 'right',
                   show: false
              },
               itemStyle: {
                   color: '#00FFFF'
              },
               emphasis: {
                   label: {
                       show: true
                  }
              }
          },
          {
               name: 'Top 5',
               type: 'effectScatter',
               coordinateSystem: 'arcgis',
               data: _self._convertData(dataValue.sort(function (a, b) {
                   return b.value - a.value;
              }).slice(0, 6)),
               symbolSize: function (val) {
                   return val[2] / 10;
              },
               showEffectOn: 'render',
               rippleEffect: {
                   brushType: 'stroke'
              },
               hoverAnimation: true,
               label: {
                   formatter: '{b}',
                   position: 'right',
                   show: true
              },
               itemStyle: {
                   color: '#00FFFF',
                   shadowBlur: 10,
                   shadowColor: '#333'
              },
               zlevel: 1
          }
      ];
  }

上述代码中用到了数据转换这个方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
    _convertData=(data) => {
       let res = [];
       for (let i = 0; i < data.length; i++) {
           let geoCoord = geoCoordMap[data[i].name];
           if (geoCoord) {
               res.push({
                   name: data[i].name,
                   value: geoCoord.concat(data[i].value)
              });
          }
      }
       return res;
  }

4、图表信息初始化之后,接下来监听地图的绘制完成事件,然后调用绘制图表函数来进行图表的绘制,代码如下:

1
2
3
4
view.when(function() {
_self.state.mapview = view;
_self._drawCharts();
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    //绘制图表
   _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);
      });
  }

5、通过以上操作过程就实现了散点图的绘制,如果需要绘制三维场景下的散点图,只需要将mapview更改为sceneview即可。

总结

本文在上一篇文章的基础之上跟大家介绍了一下使用ArcGIS JS API和eCharts来绘制二维和三维场景下的散点图的过程,为了便于代码组织,这篇文章中的代码是在src目录下新建了一个scatterDiagram的组件,如果大家觉得麻烦,可将此组件中的代码直接拷贝到App.js文件里进行学习和参考,中间没有任何问题。

附:

散点图绘制全部源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import React,{Component} from 'react';
import {loadModules} from 'esri-loader';
import './scatterDiagram.css';
import dataValue from './data/dataValue';
import geoCoordMap from './data/geoCoordMap';
class ScatterDiagram extends Component {
   state = {
       series: null,
       mapview: null,
  }
   componentDidMount=() => {
       this._initMapView();
       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: 5,
                       center: [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);
      });
  }
   //初始化图表参数
   _initCharts=() => {
       const _self = this;
       _self.state.series = [
          {
               name: 'pm2.5',
               type: 'scatter',
               coordinateSystem: 'arcgis',
               data: _self._convertData(dataValue),
               symbolSize: function (val) {
                   return val[2] / 10;
              },
               label: {
                   formatter: '{b}',
                   position: 'right',
                   show: false
              },
               itemStyle: {
                   color: '#00FFFF'
              },
               emphasis: {
                   label: {
                       show: true
                  }
              }
          },
          {
               name: 'Top 5',
               type: 'effectScatter',
               coordinateSystem: 'arcgis',
               data: _self._convertData(dataValue.sort(function (a, b) {
                   return b.value - a.value;
              }).slice(0, 6)),
               symbolSize: function (val) {
                   return val[2] / 10;
              },
               showEffectOn: 'render',
               rippleEffect: {
                   brushType: 'stroke'
              },
               hoverAnimation: true,
               label: {
                   formatter: '{b}',
                   position: 'right',
                   show: true
              },
               itemStyle: {
                   color: '#00FFFF',
                   shadowBlur: 10,
                   shadowColor: '#333'
              },
               zlevel: 1
          }
      ];
  }
   _convertData=(data) => {
       let res = [];
       for (let i = 0; i < data.length; i++) {
           let geoCoord = geoCoordMap[data[i].name];
           if (geoCoord) {
               res.push({
                   name: data[i].name,
                   value: geoCoord.concat(data[i].value)
              });
          }
      }
       return res;
  }
   //绘制图表
   _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  ScatterDiagram;

EchartsLayer.min.js源码:

1
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;}});});