作為一個熱愛旅行的開發者,我一直想要一個能夠記錄和展示自己旅游足跡的功能。市面上雖然有很多地圖應用,但大多功能復雜,而我只需要一個簡單直觀的方式來標記去過的地方和想去的地方。
于是我決定在自己的個人網站上實現一個旅游足跡地圖功能。這個功能的核心需求很簡單:
- 在地圖上標記去過的地方(綠色標記)
- 標記想去的地方(橙色標記)
- 支持點擊查看詳細信息
- 適配網站的亮色/暗色主題
- 在移動端也能良好展示
經過技術調研,我選擇了 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 功能強大,擴展性好
功能特色:
- 雙主題自動切換,與網站整體風格保持一致
- 直觀的標記系統,清晰區分已訪問和愿望清單
- 響應式設計,移動端體驗良好
- 交互彈窗提供詳細信息展示
后續改進方向:
- 添加路線規劃功能,連接相鄰的旅游點
- 支持照片上傳,為每個地點添加旅游照片
- 增加統計功能,顯示旅游里程、訪問城市數量等
- 支持數據導入導出,方便備份和分享
- 添加搜索功能,快速定位特定地點
- 集成天氣信息,顯示各地實時天氣
這個旅游足跡地圖不僅滿足了我個人記錄旅游經歷的需求,也為網站訪客提供了一個了解我旅游足跡的有趣方式。通過開源的技術棧實現,也為其他開發者提供了一個可參考的實現方案。