Echarts3D行政区划地图实现贴图并加载点位数据

5,558 阅读6分钟

哟哟哟哟哟哟~ 私密马赛 xdm. 最近浅浅的看了下用Echarts 的去画3D地图, 说实话在以前用Echarts 只是用来搞搞图表. 感觉画地图还是用高德或者arcgis的好, 没想到终究还是年轻了, 确实有被香到哦!

ceeb653ely1ga043uhzslg205s055a9x.gif

加载地图

因为画的是3D的地图所以要搭配echarts-gl,文档地址在这里哦! 说实话菜鸡我刚开始没看到. 不会咱就是看文档

在画地图之前一定要先使用Echarts提供的方法结合数据注册一下

   async function getData() {
      let res = await fetch('https://geo.datav.aliyun.com/areas_v3/bound/330100.json')
      return res.json()
    }
    getData().then(result => {
      echarts.registerMap("hangzhou", result);
      init() // 初始化图表
    })

下面就是开始配置

  • map : 地图类型 就是上面我们注册好的地图名称'hangzhou',
  • boxHeight: 坐标系组件在三维场景中的高度 默认为10
  • regionHeight: 地图的高度 这个高度是模型的高度,小于boxHeight哦!
  • environment: 环境贴图 我最中意的属性之一, 能给人带来不一样的展示效果. 如果你恰好有全景贴图可以try try 这个网站可以免费下载hdr和纹理贴图的
  • itemStyle: 这个就是调整地图边框、宽度、颜色、透明度...啥的和图表一样
  • emphasis: 鼠标 hover 高亮时图形和标签的样式 这个属性好像没法关闭 所以就和上面的itemStyle保持一致,反正我是这样写的 但我总觉得有办法但是没找到. 呜呜呜
  • shading: 三维图形的着色效果 可选
    • color: 只显示颜色,不受光照等其它因素的影响
    • lambert: 通过经典的 lambert 着色表现光照带来的明暗
    • 'realistic' 真实感渲染

这里我用到的是realistic

  • realisticMaterial: 真实感材质相关的配置项. 上面的shading 着色效果都是有对应的配置项这里就不做过多赘述了嗷!宝儿

    • detailTexture: 材质细节的纹理贴图 使用这个属性就可以对地图进行贴图但是也只能做那种抽象一点的地图效果不会像专业的gis地图一样 不过 I like. 图片的话最好用高宽是 2 的 n 次方
    • textureTiling: 材质细节纹理的平铺。默认为1,也就是拉伸填满。大于 1 的时候,数字表示纹理平铺重复的次数. 如果高宽不是 2 的 n 次方 纹理无法使用平铺哦! 宝儿
    • textureOffset: 材质细节纹理的位移。eg: [1,1]
    • roughness: 用于表示材质的粗糙度,0为完全光滑,1完全粗糙,中间的值则是介于这两者之间
    • roughnessAdjust: 粗糙度调整,在使用粗糙度贴图的时候有用
    • metalnessAdjust : 金属度调整,在使用金属度贴图的时候有用
    • normalTexture: 材质细节的法线贴图
  • light: 光照相关的设置。在 shading 为 'color' 的时候无效 光照的调整如果页面不复杂其实也无所谓啦 微调就行 具体的属性看官方文档了啦宝儿

Snipaste_2023-03-25_22-08-22.png

  • viewControl:用于鼠标的旋转,缩放等视角控制 这个属性很重要
    • projection: 投影方式,默认为透视投影'perspective',也支持设置为正交投影'orthographic' 下面这几个是常用的属性:
       viewControl: {
        alpha: 50, // 上下旋转的角度
        beta: -2, // 左右旋转的角度
        rotateSensitivity: 3, // 旋转操作的灵敏度
        panSensitivity: 3,
        panMouseButton: 'right',
        distance: 125,
        minAlpha: 5, // 上下旋转的最小 alpha 值。即视角能旋转到达最上面的角度。[ default: 5 ]
        maxAlpha: 120, // 上下旋转的最大 alpha 值。即视角能旋转到达最下面的角度。[ default: 90 ]
        minBeta: -360, // 左右旋转的最小 beta 值。即视角能旋转到达最左的角度。[ default: -80 ]
        maxBeta: 360, // 左右旋转的最大 beta 值。即视角能旋转到达最右的角度。[ default: 80 ]
        animation: false, // 是否开启动画。[ default: true ]
        animationDurationUpdate: 1000, // 过渡动画的时长。[ default: 1000 ]
        animationEasingUpdate: "cubicInOut" // 过渡动画的缓动效果。[ default: cubicInOut ]
      }
    
    

