Search code examples
c#asp.netleafletpolygonnettopologysuite

Nettopologysuite Geometry.Intersection does not output the expected polygon


I am creating a web application which shows what area is reachable in x amount of minutes inside of the Netherlands. To do this I want to intersect the polygon of the reachable area(s) with the one of the Netherlands.

I am displaying the polygons in the front-end using the leaflet library.

When computing the intersected polygon the polygon will not be a closed polygon of the intersected area, as I would expect.

The polygon of the Netherlands can be seen in this image (I only show the outline in the front-end): polygon of the Netherlands

The polygon of the reachable area can be seen in this image: polygon of the reachable area

Then using this method, I form a polygon of the intersection between these polygons and return the coordinates to display in the front-en.

public Coordinate[] GetAreaInTheNetherlands(Point[] area, Coordinate[] border)
  {
    var geometryFactory = new GeometryFactory();

    var borderPolygon = (Geometry)geometryFactory.CreatePolygon(border.Append(border.FirstOrDefault()).ToArray());
    borderPolygon = borderPolygon.Buffer(0);

    var areaPolygon = (Geometry)geometryFactory.CreatePolygon(area.Select(p => p.Coordinate).ToArray());
    areaPolygon = areaPolygon.Buffer(0);

    var intersect = areaPolygon.Intersection(borderPolygon);
    intersect = intersect.Buffer(0);

    return intersect.Coordinates;
  }

in this context area is the reachable area, and border is the coordinates of the polygon of the Netherlands.

This will output in the following polygon: example1 example2

It appear like the coordinates are correct, but maybe it should output a multi-polygon. Does anyone know how to do this or what else might be wrong in my code?


Solution

  • I was not checking the type correctly in the backend.

    In the backend I changed my method to the following.

    public Polygon[] GetAreaInTheNetherlands(Coordinate[] area, Coordinate[] border)
      {
        var geometryFactory = new GeometryFactory();
        var borderPolygon = GeometryUtils.CreatePolygons(geometryFactory, border.Append(border.FirstOrDefault()).ToArray()!).First();
        var areaPolygon = GeometryUtils.CreatePolygons(geometryFactory, area).First();
        
        var intersection = areaPolygon.Intersection(borderPolygon);
        if (intersection is MultiPolygon)
        {
          return ((MultiPolygon)intersection).Select(p => (Polygon)p).ToArray();
        }
        return new Polygon[] { (Polygon)intersection };
      }
    

    I had to adjust the way I receive the api call in the front-end a little but I was left with: new polygon

    Note: I also made a custom CreatePolygons method, since I realised that when creating a Polygon and using Buffer method the output can also be of MultiPolygon (This is never the case the code above hence the First(), but it was in some of my other methods):

    public static Polygon[] CreatePolygons(GeometryFactory geometryFactory, Coordinate?[]? coordinates, bool debug = false)
      {
        var geometry = geometryFactory.CreatePolygon(coordinates).Buffer(0);
        
        if (!geometry.IsValid) {
          System.Console.WriteLine("Geometry is not valid");
          throw new Exception("Trying to create an invalid object of type Geometry.");
        }
        
        if (geometry is Polygon)
        {
          if (debug) System.Console.WriteLine("Geometry is of type Polygon");
          return new Polygon[] { (Polygon)geometry };
        } 
        else if (geometry is MultiPolygon) 
        {
          if (debug) System.Console.WriteLine("Geometry is of type MultiPolygon");
          return ((MultiPolygon)geometry).Select(p => (Polygon)p).ToArray();
        }
        else 
        {
          System.Console.WriteLine("ERROR: Geometry is not of type Polygon or MultiPolygon.");
          return new Polygon[0];
        }
      }