使用 OpenLayers + 高德瓦片源实现旅游足迹地图
作为一个热爱旅行的开发者,我一直想要一个能够记录和展示自己旅游足迹的功能。市面上虽然有很多地图应用,但大多功能复杂,而我只需要一个简单直观的方式来标记去过的地方和想去的地方。
于是我决定在自己的个人网站上实现一个旅游足迹地图功能。这个功能的核心需求很简单:
- 在地图上标记去过的地方(绿色标记)
- 标记想去的地方(橙色标记)
- 支持点击查看详细信息
- 适配网站的亮色/暗色主题
- 在移动端也能良好展示
经过技术调研,我选择了 OpenLayers + 高德地图瓦片源的方案,既能满足功能需求,又能保证在国内的访问速度。
演示
旅游足迹地图演示
我的主页:https://fastcar.fun
你可以在我的关于页面看到这个旅游足迹地图的实际效果,地图上标记了我去过的城市和想去的地方。
功能点详解
技术选型考虑
在实现这个功能时,我面临几个技术选型的问题:
地图库选择:
- Google Maps API:需要翻墙,在国内访问不稳定
- 百度地图 API:需要申请 key,有使用限制
- 高德地图 API:同样需要申请 key
- OpenLayers + 开放瓦片源:免费、灵活、无需 key
最终选择 OpenLayers 是因为它是一个功能强大的开源地图库,支持多种瓦片源,而且可以直接使用高德地图的公开瓦片服务,无需申请 API key。
瓦片源选择:
高德地图提供了公开的瓦片服务,支持多种样式:
- 标准地图:
style=8
(亮色主题) - 暗色地图:
style=7
(暗色主题) - 卫星图:
style=6
- 路网图:
style=1
这些瓦片源都支持中文标注,非常适合国内用户使用。
核心功能实现
1. 地图初始化
使用 OpenLayers 创建地图实例,配置高德瓦片源,设置合适的中心点和缩放级别。
2. 双主题支持
创建亮色和暗色两个图层,根据网站主题动态切换显示。
3. 标记系统
使用矢量图层添加标记点,区分"去过"和"想去"两种类型,使用不同的颜色和图标。
4. 交互功能
实现点击标记显示详细信息的弹窗,包括地点名称、描述、访问时间等。
5. 响应式设计
适配移动端显示,调整地图高度和弹窗样式。
架构图解
整体架构图
数据流图
主题切换时序图
交互流程图
代码实现
地图初始化核心代码
function initMap() {// 亮色主题图层 - 高德地图标准地图lightLayer = new ol.layer.Tile({source: new ol.source.XYZ({url: 'https://webrd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',attributions: '© <a href="https://www.amap.com/">高德地图</a>',maxZoom: 18})})// 暗色主题图层 - 高德地图暗色标准地图darkLayer = new ol.layer.Tile({source: new ol.source.XYZ({url: 'https://webrd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}',attributions: '© <a href="https://www.amap.com/">高德地图</a>',maxZoom: 18}),visible: false})// 创建地图map = new ol.Map({target: 'travel-map',layers: [lightLayer, darkLayer],view: new ol.View({center: ol.proj.fromLonLat([105.0, 35.0]), // 中国中心zoom: 4,maxZoom: 18,minZoom: 2})})
}
实现要点:
- 使用高德地图的公开瓦片服务,URL 中的
{1-4}
表示负载均衡的服务器 style=8
是标准地图,style=7
是暗色地图lang=zh_cn
确保中文标注- 初始时暗色图层设置为不可见
标记添加核心代码
function addMarkers() {const vectorSource = new ol.source.Vector()// 添加已去过的地方data.visited.forEach(location => {const feature = new ol.Feature({geometry: new ol.geom.Point(ol.proj.fromLonLat([location.coordinates[1], location.coordinates[0]])),type: 'visited',data: location})vectorSource.addFeature(feature)})// 添加想去的地方data.wishlist.forEach(location => {const feature = new ol.Feature({geometry: new ol.geom.Point(ol.proj.fromLonLat([location.coordinates[1], location.coordinates[0]])),type: 'wishlist',data: location})vectorSource.addFeature(feature)})const vectorLayer = new ol.layer.Vector({source: vectorSource,style: function(feature) {const type = feature.get('type')return new ol.style.Style({image: new ol.style.Circle({radius: 15,fill: new ol.style.Fill({color: type === 'visited' ? '#22c55e' : '#f59e0b'}),stroke: new ol.style.Stroke({color: type === 'visited' ? '#16a34a' : '#d97706',width: 3})}),text: new ol.style.Text({text: type === 'visited' ? '✓' : '♡',fill: new ol.style.Fill({ color: 'white' }),font: 'bold 16px sans-serif'})})}})map.addLayer(vectorLayer)
}
实现要点:
- 注意坐标转换:
ol.proj.fromLonLat([经度, 纬度])
- 使用不同颜色区分标记类型:绿色表示已访问,橙色表示愿望清单
- 添加文字图标:✓ 和 ♡ 增强视觉识别
主题切换核心代码
function detectSiteTheme() {// 检测站点当前主题const isDark = document.documentElement.classList.contains('dark') ||document.body.classList.contains('dark') ||document.documentElement.getAttribute('data-theme') === 'dark'currentTheme = isDark ? 'dark' : 'light'switchTheme(currentTheme)
}function switchTheme(theme) {if (theme === 'dark') {// 暗色主题:显示高德暗色地图lightLayer.setVisible(false)darkLayer.setVisible(true)} else {// 亮色主题:显示高德标准地图lightLayer.setVisible(true)darkLayer.setVisible(false)}
}function watchSiteThemeChanges() {// 使用MutationObserver监听站点主题变化const observer = new MutationObserver(function(mutations) {mutations.forEach(function(mutation) {if (mutation.type === 'attributes' &&(mutation.attributeName === 'class' || mutation.attributeName === 'data-theme')) {detectSiteTheme()}})})observer.observe(document.documentElement, {attributes: true,attributeFilter: ['class', 'data-theme']})
}
实现要点:
- 支持多种主题检测方式,兼容不同的主题切换实现
- 使用 MutationObserver 监听 DOM 变化,实现主题自动切换
- 通过图层的
setVisible()
方法控制显示/隐藏
数据结构设计
{"visited": [{"id": "beijing","name": "北京","nameEn": "Beijing","coordinates": [39.9042, 116.4074],"description": "中国首都","visitDate": "2023-05"}],"wishlist": [{"id": "xian","name": "西安","nameEn": "Xi'an","coordinates": [34.3416, 108.9398],"description": "古都西安,兵马俑故乡","priority": "high"}]
}
设计要点:
- 分离已访问和愿望清单数据
- 坐标使用
[纬度, 经度]
格式 - 支持中英文名称
- 可扩展字段:访问时间、优先级等
简单总结
通过使用 OpenLayers + 高德瓦片源,我成功实现了一个功能完整的旅游足迹地图。这个方案的主要优势:
技术优势:
- 无需申请 API key,降低使用门槛
- 支持自定义样式和交互
- 高德地图在国内访问速度快,中文支持好
- OpenLayers 功能强大,扩展性好
功能特色:
- 双主题自动切换,与网站整体风格保持一致
- 直观的标记系统,清晰区分已访问和愿望清单
- 响应式设计,移动端体验良好
- 交互弹窗提供详细信息展示
后续改进方向:
- 添加路线规划功能,连接相邻的旅游点
- 支持照片上传,为每个地点添加旅游照片
- 增加统计功能,显示旅游里程、访问城市数量等
- 支持数据导入导出,方便备份和分享
- 添加搜索功能,快速定位特定地点
- 集成天气信息,显示各地实时天气
这个旅游足迹地图不仅满足了我个人记录旅游经历的需求,也为网站访客提供了一个了解我旅游足迹的有趣方式。通过开源的技术栈实现,也为其他开发者提供了一个可参考的实现方案。