ok 上面就是geo3D常用的配置项 这样一个基本的3D地图就出现了

      const option = {
       geo3D: {
         map: "hangzhou",
         boxHeight: 15,
         regionHeight: 5,
         top: -40,
         itemStyle: {
           color: "#fff",
           opacity: 1,
           borderWidth: 3,
           borderColor: "#50e6fd",
         },
         viewControl: {
           alpha: 50, // 上下旋转的角度
           beta: -2, // 左右旋转的角度
           rotateSensitivity: 3, // 旋转操作的灵敏度
           panSensitivity: 3,
           panMouseButton: 'right',
           distance: 125,
           minAlpha: 5, // 上下旋转的最小 alpha 值。即视角能旋转到达最上面的角度。[ default: 5 ]
           maxAlpha: 120, // 上下旋转的最大 alpha 值。即视角能旋转到达最下面的角度。[ default: 90 ]
           minBeta: -360, // 左右旋转的最小 beta 值。即视角能旋转到达最左的角度。[ default: -80 ]
           maxBeta: 360, // 左右旋转的最大 beta 值。即视角能旋转到达最右的角度。[ default: 80 ]
           animation: false, // 是否开启动画。[ default: true ]
           animationDurationUpdate: 1000, // 过渡动画的时长。[ default: 1000 ]
           animationEasingUpdate: "cubicInOut" // 过渡动画的缓动效果。[ default: cubicInOut ]
         },

         emphasis: {
           label: {
             show: false
           },
           itemStyle: {
             color: "#fff"
           }
         },
         label: {
           show: false
         },
         environment: './img/industrial_sunset_02_2k.hdr',
         shading: "realistic",
         realisticMaterial: {
           detailTexture: './img/test.png',
         },
         postEffect: {
           enable: true
         },
         // shading 为color 时无效
         light: {
           //光照阴影
           main: {
             color: "#fff", //光照颜色
             intensity: 1, //光照强度
             shadowQuality: 'high', //阴影亮度
             shadow: true, //是否显示阴影
             shadowQuality: "medium", //阴影质量 ultra //阴影亮度
             alpha: 55,
             beta: 10
           },
           ambient: {
             intensity: 0.7
           }
         }
       },
     };

加载地图点位

添加地图的mark点我们使用seriesscatter3D 类型 也是在echarts-gl的配置项里哦 然后指定coordinateSystemgeo3D 再根据配置项去进行配置

怎么添加mark点的icon呢 这个就是要要在label 下指定backgroundColor就行了 这个属性并没有在echarts-gl文档中出现, 如果有多个类型的mark就需要根据类型的不同生成多个scatter3D 添加到series

  backgroundColor: {
    image: url
  }

可以自己去整合一个包含不同类型的数组 然后通过遍历再去生成新的数组returnseries ps:

     position: [
        {
          pos: [
            {
              name: "A类型",
              value: [120.17005132, 30.23464188, 0] // value的数据里面包含经纬度和这个类型的数量
            },
            {
              name: "A类型",
              value: [120.18146552, 30.21745688, 0]
            },
          ],
          image: icon的路径
        },
        {
          pos: [
            {
              name: "B类型",
              value: [120.17429151, 30.23666438, 0]
            },
            {
              name: "B类型",
              value: [120.18643404, 30.22371008, 0]
            },
          ],
          image: icon的路径
        },
      ]

因为我们的数据是从接口里获取到的如果要优雅的实现点位加载那么少不了去写一个方法

    setSeries(data) {
      const dataArr = [];
      data.forEach(item => {
        dataArr.push(
          {
            type: "scatter3D",
            coordinateSystem: "geo3D",
            label: {
              show: true,
              position: "top",
              distance: -20,
              formatter(params) {
                return " ";
              },
              color: "transparent",
              padding: [15, 20],
              backgroundColor: {
                image: item.image
              }
            },
            itemStyle: { color: "transparent" },
            symbol: "",
            symbolSize: 30,
            symbolRotate: 0,
            z: 999,
            data: item.pos
          }
        )
      });
      return dataArr
    }

ok 点位的加载也完成了嗷 宝儿 但是数据量的情况下我还没有去试过echarts的性能优化我个人感觉做的非常好了应该不是问题

但是你要注意啊 宝儿 点位数据多的情况下就不要把变量写到data函数里了 数据又不在上面的html里使用,就不要变成响应式了答应我好吗

