0
点赞
收藏
分享

微信扫一扫

echatrs采用webGL 的geo3D或者map3D实现3D地图

千白莫 21小时前 阅读 1

在 Vue 3 和 TypeScript 项目中实现 3D 地图展示既能为应用增添视觉冲击力,也能提升数据表达的丰富性。ECharts GL 提供的 geo3Dmap3D 组件是实现这一功能的得力工具。下面我将为你解释它们的区别,并提供实现的思路、示例代码和实用建议。

🗺️ 使用 ECharts GL 在 Vue 3 中实现 3D 地图

✨ 效果展示

以下是通过 geo3D 实现的 3D 地图效果,支持旋转、缩放、区域高亮、数据点标记等交互功能。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue 3 + ECharts GL 3D 地图示例</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/echarts-gl@2.0.9/dist/echarts-gl.min.js"></script>
    <style>
        #mapContainer {
            width: 100%;
            height: 600px;
        }
    </style>
</head>
<body>
    <div id="mapContainer"></div>
    <script>
        // 初始化 ECharts 实例
        const chartDom = document.getElementById('mapContainer');
        const myChart = echarts.init(chartDom);
        
        // 示例配置选项
        const option = {
            tooltip: {
                trigger: 'item',
                formatter: params => {
                    if (params.componentType === 'series' && params.seriesType === 'scatter3D') {
                        return `城市: ${params.data[3]}<br/>值: ${params.data[2]}`;
                    }
                    return params.name;
                }
            },
            visualMap: {
                show: true,
                left: 'right',
                min: 0,
                max: 100,
                inRange: {
                    color: ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']
                },
                calculable: true,
                textStyle: {
                    color: '#fff'
                }
            },
            geo3D: {
                map: 'china',
                boxHeight: 5, // 地图区域的高度或厚度
                regionHeight: 5, // 地图版块厚度
                environment: 'auto',
                itemStyle: {
                    color: 'rgba(95,158,160,0.5)', // 地图板块的颜色
                    opacity: 1, // 图形的不透明度 [default: 1]
                    borderWidth: 0.5, // 图形描边的宽度
                    borderColor: '#000' // 图形描边的颜色
                },
                emphasis: {
                    itemStyle: {
                        color: '#66ffff' // 高亮状态下的颜色
                    }
                },
                label: {
                    show: true,
                    color: '#000', // 标签颜色
                    fontSize: 8 // 字体大小
                },
                shading: 'lambert', // 着色方式,可选 'color', 'lambert', 'realistic'
                light: {
                    main: {
                        intensity: 1.2, // 光照强度
                        shadow: false, // 是否显示阴影
                        alpha: 55, // 主光源绕 x 轴,即上下旋转的角度
                        beta: 10 // 主光源绕 y 轴,即左右旋转的角度
                    },
                    ambient: {
                        intensity: 0.5 // 环境光的强度
                    }
                },
                viewControl: {
                    projection: 'perspective', // 投影方式,默认为透视投影'perspective'
                    autoRotate: false, // 是否开启视角绕物体的自动旋转查看
                    autoRotateSpeed: 10, // 物体自传的速度
                    distance: 120, // 默认视角距离主体的距离
                    minDistance: 40, // 视角通过鼠标控制能拉近到主体的最小距离
                    maxDistance: 400, // 视角通过鼠标控制能拉远到主体的最大距离
                    alpha: 40, // 视角绕 x 轴,即上下旋转的角度
                    beta: 0, // 视角绕 y 轴,即左右旋转的角度
                    panMouseButton: 'left', // 平移操作使用的鼠标按键
                    rotateMouseButton: 'right' // 旋转操作使用的鼠标按键
                }
            },
            series: [
                {
                    type: 'scatter3D',
                    coordinateSystem: 'geo3D',
                    symbol: 'circle',
                    symbolSize: 10,
                    itemStyle: {
                        color: '#ff0000'
                    },
                    data: [
                        [116.46, 39.92, 50, '北京'],
                        [121.48, 31.22, 80, '上海'],
                        [114.07, 22.62, 30, '深圳'],
                        [113.53, 22.3, 40, '珠海'],
                        [113.23, 23.16, 60, '广州']
                    ]
                }
            ]
        };
        
        // 使用配置项和数据显示图表
        myChart.setOption(option);
        
        // 响应窗口大小变化
        window.addEventListener('resize', function() {
            myChart.resize();
        });
    </script>
</body>
</html>

📦 实现步骤

1. 环境搭建

首先安装必要的依赖:

npm install echarts echarts-gl

2. 准备地图数据

你需要获取相应区域的 GeoJSON 数据。可以从以下来源获取:

  • 阿里云 DataV:http://datav.aliyun.com/portal/school/atlas/area_selector
  • GitHub 资源:例如 https://github.com/Bing0915/city_geojson

3. Vue 3 组件封装 (Composition API + TypeScript)

创建一个可复用的 3D 地图组件 (Map3D.vue):

<template>
  <div ref="chartRef" class="map-3d-container"></div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue';
import * as echarts from 'echarts';
import 'echarts-gl';
import { ECharts, EChartsOption } from 'echarts';

