为什么使用echarts实现3D地图?

解:因为简单方便快捷,需求简单,不需要去了解其他语言

效果如下(贴图的话图片中没有展示被我注释掉了,environment属性引入图片即可):  

一、下载所需要的地图json数据

json数据的获取在阿里云可以直接下载,以绍兴市为例,链接地址:阿里云地图json获取

二、echarts使用

1.引入库

安装需要用到的插件(本人用的pnpm,需要可出使用方法):

pnpm install echart echart-gl

 

2.安装完成之后引入组件

import * as echarts from 'echarts'
import 'echarts-gl'

3.初始化页面

以下引入代码就可正常展示效果,页面效果做的不是很好需要自己后面去优化一下,对于途中的贴图小水滴一样的暂时只支持简单的path矢量图或者自带的默认几个效果,暂无找到解决办法,有解决的欢迎来分享一下哈。

<script setup lang="ts" name="Map">
import type { VNode } from 'vue'
import { createVNode, render } from 'vue'
import * as echarts from 'echarts'
import geoJson from './sxMap.json' // 为刚刚下载的json文件,需正确引入路径
import MapTooltip from './tip.vue' // tooltip样式需调整,单独封装之后引入页面
import 'echarts-gl'

// const blue = `image://${new URL('./img/top_location_blue.png', import.meta.url).href}`
const mapChart = ref<HTMLElement>()
const scatterData = ref([
  { name: '越城区', value: [120.585315, 29.996993, 0] },
  { name: '柯桥区', value: [120.476075, 30.078038, 0] },
  { name: '上虞区', value: [120.874185, 30.016769, 0] },
  { name: '新昌县', value: [120.905665, 29.501205, 0] },
  { name: '诸暨市', value: [120.244326, 29.713662, 0] },
  { name: '嵊州市', value: [120.82888, 29.586606, 0] },
])
const labelData = ref([
  {
    name: '越城区',
    value: [120.645315, 30.069932],
    itemStyle: { color: '#96fdfd' },
  },
  {
    name: '柯桥区',
    value: [120.636075, 29.848038, 0],
    itemStyle: { color: '#96fdfd' },
  },
  {
    name: '上虞区',
    value: [120.899185, 30.070069, 0],
    itemStyle: { color: '#96fdfd' },
  },
  {
    name: '新昌县',
    value: [120.965665, 29.447205, 0],
    itemStyle: { color: '#96fdfd' },
  },
  {
    name: '诸暨市',
    value: [120.244326, 29.723662, 0],
    itemStyle: { color: '#96fdfd' },
  },
  {
    name: '嵊州市',
    value: [120.73888, 29.626606, 0],
    itemStyle: { color: '#96fdfd' },
  },
])

