概述
ArcGIS API for JavaScript目前提供的圖表繪製接口十分有限,而且樣式也並不美觀,當我們在地圖上添加相應的圖表的時候,還是需要結合目前市面上其他的圖表可視化插件來做。本文就介紹下如何使用ArcGIS API for JavaScript 4.14版本和eCharts 4.7.0來實現這個需求。文章實現參考【GIS之家】的博客內容。首先來看下最終的效果:
實現思路
此需求的實現完全是純前端的解決方法,接下來簡要介紹下實現思路:
- 先實現一張基礎的二維地圖;
- 在地圖上添加eCharts圖表;
- 監聽地圖的視圖變化事件,重繪圖表大小,實現圖表跟隨地圖的僞縮放。
具體實現過程
1、先用ArcGIS API for JavaScript初始化一張二維地圖,代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>echarts和ArcGIS JS API結合</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.14/esri/themes/light/main.css" />
<style>
body {
margin: 0 !important;
}
#mapview {
position: absolute;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="mapview"></div>
<script src="https://js.arcgis.com/4.14/"></script>
<script>
require(["esri/Map", "esri/views/MapView","esri/Basemap", "esri/layers/TileLayer"], function(Map, MapView, Basemap, TileLayer) {
var basemap = new Basemap({ //此處自定義一張暗夜色的底圖,並沒有用JS API自帶的底圖
baseLayers: [
new TileLayer({
url: "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer",
title: "Basemap"
})
],
title: "basemap",
id: "basemap"
});
var map = new Map({
basemap: basemap
});
var view = new MapView({
container: "mapview",
map: map,
zoom: 8,
center: [104.072043,30.663724] //地圖中心點爲成都
});
});
</script>
</body>
</html>
2、然後在地圖上繪製eCharts圖表。說白了就是在mapview這個DOM元素內添加存放圖表的div元素,並設置初始大小,這就意味着頁面中要求引入eCharts和相應的jQuery開發包,我們只需要去官網下載即可,代碼如下:
引入開發包:
<script src="./libs/jquery-3.4.1.min.js"></script>
<script src="./libs/echarts.js"></script>
地圖上繪製圖表:
//初始化圖表信息函數
function echartsMapInit() {
echartsInfos = []; //繪製函數裏定義一個存放圖表配置的全局數組
echartsInfos.push({
x: 104.072043, //圖表在地圖上繪製的位置
y: 30.663724,
content: '<div id="info1" style="height:150px;width:300px;position:absolute;"></div>', //存放圖表的DOM元素
id:"info1",
echartsObj:null,
option:{ //圖表的配置信息,具體參數可到eCharts官網查看
color: ['#3398DB'],
tooltip : {
trigger: 'axis',
axisPointer : {
type : 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '3%',
containLabel: true,
},
xAxis : [
{
type : 'category',
data : ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
axisTick: {
alignWithLabel: true
},
axisLabel:{
interval:0,
rotate:-30,
},
axisLine:{
lineStyle:{
color:'#FFFFFF',
width:1,
}
}
}
],
yAxis : [
{
type : 'value',
splitLine: {
lineStyle: {
color: ['#0087ED']
}
},
nameTextStyle: {
color: ['#FFFFFF']
},
axisLine:{
lineStyle:{
color:'#FFFFFF',
width:1,
}
}
}
],
series : [
{
name:'直接訪問',
type:'bar',
barWidth: '60%',
data:[10, 52, 200, 334, 390, 330, 220]
}
]
}
});
echartsInfos.push({ //需要繪製的第二個圖表的相關信息
x: 106.492447,
y: 29.608168,
content: '<div id="info2" style="height:150px;width:300px;position:absolute;"></div>',
id:"info2",
echartsObj:null,
option:{
title: {
text: ''
},
tooltip : {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
legend: {
data:['視頻廣告','直接訪問','搜索引擎'],
textStyle:{
color: ['#FFFFFF']
},
},
toolbox: {
feature: {
saveAsImage: {}
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '10%',
containLabel: true
},
xAxis : [
{
type : 'category',
boundaryGap : false,
data : ['週一','週二','週三','週四','週五','週六','週日'],
nameTextStyle: {
color: ['#FFFFFF']
},
axisLine:{
lineStyle:{
color:'#FFFFFF',
width:1,
}
}
}
],
yAxis : [
{
type : 'value',
nameTextStyle: {
color: ['#FFFFFF']
},
axisLine:{
lineStyle:{
color:'#FFFFFF',
width:1,
}
}
}
],
series : [
{
name:'視頻廣告',
type:'line',
stack: '總量',
areaStyle: {},
data:[150, 232, 201, 154, 190, 330, 410]
},
{
name:'直接訪問',
type:'line',
stack: '總量',
areaStyle: {normal: {}},
data:[320, 332, 301, 334, 390, 330, 320]
},
{
name:'搜索引擎',
type:'line',
stack: '總量',
label: {
normal: {
show: true,
position: 'top'
}
},
areaStyle: {normal: {}},
data:[820, 932, 901, 934, 1290, 1330, 1320]
}
]
}
});
for (var i = 0; i < echartsInfos.length; i++) {
var echartsInfo = echartsInfos[i];
var mapPoint = { //座標轉換,將地理座標轉爲屏幕座標
x: echartsInfo.x,
y: echartsInfo.y,
// spatialReference: {
// wkid: view.spatialReference.wkid
// }
spatialReference: {
wkid: 4326
}
};
var screenPoint = view.toScreen(mapPoint);
var obj = {}; //重新定義一個圖表配置信息的對象
obj.x =screenPoint.x;
obj.y =screenPoint.y;
obj.content = echartsInfo.content;
obj.id = echartsInfo.id;
obj.option = echartsInfo.option;
obj.echartsObj = echartsInfo.echartsObj;
echartsInfos[i].echartsObj = loadEchartsMap(obj);
}
};
//繪製圖表函數
function loadEchartsMap(obj) {
$("#mapview").append(obj.content); //往mapview追加存放圖表的DOM元素
var dom = document.getElementById(obj.id); //繪製圖表
var myChart = echarts.init(dom);
myChart.setOption(obj.option);
positionEchartsMap(obj); //調整圖表位置及大小
return myChart;
}
3、圖表信息初始化完成之後,要重新調整圖表的位置和大小,此處圖表的大小要跟隨地圖實現縮放,所以要獲取地圖視圖的層級,根據層級來調整圖表大小,代碼如下:
//統計圖窗口位置
function relocatePopup() {
for (var i = 0; i < echartsInfos.length; i++) {
var echartsInfo = echartsInfos[i];
//座標轉換
var mapPoint = {
x: echartsInfo.x,
y: echartsInfo.y,
//spatialReference: view.spatialReference
spatialReference: {
wkid: 4326
}
};
var screenPoint = view.toScreen(mapPoint);
var obj = {};
obj.x =screenPoint.x;
obj.y =screenPoint.y;
obj.option = echartsInfo.option;
obj.id = echartsInfo.id;
obj.echartsObj = echartsInfo.echartsObj;
positionEchartsMap(obj);
}
};
//調整圖表位置及大小函數
function positionEchartsMap(obj) {
$('#' + obj.id).css('transform', 'translate3d(' + obj.x + 'px, ' + obj.y + 'px, 0)');
switch(view.zoom) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
$('#'+obj.id).css('height', '50px');
$('#'+obj.id).css('width', '100px');
break;
case 6:
case 7:
case 8:
$('#'+obj.id).css('height', '120px');
$('#'+obj.id).css('width', '200px');
break;
case 9:
case 10:
$('#'+obj.id).css('height', '150px');
$('#'+obj.id).css('width', '300px');
break;
case 11:
case 12:
$('#'+obj.id).css('height', '200px');
$('#'+obj.id).css('width', '350px');
break;
default:
$('#'+obj.id).css('height', '250px');
$('#'+obj.id).css('width', '400px');
}
if(obj.echartsObj) {
obj.echartsObj.resize();
}
};
4、最後監聽地圖視圖大小改變事件來調用relocatePopup方法重繪圖表,如下:
view.when(function() {
//監聽地圖變化事件,刷新統計圖位置
view.watch("extent", function() {
relocatePopup();
});
view.watch("rotation", function() {
relocatePopup();
});
//地圖加載完,初始化統計圖
echartsMapInit();
});
5、完成以上步驟之後,就用純前端技術實現了ArcGIS JS API和eCharts的結合來繪製二維圖表的功能。
附全部代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>echarts和ArcGIS JS API結合</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.14/esri/themes/light/main.css" />
<style>
body {
margin: 0 !important;
}
#mapview {
position: absolute;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="mapview"></div>
<script src="./libs/jquery-3.4.1.min.js"></script>
<script src="./libs/echarts.js"></script>
<script src="https://js.arcgis.com/4.14/"></script>
<script>
require(["esri/Map", "esri/views/MapView","esri/Basemap", "esri/layers/TileLayer"], function(Map, MapView, Basemap, TileLayer) {
var basemap = new Basemap({
baseLayers: [
new TileLayer({
url: "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer",
title: "Basemap"
})
],
title: "basemap",
id: "basemap"
});
var map = new Map({
basemap: basemap
});
var view = new MapView({
container: "mapview",
map: map,
zoom: 8,
center: [104.072043,30.663724]
});
view.when(function() {
view.watch("extent", function() {
relocatePopup();
});
view.watch("rotation", function() {
relocatePopup();
});
echartsMapInit();
});
function relocatePopup(e) {
for (var i = 0; i < echartsInfos.length; i++) {
var echartsInfo = echartsInfos[i];
var mapPoint = {
x: echartsInfo.x,
y: echartsInfo.y,
//spatialReference: view.spatialReference
spatialReference: {
wkid: 4326
}
};
var screenPoint = view.toScreen(mapPoint);
var obj = {};
obj.x =screenPoint.x;
obj.y =screenPoint.y;
obj.option = echartsInfo.option;
obj.id = echartsInfo.id;
obj.echartsObj = echartsInfo.echartsObj;
positionEchartsMap(obj);
}
};
function positionEchartsMap(obj) {
$('#' + obj.id).css('transform', 'translate3d(' + obj.x + 'px, ' + obj.y + 'px, 0)');
switch(view.zoom) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
$('#'+obj.id).css('height', '50px');
$('#'+obj.id).css('width', '100px');
break;
case 6:
case 7:
case 8:
$('#'+obj.id).css('height', '120px');
$('#'+obj.id).css('width', '200px');
break;
case 9:
case 10:
$('#'+obj.id).css('height', '150px');
$('#'+obj.id).css('width', '300px');
break;
case 11:
case 12:
$('#'+obj.id).css('height', '200px');
$('#'+obj.id).css('width', '350px');
break;
default:
$('#'+obj.id).css('height', '250px');
$('#'+obj.id).css('width', '400px');
}
if(obj.echartsObj) {
obj.echartsObj.resize();
}
};
function echartsMapInit() {
echartsInfos = [];
echartsInfos.push({
x: 104.072043,
y: 30.663724,
content: '<div id="info1" style="height:150px;width:300px;position:absolute;"></div>',
id:"info1",
echartsObj:null,
option:{
color: ['#3398DB'],
tooltip : {
trigger: 'axis',
axisPointer : {
type : 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '3%',
containLabel: true,
//y2: 140
},
xAxis : [
{
type : 'category',
data : ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
axisTick: {
alignWithLabel: true
},
axisLabel:{
interval:0,
rotate:-30,
},
axisLine:{
lineStyle:{
color:'#FFFFFF',
width:1,
}
}
}
],
yAxis : [
{
type : 'value',
splitLine: {
lineStyle: {
color: ['#0087ED']
}
},
nameTextStyle: {
color: ['#FFFFFF']
},
axisLine:{
lineStyle:{
color:'#FFFFFF',
width:1,
}
}
}
],
series : [
{
name:'直接訪問',
type:'bar',
barWidth: '60%',
data:[10, 52, 200, 334, 390, 330, 220]
}
]
}
});
echartsInfos.push({
x: 106.492447,
y: 29.608168,
content: '<div id="info2" style="height:150px;width:300px;position:absolute;"></div>',
id:"info2",
echartsObj:null,
option:{
title: {
text: ''
},
tooltip : {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
legend: {
data:['視頻廣告','直接訪問','搜索引擎'],
textStyle:{
color: ['#FFFFFF']
},
},
toolbox: {
feature: {
saveAsImage: {}
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '10%',
containLabel: true
},
xAxis : [
{
type : 'category',
boundaryGap : false,
data : ['週一','週二','週三','週四','週五','週六','週日'],
nameTextStyle: {
color: ['#FFFFFF']
},
axisLine:{
lineStyle:{
color:'#FFFFFF',
width:1,
}
}
}
],
yAxis : [
{
type : 'value',
nameTextStyle: {
color: ['#FFFFFF']
},
axisLine:{
lineStyle:{
color:'#FFFFFF',
width:1,
}
}
}
],
series : [
{
name:'視頻廣告',
type:'line',
stack: '總量',
areaStyle: {},
data:[150, 232, 201, 154, 190, 330, 410]
},
{
name:'直接訪問',
type:'line',
stack: '總量',
areaStyle: {normal: {}},
data:[320, 332, 301, 334, 390, 330, 320]
},
{
name:'搜索引擎',
type:'line',
stack: '總量',
label: {
normal: {
show: true,
position: 'top'
}
},
areaStyle: {normal: {}},
data:[820, 932, 901, 934, 1290, 1330, 1320]
}
]
}
});
for (var i = 0; i < echartsInfos.length; i++) {
var echartsInfo = echartsInfos[i];
var mapPoint = {
x: echartsInfo.x,
y: echartsInfo.y,
// spatialReference: {
// wkid: view.spatialReference.wkid
// }
spatialReference: {
wkid: 4326
}
};
var screenPoint = view.toScreen(mapPoint);
var obj = {};
obj.x =screenPoint.x;
obj.y =screenPoint.y;
obj.content = echartsInfo.content;
obj.id = echartsInfo.id;
obj.option = echartsInfo.option;
obj.echartsObj = echartsInfo.echartsObj;
echartsInfos[i].echartsObj = loadEchartsMap(obj);
}
};
function loadEchartsMap(obj) {
$("#mapview").append(obj.content);
var dom = document.getElementById(obj.id);
var myChart = echarts.init(dom);
myChart.setOption(obj.option);
positionEchartsMap(obj);
return myChart;
}
});
</script>
</body>
</html>