【问题标题】:Real (Great Circle) distance in PostGIS with lat/long SRID?具有纬度/经度 SRID 的 PostGIS 中的真实(大圆)距离?
【发布时间】:2008-09-23 17:49:26
【问题描述】:

我在我的 PostGIS 数据库 (-4326) 中使用 lat/long SRID。我想以有效的方式找到离给定点最近的点。我试着做一个

ORDER BY    ST_Distance(point, ST_GeomFromText(?,-4326))

这给了我在较低的 48 个州的好结果,但在阿拉斯加它给了我垃圾。有没有办法在 PostGIS 中进行实际距离计算,还是我必须提供一个合理大小的缓冲区,然后计算大圆距离,然后在代码中对结果进行排序?

【问题讨论】:

    标签: gis postgis


    【解决方案1】:

    您正在寻找 ST_distance_sphere(point,point) 或 st_distance_spheroid(point,point)。

    见:

    http://postgis.refractions.net/documentation/manual-1.3/ch06.html#distance_sphere http://postgis.refractions.net/documentation/manual-1.3/ch06.html#distance_spheroid

    这通常指测地线或测地线距离...虽然这两个术语的含义略有不同,但它们往往可以互换使用。

    或者,您可以投影数据并使用标准 st_distance 函数...这仅适用于短距离(使用 UTM 或状态平面)或所有距离都相对于一个或两个点(等距投影)。

    【讨论】:

    • 不幸的是,它不在 Debian Stable (1.1.6-2) 附带的 PostGIS 版本中。我猜是时候开始寻找反向移植了。
    【解决方案2】:

    PostGIS 1.5 使用经纬度和米来处理真实的地球距离。它知道纬度/经度本质上是有角度的,并且有 360 度线

    【讨论】:

    • 怎么样?它不适合我。
    【解决方案3】:

    这是来自 SQL Server,我使用 Haversine 进行了一段非常快的距离,可能会受到阿拉斯加问题的影响(可能会偏离一英里):

    ALTER function [dbo].[getCoordinateDistance]
        (
        @Latitude1  decimal(16,12),
        @Longitude1 decimal(16,12),
        @Latitude2  decimal(16,12),
        @Longitude2 decimal(16,12)
        )
    returns decimal(16,12)
    as
    /*
    fUNCTION: getCoordinateDistance
    
        Computes the Great Circle distance in kilometers
        between two points on the Earth using the
        Haversine formula distance calculation.
    
    Input Parameters:
        @Longitude1 - Longitude in degrees of point 1
        @Latitude1  - Latitude  in degrees of point 1
        @Longitude2 - Longitude in degrees of point 2
        @Latitude2  - Latitude  in degrees of point 2
    
    */
    begin
    declare @radius decimal(16,12)
    
    declare @lon1  decimal(16,12)
    declare @lon2  decimal(16,12)
    declare @lat1  decimal(16,12)
    declare @lat2  decimal(16,12)
    
    declare @a decimal(16,12)
    declare @distance decimal(16,12)
    
    -- Sets average radius of Earth in Kilometers
    set @radius = 6366.70701949371
    
    -- Convert degrees to radians
    set @lon1 = radians( @Longitude1 )
    set @lon2 = radians( @Longitude2 )
    set @lat1 = radians( @Latitude1 )
    set @lat2 = radians( @Latitude2 )
    
    set @a = sqrt(square(sin((@lat2-@lat1)/2.0E)) + 
        (cos(@lat1) * cos(@lat2) * square(sin((@lon2-@lon1)/2.0E))) )
    
    set @distance =
        @radius * ( 2.0E *asin(case when 1.0E < @a then 1.0E else @a end ) )
    
    return @distance
    
    end
    

    Vicenty 很慢,但精确到 1 毫米以内(我只找到了它的 javascript imp):

    /*
     * Calculate geodesic distance (in m) between two points specified by latitude/longitude (in numeric degrees)
     * using Vincenty inverse formula for ellipsoids
     */
    function distVincenty(lat1, lon1, lat2, lon2) {
      var a = 6378137, b = 6356752.3142,  f = 1/298.257223563;  // WGS-84 ellipsiod
      var L = (lon2-lon1).toRad();
      var U1 = Math.atan((1-f) * Math.tan(lat1.toRad()));
      var U2 = Math.atan((1-f) * Math.tan(lat2.toRad()));
      var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
      var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
    
      var lambda = L, lambdaP = 2*Math.PI;
      var iterLimit = 20;
      while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
        var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
        var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + 
          (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
        if (sinSigma==0) return 0;  // co-incident points
        var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
        var sigma = Math.atan2(sinSigma, cosSigma);
        var sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
        var cosSqAlpha = 1 - sinAlpha*sinAlpha;
        var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
        if (isNaN(cos2SigmaM)) cos2SigmaM = 0;  // equatorial line: cosSqAlpha=0 (§6)
        var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
        lambdaP = lambda;
        lambda = L + (1-C) * f * sinAlpha *
          (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
      }
      if (iterLimit==0) return NaN  // formula failed to converge
    
      var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
      var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
      var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
      var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
        B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
      var s = b*A*(sigma-deltaSigma);
    
      s = s.toFixed(3); // round to 1mm precision
      return s;
    }
    

    【讨论】:

    • 感谢您的回复,但我仍然希望有人在 PostGIS 中有解决方案,或者至少使用与它相同的库。
    • 我的错误——我查看了 PostGIS,发现它最初是在 PostgreSQL 上运行的。我认为您也许可以利用其中的一些逻辑来编写您需要的内容。
    猜你喜欢
    • 2016-08-29
    • 1970-01-01
    • 1970-01-01
    • 2011-10-01
    • 1970-01-01
    • 2014-02-01
    • 2011-10-13
    • 1970-01-01
    • 2011-08-16
    相关资源
    最近更新 更多