18. 地理¶
資料的座標是「地理座標」或「緯度/經度」的情況很常見。
與麥卡托、UTM 或州平面投影的座標不同,地理座標**不是笛卡爾座標**。地理座標不表示從平面上繪製的原點到直線距離。相反地,這些**球面座標**描述地球上的角度座標。在球面座標中,一個點是由相對於參考子午線的旋轉角度(經度)以及與赤道的角度(緯度)來指定的。

您可以將地理座標視為近似笛卡爾座標,並繼續進行空間計算。但是,距離、長度和面積的測量結果將是**毫無意義**的。由於球面座標測量的是**角度**距離,因此單位為「度」。此外,來自索引的近似結果和像 intersects 和 contains 這樣的真/假測試可能會非常錯誤。當接近極點或國際換日線等問題區域時,點之間的距離會變大。
例如,以下是洛杉磯和巴黎的座標。
洛杉磯:
POINT(-118.4079 33.9434)
巴黎:
POINT(2.3490 48.8533)
以下使用標準 PostGIS 笛卡爾 ST_Distance(geometry, geometry) 計算洛杉磯和巴黎之間的距離。請注意,SRID 4326 宣告了一個地理空間參考系統。
SELECT ST_Distance(
'SRID=4326;POINT(-118.4079 33.9434)'::geometry, -- Los Angeles (LAX)
'SRID=4326;POINT(2.5559 49.0083)'::geometry -- Paris (CDG)
);
121.898285970107
啊哈! 122!但是,這代表什麼?
空間參考 4326 的單位是度。所以我們的答案是 122 度。但是(再次強調),這代表什麼?
在球面上,「一度平方」的大小變化很大,隨著您遠離赤道而變小。想想看,當您朝向兩極時,地球上的子午線(垂直線)會彼此靠近。因此,122 度的距離並不**代表**任何意義。這是一個毫無意義的數字。
為了計算有意義的距離,我們必須將地理座標視為真實的球面座標,而不是近似的笛卡爾座標。我們必須測量點之間的距離,就像是球面上的真實路徑一樣,也就是大圓的一部分。
PostGIS 通過 geography
類型提供此功能。
注意
不同的空間資料庫有不同的「處理地理座標」方法
當 SRID 為地理時,Oracle 會嘗試通過透明地執行地理計算來掩蓋差異。
SQL Server 使用兩種空間類型,「STGeometry」用於笛卡爾資料,「STGeography」用於地理資料。
Informix Spatial 是 Informix 的純笛卡爾擴展,而 Informix Geodetic 是純地理擴展。
與 SQL Server 類似,PostGIS 使用兩種類型:「geometry」和「geography」。
使用 geography
而不是 geometry
類型,讓我們再次嘗試測量洛杉磯和巴黎之間的距離。
SELECT ST_Distance(
'SRID=4326;POINT(-118.4079 33.9434)'::geography, -- Los Angeles (LAX)
'SRID=4326;POINT(2.5559 49.0083)'::geography -- Paris (CDG)
);
9124665.27317673
一個很大的數字!來自 geography
計算的所有回傳值都以**公尺**為單位,因此我們的答案是 9125 公里。
舊版本的 PostGIS 使用 ST_Distance_Spheroid(point, point, measurement) 函數支持球面上的非常基本的計算。但是,ST_Distance_Spheroid 受到相當大的限制。該函數僅適用於點,並且不提供跨極點或國際換日線進行索引的支持。
當提出類似「從洛杉磯飛往巴黎的航班將會有多接近冰島?」的問題時,支持非點幾何形狀的需求變得非常明顯。

在笛卡爾平面上處理地理座標(紫色線)確實會產生**非常**錯誤的答案!使用大圓路徑(紅色線)可以得到正確的答案。如果我們將我們的 LAX-CDG 航班轉換為線字串,並使用 geography
計算到冰島某個點的距離,我們將得到正確的答案(回想一下)以公尺為單位。
SELECT ST_Distance(
ST_GeographyFromText('LINESTRING(-118.4079 33.9434, 2.5559 49.0083)'), -- LAX-CDG
ST_GeographyFromText('POINT(-22.6056 63.9850)') -- Iceland (KEF)
);
502454.906643729
因此,在 LAX-CDG 航線上最接近冰島(從其國際機場測量)的距離相對較小,為 502 公里。
對於跨越國際換日線的要素,處理地理座標的笛卡爾方法會完全失效。從洛杉磯到東京的最短大圓路徑會穿過太平洋。最短的笛卡爾路徑會穿過大西洋和印度洋。

