一 背景
GIS項(xiàng)目中底圖是必不可少的,絕大部分GIS項(xiàng)目使用的底圖是基于高德,谷歌,百度,天地圖等互聯(lián)網(wǎng)(在線/離線)底圖。由于我國(guó)特殊國(guó)情,公眾版地理信息服務(wù)(包括電子底圖)都要進(jìn)行各種坐標(biāo)偏移旋轉(zhuǎn)等數(shù)據(jù)加密處理,并獲取國(guó)土資源部數(shù)據(jù)審查并頒發(fā)審圖號(hào)才可公開(kāi)發(fā)布。企業(yè)的GIS數(shù)據(jù)都是通過(guò)傳感器或者實(shí)地測(cè)量獲得的非加密的WGS84坐標(biāo)(即常用的gps那種坐標(biāo)),當(dāng)企業(yè)將自己的業(yè)務(wù)數(shù)據(jù)疊加到互聯(lián)網(wǎng)底圖時(shí),不可避免出現(xiàn)圖層疊加偏移問(wèn)題,如下圖:

底圖偏移情況總結(jié)如下:
- 百度底圖,最坑爹的,bd-09坐標(biāo),二次加偏。
- 高德,谷歌底圖,GCJ-02坐標(biāo),俗稱的“火星坐標(biāo)系”。
- 天地圖,osm底圖,通常認(rèn)為是不偏移底圖。
二 解決方案與存在問(wèn)題
2.1 解決方案
由于底圖是不可改變的,唯一能解決疊加偏移問(wèn)題的方法是:將自己的業(yè)務(wù)數(shù)據(jù)根據(jù)一定的偏移公式轉(zhuǎn)換,從而使數(shù)據(jù)的坐標(biāo)與底圖達(dá)到相對(duì)對(duì)齊。當(dāng)前主流編程語(yǔ)言,都有類似處理坐標(biāo)偏移的公式換算庫(kù),滿足不同語(yǔ)言開(kāi)發(fā)的GIS項(xiàng)目需求。比如如下幾個(gè)地址:
https://github.com/FreeGIS/CoordinateTransform
https://github.com/wandergis/coordtransform
2.2 存在問(wèn)題
- 不支持批量計(jì)算。
當(dāng)前各種坐標(biāo)轉(zhuǎn)換庫(kù),都是實(shí)現(xiàn)單個(gè)點(diǎn)坐標(biāo)轉(zhuǎn)到底圖對(duì)應(yīng)的坐標(biāo)。但實(shí)際上,GIS存在 圖形復(fù)雜,數(shù)據(jù)量大的通用特征,每個(gè)圖層都有大量圖形記錄,每個(gè)圖形記錄,其圖形常用分類就有點(diǎn)線面多點(diǎn)多線多面等六種,每個(gè)圖形都可能由非常多的坐標(biāo)點(diǎn)組成。業(yè)務(wù)需求最好是支持批量計(jì)算。 - 動(dòng)態(tài)計(jì)算,顯示效率低。
當(dāng)前各種坐標(biāo)轉(zhuǎn)換庫(kù),都是基于前后臺(tái)的語(yǔ)言實(shí)現(xiàn)的,每次都要獲取數(shù)據(jù)源數(shù)據(jù),再通過(guò)實(shí)時(shí)計(jì)算展示,當(dāng)數(shù)據(jù)復(fù)雜,數(shù)據(jù)量大時(shí)候,效率很成問(wèn)題。解決辦法通常是,后臺(tái)先讀取數(shù)據(jù)源,然后計(jì)算好,最后將計(jì)算結(jié)果重新存入庫(kù)里,項(xiàng)目使用時(shí),直接讀取計(jì)算好的結(jié)果。 捯飭的費(fèi)勁不? - 數(shù)據(jù)轉(zhuǎn)換服務(wù)維護(hù)復(fù)雜。
當(dāng)數(shù)據(jù)庫(kù)數(shù)據(jù)發(fā)生變更時(shí),轉(zhuǎn)換服務(wù)要能增量得到變更的數(shù)據(jù),然后計(jì)算,然后再更新數(shù)據(jù)庫(kù)已存儲(chǔ)的轉(zhuǎn)換結(jié)果。整個(gè)架構(gòu)要是能自動(dòng)維護(hù)好,還是很復(fù)雜的。
三 PostGIS方案
基于現(xiàn)有方案普遍存在的問(wèn)題,筆者基于最常用的開(kāi)源GIS空間數(shù)據(jù)庫(kù)PostGIS開(kāi)發(fā)了一個(gè)function,基于一行sql搞定一切的是思想,滿足使用PostGIS的開(kāi)發(fā)者,簡(jiǎn)單的處理這些坐標(biāo)轉(zhuǎn)換問(wèn)題。PostGIS實(shí)現(xiàn)的倉(cāng)庫(kù)如下:
https://github.com/FreeGIS/Postgis_Coordinate_Transform
實(shí)現(xiàn)功能列表如下:
- 支持WGS84與bd-09,gcj-02坐標(biāo)系,百度經(jīng)緯度與百度墨卡托之間互轉(zhuǎn)。
- 支持點(diǎn)線面多點(diǎn)多線多面的復(fù)雜圖形批量轉(zhuǎn)換。
- 支持對(duì)整個(gè)表批處理轉(zhuǎn)換。
約束:
- 要求轉(zhuǎn)換的表是基于PostGIS創(chuàng)建的空間關(guān)系表
示例支持的表:
create table point_test(
gid serial primary key,
name text,
geom geometry(Point,4326)
);
不支持的表:
create table point_test(
gid serial primary key,
name text,
lon numeric, --經(jīng)度
lat numeric --緯度
);
不支持的表是普通關(guān)系表,非空間圖形表。
- 要求轉(zhuǎn)換的圖形必須是二維圖形
當(dāng)前暫不支持三維或者多維,如帶Z值的高程,帶M值的測(cè)量值等,由于過(guò)于復(fù)雜,目前筆者暫未有時(shí)間去過(guò)多實(shí)現(xiàn)。 - 轉(zhuǎn)換表圖形坐標(biāo)系必須是epsg:4326
除了將百度墨卡托坐標(biāo)轉(zhuǎn)百度經(jīng)緯度外,其他轉(zhuǎn)換方式,必須保證轉(zhuǎn)換表的坐標(biāo)系是4326,其他坐標(biāo)系,需要用戶使用ST_Transform函數(shù),將其數(shù)據(jù)先轉(zhuǎn)到4326坐標(biāo)系下,再使用該工具。百度墨卡托坐標(biāo)轉(zhuǎn)百度經(jīng)緯度轉(zhuǎn)換,數(shù)據(jù)源必須是3857的。 - 圖形數(shù)據(jù)是點(diǎn)線面多點(diǎn)多線多面
僅僅支持Point,LineString,Polygon,MultiPoint,MultiLineString,MultiPolygon六種明確類型。其他的PostGIS類型由于不常用,且不嚴(yán)格規(guī)范,通常不用于標(biāo)準(zhǔn)的空間數(shù)據(jù)庫(kù)類型,暫時(shí)不考慮實(shí)現(xiàn)。
3.1 安裝應(yīng)用
前提:PostGIS用戶,圖形表是基于PostGIS的空間關(guān)系表。
示例:在test庫(kù)安裝轉(zhuǎn)換方法
[postgres@sss~]$ psql -d test
psql (11.1)
Type "help" for help.
test=# \i FreeGIS_Coordinate_Transform.sql
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE FUNCTION
CREATE TYPE
CREATE FUNCTION
方法簡(jiǎn)介:
FreeGIS_Coordinate_Transform(
in schema_name text,
in table_name text,
in transform_type FreeGIS_coordinate_transform_type
)
schema_name:表的schema名稱。
table_name:表的名稱。
transform_type:轉(zhuǎn)換類型,枚舉型,類型如下:
BD2GCJ:百度經(jīng)緯度 轉(zhuǎn) 火星經(jīng)緯度。
GCJ2BD:火星經(jīng)緯度 轉(zhuǎn) 百度經(jīng)緯度。
WGS2GCJ:WGS84經(jīng)緯度 轉(zhuǎn) 火星經(jīng)緯度
GCJ2WGS:火星經(jīng)緯度 轉(zhuǎn) WGS84經(jīng)緯度。
BD2WGS:百度經(jīng)緯度 轉(zhuǎn) WGS84經(jīng)緯度。
WGS2BD:WGS84經(jīng)緯度 轉(zhuǎn) 百度經(jīng)緯度。
BDWGS2BDMKT:百度經(jīng)緯度 轉(zhuǎn) 百度墨卡托。
BDMKT2BDWGS:百度墨卡托 轉(zhuǎn) 百度經(jīng)緯度。
WGS2BDMKT: WGS84經(jīng)緯度 轉(zhuǎn) 百度墨卡托。
BDMKT2WGS:百度墨卡托 轉(zhuǎn) WGS84經(jīng)緯度。
轉(zhuǎn)換批處理執(zhí)行:
--將public.test表從wgs84坐標(biāo)轉(zhuǎn)火星坐標(biāo)。
select FreeGIS_Coordinate_Transform('public','test','WGS2GCJ');
執(zhí)行后,test表新增了一個(gè)transform_geom字段,就是轉(zhuǎn)換后的結(jié)果。

