Skip to content

DHI.Services.GeoServer for GIS — Internal Developer Guide (WFS → FeatureCollection)

Audience: devs using DHI.Services.GIS with GeoServer as a vector/feature source. For placeholders/auth/time/CRS details, see GeoServer Core.


What this provider does

  • Calls GeoServer WFS (or any endpoint returning GeoJSON) using your templated query.
  • Parses GeoJSON via NTS and converts to DHI.Spatial geometries.
  • Returns a FeatureCollection<string> (features + schema/attributes).
  • Sets feature CRS if the GeoJSON root has crs.properties.name.

Designed for WFS GetFeature. If pointed at WMS (images), it won’t work.


Capabilities & limitations

Operation Support Notes
Get(id) Main entry. Uses the templated URL.
Get(id, associations, outSpatialReference) Delegates to Get(id). Params currently ignored.
Get(id, filter, …) Delegates to Get(id). filter currently ignored.
GetAll() Not implemented.
GetEnvelope / GetFootprint Not implemented.
GetIds() Lists layer names via GeoServer REST /rest/layers.
Auth Optional Basic Auth; prefer HTTPS.
Geometry 3D/Measured types are flattened to 2D (see Core).
Client Instance HttpClient (you may inject); no global header sharing.

Building the HTTP request (query template)

Typical WFS (GeoJSON) template:

[group]/ows?service=WFS&version=1.1.0&request=GetFeature
&typeName=[group]:[layer]
&srsName=[crs]
&bbox=[boundingbox]
&outputFormat=application/json

If the ID carries Time, the provider appends &time=yyyy-MM-ddThh:mm:ss.fffZ (12-hour, see Core).

If your template includes [width]/[height], your ID must include Size=....


ID grammar (what you pass as {id})

Parsed by GeoServerFeatureCollectionId (semicolon-separated key=value):

  • Group=<workspace> (string)
  • Layer=<layerName> (string)
  • Crs=<EPSG:XXXX> (string) — default EPSG:3857
  • BoundingBox=<xmin,ymin,xmax,ymax> (double; culture-invariant .)
  • Size=<width,height> (ints; only if your template uses [width]/[height])
  • Time=<timestamp> ParseExact with formats: "s", "u", or "O" (e.g., 2021-09-20T00:00:00Z)

Examples

Group=demo;Layer=roads;Crs=EPSG:4326;BoundingBox=12,55,13,56
Group=hydro;Layer=gauges;Crs=EPSG:3857;BoundingBox=1320000,7400000,1340000,7420000;Time=2021-09-20T00:00:00Z
Group=land;Layer=buildings;Size=1024,768;Crs=EPSG:3857;BoundingBox=12.0,55.0,13.0,56.0

Behavior: unknown keys are ignored (only documented keys affect substitution).


Wiring

A) Connections (Web API hosts)

"geoserver": {
  "$type": "DHI.Services.GIS.WebApi.GisServiceConnection, DHI.Services.GIS.WebApi",
  "ConnectionString": "BaseUrl=http://localhost:8080/geoserver/;Query=[group]/ows?service=WFS&version=1.1.0&request=GetFeature&typeName=[group]:[layer]&srsName=[crs]&bbox=[boundingbox]&outputFormat=application/json;UserName=user;Password=pass",
  "RepositoryType": "DHI.Services.Provider.GeoServer.FeatureCollectionRepository, DHI.Services.Provider.GeoServer",
  "Name": "GeoServer WFS",
  "Id": "geoserver"
}

B) Programmatic

var query =
  "[group]/ows?service=WFS&version=1.1.0&request=GetFeature" +
  "&typeName=[group]:[layer]" +
  "&srsName=[crs]" +
  "&bbox=[boundingbox]" +
  "&outputFormat=application/json";

ServiceLocator.Register(
  new GisService(
    new DHI.Services.Provider.GeoServer.FeatureCollectionRepository(
      baseUrl: "https://my.geoserver.company/geoserver/",
      query: query,
      userName: "user",
      password: "pass")),
  "geoserver");

Examples

Fetch features for a viewport (EPSG:4326)

string id =
  "Size=800,400;" +
  "Crs=EPSG:4326;" +
  $"BoundingBox={xmin},{ymin},{xmax},{ymax};" +
  "Layer=states;Group=topp";

var repo = new FeatureCollectionRepository(
  "BaseUrl=http://localhost/geoserver/;" +
  "Query=wfs?service=WFS&version=1.0.0&request=GetFeature&typeName=[group]:[layer]&outputFormat=application/json&srsName=[crs]&bbox=[boundingbox];" +
  "UserName=admin;Password=geoserver");

var maybe = repo.Get(id);
return maybe.HasValue ? Ok(maybe.Value) : NotFound();

List layer ids via REST

var repo = Services.Get<GisService<string>>("geoserver").Repository;
foreach (var name in repo.GetIds()) Console.WriteLine(name);

Performance tips

  • Always pass a tight BoundingBox for server-side clipping.
  • Use GeoServer layer/view config (or extend your template with CQL) to reduce attributes/feature counts.
  • Consider pagination (maxFeatures/count) in templates for large responses.

Troubleshooting

  • HTTP 200 but empty → confirm endpoint is WFS with outputFormat=application/json.
  • CRS missing → your server emitted RFC-7946 GeoJSON (no crs); interpret in requested Crs.
  • Layer 404 → verify Group/Layer spelling and credentials.
  • Time weirdness → provider sends hh (12-hour). If you need HH, use your own [time] placeholder or adjust code.

Design notes (under the hood)

  • FeatureCollectionRepository builds the query by replacing placeholders; if Time is set, appends &time=… (see Core).
  • Before NTS parsing: json.Replace("MULTILINESTRING Z","MULTILINESTRING").
  • After conversion: if GeoJSON root has crs.properties.name, assign to each feature Geometry.CRS.
  • GetIds() hits /rest/layers and extracts name.