echarts+Vue2 绘制2D地图,添加散点,点击散点

举例:显示河北省地图

根据DataV.GeoAtlas地理小工具系列,获取到河北省的地图 .json数据。

例如获取到的河北省 json 地址是https://geo.datav.aliyun.com/areas_v3/bound/130000_full.json

将其在浏览器打开,在项目中新建一个hebei.json文件,复制全部数据到hebei.json。

然后新建.vue文件,在其中引入即可,具体代码如下:(复制即可直接食用,注意需要全局注册echarts)

<template>
  <div class="container">
    <div ref="myChart" style="width: 100%; height: 100%"></div>
  </div>
</template>

<script>
import hebei from "../../../public/extend/map/json/province/hebei.json";
export default {
  components: {},
  props: {},
  data() {
    return {
      chart: null,
      hebei,
      regions: [],
      tableData: [],
    };
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {
    this.$nextTick(() => {
      this.initChart();
    });
  },
  methods: {
    initChart() {
      let that = this;
      that.$echarts.registerMap("hebei", this.hebei);
      // 散点数据
      var data = [
        {
          name: "河北科技大学",
          areaname: "河北科技大学",
          value: ["114.50963", "37.97686"],
          date: "2025-10-01",
          title: "石家庄",
        },
      ];
      that.tableData = data;
      var option = {
        tooltip: {
          trigger: "item",
        },
        visualMap: {
          show: true,
          min: 0,
          max: 200,
          left: "left",
          top: "bottom",
          text: ["高", "低"], // 文本,默认为数值文本
          calculable: true,
          seriesIndex: [1],
          inRange: {
            color: ["#00467F", "#A5CC82"], // 蓝绿
          },
        },
        geo: {
          map: "hebei",
          roam: false,
          zoom: 1.0,
          aspectScale: 1,
          scaleLimit: {
            min: 1,
            max: 2,
          },
          top: 35,
          right: 150,
          left: "auto",
          selectedMode: "single",
          select: {
            disabled: true,
            itemStyle: {
              borderWidth: 2.5,
              areaColor: "rgb(8, 207, 221)",
              shadowColor: "rgba(255, 255, 255, 0.3)",
              shadowBlur: 10,
              shadowOffsetX: 4,
              shadowOffsetY: 4,
            },
            label: {
              color: "rgba(255, 255, 255, 1)",
            },
          },
          regions: that.tableData,
          label: {
            show: true, // 是否显示地图上的所有地名
            color: "rgba(255, 255, 255, 0.6)",
            fontWeight: "bold",
            fontSize: 16,
          },
          itemStyle: {
            normal: {
              borderColor: "rgba(147, 235, 248, 1)",
              borderWidth: 1,
              areaColor: {
                type: "radial",
                x: 0.5,
                y: 0.5,
                r: 0.8,
                colorStops: [
                  {
                    offset: 0,
                    color: "rgba(147, 235, 248, 0)", // 0% 处的颜色
                  },
                  {
                    offset: 1,
                    color: "rgba(147, 235, 248, .2)", // 100% 处的颜色
                  },
                ],
                globalCoord: false, // 缺省为 false
              },
              shadowColor: "rgba(128, 217, 248, 1)",
              // shadowColor: 'rgba(255, 255, 255, 1)',
              shadowOffsetX: -2,
              shadowOffsetY: 2,
              shadowBlur: 10,
            },
            emphasis: {
              areaColor: "#389BB7",
              borderWidth: 0,
            },
          },
          emphasis: {
            disabled: false,
            focus: "none",
            label: {
              color: "rgba(255, 255, 255, 1)",
              fontWeight: "bold",
              fontSize: 16,
            },
            itemStyle: {
              borderWidth: 2.5,
              borderColor: "#9AD0F7",
              areaColor: "#962029",
              shadowColor: "rgba(255, 255, 255, 0.3)",
              shadowBlur: 10,
              shadowOffsetX: 4,
              shadowOffsetY: 4,
            },
          },
          tooltip: {
            show: true,
            textStyle: {
              color: "#FFF",
            },
            backgroundColor: "#011e49",
            borderColor: "transparent",
            borderWidth: 0,
            textShadowColor: "transparent",
            formatter: function (params) {
              let cityitem = that.tableData.find((item) => {
                return item.areaname === params.name;
              });
              // console.log('data', cityitem);
              if (typeof cityitem === "undefined") return "";
              let returnHtml = `
                <div style="font-size: 20px;font-weight: bold;margin-bottom: 6px;">${cityitem.areaname}</div>
                <div style="font-size: 16px;margin-bottom: 22px;">${cityitem.title}</div>
                <div style="font-size: 18px;margin-bottom: 14px;">${cityitem.value}(个)</div>
              `;
              return returnHtml;
            },
          },
        },
        series: [
          {
            name: "散点",
            type: "scatter",
            coordinateSystem: "geo",
            data: data,
            symbol: "pin",
            symbolSize: 30,
            label: {
              normal: {
                show: false,
              },
              emphasis: {
                show: false,
              },
            },
            itemStyle: {
              normal: {
                color: "#33C481",
              },
              emphasis: {
                color: "#f00",
              },
            },
          },
        ],
      };
      if (that.chart === null) {
        that.chart = that.$echarts.init(this.$refs.myChart);
      }
      that.chart.setOption(option);
      that.chart.on("click", (params) => {
        console.log("点击到了");

        if (
          params.componentType === "series" &&
          params.componentSubType === "scatter"
        ) {
          const index = params.dataIndex;
          // 取消所有散点高亮
          that.chart.dispatchAction({
            type: "downplay",
            seriesIndex: 0, //第几条series
          });
          // 显示指定data 的tooltip
          // myChart.dispatchAction({
          //     type: 'showTip',
          //     seriesIndex: 0, //第几条series
          //     dataIndex: index, //第几个tooltip
          // });
          // 高亮指定的散点
          that.chart.dispatchAction({
            type: "highlight",
            seriesIndex: 0, //第几条series
            dataIndex: index, //第几个tooltip
          });
        }
      });

      window.addEventListener("resize", function () {
        that.chart.resize();
      });
    },
  },
  beforeDestroy() {
    if (this.chart) {
      this.chart.dispose();
    }
  },
};
</script>
<style scoped>
.container {
  width: 1006px;
  height: 696px;
}
</style>
地图贴图:

图片例子:

代码示例:(复制即可食用)

根据DataV.GeoAtlas地理小工具系列,获取到全国地图 china.json 数据。

代码中的 tuceng.jpeg 是上面的第一张图

<template>
  <div class="container">
    <div ref="myChart" style="width: 100%; height: 100%"></div>
  </div>
</template>
 
<script>
import hebei from "../../public/extend/map/json/province/china.json";
export default {
  components: {},
  props: {},
  data() {
    return {
      chart: null,
      hebei,
      regions: [],
      tableData: [],
      mapBg: require("../assets/tuceng.jpeg"),
    };
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {
    this.$nextTick(() => {
      this.initChart();
    });
  },
  methods: {
    initChart() {
      let that = this;
      that.$echarts.registerMap("hebei", this.hebei);
      let mapBg = document.createElement("img");
      mapBg.src = this.mapBg;
      // 散点数据
      var data = [
        {
          name: "河北科技大学",
          areaname: "河北科技大学",
          value: ["114.50963", "37.97686"],
          date: "2025-10-01",
          title: "石家庄",
        },
      ];
      that.tableData = data;
      var option = {
        tooltip: {
          trigger: "item",
        },
        visualMap: {
          show: true,
          min: 0,
          max: 200,
          left: "left",
          top: "bottom",
          text: ["高", "低"], // 文本,默认为数值文本
          calculable: true,
          seriesIndex: [1],
          inRange: {
            color: ["#00467F", "#A5CC82"], // 蓝绿
          },
        },
        geo: {
          map: "hebei",
          roam: true,
          zoom: 1.0,
          aspectScale: 1,
          scaleLimit: {
            min: 1,
            max: 2,
          },
          selectedMode: "single",
          select: {
            disabled: true,
            itemStyle: {
              borderWidth: 2.5,
              areaColor: "rgb(8, 207, 221)",
              shadowColor: "rgba(255, 255, 255, 0.3)",
              shadowBlur: 10,
              shadowOffsetX: 4,
              shadowOffsetY: 4,
            },
            label: {
              color: "rgba(255, 255, 255, 1)",
            },
          },
          regions: that.tableData,
          label: {
            show: true, // 是否显示地图上的所有地名
            color: "rgba(255, 255, 255, 0.6)",
            fontWeight: "bold",
            fontSize: 16,
          },
          itemStyle: {
            normal: {
              areaColor: {
                image: mapBg, // 支持为 HTMLImageElement, HTMLCanvasElement,不支持路径字符串
                repeat: "repeat", // 是否平铺, 可以是 'repeat-x', 'repeat-y', 'no-repeat'
              }
            },
            emphasis: {
              areaColor: {
                // 支持为 HTMLImageElement, HTMLCanvasElement,不支持路径字符串
                image: mapBg,
                // 是否平铺, 可以是 'repeat-x', 'repeat-y', 'no-repeat'
                repeat: "repeat"
              },
            },
          },
          emphasis: {
            disabled: false,
            focus: "none",
            label: {
              color: "rgba(255, 255, 255, 1)",
              fontWeight: "bold",
              fontSize: 16,
            },
            itemStyle: {
              borderWidth: 2.5,
              borderColor: "#9AD0F7",
              areaColor: "#962029",
              shadowColor: "rgba(255, 255, 255, 0.3)",
              shadowBlur: 10,
              shadowOffsetX: 4,
              shadowOffsetY: 4,
            },
          },
          tooltip: {
            show: true,
            textStyle: {
              color: "#FFF",
            },
            backgroundColor: "#011e49",
            borderColor: "transparent",
            borderWidth: 0,
            textShadowColor: "transparent",
            formatter: function (params) {
              let cityitem = that.tableData.find((item) => {
                return item.areaname === params.name;
              });
              // console.log('data', cityitem);
              if (typeof cityitem === "undefined") return "";
              let returnHtml = `
                <div style="font-size: 20px;font-weight: bold;margin-bottom: 6px;">${cityitem.areaname}</div>
                <div style="font-size: 16px;margin-bottom: 22px;">${cityitem.title}</div>
                <div style="font-size: 18px;margin-bottom: 14px;">${cityitem.value}(个)</div>
              `;
              return returnHtml;
            },
          },
        },
        series: [
          {
            name: "散点",
            type: "scatter",
            coordinateSystem: "geo",
            data: data,
            symbol: "pin",
            symbolSize: 30,
            label: {
              normal: {
                show: false,
              },
              emphasis: {
                show: false,
              },
            },
            itemStyle: {
              normal: {
                color: "#33C481",
              },
              emphasis: {
                color: "#f00",
              },
            },
          },
        ],
      };
      if (that.chart === null) {
        that.chart = that.$echarts.init(this.$refs.myChart);
      }
      that.chart.setOption(option);
      that.chart.on("click", (params) => {
        console.log("点击到了");

        if (
          params.componentType === "series" &&
          params.componentSubType === "scatter"
        ) {
          const index = params.dataIndex;
          // 取消所有散点高亮
          that.chart.dispatchAction({
            type: "downplay",
            seriesIndex: 0, //第几条series
          });
          // 显示指定data 的tooltip
          // myChart.dispatchAction({
          //     type: 'showTip',
          //     seriesIndex: 0, //第几条series
          //     dataIndex: index, //第几个tooltip
          // });
          // 高亮指定的散点
          that.chart.dispatchAction({
            type: "highlight",
            seriesIndex: 0, //第几条series
            dataIndex: index, //第几个tooltip
          });
        }
      });

      window.addEventListener("resize", function () {
        that.chart.resize();
      });
    },
  },
  beforeDestroy() {
    if (this.chart) {
      this.chart.dispose();
    }
  },
};
</script>
<style scoped>
.container {
  width: 900px;
  height: 630px;
}
</style>

<think>我们正在开发一个3D城市建筑大屏项目,使用VueECharts和Three.js。需要实现3D地图展示、路线规划和地图动态交互功能。以下是推荐的封装库及其作用,以及相关的开源项目参考。 ### 1. 核心库 - **Vue.js**:作为前端框架,用于构建用户界面和组件化开发。 - **Three.js**:用于创建和展示3D图形,构建3D城市建筑模型和场景。 - **ECharts**:用于展示2D图表,如柱状图、折线图等,可结合3D场景展示数据。 ### 2. 推荐的封装库及作用 #### a) 3D场景构建与渲染 - **TroisJS**:一个基于Three.js的Vue3组件库,允许在Vue中直接使用Three.js的组件,简化Three.js在Vue中的使用[^1]。 - 作用:快速搭建3D场景,包括相机、灯光、几何体等,支持响应式更新。 - **vue-threejs**:Vue的Three.js封装,提供Vue组件化的方式使用Three.js。 - 作用:便于在Vue项目中集成Three.js,实现3D模型的加载和交互。 #### b) 地图处理与可视化 - **CesiumJS**:虽然不直接基于Three.js,但它是专业的地理空间可视化库,支持3D地形、影像、矢量数据等。如果项目需要真实地理坐标,可以考虑结合使用(通过Cesium的3D Tiles和Three.js的集成)[^2]。 - **maptalks.js**:一个轻量级的地理信息可视化库,支持与Three.js集成,用于创建3D地图[^3]。 - 作用:提供地理信息基础,支持路线规划、标记等。 #### c) 路线规划 - **GraphHopper**:开源的路线规划引擎,提供API和JavaScript库,可用于计算两之间的最优路径(驾车、步行等)[^4]。 - **OSRM**:开源的路由引擎,提供快速的路线规划服务,支持Node.js集成[^5]。 - 作用:在后端或前端计算路线,将路线坐标传递给Three.js进行3D可视化。 #### d) 地图交互 - **three.interactive**:一个Three.js的交互扩展库,允许为3D对象添加鼠标事件(点击、悬停等)[^6]。 - 作用:实现3D建筑的点击交互、路线选择的交互等。 - **vue-use-gesture**:一个Vue组合式API,用于处理手势交互(拖动、缩放等)[^7]。 - 作用:结合Three.js实现3D场景的旋转、缩放、平移等交互。 #### e) 数据可视化(结合ECharts) - **echarts-gl**:ECharts的3D扩展,支持在3D场景中绘制图表(如3D柱状图、图等)[^8]。 - 作用:在3D城市地图上叠加数据可视化,如建筑物高度用柱状图表示。 - **vue-echarts**:VueECharts组件,便于在Vue中使用ECharts[^9]。 ### 3. 开源项目参考 1. **Astral 3D Editor**:一个基于Vue3和Three.js的国产开源3D数字孪生编辑器,支持轻量化解析CAD/BIM模型,提供从数据到可视化的解决方案[^10]。适用于智慧园区、智慧建筑等场景。 - 特:支持多种3D格式,提供场景搭建、灯光、摄像机初始化等功能。 2. **antv L7**:阿里开源的地理空间数据可视化引擎,支持2D和3D地图(使用Three.js渲染)[^11]。虽然主要关注地理空间,但可以用于城市建筑可视化。 - 特:提供丰富的图层和交互能力,支持路线规划可视化。 3. **CityBuilder**:一个基于Three.js的3D城市构建工具,支持导入GIS数据生成3D城市模型,并实现交互[^12]。 - 特:开源项目,可用于构建基础3D城市场景,并添加路线规划功能。 4. **Vue3 + Three.js + ECharts 大屏项目模板**:许多开源的大屏项目模板(如GitHub上的vue-big-screen)集成了这些技术,可作为参考[^13]。 ### 4. 实现步骤建议 1. **搭建基础场景**:使用TroisJS或vue-threejs在Vue中初始化Three.js场景,包括相机、灯光、渲染器。 2. **加载城市模型**:使用Three.js加载3D建筑模型(如GLTF格式)或通过程序生成建筑(基于GIS数据)。 3. **集成地图底图**:如果需要真实地理坐标,使用maptalks.js或Cesium作为底图,并与Three.js集成。 4. **路线规划**:通过GraphHopper或OSRM计算路线,将路线坐标转换为3D空间中的线条或管道,添加到场景中。 5. **交互功能**:使用three.interactive为建筑和路线添加点击事件,实现信息弹窗、高亮等效果。 6. **数据可视化**:在3D场景中,使用echarts-gl在建筑物顶部添加柱状图,或使用vue-echarts2D面板展示数据。 7. **动态效果**:实现动画效果,如车辆沿路线移动、建筑生长动画等。 ### 5. 注意事项 - 性能优化:3D场景对性能要求高,需注意模型面数、纹理压缩、实例化渲染等。 - 坐标转换:若使用真实地理数据,需将经纬度坐标转换为Three.js的3D坐标。 §§相关问题§§ 1. 如何将真实地理坐标(经纬度)转换为Three.js中的3D坐标? 2. 在Vue中如何优化Three.js的性能以避免内存泄漏? 3. 有哪些方法可以实现3D建筑模型的动态生长效果? 4. 如何实现3D场景中的路线动画(如车辆移动)?</think>以下是为开发3D城市建筑大屏项目推荐的封装库及实现方案,结合Vue3、ECharts和Three.js技术栈,重解决3D地图展示、路线规划和动态交互需求: --- ### 一、核心封装库推荐及作用 #### 1. **Three.js 基础封装** - **TroisJS**(Vue3专用) - 作用:提供声明式Three.js组件(如`<Renderer>`, `<Camera>`, `<Mesh>`),简化Three.js在Vue中的集成 - 关键能力:自动响应式更新、场景管理、灯光控制 - 示例:创建3D建筑场景仅需模板语法 ```vue <template> <Renderer> <Camera :position="{ z: 10 }" /> <Scene> <Box :size="1" :position="{ y: 2 }" /> </Scene> </Renderer> </template> ``` #### 2. **地理空间可视化** - **CesiumJS + vue-cesium** - 作用:实现高精度3D地形与建筑可视化 - 核心功能:WGS84坐标支持、3DTiles建筑加载、地形高程数据渲染 - 优势:支持真实地理坐标系统,适合城市规划场景 - **maptalks.three**(轻量级替代) - 作用:将Three.js对象集成到2D/3D地图(适合非地理坐标系场景) #### 3. **路线规划引擎** - **OSRM.js**(开源路由引擎) - 作用:计算最优路径(驾车/步行),返回路径坐标 - 集成方式:通过Axios调用本地或远程OSRM服务 ```javascript axios.get(`http://router.project-osrm.org/route/v1/driving/${startLng},${startLat};${endLng},${endLat}`) ``` - **GraphHopper JS**(商业级替代) - 优势:支持多途经、实时交通数据 #### 4. **动态交互控制** - **three.interactive** - 作用:为Three.js对象添加鼠标事件(点击/悬停) - 关键API:`addObject()`注册交互对象,`on('click')`事件监听 - **tween.js** - 作用:实现路径动画、相机运动平滑过渡 - 示例:相机飞行到目标建筑 ```javascript new TWEEN.Tween(camera.position) .to({ x: 10, y: 5, z: 8 }, 1000) .easing(TWEEN.Easing.Cubic.Out) .start(); ``` #### 5. **ECharts 3D 融合** - **echarts-gl** - 作用:在Three.js场景中嵌入3D图表 - 典型应用:建筑顶部的数据柱状图、悬浮数据标签 - 配置要:通过`coordinateSystem: 'three'`绑定到同一坐标系 --- ### 二、地图交互实现方案 #### 1. **3D建筑展示** ```javascript // 加载GLTF建筑模型 import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; const loader = new GLTFLoader(); loader.load('city.gltf', (gltf) => { scene.add(gltf.scene); // 添加交互 interactive.add(gltf.scene.children[0]); }); ``` #### 2. **路线规划可视化** ```javascript // 将OSRM返回的路径转为3D线条 const pathPoints = osrmResponse.routes[0].geometry.coordinates.map(coord => new THREE.Vector3(coord[0], 0, coord[1]) ); const pathGeometry = new THREE.BufferGeometry().setFromPoints(pathPoints); const pathLine = new THREE.Line(pathGeometry, new THREE.LineBasicMaterial({ color: 0xff0000 })); scene.add(pathLine); ``` #### 3. **动态交互功能** - **建筑点击高亮** ```javascript interactive.on('click', (event) => { event.object.material.emissive.set(0xff0000); // 红色发光 }); ``` - **路线漫游动画** ```javascript const cameraPath = new THREE.CatmullRomCurve3(pathPoints); function animateCamera() { const point = cameraPath.getPoint(progress); camera.position.copy(point); requestAnimationFrame(animateCamera); } ``` --- ### 三、开源项目参考 1. **[Astral 3D Editor](https://github.com/Astral3D/editor)** [^3] - 特:国产开源数字孪生编辑器,支持BIM/CAD模型解析 - 适用:智慧园区可视化,提供Vue3+Three.js完整工作流 2. **[CIM-BIM-Vue3](https://github.com/giscafer/cim-bim-vue3)** - 特:集成Cesium的城市信息模型平台 - 亮:建筑剖切分析、管线可视化 3. **[ThreeJS-Vue-Boilerplate](https://github.com/troisjs/troisjs)** - 特:TroisJS官方示例项目 - 包含:模型加载、粒子系统、后期特效实现 4. **[OSRM Demo Frontend](https://github.com/Project-OSRM/osrm-frontend)** - 作用:路线规划前端参考实现 - 技术:Leaflet + 路径动画 --- ### 四、性能优化建议 1. **模型轻量化**:使用Draco压缩GLTF模型(`gltf-pipeline`工具) 2. **LOD控制**:根据相机距离切换建筑细节层次 ```javascript const lod = new THREE.LOD(); lod.addLevel(highDetailModel, 50); // 50米内高模 lod.addLevel(lowDetailModel, 100); // 100米外低模 ``` 3. **GPU拾取优化**:用`GPU_Picking`替代`Raycaster`(万级对象场景) > 提示:大屏项目建议使用WebGL2渲染,启用`THREE.WebGLRenderer`的`antialias`和`powerPreference: "high-performance"`参数[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值