3.2 底圖疊加示例
3.2.1 疊加osm,天地圖底圖
企業(yè)的坐標(biāo),可不做處理,直接疊加。

3.2.2 疊加谷歌,高德底圖
將企業(yè)的數(shù)據(jù),使用wgs84轉(zhuǎn)火星坐標(biāo),然后疊加:
select FreeGIS_Coordinate_Transform('public','test_pt','WGS2GCJ');
執(zhí)行展示:
--transform_geom字段存儲(chǔ)了轉(zhuǎn)換結(jié)果,示例使用如下:
create or replace view v_test_pt as select gid,name,transform_geom as geom from test_pt;
將v_test_pt 發(fā)布成地理服務(wù)后,疊加地圖如下:


依次制作了測(cè)試線和面,測(cè)試情況如下




3.2.3 疊加百度底圖
將企業(yè)的數(shù)據(jù),使用wgs84轉(zhuǎn)百度墨卡托坐標(biāo),然后疊加:
select FreeGIS_Coordinate_Transform('public','test_pg','WGS2BDMKT');

說(shuō)明下:將wgs批量轉(zhuǎn)到百度地圖上,需要先轉(zhuǎn)到百度經(jīng)緯,再?gòu)陌俣冉?jīng)緯轉(zhuǎn)到百度墨卡托,這些步驟被WGS2BDMKT封裝了。
百度很麻煩很麻煩?。。?br>
其他底圖的偏移操作是,將wgs偏移到對(duì)應(yīng)的底圖,然后使用標(biāo)準(zhǔn)的墨卡托投影函數(shù)疊加到底圖上即可。
百度底圖操作是,將wgs偏移到對(duì)應(yīng)的底圖,但是底圖是墨卡托坐標(biāo)系,百度經(jīng)緯度不能使用標(biāo)準(zhǔn)的墨卡托投影函數(shù),二次加密的,所以要寫(xiě)個(gè)自定義函數(shù),將百度經(jīng)緯度自己轉(zhuǎn)到百度墨卡托,才能疊加到百度底圖。
這是其他轉(zhuǎn)換庫(kù)都沒(méi)注意到,沒(méi)涉及到的,坑爹的百度?。?!