Recently, in the Big Screen Visualization project, there was a map display function, which required:

  1. Good looking is number one
  2. Support drilling in and out
  3. Provinces and cities can be displayed separately, because the customer data is either provincial or municipal

The first thing that comes to mind is Echarts, so open the Echarts official case and find a similar map demo

Look at the complete code to see that he is a request for a path to obtain data, asked the next path to see that he is a string of json data with coordinatesCdn.jsdelivr.net/gh/apache/e…

At this point, I was involved in the knowledge blind area, so Baidu checked geoJson and found that Aliyun dataV provided a website to obtain geoJson dataDatav.aliyun.com/tools/atlas…

Following the demo tutorial, try to implement:

1. First lay out and import the required files

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, Word-wrap: break-word! Important; "> < span style =" max-width: 100%; padding: 0; } .echarts-map { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: url("./background.png") no-repeat; } </style> </head> <body> <div class="echarts-map" id="3dMap"></div> </body> <script SRC = "https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js" > < / script > < script SRC = "https://cdn.bootcdn.net/ajax/libs/echarts/5.1.2/echarts.min.js" > < / script > < / HTML >Copy the code

2. Map rendering

let mapEcharts = null; if (mapEcharts) { mapEcharts.dispose(); // Destroy the instance. After the instance is destroyed, it cannot be used again. } mapEcharts = echarts.init(document.getelementById ("3dMap")); Mapecharts.showloading (); $.getJSON('https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json', JsonData => {echarts.registermap (' China ', jsonData); Let options = {series: [{name: "map", type: "map", // map map: 'China ',],}; mapEcharts.setOption(options); // instance configuration item with data // hideLoading mapecharts.hideloading (); })Copy the code

You can see here that the map is basically out, and the next step is to beautify the map. You can see the data of Hainan Islands on the map, but actually the data of Hainan Islands is already in the lower right corner, so I have compiled a datachina.jsIn the local, you can get the big guy in the demo.

3. Beautify the map

Add the map gradient color and select the color

Let options = {series: [{name: "map", type: "map", // map: mapName, // False, // do not allow a single mouse to be selected: true, // start mouse events, scale, move // label: {show: true, color: "#000a3c",}, // itemStyle: {type: "radial", x: 0.5, y: 0.5, r: 3, colorStops: [{offset: 0, 0, 0] 0, color: "rgba(223, 231, 242, 1)", // 0%}, {offset: 1, color: "Rgba (2, 99, 206, 1)", // 100% color},], globalCoord: false, // Default to false}, borderWidth: 1, // border size: "Rgba (104, 152, 190, 1)", // shadowColor: "rgba(128, 217, 248, 1)", -2, // shadowOffsetY: 2, // shadowOffsetY: 2, // shadowOffsetY: 10, // Shadowblock background shadow length}, // select style emphasis: {label: { color: "#ffffff", }, itemStyle: { areaColor: "#a5d4fe", }, }, }, ], };Copy the code

4. Create fake 3D effects

Let options = {geo: {map: mapName, // map type. X: 4.0, y: 4.0, r: 4.0 zoom: 1, roam: true, animation: false, itemStyle: {type: "radial", x: 4.0, y: 4.0, r: 4.0 0.8, colorred: [{offset: 0, color: "rgba(147, 235, 248, 1)", // 0%}, {offset: 1, color: "Rgba (2, 99, 206, 1)", // 100% color},], globalCoord: false, // default is false}, shadowColor: "#105781", // map area shadowColor. },}, series: [{name: "map", type: "map", shadowOffsetX: 0, shadowOffsetY: 10, shadowOffsetX: 0, shadowOffsetY: 10,},}, series: [{name: "map", type: "map", MapName, / / load register map selectedMode: false, / / don't let alone selected levenshtein distance: true, / / mouse events, scale zoom, move move / / graphics on the text label label: {show: False, color: "#000a3c",}, // Map style itemStyle: {type: "radial", x: 0.5, y: 0.5, r: 3, colorStops: [{offset: 0, color: "rgba(223, 231, 242, 1)", // 0%}, {offset: 1, color: "Rgba (2, 99, 206, 1)", // 100% color},], globalCoord: false, // Default to false}, borderWidth: 1, // border size: "Rgba (104, 152, 190, 1)", // shadowColor: "rgba(128, 217, 248, 1)", -2, // shadowOffsetY: 2, // shadowOffsetY: 2, // shadowOffsetY: 10, // Shadowblock background shadow length}, // select style emphasis: {label: { color: "#ffffff", }, itemStyle: { areaColor: "#a5d4fe", }, }, }, ], };Copy the code

You can see that you now have a 3D like shadow effect

5. Achieve drill-down effect

Mapecharts.on ("click", (params) => {// clearTimeout(timeFn); timeFn = setTimeout(function () { if ( allAreaCode.filter((item) => item.name.indexOf(params.name) > -1)[0] ) { let areaCode = allAreaCode.filter( (item) => item.name.indexOf(params.name) > -1 )[0].code; loadMap( `https://geo.datav.aliyun.com/areas_v3/bound/${areaCode}_full.json` ) .then((data) => { initMap(data, areaCode); }) .catch(() => { loadMap( `https://geo.datav.aliyun.com/areas_v3/bound/${areaCode}.json` ) .then((res) => { initMap(res, areaCode); }) .catch(() => {}); }); historyList.push({ code: areaCode, name: params.name, }); let result = []; let obj = {}; for (let i = 0; i < historyList.length; i++) { if (! obj[historyList[i].code]) { result.push(historyList[i]); obj[historyList[i].code] = true; } } historyList = result; }}, 250); });Copy the code

LoadMap encapsulates the data request and initMap is the rendering map, which will be shown in the entire code.

The main logic here is to listen for map click events, filter to areaCode, and request data through areaCode Mosaic path. The address here can be obtained from the above website. The reason why _full and common are divided is that map data may contain sub-area data, such as Guangzhou city, Buddha listing and so on in Guangdong province……

AllAreaCode is the code data I spent a lot of time collecting nationwide, provincial and urban code data. This data may have some errors. The big guy who knows crawlers can directly climb the data of the website above and process it into my data format

After rendering the map, push the history record into the historyList and use it later.

5. Achieve the drilling effect

Mapecharts.on ("dblclick", (params) => {// clearTimeout(timeFn); If (historyList.length == 1) {alert(" the map has reached the top level "); return; } let map = historyList.pop(); If (historyList[historylist.length-1]. Code == "China ") {initMap(China," China ", "China "); } else { loadMap( `https://geo.datav.aliyun.com/areas_v3/bound/${ historyList[historyList.length - 1].code }_full.json` ).then((data) => { initMap(data, historyList[historyList.length - 1].code); }).catch(() => { loadMap( `https://geo.datav.aliyun.com/areas_v3/bound/${ historyList[historyList.length - 1].code }.json` ) .then((res) => { initMap(res, historyList[historyList.length - 1].code); }) .catch(() => {}); }); }});Copy the code

The double click drill out method is used here. When double click, the last element is deleted from the history record, and the last element data of the deleted array is taken to render the map

6. Synchronize mouse events between the Map layer and geo layer

If mouse events are enabled, the Map layer and geo layer are separate, whether scale or move, for example:

In this case we can synchronize their data through event listening

mapEcharts.on("georoam", (params) => { let option = mapEcharts.getOption(); // Get the option object if (params.zoom! = null && params.zoom ! Geo [0].zoom = option.series[0].zoom; Geo [0].center = option.series[0].center; } else {// Capture option.geo[0].center = option.series[0].center; // Capture option.geo[0].center = option.series[0].center; } mapecharts.setoption (option); // Set option});Copy the code

7. Final code and effects

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, Word-wrap: break-word! Important; "> < span style =" max-width: 100%; padding: 0; } .echarts-map { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: url("./background.png") no-repeat; } </style> </head> <body> <div class="echarts-map" id="3dMap"></div> </body> <script SRC = "https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js" > < / script > < script SRC = "https://cdn.bootcdn.net/ajax/libs/echarts/5.1.2/echarts.min.js" > < / script > < script SRC = ". / China. Js "> < / script > <script src="./geoAtlasJson.js"></script> <script> let mapEcharts = null; let historyList = []; let timeFn = null; if (mapEcharts) { mapEcharts.dispose(); // Destroy the instance. After the instance is destroyed, it cannot be used again. } mapEcharts = echarts.init(document.getelementById ("3dMap")); Historylist. push({code: "China ", name:" China ",}); Mapecharts.showloading (); InitMap (China, "China "," China "); Mapecharts.on ("click", (params) => {// clearTimeout(timeFn); timeFn = setTimeout(function () { if ( allAreaCode.filter((item) => item.name.indexOf(params.name) > -1)[0] ) { let areaCode = allAreaCode.filter( (item) => item.name.indexOf(params.name) > -1 )[0].code; loadMap( `https://geo.datav.aliyun.com/areas_v3/bound/${areaCode}_full.json` ) .then((data) => { initMap(data, areaCode); }) .catch(() => { loadMap( `https://geo.datav.aliyun.com/areas_v3/bound/${areaCode}.json` ) .then((res) => { initMap(res, areaCode); }) .catch(() => {}); }); historyList.push({ code: areaCode, name: params.name, }); let result = []; let obj = {}; for (let i = 0; i < historyList.length; i++) { if (! obj[historyList[i].code]) { result.push(historyList[i]); obj[historyList[i].code] = true; } } historyList = result; }}, 250); }); Mapecharts.on ("dblclick", (params) => {// clearTimeout(timeFn); If (historyList.length == 1) {alert(" the map has reached the top level "); return; } let map = historyList.pop(); console.log(historyList[historyList.length - 1]) if (historyList[historyList.length - 1].code == "china") { InitMap (China, "China "," China "); } else { loadMap( `https://geo.datav.aliyun.com/areas_v3/bound/${ historyList[historyList.length - 1].code }_full.json` ) .then((data) => { initMap(data, historyList[historyList.length - 1].code); }) .catch(() => { loadMap( `https://geo.datav.aliyun.com/areas_v3/bound/${ historyList[historyList.length - 1].code }.json` ) .then((res) => { initMap(res, historyList[historyList.length - 1].code); }) .catch(() => {}); }); }}); mapEcharts.on("georoam", (params) => { let option = mapEcharts.getOption(); // Get the option object if (params.zoom! = null && params.zoom ! Geo [0].zoom = option.series[0].zoom; Geo [0].center = option.series[0].center; } else {// Capture option.geo[0].center = option.series[0].center; // Capture option.geo[0].center = option.series[0].center; } mapecharts.setoption (option); // Set option}); Async function loadMap(URL, pathName) {return await $.getjson (URL); } function initMap(mapData, mapData) {echarts.registermap (mapData, mapData); Let options = {geo: {map: mapName, // map type. X: 4.0, y: 4.0, r: 4.0 zoom: 1, roam: true, animation: false, itemStyle: {type: "radial", x: 4.0, y: 4.0, r: 4.0 0.8, colorred: [{offset: 0, color: "rgba(147, 235, 248, 1)", // 0%}, {offset: 1, color: "Rgba (2, 99, 206, 1)", // 100% color},], globalCoord: false, // default is false}, shadowColor: "#105781", // map area shadowColor. },}, series: [{name: "map", type: "map", shadowOffsetX: 0, shadowOffsetY: 10, shadowOffsetX: 0, shadowOffsetY: 10,},}, series: [{name: "map", type: "map", MapName, / / load register map selectedMode: false, / / don't let alone selected levenshtein distance: true, / / mouse events, scale zoom, move move / / graphics on the text label label: {show: False, color: "#000a3c",}, // Map style itemStyle: {type: "radial", x: 0.5, y: 0.5, r: 3, colorStops: [{offset: 0, color: "rgba(223, 231, 242, 1)", // 0%}, {offset: 1, color: "Rgba (2, 99, 206, 1)", // 100% color},], globalCoord: false, // Default to false}, borderWidth: 1, // border size: "Rgba (104, 152, 190, 1)", // shadowColor: "rgba(128, 217, 248, 1)", -2, // shadowOffsetY: 2, // shadowOffsetY: 2, // shadowOffsetY: 10, // Shadowblock background shadow length}, // select style emphasis: {label: { color: "#ffffff", }, itemStyle: { areaColor: "#a5d4fe", }, }, }, ], }; mapEcharts.setOption(options); // instance configuration item with data // hideLoading mapecharts.hideloading (); } </script> </html>Copy the code

Demo Github address: github.com/ljnMeow/ech…

8. Final words

In fact, it took a lot of time to make this demo. At first, I wanted to implement a 3D map directly through Echarts-GL, but according to the official documents, it seems that THE MAP3D of Echarts-GL does not support gradient background, so I gave up directly. Secondly, it took time to collect all the data of provinces and cities. Have to sigh our great Summer is really large and abundant.

Finally, I hope this tutorial is helpful to you, if you feel ok, please leave a like 👍, or github star🌟 or 🙏.

Wish each big guy rise step by step 🧨