2008-09-23 4 views
6

PostGIS 데이터베이스에서 위도/경도 SRID를 사용하고 있습니다 (-4326). 주어진 점에 가장 가까운 점을 효율적인 방식으로 찾고 싶습니다. 나는 시도를했다위도/경도 SRID가있는 PostGIS의 실제 (큰 원) 거리?

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

내가 낮은 48의 상태로 나에게 좋은 결과를 준다. 그러나 알래스카에서 그것은 쓰레기를 준다. PostGIS에서 실제 거리 계산을 할 수있는 방법이 있습니까? 아니면 합리적인 크기의 버퍼를 제공 한 다음 큰 원 거리를 계산하고 나중에 코드에서 결과를 정렬해야합니까?

답변

9

당신은 ST_distance_sphere (점, 점) 또는 st_distance_spheroid (점, 점)을 찾고 있습니다.

볼 : 두 용어는 약간 다른 의미를 갖는다 동안 http://postgis.refractions.net/documentation/manual-1.3/ch06.html#distance_spheroid

http://postgis.refractions.net/documentation/manual-1.3/ch06.html#distance_sphere 일반적 측지선 또는 측지 거리 ... 칭한다 그들이 교환 할 수 경향이있다.

또는 데이터를 투영하고 표준 st_distance 함수를 사용할 수 있습니다. 단거리 (UTM 또는 상태 평면 사용) 또는 모든 거리가 한 점 또는 두 점 (등거리 투영)에 상대적 인 경우에만 실용적입니다. .

+0

유감 스럽지만 Debian Stable (1.1.6-2)과 함께 제공되는 PostGIS 버전에는 없습니다. 백 포트 찾기를 시작할 시간입니다. –

2

이 SQL 서버에서, 그리고 내가 (마일으로 해제 할 수 있습니다) 당신의 알래스카 문제로 고통 수있는 말도 안되게 빠른 거리 하버 사인을 사용

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((@[email protected])/2.0E)) + 
    (cos(@lat1) * cos(@lat2) * square(sin((@[email protected])/2.0E)))) 

set @distance = 
    @radius * (2.0E *asin(case when 1.0E < @a then 1.0E else @a end)) 

return @distance 

end 

Vicenty이 내에 정확한 느리지 만, 1mm (그리고 나는 단지 그것의 꼬마 도깨비 자바 스크립트를 발견) :

/* 
* 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; 
} 
+0

응답 주셔서 감사합니다. 그러나 저는 여전히 누군가가 PostGIS에서 해결책을 찾거나 최소한 동일한 라이브러리를 사용하기를 바랍니다. –

+0

내 실수 - PostGIS를보고 처음에는 PostgreSQL에서 실행되는 것을 보았습니다. 나는 당신이이 논리의 일부를 활용하여 필요한 것을 쓸 수있을 것이라고 생각했습니다. – nathaniel

4

PostGIS 1.5는 위도와 경도를 사용하여 실제 지구 거리를 처리합니다. 위도/경도가 본질적으로 각도가 있으며 360도 선을 가지고 있다는 것을 알고 있습니다

+0

어떻게? 그것은 나를위한 것이 아닙니다. –