PostGIS 坐标转换(SRID)的边界问题引发的专业知识 - ST_Transform

1 minute read

背景

某个用户在使用PostgreSQL ST_Transform转换坐标时,遇到一个边界问题(暂时不清楚是不是BUG,因为对SRID还不太了解),导致距离计算不准确。

菜鸟的舍余同学给出了专业的解答:

不是用不用26986的问题,可以先了解下26986坐标系是怎么定义的哈,为什么它计算负经度(西半球)时没问题呢?这是因为26986为美国马萨诸塞州地方坐标系(区域坐标系),其两个主要定义参数原点为经纬度(-71.5,41),也就是中央子午线为-71.5度,离中国太远了(中国是以东经120度为中心的),不能随便用投影坐标系呀(简单解释就是,离中央子午线越远的地方投影误差越大)

如果是计算球面经纬度距离,postgis有现成函数,无需先投影成平面哈,除非进行其他平面操作,如计算面积,那怎么选投影srid呢?参考https://wenku.baidu.com/view/5975ff2ebe23482fb5da4c24.html, 这里需要注意下地理坐标系和投影坐标系的区别(一个球面一个平面的)。

更多背景知识请参考:

《地理坐标系(球面坐标系)和投影坐标系(平面坐标系)》

Geographic Coordinate System 地理坐标

4214 GCS_Beijing_1954  
4326 GCS_WGS_1984   
4490 GCS_China_Geodetic_Coordinate_System_2000  
4555 GCS_New_Beijing  
4610 GCS_Xian_1980  

Projected Coordinate System 投影坐标

2327 Xian_1980_GK_Zone_13 
2328 Xian_1980_GK_Zone_14 
2329 Xian_1980_GK_Zone_15 
2330 Xian_1980_GK_Zone_16 
2331 Xian_1980_GK_Zone_17 
2332 Xian_1980_GK_Zone_18 
2333 Xian_1980_GK_Zone_19 
2334 Xian_1980_GK_Zone_20 
2335 Xian_1980_GK_Zone_21
2336 Xian_1980_GK_Zone_22 
2337 Xian_1980_GK_Zone_23 
2338 Xian_1980_GK_CM_75E 
2339 Xian_1980_GK_CM_81E 
2340 Xian_1980_GK_CM_87E 
2341 Xian_1980_GK_CM_93E 
2342 Xian_1980_GK_CM_99E 
2343 Xian_1980_GK_CM_105E 
2344 Xian_1980_GK_CM_111E 
2345 Xian_1980_GK_CM_117E 
2346 Xian_1980_GK_CM_123E 
2347 Xian_1980_GK_CM_129E 
2348 Xian_1980_GK_CM_135E  
2349 Xian_1980_3_Degree_GK_Zone_25 
2350 Xian_1980_3_Degree_GK_Zone_26 
2351 Xian_1980_3_Degree_GK_Zone_27 
2352 Xian_1980_3_Degree_GK_Zone_28 
2353 Xian_1980_3_Degree_GK_Zone_29 
2354 Xian_1980_3_Degree_GK_Zone_30 
2355 Xian_1980_3_Degree_GK_Zone_31 
2356 Xian_1980_3_Degree_GK_Zone_32 
2357 Xian_1980_3_Degree_GK_Zone_33 
2358 Xian_1980_3_Degree_GK_Zone_34 
2359 Xian_1980_3_Degree_GK_Zone_35 
2360 Xian_1980_3_Degree_GK_Zone_36 
2361 Xian_1980_3_Degree_GK_Zone_37 
2362 Xian_1980_3_Degree_GK_Zone_38 
2363 Xian_1980_3_Degree_GK_Zone_39 
2364 Xian_1980_3_Degree_GK_Zone_40 
2365 Xian_1980_3_Degree_GK_Zone_41 
2366 Xian_1980_3_Degree_GK_Zone_42 
2367 Xian_1980_3_Degree_GK_Zone_43 
2368 Xian_1980_3_Degree_GK_Zone_44 
2369 Xian_1980_3_Degree_GK_Zone_45

例子

下面两个4326坐标系的坐标,只相差了一点点,但是转换为26986坐标系时,出现了翻转。

postgres=# select ST_AsEWKT(ST_Transform(ST_GeomFromText('POINT(108.50000000001 22.8)', 4326), 26986));  
                      st_asewkt                        
-----------------------------------------------------  
 SRID=26986;POINT(8123333.59043839 12671815.6459695)  
(1 row)  
  
postgres=# select ST_AsEWKT(ST_Transform(ST_GeomFromText('POINT(108.5000000001 22.8)', 4326), 26986));  
                      st_asewkt                         
------------------------------------------------------  
 SRID=26986;POINT(-7723333.59044452 12671815.6459593)  
(1 row)  

使用转换后的坐标,计算距离,导致数据不准确,原因就是这个SRID的中央子午线离计算点太远,离中央子午线越远的地方投影误差越大。

postgres=# select * from spatial_ref_sys where srid=26986;
-[ RECORD 1 ]-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
srid      | 26986
auth_name | EPSG
auth_srid | 26986
srtext    | PROJCS["NAD83 / Massachusetts Mainland",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",42.68333333333333],PARAMETER["standard_parallel_2",41.71666666666667],PARAMETER["latitude_of_origin",41],PARAMETER["central_meridian",-71.5],PARAMETER["false_easting",200000],PARAMETER["false_northing",750000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],AUTHORITY["EPSG","26986"]]
proj4text | +proj=lcc +lat_1=42.68333333333333 +lat_2=41.71666666666667 +lat_0=41 +lon_0=-71.5 +x_0=200000 +y_0=750000 +datum=NAD83 +units=m +no_defs 


postgres=# select * from spatial_ref_sys where srid=4326;
-[ RECORD 1 ]---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
srid      | 4326
auth_name | EPSG
auth_srid | 4326
srtext    | GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]
proj4text | +proj=longlat +datum=WGS84 +no_defs 

PROJCS表示投影坐标,GEOGCS表示球面坐标。

解决方法,不要转换为26986坐标系统(即平面坐标),如果求球面坐标,使用PostGIS对应函数即可。即使要求平面距离,那么也应该使用国内常用坐标系(使用央子午线在国内的坐标系,参考本文前面的内容)。

http://postgis.net/docs/manual-2.0/ST_Distance.html

try this:  
  
postgres=# select ST_Distance(ST_GeographyFromText('SRID=4326;POINT(108.51 22.8)'), ST_GeographyFromText('SRID=4326;POINT(108.499999999999999 22.79)'));  
-[ RECORD 1 ]--------------------  
st_distance | 1510.16913796499989  
  
-- Geography example -- same but note units in meters - use sphere for slightly faster less accurate  
  
-- Geometry example - units in meters (SRID: 26986 Massachusetts state plane meters) (most accurate for Massachusetts)  

Flag Counter

digoal’s 大量PostgreSQL文章入口