18. 地理

資料的座標是「地理座標」或「緯度/經度」的情況很常見。

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

_images/cartesian_spherical.jpg

您可以將地理座標視為近似笛卡爾座標,並繼續進行空間計算。但是,距離、長度和面積的測量結果將是**毫無意義**的。由於球面座標測量的是**角度**距離,因此單位為「度」。此外,來自索引的近似結果和像 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 受到相當大的限制。該函數僅適用於點,並且不提供跨極點或國際換日線進行索引的支持。

當提出類似「從洛杉磯飛往巴黎的航班將會有多接近冰島?」的問題時,支持非點幾何形狀的需求變得非常明顯。

_images/lax_cdg.jpg

在笛卡爾平面上處理地理座標(紫色線)確實會產生**非常**錯誤的答案!使用大圓路徑(紅色線)可以得到正確的答案。如果我們將我們的 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 公里。

對於跨越國際換日線的要素,處理地理座標的笛卡爾方法會完全失效。從洛杉磯到東京的最短大圓路徑會穿過太平洋。最短的笛卡爾路徑會穿過大西洋和印度洋。

_images/lax_nrt.png
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 1

  • ST_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。

註腳

1(1,2)

緩衝區和交集函數實際上是轉換為 geometry 的包裝器,並非在本機以球面座標執行。因此,對於無法乾淨地轉換為平面表示法的非常大範圍的物件,它們可能無法回傳正確的結果。

例如,ST_Buffer(geography,distance) 函數會將 geography 物件轉換為「最佳」投影,對其進行緩衝,然後再轉換回地理座標。如果沒有「最佳」投影(物件太大),則操作可能會失敗或回傳格式錯誤的緩衝區。