In a Ruby-on-Rails application, I need the area in square meters (or any other surface measure, be it ares, hectares, square kilometers, but not in pseudo surface measure like square degrees) of a geographic polygon.
The application already uses rgeo and PostGIS (via the activerecord-postgis-adapter) and already requires rgeo's optional libGEOS support. So I'd like to make use of these tools for the area calculation, too.
I can use PostGIS (via the activerecord-postgis-adapter) to compute the area of any rgeo geometry:
def calculate_area(geometry)
ActiveRecord::Base.connection.execute("
SELECT
ST_Area(
geography(
ST_Transform(
ST_GeomFromText('#{geometry.as_json}', #{geometry.srid}),
4326
)
)
) AS area;
")[0]['area'].to_f
end
(Nesting ST_Area()
's argument in geography()
is what causes the result to be in square meters.)
However, having to extract the result from execute()
's return value and having the lengthy SQL code embedded in Ruby is rather ugly.
Also, rgeo already provides an area
method on the RGeo::Feature::Surface
module, which RGeo::Feature::Polygon
includes. Its default implementation just isn't all that useful:
srs_db = RGeo::CoordSys::SRSDatabase::ActiveRecordTable.new
wgs84_factory = RGeo::Geographic.spherical_factory(srid: 4326, srs_database: srs_db)
# 1/8 of the Earth, should be around 6.4e13 sqm
polygon = wgs84_factory.parse_wkt('POLYGON ((0 0, 0 90, 90 0, 0 0))')
polygon.area # RGeo::Error::UnsupportedOperation: Method Surface#area not defined.
I have installed rgeo such that it can use libGEOS, which I can verify like so:
RGeo::GEOS.supported? # true
Thus, I thought that I might be luckier with a GEOS-specific factory:
geos_wgs84_factory = RGeo::Geos.factory(srid: 4326, srs_database: srs_db)
geos_polygon = RGeo::Feature.cast(polygon, geos_wgs84_factory)
geos_polygon.area # 4050.0
Well, that's the area in square degrees (90° ⨉ 90° / 2). How can I get the area in square meters without calculating it myself and without longish SQL?
Must I convert my polygon to an area-preserving projected coordinate reference system? (Those seem to come with a working area
method in rgeo.) That'd feel kinda wrong to me, but maybe it's still the right thing to do.
.area
on the result would have worked, too, though, I believe.