Internally, ST_Buffer(geography, ...)
uses a fixed projection guess with _ST_BestSRID
, which are typically UTM zones or whatever makes sense to the algorithm. This is why you see the differences, because they are different projections that are not optimized for the location of the points.
For simple point buffers, you could use a custom azimuthal equidistant projection, which are better suited for unique points around the globe.
For PostGIS 2.2 and earlier, get a new ST_Transform function to use custom projections:
CREATE FUNCTION ST_Transform(geom geometry, from_proj text, to_srid integer)
RETURNS geometry AS
'SELECT postgis_transform_geometry(ST_SetSRID($1, 99), $2, proj4text, $3)
FROM spatial_ref_sys WHERE srid=$3;'
LANGUAGE sql IMMUTABLE STRICT;
And use it to determine buffers of various places:
SELECT ST_Area(
ST_Transform(ST_Buffer(ST_MakePoint(0, 0), 100000.0),
concat('+proj=aeqd +lat_0=', ST_Y(ST_Centroid(geom)),
' +lon_0=', ST_X(ST_Centroid(geom)),
' +x_0=0 +y_0=0 +ellps=WGS84 +no_defs '),
4326)::geography) / 1000000 AS area_km2
FROM (
SELECT 'SRID=4326;POINT(0 0)'::geometry geom
UNION SELECT 'SRID=4326;POINT(30 30)'::geometry
) s;
area_km2
------------------
31213.8449025783
31213.8469309491
(2 rows)
I hope a 0.002 km² difference is acceptable. You can also experiment with Lambert azimuthal equal-area projections by using +proj=laea
for the PROJ text; I get differences as low as 0.00007 km².