How do you calculate the area of a polygon using the NetTopologySuite in meters squared?

625 views Asked by At

I am trying to calculate the area of a polygon using the NetTopologySuite version 2.5.0 in meters squared.

  var gf_4326 = NetTopologySuite.NtsGeometryServices.Instance.CreateGeometryFactory(4326);

  var ply1 = gf_4326.CreatePolygon(new[] {
    new NetTopologySuite.Geometries.Coordinate(-37.736229, 144.998645),
    new NetTopologySuite.Geometries.Coordinate(-37.735903, 144.998704),
    new NetTopologySuite.Geometries.Coordinate(-37.73591958016702, 144.9988860085938),
    new NetTopologySuite.Geometries.Coordinate(-37.736248, 144.998830),
    new NetTopologySuite.Geometries.Coordinate(-37.736229, 144.998645),
  });

  double area = ply1.Area;

In this example the area is not in meters squared. The area is 6.106743190353325E-08 but should be the size of a suburban property. This is orders of magnitude too low.

What am I doing wrong?

1

There are 1 answers

0
Danwize On

Rather than use the ProjNet (no longer maintained) as recommended in this Microsoft article, I opted to use the .net library that Microsoft uses in sql server to do these calculations. I found this much easier than using ProjNet and adding various well known strings to do the projections.

This code depends on the Microsoft.SqlServer.Types and Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite nuget packages.

using Microsoft.SqlServer.Types;
using NetTopologySuite.Geometries;
using System.Data.SqlTypes;

namespace someNamespace;

public static class GeometryExtensions
{
  /// <summary>
  /// Get the area of a 4326 srid geometry in degrees and in square meters.
  /// </summary>
  /// <param name="geometry"></param>
  /// <returns></returns>
  /// <see cref="https://learn.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.types.sqlgeometry?view=sql-dacfx-161"/>
  public static (double degrees, double squareMeters) GetArea(this Geometry geometry) => (geometry.Area, geometry.ToSqlGeography().STArea().Value);

  /// <summary>
  /// Convert to SqlGeography type.
  /// </summary>
  /// <param name="geometry"></param>
  /// <returns></returns>
  public static SqlGeography ToSqlGeography(this Geometry geometry)
  {
    var chars = new SqlChars(geometry.ToString());
    var sqlGeometry = SqlGeography.STGeomFromText(chars, geometry.SRID);
    //You can call make valid to sort the data correctly.  The vertices for
    //holes need to be clock-wize, and the shell needs to be counter clock-wize.
    return sqlGeometry.MakeValid(); 
  }
}

To use the extension method:

var geometryFactory = new GeometryFactory().WithSRID(4326);
var geometry = new Polygon(
    new LinearRing(
        new Coordinate[] {
        new Coordinate(-111.8957167797442, 40.74144871385557),
        new Coordinate(-111.896525396994, 40.74148525294619),
        new Coordinate(-111.8965956236037, 40.73991559146825),
        new Coordinate(-111.8956929648341, 40.73991533299716),
        new Coordinate(-111.8957167797442, 40.74144871385557), //last coordinate is the same as the first to complete the polygon.
        }
    ),
    new LinearRing[] {
        new LinearRing(
            new Coordinate[] {
                new Coordinate(-111.8960808793144, 40.74070224387572),
                new Coordinate(-111.8962675667794, 40.74070224015685),
                new Coordinate(-111.8962845392151, 40.74058168344138),
                new Coordinate(-111.8960893345688, 40.74058972026804), 
                new Coordinate(-111.8960808793144, 40.74070224387572) //last coordinate is the same as the first to complete the polygon.
            }
        )
    },
    geometryFactory
);

//this is the area in degrees 1.3057656726336629E-06
Console.WriteLine(geometry.Area);
//this is the area in square meters. 12247.619245234877
Console.WriteLine(geometry.GetArea().squareMeters);