169.在Vue3中使用OpenLayers + D3实现地图区块呈现不同颜色的效果
一、效果展示
二、准备工作 / 前置条件
Node.js + npm/yarn 已安装
使用 Vite 创建的 Vue 3 项目(或其它 Vue 3 脚手架)
安装依赖:
npm install ol d3 # or yarn add ol d3
准备 GeoJSON 文件(示例里假设文件路径为
src/assets/map/china.json
或放public/map/china.json
)
提示:如果用
import chinaData from '@/assets/map/china.json'
,确保你的项目支持@
到src
的别名(多数 Vue 项目有)。如果用fetch
,把文件放public
目录(/map/china.json
)并用fetch('/map/china.json')
。
三、完整可复制的示例组件(MapColor.vue)
直接把下面文件拷贝到
src/components/MapColor.vue
,并保证china.json
在src/assets/map/china.json
(或调整 import 路径)。
<!--* @Author: 彭麒* @Date: 2025/9/9* @Email: 1062470959@qq.com* @Description: 此源码版权归吉檀迦俐所有,可供学习和借鉴或商用。-->
<template><div class="container"><div class="w-full flex justify-center flex-wrap"><div class="font-bold text-[24px]">vue3+openlayers: 使用D3实现地图区块呈现不同颜色的效果</div></div><div id="vue-openlayers"></div></div>
</template><script setup>
import { ref } from "vue";
import "ol/ol.css";
import { Map, View } from "ol";
import SourceVector from "ol/source/Vector";
import LayerVector from "ol/layer/Vector";
import GeoJSON from "ol/format/GeoJSON";
import { Tile } from "ol/layer";
import XYZ from "ol/source/XYZ";
import Style from "ol/style/Style";
import Fill from "ol/style/Fill";
import Stroke from "ol/style/Stroke";
import { fromLonLat } from "ol/proj";
import * as d3 from "d3";// 引用数据
const geojsonObject = ref(null);
import {onMounted} from "vue";let map; // 地图实例// 视图
const view = new View({projection: "EPSG:3857",center: fromLonLat([122.8, 41.5]),zoom: 6,
});// 样式函数
const getStyle = () => {let colors = d3.schemeCategory10;let stroke = new Stroke({ color: "yellow", width: 1 });let randomIndex = Math.floor(Math.random() * 9);let fill = new Fill({ color: colors[randomIndex] });return new Style({ stroke, fill });
};// 初始化地图
const initMap = (geoData) => {// 在这里创建数据源const source = new SourceVector({features: new GeoJSON().readFeatures(geoData, {dataProjection: "EPSG:4326",featureProjection: "EPSG:3857",}),});map = new Map({target: "vue-openlayers",layers: [new Tile({source: new XYZ({url: "http://{a-c}.tile.openstreetmap.de/{z}/{x}/{y}.png",}),}),new LayerVector({source: source,style: getStyle,}),],view: view,});
};
const loadGeoJSON = async () => {try {const response = await fetch('/src/assets/map/china.json');geojsonObject.value = await response.json();// 传入实际的 GeoJSON 数据而不是 ref 对象initMap(geojsonObject.value);} catch (error) {console.error('加载GeoJSON文件失败:', error);}
};
// 生命周期
onMounted(() => {loadGeoJSON();
});
</script><style scoped>
.container {width: 840px;height: 550px;margin: 50px auto;border: 1px solid #42B983;
}#vue-openlayers {width: 800px;height: 420px;margin: 0 auto;border: 1px solid #42B983;position: relative;
}
</style>
四、关键点解析
1. 为什么要用属性哈希来映射颜色?
如果直接在样式函数里 Math.random()
生成颜色,每次渲染(或图层刷新、要素重绘)颜色都会改变,用户体验差。哈希算法可以把同一字符串(比如省名)稳定映射到同一颜色索引,从而保证一致性。
2. 为什么要做样式缓存(styleCache
)?
OpenLayers 的样式对象(Style
/ Fill
/ Stroke
)比较重,每次返回新对象会造成渲染性能问题。将每个颜色对应的 Style
缓存并复用可以显著提升性能。
3. GeoJSON 属性名问题
不同数据来源 properties
的字段名不一致(name
/ NAME
/ province
/ 省名
等)。最好在调试时检查数据,例如:
const features = new GeoJSON().readFeatures(chinaData); console.log(features[0].getProperties());
以确认你要使用的字段名。
4. 使用 import
vs fetch
import chinaData from '@/assets/map/china.json'
:将 GeoJSON 打包到 bundle 中,构建时被处理。路径要根据项目别名设置调整。fetch('/map/china.json')
:把文件放public
,运行时按路径读取(不会被打包)。适合大文件或运行时更新的 GeoJSON。
五、常见问题 & 解决方法
1) 瓦片不显示或跨域(CORS)问题
使用 HTTPS 的瓦片源(如
https://{a-c}.tile.openstreetmap.org/...
),避免混合内容报错。若使用自己的瓦片服务,需后端配置 CORS。
2) @
别名找不到
Vite 默认并不自动创建
@
,但 Vue 官方模板通常配置了。若出错,使用相对路径import chinaData from '../assets/map/china.json'
。
3) GeoJSON 地理坐标系不对
本文示例假设 GeoJSON 使用
EPSG:4326
(经纬度)。readFeatures
里正确设置dataProjection
与featureProjection
很重要,否则要素会显示在错误位置或看不见。
4) 要素样式在缩放下看起来“像素化”或边界线粗细不合适
可以在
style
函数里使用resolution
参数动态调整边界线宽度,或在高缩放级别改变样式。
六、进阶扩展
添加鼠标悬停 tooltip —— 使用
Overlay
或监听pointermove
事件,显示要素名称或统计信息。图例(Legend)生成 —— 根据
styleCache
或 colors 映射生成页面图例(缩略色块 + 名称)。按属性着色(例如按人口、密度做渐变) —— 使用
d3.scaleSequential
或d3.scaleLinear
映射数值到颜色。按分组着色 —— 先对要素分组,给不同组分配颜色数组段,或用颜色渐变区分数值区间。
性能优化 —— 要素非常多(数万)时,考虑简化要素几何(TopoJSON、 simplify)、使用矢量瓦片或按需加载。