使用 OpenLayers + 高德瓦片源实现旅游足迹地图
详细介绍如何使用 OpenLayers 集成高德地图瓦片源,实现一个支持主题切换、交互弹窗的旅游足迹地图功能
开头介绍#
作为一个热爱旅行的开发者,我一直想要一个能够记录和展示自己旅游足迹的功能。市面上虽然有很多地图应用,但大多功能复杂,而我只需要一个简单直观的方式来标记去过的地方和想去的地方。
于是我决定在自己的个人网站上实现一个旅游足迹地图功能。这个功能的核心需求很简单:
- 在地图上标记去过的地方(绿色标记)
- 标记想去的地方(橙色标记)
- 支持点击查看详细信息
- 适配网站的亮色/暗色主题
- 在移动端也能良好展示
经过技术调研,我选择了 OpenLayers + 高德地图瓦片源的方案,既能满足功能需求,又能保证在国内的访问速度。
演示#
你可以在我的关于页面看到这个旅游足迹地图的实际效果,地图上标记了我去过的城市和想去的地方。
功能点详解#
技术选型考虑#
在实现这个功能时,我面临几个技术选型的问题:
地图库选择:
- 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
})
})
}
javascript实现要点:
- 使用高德地图的公开瓦片服务,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)
}
javascript实现要点:
- 注意坐标转换:
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']
})
}
javascript实现要点:
- 支持多种主题检测方式,兼容不同的主题切换实现
- 使用 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"
}
]
}
json设计要点:
- 分离已访问和愿望清单数据
- 坐标使用
[纬度, 经度]
格式 - 支持中英文名称
- 可扩展字段:访问时间、优先级等
简单总结#
通过使用 OpenLayers + 高德瓦片源,我成功实现了一个功能完整的旅游足迹地图。这个方案的主要优势:
技术优势:
- 无需申请 API key,降低使用门槛
- 支持自定义样式和交互
- 高德地图在国内访问速度快,中文支持好
- OpenLayers 功能强大,扩展性好
功能特色:
- 双主题自动切换,与网站整体风格保持一致
- 直观的标记系统,清晰区分已访问和愿望清单
- 响应式设计,移动端体验良好
- 交互弹窗提供详细信息展示
后续改进方向:
- 添加路线规划功能,连接相邻的旅游点
- 支持照片上传,为每个地点添加旅游照片
- 增加统计功能,显示旅游里程、访问城市数量等
- 支持数据导入导出,方便备份和分享
- 添加搜索功能,快速定位特定地点
- 集成天气信息,显示各地实时天气
这个旅游足迹地图不仅满足了我个人记录旅游经历的需求,也为网站访客提供了一个了解我旅游足迹的有趣方式。通过开源的技术栈实现,也为其他开发者提供了一个可参考的实现方案。