a6653c0dly1gvw13l57s5j20j80j7aap.jpg

下面就是完整的代码

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>ECharts</title>
  <!-- 引入刚刚下载的 ECharts 文件 -->
  <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.1/dist/echarts.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/echarts-gl@2.0.9/dist/echarts-gl.min.js"></script>
</head>
<style>
  body {
    margin: 0;
    padding: 0;
    overflow: hidden;
  }
</style>

<body>
  <!-- 为 ECharts 准备一个定义了宽高的 DOM -->
  <div style="width: 100%;height: 100vh;">
    <div id="main" style="width: 100%;height:100%;position: relative;"></div>
  </div>
  <script type="text/javascript">
      const   position = [
        {
          pos: [
            {
              name: "A类型",
              value: [120.17005132, 30.23464188, 0] // value的数据里面包含经纬度和这个类型的数量
            },
            {
              name: "A类型",
              value: [120.18146552, 30.21745688, 0]
            },
          ],
          image: icon的路径
        },
        {
          pos: [
            {
              name: "B类型",
              value: [120.17429151, 30.23666438, 0]
            },
            {
              name: "B类型",
              value: [120.18643404, 30.22371008, 0]
            },
          ],
          image: icon的路径
        },
      ]
    const myChart = echarts.init(document.getElementById('main'));
    async function getData() {
      let res = await fetch('https://geo.datav.aliyun.com/areas_v3/bound/330100.json')
      return res.json()
    }
    getData().then(result => {
      echarts.registerMap("hangzhou", result);
      init()
    })
    // 基于准备好的dom,初始化echarts实例
    function init() {
      // 指定图表的配置项和数据
      const option = {
        geo3D: {
          map: "hangzhou",
          boxHeight: 15,
          regionHeight: 5,
          top: -40,
          itemStyle: {
            color: "#fff",
            opacity: 1,
            borderWidth: 3,
            borderColor: "#50e6fd",
          },
          viewControl: {
            alpha: 50, // 上下旋转的角度
            beta: -2, // 左右旋转的角度
            rotateSensitivity: 3, // 旋转操作的灵敏度
            panSensitivity: 3,
            panMouseButton: 'right',
            distance: 125,
            minAlpha: 5, // 上下旋转的最小 alpha 值。即视角能旋转到达最上面的角度。[ default: 5 ]
            maxAlpha: 120, // 上下旋转的最大 alpha 值。即视角能旋转到达最下面的角度。[ default: 90 ]
            minBeta: -360, // 左右旋转的最小 beta 值。即视角能旋转到达最左的角度。[ default: -80 ]
            maxBeta: 360, // 左右旋转的最大 beta 值。即视角能旋转到达最右的角度。[ default: 80 ]
            animation: false, // 是否开启动画。[ default: true ]
            animationDurationUpdate: 1000, // 过渡动画的时长。[ default: 1000 ]
            animationEasingUpdate: "cubicInOut" // 过渡动画的缓动效果。[ default: cubicInOut ]
          },

          emphasis: {
            label: {
              show: false
            },
            itemStyle: {
              color: "#fff"
            }
          },
          label: {
            show: false
          },
          environment: './img/industrial_sunset_02_2k.hdr',
          shading: "realistic",
          realisticMaterial: {
            detailTexture: './img/test.png',
          },
          postEffect: {
            enable: true
          },
          // shading 为color 时无效
          light: {
            //光照阴影
            main: {
              color: "#fff", //光照颜色
              intensity: 1, //光照强度
              shadowQuality: 'high', //阴影亮度
              shadow: true, //是否显示阴影
              shadowQuality: "medium", //阴影质量 ultra //阴影亮度
              alpha: 55,
              beta: 10
            },
            ambient: {
              intensity: 0.7
            }
          }
        },
        series: setSeries()
      };
      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
    }
   function setSeries(data) {
      const dataArr = [];
      data.forEach(item => {
        dataArr.push(
          {
            type: "scatter3D",
            coordinateSystem: "geo3D",
            label: {
              show: true,
              position: "top",
              distance: -20,
              formatter(params) {
                return " ";
              },
              color: "transparent",
              padding: [15, 20],
              backgroundColor: {
                image: item.image
              }
            },
            itemStyle: { color: "transparent" },
            symbol: "",
            symbolSize: 30,
            symbolRotate: 0,
            z: 999,
            data: item.pos
          }
        )
      });
      return dataArr
    }
  </script>
</body>

</html>