Skip to content

DHI.Spatial.GeoAPI — Internal Developer Guide

This tiny bridge converts between our DHI.Spatial geometry types and NetTopologySuite (NTS) geometries using well-known binary (WKB) as the interchange. It lets you run robust NTS operations (area, intersect, buffer, etc.) while keeping DHI’s lightweight model on the edges.


What it exposes

Namespace: DHI.Spatial.GeoAPI

public static class Geometry
{
    public static IGeometry FromWkb(byte[] wkb);
    public static NetTopologySuite.Geometries.Geometry ToGeoAPIGeometry(this IGeometry geometry);
    public static byte[] ToWkb(this IGeometry geometry);
}

Internally it reuses:

  • GeometryFactory (static)
  • WKBWriter (static)
  • A fresh WKBReader per call

Supported geometry types

Type FromWkb → DHI.Spatial DHI.Spatial → ToWkb Notes
Point Z read from WKB into Position if present; Z is not written back (2D WKB output).
LineString Z ignored; XY only.
Polygon (outer + holes) Rings must be closed. ToWkb will auto-close any unclosed ring.
MultiPoint Z ignored; XY only.
MultiLineString Z ignored; XY only.
MultiPolygon Holes must be closed; unclosed holes are skipped on read.
GeometryCollection Not handled by this bridge. Use NTS directly if needed.

ToGeoAPIGeometry(this IGeometry) is implemented by: geometry.ToWkb()WKBReader.Read(…). That means it inherits the same dimensional rules as ToWkb.


Coordinate & CRS expectations

  • Coordinates are treated as plain XY (optionally Z for Point on read only).
  • No SRID/CRS is carried through WKB here. If you need SRID, set it later on the returned NTS geometry, or manage CRS metadata on the DHI side (IGeometry.CRS).

Ring & hole handling

  • Write (ToWkb) For Polygon and MultiPolygon, each linear ring is auto-closed (first != last → append first).

  • Read (FromWkb)

    • Polygon: outer shell + all holes imported.
    • MultiPolygon: each polygon’s holes are imported only if closed. Unclosed rings are ignored:
      • .NET 4.7.2 check: StartPoint.Equals(EndPoint)
      • Newer targets: StartPoint.EqualsExact(EndPoint)

Z-dimension rules

  • Read: Point preserves Z into Position; all other types ignore Z.
  • Write: always emits 2D WKB (XY only). Even if Position.Z exists, it’s dropped.

If you need full 3D round-trips, extend the writer/reader to use NTS WKBWriter(3) and populate Z-coordinates when building NTS objects.


Usage examples

1) Read WKB (from DB) → DHI geometry

byte[] wkb = /* read from DB */;
IGeometry g = DHI.Spatial.GeoAPI.Geometry.FromWkb(wkb);

// Use within our stack
if (g is Polygon poly)
{
    // poly.Coordinates: List<List<Position>>
}

2) DHI geometry → WKB (store/send)

var poly = new Polygon();
poly.Coordinates.Add(new List<Position> {
    new(0,0), new(10,0), new(10,5), new(0,5), new(0,0)
});

byte[] wkb = poly.ToWkb();
// Save to DB, send over wire, etc.

3) Run NTS ops by round-tripping via WKB

IGeometry dhi = Geometry.FromWKT("POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0))");
var nts = dhi.ToGeoAPIGeometry();

var buffered = nts.Buffer(1.0); // NTS operation
var dhiBuffered = DHI.Spatial.GeoAPI.Geometry.FromWkb(new WKBWriter().Write(buffered));
// now back in DHI.Spatial

Error behavior

  • Unsupported geometry → NotSupportedException("Geometry type '…' is not supported.")
  • Invalid WKB → exceptions from WKBReader.Read.
  • MultiPolygon: unclosed hole rings are silently skipped on read (by design).

Performance notes

  • WKBWriter and GeometryFactory are static singletons (stateless in our usage).
  • WKBReader is new per call (cheap).
  • Zero allocations on our geometry side beyond list building. If you do heavy multi-threaded work and want to avoid shared singletons, you can locally instantiate a WKBWriter per hot path.

When to use this bridge

  • You want to keep DHI.Spatial at API boundaries but leverage NTS for computational geometry.
  • Persisting geometries as WKB in a database.
  • Interop with systems that already speak WKB.

If you need GeometryCollection, 3D, SRID-aware pipelines, or advanced predicates, call NTS directly and only convert at the very edge where DHI types are required.