How do I calculate Manhattan distance with PostGIS?
I am answering my own question with a proposed query.
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
This results in the following with some columns snipped out:
Kedzie-Ravenswood Brown Line 3738.52830193659 3796.29623843171 Addison-O'Hare Blue Line 4105.37381385087 5790.20002649655
The first numbered column is the distance (in feet, because I'm using EPSG 3435) calculated by the ST_Distance PostGIS function, and the second numbered column is the result of the Manhattan distance formula.
I spot-checked the second result, getting the walking distance from Google Maps between the Addison Blue Line CTA station and the building at 3226 W Belle Plaine Ave (noted as '100533644' in the query). Google Maps outputted a 1.1 miles walking route while the Postgres result of 5,790 feet = 1.09 miles. The difference is acceptable for my purposes.
I think I also found a slightly more elegant solution that uses trigonometry and the built in
ST_Azimuth function and encapsulated it into a nice function:
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