const cacheCom = new Map<string, HTMLDivElement>()
const initMap = async () => {
  // 名称为引入json文件的名称,需要和下面的geo3D名称对应
  echarts.registerMap('shaoxing', geoJson as any)
  const myChart = echarts.init(mapChart.value!)
  const data3d = scatterData.value.map((el) => {
    return {
      name: el.name,
      value: el.value,
    }
  })
  // 图表配置项
  const option = {
    tooltip: {
      trigger: 'item',
      className: 'map-tooltip',
      formatter: (params: any) => {
        const { name } = params.data
        // 本需求为鼠标悬浮某个区域之后需要获取当下区域的数据,所以加上缓存,避免大量请求接口
        // 如果没有此需求可以将数据进行修改一下去掉缓存即可
        const cacheNode = cacheCom.get(name)
        if (cacheNode) {
          return cacheNode
        }
        else {
          // 创建虚拟DOM节点 引入封装好的toopltip组件并传参
          const tip = createVNode(MapTooltip, {
            info: params.data,
          })
          const mountNode = document.createElement('div')
          render(tip, mountNode)
          cacheCom.set(name, mountNode)
          return mountNode
        }
      },
    },
    geo3D: {
      map: 'shaoxing',
      roam: true,
      shading: 'realistic', // 设置阴影效果
      // top: '0',
      regionHeight: 5, // 地图厚度
      // environment: bg,
      itemStyle: {
        // 图片配置区域
        color: 'rgba(36, 63, 123, 0.2)', // 区域颜色
        opacity: 0.7,
        borderWidth: 3,
        borderColor: '#3898ff', // 地图边缘线条颜色
      },
      viewControl: {
        autoRotate: false,
        // autoRotateAfterStill: 3,
        // 无法旋转
        rotateSensitivity: 0,
        panSensitivity: 0, // 禁用平移
        zoomSensitivity: 0, // 禁用缩放
        projection: 'orthographic',
        orthographicSize: 85, // 透视投影方式下,相机距离主体的距离,是相机位置和目标点连线和视点方向夹角的函数。默认情况下,该距离会通过自适应的方式计算。用户可以手动设置一个固定的距离值。
        distance: 210,
        minAlpha: 15, // 上下旋转的最小 alpha 值。即视角能旋转到达最上面的角度。[ default: 5 ]
        maxAlpha: 90, // 上下旋转的最大 alpha 值。即视角能旋转到达最下面的角度。[ default: 90 ]
        minBeta: -360, // 左右旋转的最小 beta 值。即视角能旋转到达最左的角度。[ default: -80 ]
        maxBeta: 360, // 左右旋转的最大 beta 值。即视角能旋转到达最右的角度。[ default: 80 ]
        animation: true, // 是否开启动画。[ default: true ]
        animationDurationUpdate: 2000, // 过渡动画的时长。[ default: 1000 ]
        animationEasingUpdate: 'cubicInOut', // 过渡动画的缓动效果。[ default: cubicInOut ]
      },
      emphasis: {
        disabled: true, // 是否可以被选中
        label: {
          // 移入时的高亮文本
          show: true,
          color: '#fff', // 显示字体颜色变淡
          fontSize: 20, // 显示字体变大
          fontFamily: 'GBKFont',
          borderColor: 'red', // 地图边缘线条颜色
        },
        itemStyle: {
          color: 'rgba(98, 241, 255, 0.6)', // 显示移入的区块变色
          fontFamily: 'GBKFont',
          borderColor: 'red', // 地图边缘线条颜色
        },
      },
      label: {
        show: true,
        // borderWidth: 10,
        color: '#ffffff', // 地图初始化区域字体颜色
        fontSize: 20,
        fontFamily: 'GBKFont',
        // fontWeight: 'bold',
        lineHeight: 22,
        formatter(params: any) {
          return `${params.name}`
        },
      },
      realisticMaterial: {
        // detailTexture: bgBase64,
        roughness: 0,
      },
      postEffect: {
        enable: false,
      },
      groundPlane: {
        show: false,
      },
      light: {
        // 光照阴影
        main: {
          color: '#fff', // 光照颜色
          intensity: 1, // 光照强度
          shadowQuality: 'high', // 阴影亮度
          shadow: true, // 是否显示阴影
          // shadowQuality: 'medium', // 阴影质量 ultra //阴影亮度
          alpha: 85,
          beta: 10,
        },
        ambient: {
          intensity: 0.7,
        },
      },
      // 高亮区域设置,本次未做展示,注释关闭即可看到
      //   regions: [
      //     {
      //       name: '柯桥区',
      //       itemStyle: {
      //         color: '#fff',
      //         distance: '20px',
      //         borderWidth: 10,
      //       },
      //     },
      //   ],
    },
    series: [
      {
        type: 'map3D', // 加载series数据
        map: 'shaoxing',
        itemStyle: {
          // 图片配置区域
          color: 'rgba(36, 63, 123, 0.6)', // 区域颜色
          opacity: 0,
          borderWidth: 0,
          borderColor: '#3898ff', // 地图边缘线条颜色
        },
        data: data3d,
        regions: [],
        zlevel: 5,
        viewControl: {
          autoRotate: false,
          // autoRotateAfterStill: 3,
          // 无法旋转
          rotateSensitivity: 0,
          panSensitivity: 0, // 禁用平移
          zoomSensitivity: 0, // 禁用缩放
          projection: 'orthographic',
          orthographicSize: 85, // 透视投影方式下,相机距离主体的距离,是相机位置和目标点连线和视点方向夹角的函数。默认情况下,该距离会通过自适应的方式计算。用户可以手动设置一个固定的距离值。
          distance: 120,
          minAlpha: 15, // 上下旋转的最小 alpha 值。即视角能旋转到达最上面的角度。[ default: 5 ]
          maxAlpha: 90, // 上下旋转的最大 alpha 值。即视角能旋转到达最下面的角度。[ default: 90 ]
          minBeta: -360, // 左右旋转的最小 beta 值。即视角能旋转到达最左的角度。[ default: -80 ]
          maxBeta: 360, // 左右旋转的最大 beta 值。即视角能旋转到达最右的角度。[ default: 80 ]
          animation: true, // 是否开启动画。[ default: true ]
          animationDurationUpdate: 1000, // 过渡动画的时长。[ default: 1000 ]
          animationEasingUpdate: 'cubicInOut', // 过渡动画的缓动效果。[ default: cubicInOut ]
        },
      },
      {
        type: 'scatter3D',
        coordinateSystem: 'geo3D',
        // symbolSize: 30,
        zlevel: 99,
        geo3DIndex: 0,
        shadowBlur: 10,
        silent: false,
        itemStyle: {
          opacity: 1,
          width: 1,
        },
        emphasis: {
          disable: false,
        },
        label: {
          show: false,
        },
        // symbolSize: [20, 20],
        symbol: 'pin',
        symbolSize: 25,
        data: labelData.value,
        shading: 'lambert',
      },
    ],
  }
  myChart.setOption(option)
  window.addEventListener('resize', () => {
    myChart.resize()
  })
}
onMounted(() => {
  // 初始化地图
  initMap()
})
</script>

<template>
  <div ref="mapChart" class="mapChart" />
</template>

<style lang="scss" scoped>
::v-deep(.map-tooltip) {
  padding: 0 !important;
  background: transparent !important;
  border: none !important;
}
</style>

4. 封装的tooltip页面 

其实就和封装正常的页面是一样的,不过就是引入这里的话稍微有点不一样,代码的话就随便放一个好了,样式的话自行根据需求来修改就好。

<script setup lang="ts" name="Tip">
import { debounce, throttle } from 'lodash'

const props = defineProps<{
  info: { name: string; value: Array<number> }
}>()
const number = ref(0)
const disposed = ref(0)
const feedback = ref(0)

</script>

<template>
  <div class="Tip">
    <div class="header">
      {{ props.info.name }}
    </div>
    <div class="content">
       自己的内容
    </div>
  </div>
</template>

<style scoped lang="scss">
.Tip {
  padding: 15px 30px;
  width: 280px;
  height: 160px;
  background: url(./img/floating-window_bg.png) no-repeat;
  background-size: 100% 100%;
  color: #fff;

  .header {
    text-align: center;
    font-size: 20px;
    margin-bottom: 10px;
    font-weight: 700;
    letter-spacing: 4px;
  }
}
</style>

 


总结

基本的3D效果是可以达到的,里面有包含对于tooltip效果样式的调整、部分区域的高亮展示、以及不用数据展示不同颜色、地图添加纹理贴图、对于边缘线的样式修改以及厚度是否旋转等等,基础的使用基本上问题不大。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