// 定义组件接收的 props
interface Map3DProps {
  mapData: any; // GeoJSON 数据
  mapName?: string; // 注册的地图名称
  option?: EChartsOption; // 可自定义的 ECharts 配置项
}

const props = withDefaults(defineProps<Map3DProps>(), {
  mapName: 'myMap',
});

const chartRef = ref<HTMLElement>();
let chartInstance: ECharts | null = null;

// 初始化图表
const initChart = () => {
  if (!chartRef.value) return;

  chartInstance = echarts.init(chartRef.value);
  
  // 注册地图
  echarts.registerMap(props.mapName, props.mapData);

  const defaultOption: EChartsOption = {
    tooltip: {
      trigger: 'item',
      formatter: (params: any) => {
        if (params.componentType === 'series' && params.seriesType === 'scatter3D') {
          return `城市: ${params.data[3]}<br/>值: ${params.data[2]}`;
        }
        return params.name;
      }
    },
    geo3D: {
      map: props.mapName,
      boxHeight: 5,
      regionHeight: 5,
      itemStyle: {
        color: 'rgba(95,158,160,0.5)',
        borderWidth: 0.5,
        borderColor: '#000'
      },
      emphasis: {
        itemStyle: {
          color: '#66ffff'
        }
      },
      label: {
        show: true,
        color: '#000',
        fontSize: 8
      },
      shading: 'lambert',
      viewControl: {
        distance: 120,
        alpha: 40,
        beta: 0,
        panMouseButton: 'left',
        rotateMouseButton: 'right'
      }
    }
  };

  // 合并默认配置和传入的配置
  const finalOption = { ...defaultOption, ...props.option };
  chartInstance.setOption(finalOption);
};

// 响应窗口变化
const handleResize = () => {
  chartInstance?.resize();
};

onMounted(() => {
  initChart();
  window.addEventListener('resize', handleResize);
});

onUnmounted(() => {
  window.removeEventListener('resize', handleResize);
  chartInstance?.dispose();
});

// 监听 props 变化
watch(() => props.mapData, () => {
  if (chartInstance) {
    initChart();
  }
});

// 暴露方法或实例给父组件 (如果需要)
defineExpose({
  getInstance: () => chartInstance
});
</script>

<style scoped>
.map-3d-container {
  width: 100%;
  height: 600px;
}
</style>

4. 在父组件中使用

<template>
  <div>
    <Map3D :mapData="chinaJson" :mapName="china" :option="customOption" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import Map3D from '@/components/Map3D.vue';
import chinaJson from '@/assets/json/china.json'; // 导入 GeoJSON 数据

// 可以自定义配置覆盖默认配置
const customOption = ref({
  geo3D: {
    itemStyle: {
      color: 'rgba(25, 100, 150, 0.7)' // 自定义颜色
    }
  },
  series: [
    {
      type: 'scatter3D',
      coordinateSystem: 'geo3D',
      symbol: 'pin',
      symbolSize: 20,
      itemStyle: {
        color: '#ff0000'
      },
      data: [
        [116.46, 39.92, 50, '北京'],
        [121.48, 31.22, 80, '上海'],
        [114.07, 22.62, 30, '深圳']
      ]
    }
  ]
});
</script>

💡 高级功能与技巧

  1. 添加 3D 柱图 (bar3D) 你可以在地图上叠加 3D 柱图来表示不同区域的数据大小。

    // 在 option.series 中添加
    {
      type: 'bar3D',
      coordinateSystem: 'geo3D',
      shading: 'lambert',
      barSize: 1, // 柱子大小
      minHeight: 0.5, // 最小高度
      itemStyle: {
        color: '#5E5FFF'
      },
      data: originalDatas.datas.map((item) => {
        // 将数据转换为 bar3D 需要的格式 [经度, 纬度, 值]
        return [item.lng, item.lat, item.value];
      })
    }
    
  2. 使用纹理贴图 通过 realisticMaterial.detailTexture 可以为地图表面添加纹理,增强质感。

    geo3D: {
      // ... 其他配置
      shading: 'realistic',
      realisticMaterial: {
        detailTexture: '/path/to/texture.png', // 纹理贴图路径
        textureTiling: 1 // 纹理平铺
      }
    }
    
  3. 处理点击事件与高亮 为图表添加点击事件,并实现区域高亮效果。

    // 在初始化图表后
    chartInstance.on('click', (params: any) => {
      console.log('点击了:', params.name);
      // 可以通过 dispatchAction 高亮区域
      chartInstance.dispatchAction({
        type: 'highlight',
        seriesIndex: 0,
        name: params.name
      });
    });
    

⚠️ 注意事项

  • 版本兼容性: 确保 echartsecharts-gl 版本兼容。较新的 echarts (如 v5.4.3) 配合较新的 echarts-gl (如 v2.0.9) 通常较好。
  • 性能考虑: 3D 地图对性能要求较高,数据量大时注意优化(如减少不必要的视觉元素、简化几何体)。
  • 地图数据: 确保 GeoJSON 数据准确,并正确注册到 ECharts。
  • TypeScript 类型: 由于 ECharts GL 的类型定义可能不如 ECharts 核心库完善,有时可能需要 // @ts-ignore 或自行补充类型定义。
举报

相关推荐

0 条评论