1.計算距離和范圍的思路
其實計算距離和面積屬于典型的Measurements(測量)需求,PostGIS也是把相應的函數劃分在了Spatial Relationships and Measurements這個板塊里。ps:PostGIS的reference:http://postgis.net/docs/manual-2.3/reference.html
測量是一門古老且歷史悠久的技術,最早的記錄在公元前兩千多年大禹治水的時候就有了,他本身跟計算機和互聯網沒什么關系,思路大體上是:
我要測量的范圍大概有多大?->我要在哪個地方測量?->我要測量多準?
比較形象的比喻就是我在量一個東西的時候需要根據東西的大小和位置先找把合適的尺子,然后再開始測量。
弄清以上三點選擇合適的測量方式(尺子)再進行測量(計算)是我個人推薦的方式。
2.在這就需要提一個組織:
EPSG(http://www.epsg.org)
EPSG:European Petroleum Survey Group,歐洲石油調查組織(你沒看錯),該組織負責專門維護地球上所有的測量坐標系統(找石油),并且給每組坐標系統都賦予了一個編號和一組描述(WKT),比如大家常用的WGS84坐標系編號就是EPSG:4326,再比如互聯網地圖(谷歌、高德等)常用的偽墨卡托投影編號就是EPSG:3857。(關于常用的坐標系統以后的文章會單講)
可以理解成EPSG給大家維護了無數把尺子,并且給每把尺子搞了個編號,還標明了這把尺子適合什么條件下用。
舉三個例子幫助大家理解:
先推薦一個查EPSG坐標系非常棒的網站:epsg.io(需要翻墻)
例子1. EPSG:4326:
直接在瀏覽器里敲:http://epsg.io/4326(注意要翻墻),進去你會看到大大的幾行字:
EPSG:4326
Geodectic coordinate system
WGS 84 -- WGS84 - World Geodetic System 1984, used in GPS
翻譯過來就是:EPSG:4326,一個地理坐標系(也叫大地坐標系),用于描述WGS84坐標系,其是從1984年開始在GPS中使用的全球地理坐標系統。(從字面看是不是感覺蠻好理解的)
再下面是Attributes、Covered area、Export三個板塊:
Attributes中先重點看下Uint(單位),這里寫的是degree(度),CRS、Ellipsoid等參數看不懂沒關系,以后文章會單獨講
Corverd area是重點要看的:
看到紅色的框框沒,這是指該坐標系適合計算的范圍,幾乎覆蓋了全球,再看下面的三條描述:
Center coordinates,指坐標系的中心點坐標
WGS84 bounds,指坐標系的適用范圍
World,清晰的告訴你這是給全球范圍用的坐標系
所以問題來了:這個坐標系適合計算距離和面積嗎?
答案是:否
因為該坐標系是大地坐標系(Geodectic coordinate system),單位是度(角度單位),角度用來測量長度和面積是不合適的(尺子不好用啊),但可用于定位,而且它的范圍又覆蓋了全球,所以很適合全球定位(GPS衛星定位的坐標系用的就是它)
再看一個坐標系
例子2. EPSG:3857:
點開http://epsg.io/3857可以看到:
EPSG:3857
Projected coordinate system
WGS 84 / Pseudo-Mercator -- Spherical Mercator, Google Maps, OpenStreetMap, Bing, ArcGIS, ESRI
意思就是,EPSG:3857是一個投影坐標系(Projected coordinate system),在WGS84坐標系基礎上進行了偽墨卡托投影(Pseudo-Mercator)。球形墨卡托地圖、谷歌地圖、osm地圖、bing地圖、ArcGIS、ESRI會常用該坐標系。
投影坐標系(Projected coordinate system)是在大地坐標系(Geodectic coordinate system)的基礎上,經過數學運算,把大地坐標系的曲面坐標映射到平面上產生的一種平面坐標系。
不太嚴謹的理解就是你有個橘子,沒剝開的時候在橘子皮上畫的畫就是大地坐標系,包開以后把橘子皮拍平了上面畫的就是投影坐標系。
再看下Unit(單位)是metre(米)
Covered area的WGS84 bounds是-180,-85.06到180,85.06,描述是World between 85.06°S and 85.06°N.,嗯。。。幾乎覆蓋整個地球了,感覺用它來計算距離和面積妥妥的。
但很遺憾答案還是:否,用它算測不了實地距離 >3<
雖然EPSG:3857是平面坐標系,單位是長度(米),但是他用了一個長度和面積都不靠譜的投影坐標系:Pseudo-Mercator(偽墨卡托投影,該投影是正軸等角切圓柱投影,在高緯度地區形變的非常嚴重)。看到這你貌似突然知道了一個驚天秘密:原來高德地圖和谷歌地圖上面畫的東東都是變形的!!沒錯,就是變形的,你看到的這些互聯網地圖用的都是類似的投影,他們在高緯度地區都是拉伸嚴重的,遠比他的實地面積要大。但是正軸墨卡托投影有個優點:投影后角度不變形,所以用來導航和定位非常合適。
這個例子還反應了一件事:能幾乎覆蓋全球的坐標系一般都算不了正確的距離和面積(沒有一個坐標系能完美解決地球上的所有問題),想算正確的距離?找個小點的范圍吧。
在這直接扔一個北京市可以用的:
例子3.EPSG:4527:
點開http://epsg.io/4527可以看到:
EPSG:4527
Projected coordinate system
CGCS2000 / 3-degree Gauss-Kruger zone 39
翻譯過來就是:EPSG:4527,是投影坐標系,基于國家2000大地坐標系做的高斯-克呂格3度帶投影中的第39帶坐標系(看不懂沒關系,高斯克呂格以后會慢慢講,知道它形變很小就好了)
其中國家2000(CGCS2000)指的就是咱中國自己的大地坐標系(還有兩個比較舊的:北京54、西安80),Unit是米,重點看Covered area:
紅色就是該投影最適合使用的范圍,WGS84 bounds:115.5 22.6到118.5,49.88,正好把北京市(116.46,39.92)包括在內,下面的描述也很清楚:China - onshore between 115°30'E and 118°30'E.(適合中國內陸東經115°30'到東經118°30'的范圍),跟圖上紅框區域完全匹配有木有,恭喜你找到了一把測量北京市距離和面積的好尺子。
3.PostGIS算北京市范圍內的距離和面積:
坐標系(尺子)有了,就差運算(測量)了:
算距離:
在北京選Mobike總部曼寧國際北門這面墻的兩個端點試驗:
左邊點是:經度116.4677961543,緯度39.9486461337
右邊點是:經度116.4680989087,緯度39.9486998528
他們的坐標系都是WGS84大地坐標系,也就是EPSG:4326。
執行查詢:
SELECT st_distance(st_transform(st_geometryfromtext('POINT(116.4677961543 39.9486461337)',4326),4527),st_transform(st_geometryfromtext('POINT(116.4680989087 39.9486998528)',4326),4527));
結果是:26.55米
st_distance
------------------
26.5520226594034
(1 row)
其中用到幾個函數:
st_geometryfromtext(geometry,srid):該方法作用是根據描述的幾何對象(geometry)的字符串轉化成幾何對象,POINT說明幾何對象是點類型,第二個參數srid是4326,是指這個點類型對象的空間參考(也就是EPSG的編號,也是所在的坐標系)是EPSG:4326,即WGS84大地坐標系。
st_transform(geometry,srid):該方法是把某個幾何對象(geometry)的所有坐標從一個坐標系轉換到另一個坐標系。在這做的就是把EPSG:4326轉換為EPSG:4527(量北京的尺子到手)。
st_distance(geometry,geometry):該方法用于計算兩點距離,所用坐標系根據geometry帶的srid(EPSG編號)決定。
上述查詢的含義就是:
兩個4236坐標系下的點對象轉換成4527坐標系后計算直線距離,這個距離與地面實際距離很接近。
再用google地圖上瞄著位置手動量了下:結果差不多,26.52米
如果用3857試試:
SELECT st_distance(st_transform(st_geometryfromtext('POINT(116.4677961543 39.9486461337)',4326),3857),st_transform(st_geometryfromtext('POINT(116.4680989087 39.9486998528)',4326),3857));
結果是:34.59米,距離明顯變長了,結果錯誤
st_distance
------------------
34.5933990100333
(1 row)
算面積:
同樣還是曼寧國際選了它前面停車場的矩形范圍的四個端點:
116.4679312706,39.9482801227
116.4677961543,39.9486461337
116.4680989087,39.9486998528
116.4682182670,39.9483181633
執行查詢:
SELECT st_area(ST_Transform(st_geometryfromtext('POLYGON((116.4679312706 39.9482801227,116.4677961543 39.9486461337,116.4680989087 39.9486998528,116.4682182670 39.9483181633,116.4679312706 39.9482801227))',4326),4527));
結果:1100平方米約等于1.65畝
st_area
------------------
1101.47601530779
(1 row)
這里geometry的類型用的是POLYGON(多邊形),注意要閉合(首尾的經緯度要一樣)
st_area(geometry,srid):該函數用于計算在某個srid(某個坐標系下)一個幾何對象的面積
上述查詢的含義就是:
一個4236坐標系下的多邊形對象轉換成4527坐標系后計算其面積,這個面積與地面實際實際面積很接近。
可能有人會好奇PostGIS是怎么知道這些編號的?
請執行下面語句:
SELECT * FROM spatial_ref_sys WHERE srid IN (4326,3857,4257);
結果:看到沒,PostGIS自帶的這張表spatial_ref_sys(空間參考系統)包含了幾乎EPSG所有坐標系編號定義和描述,你甚至可以自己定義一套坐標系統放到里面(只要你會寫WKT和proj4text)。