문제

PostGIS 데이터베이스 (-4326)에서 LAT/Long SRID를 사용하고 있습니다. 효율적인 방식으로 주어진 지점에 가장 가까운 지점을 찾고 싶습니다. 나는 노력했다

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

48 개 주에서는 괜찮아 지지만 알래스카에서는 쓰레기를줍니다. PostGIS에서 실제 거리 계산을 수행 할 수있는 방법이 있습니까, 아니면 합리적인 크기의 버퍼를 제공 한 다음 그레이트 원 거리를 계산하고 나중에 코드에서 결과를 정렬해야합니까?

도움이 되었습니까?

해결책

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 또는 상태 평면 사용) 또는 모든 거리가 1 ~ 2 점 (등가 투영)과 관련이있는 경우에만 실용적입니다.

다른 팁

Postgis 1.5는 LAT LONG 및 미터를 사용하여 진정한 지구 거리를 처리합니다. Lat/Long은 본질적으로 각도이며 360도 선이 있음을 알고 있습니다.

이것은 SQL Server에서 나온 것이며, 알래스카 문제로 고통받을 수있는 엄청나게 빠른 거리에 Haversine을 사용합니다 (1 마일 씩 꺼질 수 있음).

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 mm 이내에 정확합니다 (그리고 나는 그것의 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;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top