i have a postgis table buildings
containing house perimeters located in Germany, with srid
3857, and i would like to calculate the area of each house.
my assumption is that
select st_area(st_transform(geom, utmzone(st_centroid(geom)))) as area from buildings
returns the most precise results, but i would like to have that confirmed by you.
running
select utmzone(st_centroid(geom)), count(*) from buildings group by utmzone(st_centroid(geom));
gives me:
in the following query, i employ a few more ways to calculate the areas:
select
rel_err(st_area(geom), area) a,
rel_err(st_area(st_transform(geom, 4326)), area) b,
rel_err(st_area(st_transform(geom, 4326)::geography), area) c,
rel_err(st_area(st_transform(geom, 25832)), area) d,
rel_err(st_area(st_transform(geom, 25833)), area) e,
rel_err(st_area(st_transform(geom, 31467)), area) f,
rel_err(st_area(st_transform(geom, 32631)), area) g,
rel_err(st_area(st_transform(geom, 32632)), area) h
from
(select st_area(st_transform(geom, utmzone(st_centroid(geom)))) as area, geom from buildings) q;
The resulting output looks like this:
(where rel_err(x, a) := (x-a)/a
)
so here is my question: is the calculated value area
really the best approximation to the area of the house perimeters, or is the value underlying one of the columns c
to h
even more precise? or is there another even more precise calculation method?
The answer is c
, geography-based distance over a spheroid.
SRID 3857
is pretty bad at distances and areas the further north you go. It thinks the poles are lines as long as the equator. See what happens when you ask about a location 200 meters east from the North Pole:
with the_north_pole as (select st_point(0,90,4326) geom)
select ST_Distance( st_transform(geom,3857)
,ST_Translate(st_transform(geom,3857),200,0))--moving 200m East
from the_north_pole ;
st_distance |
---|
200 |
In reality, 200 meters east from the North Pole is still the North Pole - you're just spinning in place, around the point. If you use proper geography
, it knows that if you go 180 degrees east, you haven't moved:
select ST_Distance( st_point(0 ,90,4326)::geography
,st_point(180,90,4326)::geography
,use_spheroid=>true );
st_distance |
---|
0 |
Your area of interest isn't as extreme, but this effect still applies: all your flat projections are stretching things further north, which is why you see their surface areas shrink when you deproject them to geography(polygon,4326)
. The larger zone you use, the more distorted everything typically gets, so
3857
is your most distorted result,2583x
,3263x
31467
strikes a good balance as a local projectionAdditionally, in reality all large "flat" surfaces are actually curved to match the curvature of the globe and geography
-based calculations account for this, while projected geometries
think it's all perfectly flat.
The price is of course performance: geography calculations over a sphere are expensive, over a spheroid even more so. This expense might not really be worth it if you consider that the locations of vertices of your buildings are likely further from the ground truth than the differences between the ST_Area()
results in these cases are from each other.