赞
踩
项目需要做一个在3D传播轨迹功能,从网上找了一些资料,决定使用echarts来展示,地图是2维世界(包括中国各省份)的地图,如果要使用3D地图也可以。
echarts官网。
为了展示效果,做了个小demo。点击不同的作物,有不同的作物轨迹展示。并且有国外到国内、国内的、总的传播轨迹,可以点击右下方的图例进行切换展示。
在< head >标签中引入
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.2.2/echarts.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<div id="main" style="height: 100vh;">
</div>
重要js代码中都有注释,注释中的()括号内与注释无关
//全球部分点的坐标,也就是传播路径所需要起点、终点坐标 var geoCoordMap = { 石家庄: [114.4995, 38.1006], 哈尔滨: [127.9688, 45.368], 杭州: [119.5313, 29.8773], 广州: [113.5107, 23.2196], 武汉: [114.3896, 30.6628], 南昌: [116.0046, 28.6633], 长沙: [113.0823, 28.2568], 西安: [109.1162, 34.2004], 上海: [121.4648, 31.2891], 天津: [117.4219, 39.4189], 重庆: [107.7539, 30.1904], 尼日利亚: [-4.388361, 11.186148], 洛杉矶: [-118.24311, 34.052713], 香港: [114.195466, 22.282751], 芝加哥: [-87.801833, 41.870975], 加纳库马西: [-4.62829, 7.72415], 曼彻斯特: [-1.657222, 51.886863], 汉堡: [10.01959, 54.38474], 阿拉木图: [45.326912, 41.101891], 伊尔库茨克: [89.116876, 67.757906], 巴西: [-48.678945, -10.493623], 埃及: [31.815593, 31.418032], 巴塞罗纳: [2.175129, 41.385064], 柬埔寨: [104.88659, 11.545469], 米兰: [9.189948, 45.46623], 蒙得维的亚: [-56.162231, -34.901113], 莫桑比克: [32.608571, -25.893473], 阿尔及尔: [3.054275, 36.753027], 阿联酋迪拜: [55.269441, 25.204514], 布达佩斯: [17.108519, 48.179162], 悉尼: [150.993137, -33.675509], 加州: [-121.910642, 41.38028], 墨尔本: [144.999416, -37.781726], 墨西哥: [-99.094092, 19.365711], 温哥华: [-123.023921, 49.311753] }; var color =['#a6c84c', '#ffa022', '#46bee9'] //传播轨迹的起点和终点,value可以设置点的涟漪圆圈特效大小 var BJData = [ [{ name: '巴西' },{ name: '广州', value:100}], [{ name: '广州' }, { name: '上海', value: 95 }], [{ name: '广州' }, { name: '重庆', value: 90 }], [{ name: '广州' }, { name: '长沙', value: 80 }], [{ name: '广州' }, { name: '杭州', value: 70 }], [{ name: '广州' }, { name: '石家庄', value: 60 }], [{ name: '广州' }, { name: '哈尔滨', value: 50 }], [{ name: '广州' }, { name: '南昌', value: 40 }], [{ name: '广州' }, { name: '天津', value: 30 }], [{ name: '广州' }, { name: '武汉', value: 20 }], [{ name: '广州' }, { name: '西安', value: 10 }], [{ name: '广州' }, { name: '广州', value: 10 }] ]; //数据转换方法,返回起点、终点和两点的经纬度 var convertData = function(data){ var res = []; for (var i = 0; i < data.length; i++) { var dataItem = data[i]; // console.log(color[i]) var fromCoord = geoCoordMap[dataItem[0].name]; var toCoord = geoCoordMap[dataItem[1].name]; if (fromCoord && toCoord) { res.push({ fromName: dataItem[0].name, toName: dataItem[1].name, coords: [fromCoord, toCoord], /* lineStyle:{ //设置单个线样式 color:color[i], width: 1, opacity: 0.6, //图像透明度,为0时,线完全透明 curveness: 0.2 //值越大,曲度越大 } */ }); } } return res; }; var series = []; [ [ ,BJData] ].forEach(function (item, i) { series.push( { name: 'xx作物', //轨迹名称,可用于tooltip显示 type: 'lines', //类型 zlevel: 1, //symbol 的取值'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none'和自定义图片 symbol: ['none', 'arrow'], //线的起点无特效,终点箭头展示 symbolSize: 5, //箭头大小 effect: { show: true, //是否显示特效 period: 4, // 特效动画的时间 trailLength: 0.3, // 特效尾迹的长度。取从 0 到 1 的值,默认为 0.2,数值越大尾迹越长 color: '#a4f109', //特效轨迹颜色 //'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none',或者照片 symbol: 'arrow', // 特效图形的标记 symbolSize: 8, loop:true,//是否循环显示特效 }, lineStyle: { //某个类型统一的样式(在下个Vue页面展示) normal: { color: color[i], //因为只有一个类型 width: 2, opacity: 0.6, //图像透明度,为0时,线完全透明 curveness: 0.2 //值越大,曲度越大 } }, data: convertData(item[1])//data需要起点、终点和两点的经纬度 }, { type: "effectScatter", //涟漪特效动画的散点 coordinateSystem: "geo", zlevel: 2, rippleEffect: { //涟漪特效 period: 4, //动画时间,值越小速度越快 brushType: "stroke", //波纹绘制方式 stroke, fill scale: 4 //波纹圆环最大限制,值越大波纹越大 }, label: { normal: { show: true, position: "right", //显示位置 offset: [5, 0], //偏移设置 formatter: "{b}" ,//圆环显示文字 /* textStyle: { fontSize: 9, color: '#fff' } */ }, }, symbol: "circle", symbolSize: function (val) {//根据value值设置圆圈大小 //console.log(val)//下面data属性中的value var level = 0 ; var state= Math.floor(val[2]/40) ; switch (state) { case 0: level=0; break; case 1: level=1; break; case 2: level=2; break; case 3: level=3; break; case 4: level=4; break; case 5: level=5; break; case 6: level=6; break; case 7: level=7; break; case 8: level=8; break; case 9: level=9; break; default: level=10; } return 5+level; //圆环大小 }, data: item[1].map(function (dataItem) { return { name: dataItem[1].name, //需要展示在地图上的点。 value: geoCoordMap[dataItem[1].name] .concat([dataItem[1].value]) }; }) }); }); option = { //容器背景颜色 backgroundColor: '#6daef5', //悬浮提示 tooltip: { trigger: "item", //悬浮提示框颜色 backgroundColor: "#fff", borderColor: "#FFFFCC", showDelay: 0, hideDelay: 0, transitionDuration: 0, //下面的提示框内容可以自己设置 /* formatter: function (params, ticket, callback) { //根据业务自己拓展要显示的内容 var res = ""; var name = params.name; var value = params.value[params.seriesIndex + 1]; res = "<span style='color:#fff;'>" + name.toString().split(' ')[0] + "</span><br/>作物:" + name.toString().split(' ')[1]; return res; } */ }, //图例 legend: { orient: 'vertical', top: 'bottom', left: 'right', //data:['xx作物'] //可以不设置 textStyle: { color: '#fff' }, selectedMode: 'single' }, //effectScatter的颜色渐变 /* color:{ type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: 'red' // 0% 处的颜色 }, { offset: 1, color: 'blue' // 100% 处的颜色 }], global: false // 缺省为 false }, */ geo: { map: "world", label: { emphasis: { show: true, textStyle : { fontSize:9, color: '#fff' } } }, roam: true, //是否允许缩放 layoutCenter: ["50%", "50%"], //地图位置 layoutSize: "180%", //地图大小 itemStyle: { normal: { color:'#eef3e4', //地图背景颜色 borderColor: "#5bc1c9" //省市边界线 }, emphasis: { areaColor: "rgba(37, 43, 61, .5)", //悬浮背景 borderColor:'#fff', //鼠标悬浮边框颜色 borderWidth:3, } } }, series: series }; $.get('.././world.json',function (jsonData) {//地图geojson格式 echarts.registerMap('world', jsonData); var chart = echarts.init(document.getElementById('main')); chart.setOption(option); });
注意 echarts可以结合百度地图api、也可以使用geojson格式来加载地图
国内geojson:阿里云数据可视化平台,国内各省各市的geojson都可以从阿里云可视化平台下载。全球的可以从官网下载,不过从5.1.0版本开始就没有地图了,如果要下载就要点5.0版本,从里面找到world.js即可,3D地球的也在里面。如果要全球地图结合中国各省的geojson可以私信我(是自己结合world.json和中国各省json结合的)。
或者从下面的方式引入。下面就是官方例子里面的代码,不过好像不行
官方迁徙图例子
官方例子要切换到5.1版本前。
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@5.0.2/dist/echarts.min.js"></script> <!-- Uncomment this line if you want to dataTool extension <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@5.0.2/dist/extension/dataTool.min.js"></script> --> <!-- Uncomment this line if you want to use gl extension --> <!-- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-gl@2/dist/echarts-gl.min.js"></script> --> <!-- Uncomment this line if you want to echarts-stat extension <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-stat@latest/dist/ecStat.min.js"></script> --> <!-- Uncomment this line if you want to use map --> <!-- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@5.0.2/map/js/china.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@5.0.2/map/js/world.js"></script> --> <!-- Uncomment these two lines if you want to use bmap extension --> <!-- <script type="text/javascript" src="https://api.map.baidu.com/api?v=2.0&ak=您的密钥"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@{{version}}/dist/extension/bmap.min.js"></script> -->
因为项目需求,就将轨迹显示的封装到一个js文件中了。也遇到了一些很奇怪的问题。就是上面的效果图的小demo
在public下的index.html文件中引入
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.2.2/echarts.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
因为渲染地图需要dom元素,但vue中引入js文件,会先加载js文件,导致获取不到dom元素。所以只是将渲染地图所需要的option属性封装了,然后在所引入的vue页面在进行地图渲染。
//全球部分点的坐标 var geoCoordMap = { 石家庄: [114.4995, 38.1006], 哈尔滨: [127.9688, 45.368], 杭州: [119.5313, 29.8773], 广州: [113.5107, 23.2196], 武汉: [114.3896, 30.6628], 南昌: [116.0046, 28.6633], 长沙: [113.0823, 28.2568], 西安: [109.1162, 34.2004], 上海: [121.4648, 31.2891], 天津: [117.4219, 39.4189], 重庆: [107.7539, 30.1904], 香港: [114.195466, 22.282751], 澳门:[113.549134,22.198751], 昆明:[102.73,25.04], 乌鲁木齐:[87.68,43.77], 宁夏:[106.258754,38.471317], 兰州:[103.826308,36.059421], 成都:[104.075931,30.651651], 北京:[116.407526,39.904030], 阿富汗:[ 64.56,32.12], 巴西: [-48.678945, -10.493623], 秘鲁:[ -76.33,-7.26], 吕宋:[ 121.16,14.24], 印尼:[114.56,-3.06], 墨西哥: [-99.094092, 19.365711], 印度:[78.53,26.10], 开罗:[ 30.05,31.50], 尼日利亚: [-4.388361, 11.186148], 洛杉矶: [-118.24311, 34.052713], 芝加哥: [-87.801833, 41.870975], 加纳库马西: [-4.62829, 7.72415], 曼彻斯特: [-1.657222, 51.886863], 汉堡: [10.01959, 54.38474], 阿拉木图: [45.326912, 41.101891], 伊尔库茨克: [89.116876, 67.757906], 埃及: [31.815593, 31.418032], 巴塞罗纳: [2.175129, 41.385064], 柬埔寨: [104.88659, 11.545469], 米兰: [9.189948, 45.46623], 蒙得维的亚: [-56.162231, -34.901113], 莫桑比克: [32.608571, -25.893473], 阿尔及尔: [3.054275, 36.753027], 阿联酋迪拜: [55.269441, 25.204514], 布达佩斯: [17.108519, 48.179162], 悉尼: [150.993137, -33.675509], 加州: [-121.910642, 41.38028], 墨尔本: [144.999416, -37.781726], 温哥华: [-123.023921, 49.311753] }; //国内外轨迹颜色 var color = ['#a6c84c', '#ffa022', '#46bee9']; //作物轨迹的起点和终点 var cropData=[ [{cropName:'菠萝'},{origin:'阿富汗'},{end:'西安'}], [{cropName:'马铃薯'},{origin:'巴西'},{end:'澳门'}], [{cropName:'番茄'},{origin:'秘鲁'},{end:'澳门'}], [{cropName:'红薯'},{origin:'吕宋'},{end:'广州'}], [{cropName:'茄子'},{origin:'印尼'},{end:'昆明'}], ] //轨迹+扩散的起点和终点 var cropGlobe=[ ]; var cropOversea=[ [{ name: '' },{ name: '', value:100}] ] var cropInter=[ [{ name: '广州' }, { name: '上海', value: 10}], [{ name: '广州' }, { name: '重庆', value: 10}], [{ name: '广州' }, { name: '长沙', value: 10 }], [{ name: '广州' }, { name: '杭州', value: 10}], [{ name: '广州' }, { name: '石家庄', value: 10 }], [{ name: '广州' }, { name: '哈尔滨', value: 10 }], [{ name: '广州' }, { name: '南昌', value: 10 }], [{ name: '广州' }, { name: '天津', value: 10 }], [{ name: '广州' }, { name: '武汉', value: 10 }], [{ name: '广州' }, { name: '西安', value: 10 }], [{ name: '广州' }, { name: '香港', value: 10 }], [{ name: '广州' }, { name: '澳门', value: 10 }], [{ name: '广州' }, { name: '广州', value: 10 }], [{ name: '广州' }, { name: '昆明', value: 10 }], [{ name: '广州' }, { name: '乌鲁木齐', value: 10 }], [{ name: '广州' }, { name: '宁夏', value: 10 }], [{ name: '广州' }, { name: '兰州', value: 10 }], [{ name: '广州' }, { name: '成都', value: 10 }], [{ name: '广州' }, { name: '北京', value: 10 }], ] //匹配作物,并设置数据 function matchCrop(cropName){ for(let i=0; i<cropData.length; i++){ let dataItem = cropData[i]; if(dataItem[0].cropName == cropName){ //国外轨迹设置 cropOversea[0][0].name = dataItem[1].origin; cropOversea[0][1].name = dataItem[2].end; //console.log(cropOversea) //国内轨迹设置 cropInter.forEach(function(item, i){ item[0].name = dataItem[2].end }) // console.log(cropInter) //全球轨迹设置 cropGlobe = cropOversea.concat(cropInter) //console.log(cropGlobe) return; } } } //将地点转成经纬度(生成轨迹方法) var convertData = function(data){ var res = []; for (var i = 0; i < data.length; i++) { var dataItem = data[i]; var fromCoord = geoCoordMap[dataItem[0].name]; var toCoord = geoCoordMap[dataItem[1].name]; if (fromCoord && toCoord) { res.push({ fromName: dataItem[0].name, toName: dataItem[1].name, coords: [fromCoord, toCoord], }); } } return res; }; //点效果数据(因为是将所有点渲染出来包括起点和终点) function pointEffect(items){ let item = items.slice(0,1); //深拷贝 var obj1 = JSON.stringify(item); var obj2 = JSON.parse(obj1); obj2[0][1].name = obj2[0][0].name; items.unshift(obj2[0]) return items } export function mapTrack(cropName){ matchCrop(cropName);//设置数据 var option; var series =[]; [ ['国外',cropOversea], ['国内',cropInter], ['全球',cropGlobe] ].forEach(function(item, i){ series.push( { name: item[0]+'-'+cropName, type: 'lines', zlevel: 1, symbol: ['none', 'arrow'], symbolSize:7, effect: { show: true, //是否显示特效 period: 4, // 特效动画的时间 trailLength: 0.3, // 特效尾迹的长度。取从 0 到 1 的值,默认为 0.2,数值越大尾迹越长 color: '#fff', //特效轨迹颜色 //'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none',或者照片 symbol: 'arrow', // 特效图形的标记 symbolSize: 8, loop:true,//是否循环显示特效 }, lineStyle: { normal: { color: color[i], width: 2, opacity: 0.6, //图像透明度,为0时,线完全透明 curveness: 0.2 //值越大,曲度越大 } }, data: convertData(item[1]) }, { type: "effectScatter", coordinateSystem: "geo", zlevel: 2, rippleEffect: { //涟漪特效 period: 4, //动画时间,值越小速度越快 brushType: "stroke", //波纹绘制方式 stroke, fill scale: 4 //波纹圆环最大限制,值越大波纹越大 }, label:{ show:true, position:"right", formatter: '{b}', }, symbol:"circle", symbolSize: function(val){ var level = 0 ; var state= Math.floor(val[2]/40) ; switch (state) { case 0: level=0; break; case 1: level=1; break; case 2: level=2; break; case 3: level=3; break; case 4: level=4; break; case 5: level=5; break; case 6: level=6; break; case 7: level=7; break; case 8: level=8; break; case 9: level=9; break; default: level=10; } return 4+level; //圆环大小 }, itemStyle:{ color: color[i] }, data: pointEffect(item[1]).map(function(dataItem){ return{ name: dataItem[1].name, value:geoCoordMap[dataItem[1].name].concat([dataItem[1].value]) } }) } ) }); option={ //地图外颜色 backgroundColor: '#6daef5', //悬浮提示 tooltip: { trigger: 'item', backgroundColor: "#fff", borderColor: "#FFFFCC", showDelay: 0, hideDelay: 0, transitionDuration: 0, }, legend: { orient: 'vertical', top: 'bottom', left: 'right', textStyle: { color: '#fff' }, selectedMode: 'single' }, geo: { // show:true, map: "world", label: { emphasis: { show: true, textStyle : { fontSize:9, color: '#fff' } } }, roam: true, //是否允许缩放 layoutCenter: ["50%", "50%"], //地图位置50,50 layoutSize: "240%",//180 itemStyle: { normal: { color:'#eef3e4', //地图背景颜色 borderColor: "#5bc1c9" //省市边界线 }, emphasis: { areaColor: "rgba(37, 43, 61, .5)", //悬浮背景 borderColor:'#fff', //鼠标悬浮边框颜色 borderWidth:3, } } }, series: series }; //重新刷新作物数据 cropGlobe=[ ]; cropOversea=[ [{ name: '' },{ name: '', value:100}] ] cropInter=[ [{ name: '广州' }, { name: '上海', value: 10}], [{ name: '广州' }, { name: '重庆', value: 10}], [{ name: '广州' }, { name: '长沙', value: 10 }], [{ name: '广州' }, { name: '杭州', value: 10}], [{ name: '广州' }, { name: '石家庄', value: 10 }], [{ name: '广州' }, { name: '哈尔滨', value: 10 }], [{ name: '广州' }, { name: '南昌', value: 10 }], [{ name: '广州' }, { name: '天津', value: 10 }], [{ name: '广州' }, { name: '武汉', value: 10 }], [{ name: '广州' }, { name: '西安', value: 10 }], [{ name: '广州' }, { name: '香港', value: 10 }], [{ name: '广州' }, { name: '澳门', value: 10 }], [{ name: '广州' }, { name: '广州', value: 10 }], [{ name: '广州' }, { name: '昆明', value: 10 }], [{ name: '广州' }, { name: '乌鲁木齐', value: 10 }], [{ name: '广州' }, { name: '宁夏', value: 10 }], [{ name: '广州' }, { name: '兰州', value: 10 }], [{ name: '广州' }, { name: '成都', value: 10 }], [{ name: '广州' }, { name: '北京', value: 10 }], ] return option; }
<template> <div id="all"> <button @click="setTrack1()">菠萝</button> <button @click="setTrack2()">马铃薯</button> <button @click="setTrack3()">番茄</button> <button @click="setTrack4()">红薯</button> <button @click="setTrack()">轨迹</button>{{cropName}} <div id="main" style="height: 90vh;"> </div> </div> </template> <script> import {mapTrack} from '../../public/MapTrack' import {ref} from 'vue' export default { setup () { var cropName = ref('作物'); //渲染地图 const setTrack = () =>{ var option = mapTrack(cropName.value)//设置option $.get('./world.json',function (jsonData) {//json放在同级目录下或者public下,按‘./world.json’引入 echarts.registerMap('world', jsonData);//注册地图 var chart = echarts.init(document.getElementById('main')) chart.setOption(option);//设置option }); } const setTrack1 =()=>{ cropName.value = '菠萝'; } const setTrack2 =()=>{ cropName.value = '马铃薯'; } const setTrack3 =()=>{ cropName.value = '番茄'; } const setTrack4 =()=>{ cropName.value = '红薯'; } return { setTrack, setTrack1, setTrack2, setTrack3, setTrack4, cropName } }, } </script> <style> </style>
遇到的奇怪错误
错误1:Uncaught SyntaxError: Unexpected token < in JSON at position 0
如果遇到这个错误,我是将json文件和js文件放在public下,或者需要引入vue页面的同级目录下,不要放的太深。然后get()中的文件路径按下面的方式写。
$.get('./world.json',function (jsonData) {//json放在同级目录下或者public下,按‘./world.json’引入
echarts.registerMap('world', jsonData);//注册地图
var chart = echarts.init(document.getElementById('main'))
chart.setOption(option);//设置option
});
就很奇怪,明明json文件的路径都不对,也能加载出地图。后来我改成正确的路径反而还报了上面的那个错误。
好了,3D轨迹可视化就说到这,如果有帮助,希望可以点个赞,收藏一波。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。