足迹地图更新:区域高亮显示
距离我第一次发布 基于高德地图实现 Hugo 足迹地图短代码 已经过去了一段时间,一直都没有进行更新。
直到昨天 S 大佬提出了一个高品质需求,更新刻不容缓:

当然最终还是大佬靠自己解决了😈……大家可围观大佬的足迹: 足迹 - 旅行漫记
但是觉得这个想法还是挺实用的,有一种逐渐点亮整个世界的感觉,成就满满!
整个过程也很简单,借助 AI,很快实现了鼠标悬停时的高亮效果,后来又实现了已标记地点的自动保持高亮的效果,最后因这两种效果都想保持且希望实现随时转换,因此直接解耦成单独的 js 文件,实现插件化调用。
演示效果可访问: 足迹 - 小十的个人博客
一、主要特性
1. 国家与国内省份高亮
不再局限于孤立的标记点,地图集成了边界解析引擎,国内精确到省级行政区,国外精确到国家级,并实现两者兼容展示。足迹落在哪里,哪里的版图就会被点亮。
2. 高亮功能插件化
不同的使用者可能对高亮的展示有不同的偏好,将高亮功能的实现拆分成了两个独立的插件:
- 🌍 永久高亮模式 (
plugin-visited.js):只要你的足迹数据里包含该区域的坐标,该省份/国家就会被永久高亮(默认呈现清新的薄荷青配色)。 - 🖱️ 悬浮高亮模式 (
plugin-hover.js):默认地图保持纯净,只有当鼠标滑过标记点或地图空白区域时,该区域才会高亮。
3. 智能标签排除(如“计划”行程)
有些地点我们可能只是打了个 ["计划"] 的标签,人还没去过。现在,插件内置了标签过滤,可以通过配置排除特定标签,未成行的足迹不会干扰到区域的点亮状态。
二、如何获取与处理边界数据
要实现区域高亮,最核心的基础就是 GeoJSON 边界数据。
1. 获取国内省份数据
国内的省级边界数据相对容易获取,强烈推荐使用 阿里云 DataV 数据可视化平台 。
直接下载 GeoJSON 即可,并将文件命名为 provinces.geojson。
2. 获取全球国家数据
全球数据我使用的是 Natural Earth 提供的开源标准数据集: Datahub - Geo Countries 。
在这个页面你可以下载到包含全球所有国家边界的 countries.geojson 文件。
但是这个毕竟是以西方为主导编写的数据,有一些错误观念在里面,但一时没找到合适的,大家如果有靠谱的数据,也欢迎评论推荐!
3. 使用 Mapshaper 压缩数据
标准的全球 GeoJSON 文件高达 14.6MB,特别是包含了海岸线细节。
如果直接交给前端解析,不仅会造成线程阻塞,还容易导致 3D 引擎在渲染几十万个顶点时直接崩溃。
因为只需要体现一个大概区域即可,所以需要给数据进行拓扑简化:
- 打开在线 GIS 拓扑处理工具: Mapshaper (mapshaper.org) 。
- 导入 14.6MB 的
countries.geojson。 - 点击右上角的
Simplify(简化) 按钮。 - 勾选
prevent shape removal(防止小岛屿彻底消失),点击 Next。 - 将页面上方的滑块向左拉,拉到大概
2%到4%的位置。会发现地图的大致轮廓依然清晰,但顶点数量锐减了 95% 以上。 - 点击右上角
Export,选择GeoJSON格式导出。
经过这一步, 14MB 的文件会压缩至 500KB 左右,重命名为 world.geojson,将这两个文件放入项目的 static/data/ 目录下,基础数据准备就完成了。
三、WebGL 引擎的踩坑记录
这次更新表面看起来只是加载了两个 GeoJSON 图层,但过程还是比较曲折。
1. 透明度导致事件穿透(拾取失效)
在悬浮高亮模式中,未激活的省份应该是完全透明的,因此将样式设为 fillOpacity: 0。
现象:高德的 3D WebGL 引擎为了性能优化,直接在底层的射线碰撞检测(Hit Test)中剔除了完全透明的物体,导致鼠标滑过省份时根本触发不了 mouseover 事件。
措施:在插件内部运用代理模式封装了一个 getSafeStyle() 方法。外部配置依然写 0,但插件在传给高德底层时,会将其转换为 0.01。肉眼几乎看不出来,却能触发全地图的鼠标交互。
getSafeStyle(styleObj) {
const safeStyle = { ...styleObj };
// 如果是 0,强行转为 0.01 规避 WebGL 事件剔除 Bug
if (safeStyle.fillOpacity === 0) safeStyle.fillOpacity = 0.01;
if (safeStyle.strokeOpacity === 0) safeStyle.strokeOpacity = 0.01;
return safeStyle;
}2. 两个 GeoJSON 文件兼容问题
同时加载 provinces.geojson (中国省份) 和 world.geojson (世界地图) 时,国内的省份高亮全部失效了。
现象:世界地图里本身就包含了一个完整的“中国”区块。虽然没让它亮,但它作为一个隐形的巨大多边形,层级盖在了各个省份之上,形成了一个隐形遮罩,把鼠标悬浮事件全挡住了。
措施:在解析 GeoJSON 时,通过 properties 进行拦截。一旦发现是“中国”,直接 return null,在物理层面彻底将高亮交互功能恢复到 34 个省级区块上。
3. 复杂群岛导致的破面拉丝与引擎崩溃
像美国、英国、印度,甚至国内的广东、浙江,在地理上拥有大量的海外飞地和附属小岛,这种数据结构在 GeoJSON 中被称为 MultiPolygon(多重多边形)。
现象:高德原生的 AMap.GeoJSON 插件在渲染这种嵌套极深的 3D 数组时存在底层 Bug。它会在主大陆和岛屿之间的海面上,错误地拉出网格连线,形成满屏的“重影和蜘蛛网”(GIS 领域俗称 破面拉丝 Polygon Spiking),最终导致地图彻底卡死。
措施:放弃高德内置的自动解析,重写了底层分发逻辑。只要遇到 MultiPolygon,就把它强行拆解,为独立的岛屿单独实例化一个 AMap.Polygon**。最后,通过判断 _pName(所属国家/省份名称),在逻辑层面将岛屿绑定在一起。
核心代码如下:
let polygonsCoords = [];
// 1. 拆解 MultiPolygon 为一维的普通 Polygon 坐标数组
if (geom.type === 'Polygon') {
polygonsCoords = [geom.coordinates];
} else if (geom.type === 'MultiPolygon') {
polygonsCoords = geom.coordinates;
}
// 2. 每一座岛屿独立渲染,彻底消灭破面拉丝
polygonsCoords.forEach(coords => {
const polygon = new AMap.Polygon({
path: coords,
// ... 注入安全样式
});
// 3. 逻辑绑定,记录它的名称(如 '英国')
polygon._geoJsonGeometry = geom;
polygon._pName = pName;
// 鼠标悬浮时,记录当前悬浮的名称
polygon.on('mouseover', () => { this.hoveredName = pName; this.refreshStyles(); });
this.regionPolygons.push(polygon);
});
// 4. 在 refreshStyles 中,只要名字对上,主大陆和所有群岛一起高亮
this.regionPolygons.forEach(polygon => {
if (polygon._pName && polygon._pName === this.hoveredName) {
polygon.setOptions(safeActive);
} else {
polygon.setOptions(safeDefault);
}
});四、两种高亮模式的插件化
为了确保主干代码的简洁,将高亮相关的功能独立成单独的文件,以插件方式进行接入:
1. 按顺序引入 JS 文件
主核心文件负责绘制基础地图、点位和弹窗。高亮插件会自动寻找主干并挂载。
<script src="/js/footprintmap.js"></script>
<script src="/js/plugin-visited.js"></script>
<script src="/js/plugin-hover.js"></script>2. 在配置中一键切换模式
在 footprintmap.js 顶部的 CONFIG 中,你可以随意切换高亮模式:
HIGHLIGHT: {
// 模式选择:'visited' (永久高亮), 'hover' (悬浮高亮), 'none' (关闭)
mode: 'visited',
geojsonUrls: [
'./static/data/provinces.geojson',
'./static/data/world.geojson' // 全球与省份可同时加载
],
// 带有此标签的足迹不会触发高亮
excludeTags: ['计划'],
style: {
default: {
strokeColor: '#ffffff', strokeOpacity: 0, strokeWeight: 0,
fillColor: '#ffffff', fillOpacity: 0, zIndex: 1
},
active: {
strokeColor: '#5ee7df', strokeOpacity: 0.8, strokeWeight: 1,
fillColor: '#06beb6', fillOpacity: 0.07, zIndex: 10
}
}
}五、结语
所有的源码、数据处理示例均已更新至 GitHub 仓库: XiaoTen-FootprintMap GitHub
如果你觉得这个项目还不错,欢迎去给个 Star ⭐️ 支持一下!快去更新你的足迹地图,点亮你的世界版图吧!🌍
© 转载需附带本文链接,依据 CC BY-NC-SA 4.0 发布。