話不多說,先上效果圖
以前在做項目時,經常會聽到客戶說,你們這個地圖是哪來的,太丑了,能不能換成百度地圖……高德也行……
大家生活中,基本上都已經習慣了使用百度地圖和高德地圖,而在做項目時,用這兩個地圖做為底圖,也基本成為了標配。但在開發中使用這兩個地圖,會遇到一個攔路虎,坐標偏移問題。
全球現在用的最多的坐標,是wgs84坐標,專業GPS設備和手機GPS定位得到的坐標,通常都是這個坐標。我們國家為了保密需要,要求在國內發布的互聯網地圖,必須要在這個基礎上進行加密偏移。加密后的坐標叫做國測局坐標,俗稱火星坐標。高德地圖、騰訊地圖、國內的谷歌地圖都是這個坐標。百度地圖則是在火星坐標的基礎上再次加密,形成了百度坐標。
leaflet有一個加載互聯網地圖的插件leaflet.ChineseTmsProviders,可以輕松實現加載高德、百度、天地圖、谷歌等在線地圖瓦片,但並沒有去解決它們的偏移問題。高德和百度地圖倒是提供了wgs84坐標轉成自己坐標的在線接口,但僅支持單向轉入,不支持反向再轉回來,這會導致地圖拾取坐標等功能無法得到wgs84坐標。
網上流傳着一份wgs84坐標、火星坐標和百度坐標之間相互轉換的算法。在多個項目中使用后發現,基本很准,偶爾有誤差,但很小,也就幾米以內,平時用時基本感覺不到。
如何集成到leaflet
兩種思路:
第一種,把糾偏算法封裝成一個接口,類似上面提到的百度、高德地圖的坐標轉換接口,在向地圖加載數據前,先調用這個接口完成坐標的轉換再添加到地圖上。等於是把自己的數據偏移到互聯網地圖坐標上。這種是最常見的。
第二種,百度、高德的地圖都是瓦片地圖,每一張瓦片在加載時都會去計算它的經緯度位置,我們可以在計算經緯度位置時加入糾偏算法,把瓦片的坐標位置糾偏回來。當所有瓦片的位置正確了,整個地圖也就不存在偏移了。等於是把火星坐標或百度坐標的瓦片糾偏回wgs84坐標。
兩種方案進行比較,第一種明顯是被百度、高德的坐標轉換接口帶節奏了。leaflet是開源的,我們可以通過研究源碼實現對瓦片的糾偏,從而真正實現對地圖的糾偏,而不是每次去調用坐標轉換接口,讓數據將錯就錯。
第二種方案還可以進一步延伸,把對瓦片的糾偏封裝成插件,最終目標是引入這個插件以后實現對地圖的自動糾偏。
瓦片位置
對瓦片糾偏,先要找到加載瓦片、計算瓦片位置的代碼在哪。
上文中提到的,加載互聯網地圖的插件leaflet.ChineseTmsProviders本質是一個圖層,它繼承了TileLayer
TileLayer繼承了GridLayer
加載瓦片的代碼主要是在GridLayer中寫的。
計算瓦片位置的代碼在 _getTiledPixelBounds 方法和 _setZoomTransform 方法中。
瓦片糾偏
瓦片糾偏分三步:
第一步:准備坐標轉換的算法
第二步:根據互聯網地圖名稱獲取坐標類型
第三步:在獲取瓦片和地圖縮放的方法中,調用糾偏算法
封裝成插件
有個問題,既然要封裝成插件,就要做到耦合,不能直接修改leaflet的源碼。這里可以參考leaflet的源碼,使用 include 方式對方法進行重寫來做到修改源碼。
include方式
通過例子了解一下:比如leaflet源碼中 Polygon.toGeoJSON() 方法不是在 Polygon.js 文件中寫的,而是用 include 方式寫在了GeoJSON.js文件中。Polygon類本來是沒有toGeoJSON()方法的,這樣就增加了這個方法。如果Polygon類中已經有了toGeoJSON()方法,這樣寫會根據執行的順序,后執行的會把先加載的重寫。
最后,我們把上面的代碼封裝成一個js插件,大家引用這個插件,就能實現了對地圖的糾偏,不需要寫一行js代碼,這才是我心目中真正的優雅。
最終效果
下圖是引用糾偏插件前后的對比:
注意:leaflet會以map初始化以后,加載的第一個圖層的坐標,作為整個map的坐標,所以地圖初始化以后,要第一個添加互聯網地圖作為底圖。
總結
- leaflet有一個加載國內互聯網地圖的插件,但存在坐標偏移問題。
- 常見的偏移坐標有國測局坐標和百度坐標。網上有一份wgs84坐標國測局坐標和百度坐標相互轉換的算法,需要自己集成到leaflet中
- 糾偏算法集成到leaflet中有兩種思路,一種是把自己的數據偏移到互聯網地圖,另一種是把互聯網地圖的瓦片糾偏回自己的數據。
- 采用第二種思路,把糾偏算法封裝成插件,對互聯網地圖的瓦片糾偏,在插件中復寫源碼的方式最為優雅。
在線示例
不熟悉github的童鞋,可以微信搜索《GIS兵器庫》或掃描下面的二維碼,回復 “地圖糾偏” 獲得糾偏插件的下載鏈接。
本文會經常更新,請閱讀原文:[http://gisarmory.xyz/blog/index.html?blog=leafletMapCorrection,以避免被陳舊、錯誤的知識誤導。
微信搜索《GIS兵器庫》或掃描上文的二維碼,關注GIS兵器庫公眾號, 可以第一時間獲得GIS文章更新。
本文章采用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名《GIS兵器庫》(包含鏈接: http://gisarmory.xyz/blog/),不得用於商業目的,基於本文修改后的作品務必以相同的許可發布。