Pergunta

Eu estou usando um longo SRID lat / no meu banco de dados PostGIS (-4.326). Eu gostaria de encontrar as mais próximas aponta para um determinado ponto de uma maneira eficiente. Eu tentei fazer um

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

que me dá ok resultados nos 48 estados mais baixos, mas no Alasca que me dá lixo. Existe uma maneira de fazer cálculos de distância verdadeira em PostGIS, ou eu vou ter que dar um buffer de tamanho razoável e depois calcular os grandes distâncias de círculo e classificar os resultados no código depois?

Foi útil?

Solução

Você está procurando ST_distance_sphere (point, point) ou st_distance_spheroid (point, point).

Veja:

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

Este é normalmente referido uma geodésica ou a distância geodésica ... enquanto os dois termos têm significados ligeiramente diferentes, eles tendem a ser usados ??de forma alternada.

Como alternativa, você pode projetar os dados e usar a função ST_Distance padrão ... isso só é prático em distâncias curtas (usando UTM ou estado avião) ou se todas as distâncias são relativas a um um ou dois pontos (projeções eqüidistantes).

Outras dicas

PostGIS 1,5 alças verdadeiros distâncias globo usando anseia lat e metros. Ele está ciente de que lat / long é angular na natureza e tem uma 360 linha de grau

Esta é a partir do SQL Server, e eu uso Haversine para uma distância ridiculamente rápido que pode sofrer de seu problema Alaska (pode ser desligado por uma milha):

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 é lento, mas com precisão de 1 mm (e eu só encontrei um imp javascript dela):

/*
 * 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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top