PostgreSQL中的距离计算问题 ST_Length

  • 背景:PostgreSQL 可以扩展PostGIS 模块,来完成地理空间计算方面的任务
  • 问题描述:使用内置函数 ST_Length来计算轨迹长度时,发现计算的结果和实际不太符合,查阅相关博客发现原来是坐标系的问题。(但是各个博客的内容可能不太准确,折腾了半天,最后找到官网,成功把问题解决,只能说,官网是个好东西
  • 问题解决:
    • 概念: geometry和geography的区别

      • geometry:planar 平面坐标系【supported by SQL Server conforms to the Open Geospatial Consortium (OGC) Simple Features for SQL Specification version 1.1.0.】
      • geography: terrestrial 地理坐标系【stores ellipsoidal (round-earth) data, such as GPS latitude and longitude coordinates.】
      • 也就是说,geometry是一个平面几何的概念,而geography是一个地球的地理的概念。所以当两个点形成一条轨迹时,输入经纬度直接采用geometry即几何的方法来算的话,会不准,因为地球是一个球体,而不是一个简单的平面
    • ST_Length函数的使用

      • 先来看看官网的描述(点击蓝色的两个字可以直达官网):

      ST_Length — Returns the 2D length of the geometry if it is a LineString or MultiLineString. geometry are in units of spatial reference and geography are in meters (default spheroid)

      输入LineString 或者 MultiLineString 格式的数据,然后返回二维的长度。对于geometry类型,返回对应空间参考系的单位,对于geography返回以为单位。

      • 再看看函数定义

      float ST_Length(geometry a_2dlinestring);
      float ST_Length(geography geog, boolean use_spheroid=true);

      很明显,是分为两种输入类型来的。两种不同的输入返回输入的结果。而对于geography 类型的use_spheroid这个参数,默认为true,用来指定是否使用参考面(因为地图是个不规则球体,有各种参考面),如果为false,则不使用参考面,把地球当做一个完美的球体来看待,因而精度会降低,但是能剩下不少计算的时间。

    • 具体解决办法
      回归到问题, 直接把LineString 或 MultiLineString输入函数,属于 geometry 类型,所以如果是计算地理上面的数据,是不准确的。因此,使用ST_GeographyFromText函数将数据从geometry 类型转换为geography ,然后再进行计算,调用的就是第二个方法,得到正确的结果。
      官网示例:

      SELECT ST_Length(the_geog) As length_spheroid,  ST_Length(the_geog,false) As length_sphere
      		FROM 
      			(
      			SELECT ST_GeographyFromText('SRID=4326;LINESTRING(-72.1260 42.45, -72.1240 42.45666, -72.123 42.1546)') As the_geog
      			) As foo;

      结果:
      length_spheroid | length_sphere
      ------------------±-----------------
      34310.5703627288 | 34346.2060960742

      个人示例:

  1. 查看原始数据
SELECT gid,geom FROM bfmap_ways limit 10;

在这里插入图片描述
从上图可以看到,原始数据有两个字段,gid和geom,其中geom是二进制格式。

  1. 使用st_astext将二进制格式转换为文本格式,然后使用ST_GeographyFromText将数据转换为Geography格式,最后用st_length计算长度
SELECT gid, st_astext(geom) as geom,st_length(ST_GeographyFromText(st_astext(geom))) as length FROM bfmap_ways limit 10;

在这里插入图片描述
总结:不清楚的时候,尽可能去找官网!官网的描述最准确,最详细!最后再一次附上官网 http://postgis.net/docs/manual-2.3/ST_Length.html