Como calculo a distância de Manhattan com o PostGIS?

9

Estou usando a função ST_Distance para calcular a distância entre duas geometrias (uma estação de trem e um prédio) Como sei que todos os prédios e todas as estações de trem estão em Chicago, que possui uma excelente / completa malha viária, eu gostaria de usar Manhattan distância (ou táxi) .

A fórmula genérica para isso é a diferença em X mais a diferença em Y, então Abs (X1-X2) + Abs (Y1-Y2).

Que consulta do PostgreSQL faria isso funcionar?

stevevance
fonte
11
Uma rápida reflexão aqui: sua grade 'x' e 'y' não estão necessariamente alinhadas com o sistema de coordenadas 'x' e 'y'. Portanto, pode ser necessário girar o vetor antes de extrair os componentes e calcular.
Craig Ringer
@CraigRinger Transformei as coordenadas na projeção predominante localmente, EPSG 3435, Illinois StatePlane East Feet. Isso é usado pela cidade de Chicago para todo o seu trabalho GIS. Respondi minha própria pergunta com alguma validação usando o cálculo de curta distância do Google Maps.
stevevance
11
Você também considerou estender seus dados PostGIS com o módulo pgRouting e usar as funções integradas? Aparentemente, o método A * usa algo semelhante .
precisa saber é o seguinte
@RyanDalton Eu considerei usar o pgRouting para outro projeto meu, mas o incômodo de configurá-lo para este projeto não vale os resultados mais precisos ou o custo do recurso no cálculo da rota.
stevevance

Respostas:

6

Estou respondendo minha própria pergunta com uma consulta proposta.

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

Isso resulta no seguinte com algumas colunas cortadas:

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

A primeira coluna numerada é a distância (em pés, porque estou usando o EPSG 3435) calculada pela função ST_Distance PostGIS, e a segunda coluna numerada é o resultado da fórmula da distância de Manhattan.

Eu verifiquei o segundo resultado, obtendo uma curta distância do Google Maps entre a estação CTA da Addison Blue Line e o prédio na 3226 W Belle Plaine Ave (anotada como '100533644' na consulta). O Google Maps produziu uma rota a pé de 1,1 milhas, enquanto o Postgres resulta de 5.790 pés = 1,09 milhas. A diferença é aceitável para meus propósitos.

stevevance
fonte
11
isso é ótimo - você pode ter resolvido um problema que é tratado de outra maneira na função 'distância de condução' do PGRouting ... Vou testar isso para alguns problemas que temos na DPS ...!
DPSSpatial
@mapBaker Obrigado! O pgRouting é muito complexo para minha simples necessidade de matemática.
stevevance
3

Eu acho que também encontrei uma solução um pouco mais elegante que usa trigonometria e a ST_Azimuthfunção incorporada e a encapsulou em uma boa função:

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
fonte