SELECT ST_Distance(
ST_GeometryFromText('Point(-118.4079 33.9434)'), -- LAX
ST_GeometryFromText('Point(139.733 35.567)')) -- NRT (Tokyo/Narita)
AS geometry_distance,
ST_Distance(
ST_GeographyFromText('Point(-118.4079 33.9434)'), -- LAX
ST_GeographyFromText('Point(139.733 35.567)')) -- NRT (Tokyo/Narita)
AS geography_distance;
geometry_distance | geography_distance
-------------------+--------------------
258.146005837336 | 8833954.76996256
18.1. 使用 Geography¶
為了將幾何資料載入 geography 表中,必須先將幾何資料投影到 EPSG:4326(經度/緯度),然後再將其轉換為 geography。ST_Transform(geometry,srid) 函數將座標轉換為地理座標,Geography(geometry) 函數或 ::geography
後綴會「轉換」為 geography。
CREATE TABLE nyc_subway_stations_geog AS
SELECT
ST_Transform(geom,4326)::geography AS geog,
name,
routes
FROM nyc_subway_stations;
在 geography 表上建立空間索引與幾何表完全相同
CREATE INDEX nyc_subway_stations_geog_gix
ON nyc_subway_stations_geog USING GIST (geog);
不同之處在於底層:geography 索引將正確處理涵蓋極點或國際換日線的查詢,而幾何索引則不會。
以下是一個查詢,用於查找帝國大廈 500 公尺內的所有地鐵站。
對於 geography 類型,只有少量的原生函數
ST_AsText(geography) 返回
text
ST_GeographyFromText(text) 返回
geography
ST_AsBinary(geography) 返回
bytea
ST_GeogFromWKB(bytea) 返回
geography
ST_AsSVG(geography) 返回
text
ST_AsGML(geography) 返回
text
ST_AsKML(geography) 返回
text
ST_AsGeoJson(geography) 返回
text
ST_Distance(geography, geography) 返回
double
ST_DWithin(geography, geography, float8) 返回
boolean
ST_Area(geography) 返回
double
ST_Length(geography) 返回
double
ST_Covers(geography, geography) 返回
boolean
ST_CoveredBy(geography, geography) 返回
boolean
ST_Intersects(geography, geography) 返回
boolean
ST_Buffer(geography, float8) 返回
geography
1ST_Intersection(geography, geography) 返回
geography
1
18.2. 建立 Geography 表¶
建立具有 geography 列的新表的 SQL 與建立幾何表的 SQL 非常相似。但是,geography 包含在建立表時直接指定物件類型的能力。例如
CREATE TABLE airports (
code VARCHAR(3),
geog GEOGRAPHY(Point)
);
INSERT INTO airports
VALUES ('LAX', 'POINT(-118.4079 33.9434)');
INSERT INTO airports
VALUES ('CDG', 'POINT(2.5559 49.0083)');
INSERT INTO airports
VALUES ('KEF', 'POINT(-22.6056 63.9850)');
在表定義中,GEOGRAPHY(Point)
將我們的機場資料類型指定為點。新的 geography 欄位不會在 geometry_columns
視圖中註冊。相反地,它們會在名為 geography_columns
的視圖中註冊。
SELECT * FROM geography_columns;
f_table_name | f_geography_column | srid | type
--------------------------+--------------------+------+----------
nyc_subway_stations_geog | geog | 0 | Geometry
airports | geog | 4326 | Point
注意
以上輸出省略了一些欄位。
18.3. 轉換為 Geometry¶
雖然 geography 類型的基本函數可以處理許多使用案例,但有時您可能需要存取僅由 geometry 類型支持的其他函數。幸運的是,您可以來回轉換物件,從 geography 轉換為 geometry,反之亦然。
PostgreSQL 轉換的語法慣例是在您要轉換的值的末尾附加 ::typename
。因此,2::text
會將數字 2 轉換為文字字串 '2'。'POINT(0 0)'::geometry
會將點的文字表示法轉換為幾何點。
ST_X(point) 函數僅支持 geometry 類型。我們如何從 geography 中讀取 X 座標?
SELECT code, ST_X(geog::geometry) AS longitude FROM airports;
code | longitude
------+-----------
LAX | -118.4079
CDG | 2.5559
KEF | -21.8628
透過在我們的 geography 值後附加 ::geometry
,我們將物件轉換為 SRID 為 4326 的 geometry。從那裡開始,我們可以使用任何我們想要的幾何函數。但是,請記住,現在我們的物件是一個 geometry,座標將被解釋為笛卡爾座標,而不是球面座標。
18.4. 為何(不)使用 Geography¶
地理座標是普遍接受的座標 - 每個人都了解緯度/經度的含義,但很少有人了解 UTM 座標的含義。為什麼不一直使用 geography?
首先,如先前所述,目前直接支援 geography 類型的函數非常少。您可能會花費大量時間來解決 geography 類型的限制。
其次,在球體上進行的計算比笛卡爾計算在計算上要昂貴得多。例如,笛卡爾距離公式(畢氏定理)需要調用一次 sqrt()。球體距離公式(半正矢公式)則需要調用兩次 sqrt()、一次 arctan()、四次 sin() 和兩次 cos()。三角函數的運算成本非常高,而球體計算會涉及到許多三角函數。
結論是?
如果您的資料在地理上很集中(包含在一個州、縣或城市內),請使用 geometry 類型以及適合您資料的笛卡爾投影。請參考 http://epsg.io 網站,輸入您所在區域的名稱,以選擇可能的參考系統。
如果您需要測量地理位置分散的資料集的距離(涵蓋世界大部分地區),請使用 geography 類型。使用 geography
所節省的應用程式複雜性,將抵消任何效能問題。而且,轉換為 geometry
可以彌補大部分的功能限制。
18.5. 函數列表¶
ST_Distance(geometry, geometry): 對於 geometry 類型,會回傳在投影單位中兩個幾何圖形之間二維笛卡爾最小距離(基於空間參考)。對於 geography 類型,預設回傳兩個地理位置之間以公尺為單位的球面最小距離。
ST_GeographyFromText(text): 從 Well-Known Text (WKT) 表示法或擴充的 WKT 回傳指定的 geography 值。
ST_Transform(geometry, srid): 回傳一個新的幾何圖形,其座標已轉換為由整數參數所參考的 SRID。
ST_X(point): 回傳點的 X 座標,如果不可用則回傳 NULL。輸入必須是一個點。
ST_Azimuth(geography_A, geography_B): 回傳從 A 到 B 的方向,以弧度表示。
ST_DWithin(geography_A, geography_B, R): 如果 A 在 B 的 R 公尺範圍內,則回傳 true。
註腳