¿Cómo calculo la distancia de Manhattan con PostGIS?

9

Estoy usando la función ST_Distance para calcular la distancia entre dos geometrías (una estación de tren y un edificio) Como sé que todos los edificios y todas las estaciones de tren están en Chicago, que tiene una cuadrícula de calles excelente / completa, me gustaría usar Manhattan (o taxi) distancia .

La fórmula genérica para esto es la diferencia en X más la diferencia en Y, entonces Abs (X1-X2) + Abs (Y1-Y2).

¿Qué consulta PostgreSQL haría que esto funcione?

stevevance
fuente
1
Un pensamiento rápido aquí: su cuadrícula 'x' e 'y' no están necesariamente alineadas con las 'x' e 'y' del sistema de coordenadas. Por lo tanto, es posible que deba rotar el vector antes de extraer los componentes y calcular.
Craig Ringer
@CraigRinger Transforme las coordenadas a la proyección predominante localmente, EPSG 3435, Illinois StatePlane East Feet. Esto es utilizado por la Ciudad de Chicago para todo su trabajo SIG. He respondido a mi propia pregunta con alguna validación usando el cálculo de distancia a pie de Google Maps.
stevevance
1
¿También ha considerado extender sus datos de PostGIS con el módulo pgRouting y usar sus funciones integradas? Aparentemente, el método A * usa algo similar .
RyanKDalton
@RyanDalton He considerado usar pgRouting para otro proyecto mío, pero la molestia de configurar uno para este proyecto no vale los resultados más precisos o el costo de recursos para calcular la ruta.
stevevance

Respuestas:

6

Estoy respondiendo mi propia pregunta con una consulta propuesta.

select *, ABS(x_permit-x_station)+ABS(y_permit-y_station) as manhattan FROM (SELECT
longname AS NAME,
lines AS metadata,
T .slug,
ST_Distance (
    T .geom,
    ST_Transform (P .geometry, 3435)
) AS distance, 
ST_X(ST_Transform(p.geometry, 3435)) as x_permit,
ST_Y(ST_Transform(p.geometry, 3435)) as y_permit,
ST_X(t.geom) as x_station,
ST_Y(t.geom) as y_station
FROM
permits P,
stations_cta T
WHERE
P .permit_ = '100533644'
ORDER BY
distance
LIMIT 2) as foo

Esto da como resultado lo siguiente con algunas columnas recortadas:

Kedzie-Ravenswood   Brown Line  3738.52830193659    3796.29623843171
Addison-O'Hare  Blue Line   4105.37381385087    5790.20002649655

La primera columna numerada es la distancia (en pies, porque estoy usando EPSG 3435) calculada por la función ST_Distance PostGIS, y la segunda columna numerada es el resultado de la fórmula de distancia de Manhattan.

Verifiqué el segundo resultado, obteniendo la distancia a pie de Google Maps entre la estación de CTA de la Línea Azul de Addison y el edificio en 3226 W Belle Plaine Ave (anotado como '100533644' en la consulta). Google Maps generó una ruta de caminata de 1.1 millas mientras que el resultado de Postgres de 5,790 pies = 1.09 millas. La diferencia es aceptable para mis propósitos.

stevevance
fuente
1
esto es genial: es posible que haya resuelto un problema que de otro modo se trata en la función de 'distancia de conducción' de PGRouting ... ¡Probaré esto para algunos problemas que tenemos en DPS ...!
DPSEspacial
@mapBaker ¡Gracias! pgRouting es demasiado complejo para mi simple necesidad matemática.
stevevance
3

Creo que también encontré una solución un poco más elegante que utiliza trigonometría y la ST_Azimuthfunción integrada y la encapsuló en una función agradable:

CREATE OR REPLACE FUNCTION JZ_TaxiCab(p1 geometry, p2 geometry)
RETURNS REAL AS $$
DECLARE 
    az REAL;
    h REAL;
BEGIN
    az := ST_Azimuth(p1, p2);
    /* Cast to geography to get result in meters */
    h := ST_Distance(p1::geography, p2::geography);
    /*   Note: we have to take abs() because the values returned by
         can be positive or negative. We really don't necessarily care
         about the reference point since it's going to be a right triangle.
    */
    RETURN h * abs(sin(az)) + h * abs(cos(az));
END;
$$  LANGUAGE plpgsql
jzarob
fuente