Comment calculer la distance de Manhattan avec PostGIS?

9

J'utilise la fonction ST_Distance pour calculer la distance entre deux géométries (une gare et un bâtiment) Puisque je sais que tous les bâtiments et toutes les gares sont à Chicago, qui a une excellente / complète grille de rue, j'aimerais utiliser Manhattan (ou taxi) distance .

La formule générique pour cela est la différence de X plus la différence de Y, donc Abs (X1-X2) + Abs (Y1-Y2).

Quelle requête PostgreSQL ferait fonctionner cela?

stevevance
la source
1
Une pensée rapide ici: Votre grille «x» et «y» ne sont pas nécessairement alignées avec les «x» et «y» du système de coordonnées. Vous devrez donc peut-être faire pivoter le vecteur avant d'extraire les composants et de calculer.
Craig Ringer
@CraigRinger Je transforme les coordonnées en projection prévalant localement, EPSG 3435, Illinois StatePlane East Feet. Il est utilisé par la ville de Chicago pour tous ses travaux SIG. J'ai répondu à ma propre question avec une certaine validation en utilisant le calcul de la distance de marche de Google Maps.
stevevance
1
Avez-vous également envisagé d'étendre vos données PostGIS avec le module pgRouting et d'utiliser ses fonctions intégrées? Apparemment, la méthode A * utilise quelque chose de similaire .
RyanKDalton
@RyanDalton J'ai envisagé d'utiliser pgRouting pour un autre de mes projets, mais les tracas d'en créer un pour ce projet ne valent pas les résultats plus précis ni le coût des ressources pour calculer l'itinéraire.
stevevance

Réponses:

6

Je réponds à ma propre question avec une requête proposée.

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

Cela se traduit par ce qui suit avec certaines colonnes coupées:

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

La première colonne numérotée est la distance (en pieds, car j'utilise EPSG 3435) calculée par la fonction ST_Distance PostGIS, et la deuxième colonne numérotée est le résultat de la formule de distance de Manhattan.

J'ai vérifié le deuxième résultat, obtenant la distance de marche de Google Maps entre la station CTA Addison Blue Line et le bâtiment au 3226 W Belle Plaine Ave (noté «100533644» dans la requête). Google Maps a généré un itinéraire de marche de 1,1 mile tandis que le résultat Postgres de 5790 pieds = 1,09 miles. La différence est acceptable à mes fins.

stevevance
la source
1
c'est génial - vous avez peut-être résolu un problème qui est autrement traité dans la fonction de «distance de conduite» de PGRouting ... Je vais tester cela pour certains problèmes que nous avons chez DPS ...!
DPSSpatial
@mapBaker Merci! pgRouting est trop complexe pour mes besoins mathématiques simples.
stevevance
3

Je pense que j'ai également trouvé une solution légèrement plus élégante qui utilise la trigonométrie et la ST_Azimuthfonction intégrée et l'encapsulé dans une belle fonction:

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
la source