Gebruik van GeoJSON in REST API's (input gewenst!)

De laatste tijd komen er verschillende vragen over het verkeerd gebruik of ontbreken van GeoJSON in REST API’s die bij Kadaster/PDOK en DSO aangeboden worden. We willen geen WFS nabouwen (daar is immers WFS voor uitgevonden), maar omdat de API’s andere ingangen bieden en bovendien niet elke API ook als WFS wordt aangeboden, lijkt het óók aanbieden van een application/geo+json response (en dus gebaseerd op features) mij een welkome aanvulling voor deze REST API’s. Dit kan eenvoudig middels content negotiation, echter heb ik dan wel een aantal vragen voor de GIS mensen onder ons over hoe we een aantal features zouden kunnen doorvertalen.

Hoe gaan we om met resources die helemaal geen geometrieën hebben?

Neem bijvoorbeeld de /nummeraanduidingen van de BAG. Een nummeraanduiding heeft zelf geen geometrie. Ziet deze er dan ongeveer uit zoals hieronder?

{
  "type": "FeatureCollection",
  "features": [{
    "type": "Feature",
    "properties": {
      "id": 1,
      "titel": "item 1"
    }
  }, {
    "type": "Feature",
    "properties": {
      "id": 2,
      "titel": "item 2"
    }
  }
}

En is het dan zinvol om hier überhaupt een GeoJSON response voor aan te bieden? Ik weet niet wat GIS clients hiermee doen, maar we kunnen er natuurlijk ook voor kiezen om het verzoek voor application/geo+json alleen te accepteren als er ook geometrieën in het spel zijn.

Hoe gaan we om met resources die meerdere geometrieën hebben?

Neem bijvoorbeeld de /wegdelen van het Nationaal Wegenbestand. Eén wegdeel heeft zowel een vlakGeometrie als een lijnGeometrie. Beiden zijn properties van dezelfde resource. GeoJSON ondersteunt naar mijn weten maar één geometrie per Feature; een FeatureCollection heeft zelf weer geen properties. Bijvoorbeeld het fictieve wegdeel /wegdelen/123 in application/json:

{
  "id": 123,
  "titel": "Wegdeel 123",
  "vlakGeometrie": {
    "type": "Point", //zal in de praktijk nooit een Point zijn maar dat terzijde ;)
    "coordinates": [0.0, 0.0]
  },
  "puntGeometrie": {
    "type": "Point",
    "coordinates": [0.0, 0.0]
  },
}

In GeoJSON zul je dan denk ik zoiets moeten krijgen:

{
  "type": "FeatureCollection",
  "properties": { //mag niet, zie uitleg hieronder
    "id": 123,
    "titel": "Wegdeel 123"
  },
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [0.0, 0.0]
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [0.0, 0.0]
      }
    }
  ]
}

Bovenstaande is echter geen oplossing, want dan loop je tegen de volgende problemen aan:

  1. een FeatureCollection kent geen properties in GeoJSON.
  2. vlakGeometrie en puntGeometrie zijn niet van elkaar te onderscheiden (je weet niet welke Feature welke van de 2 uitdrukt.
  3. een FeatureCollection mag geen onderdeel zijn van een andere FeatureCollection. Hiermee wordt het uitdrukken van een lijst met resources (in dit geval zou dat /wegdelen zijn) die meerdere geometrieën hebben onmogelijk.

Navigatie in GeoJSON

Momenteel bieden we enkel HAL (application/hal+json) responses aan in onze API’s. Een van de belangrijkste redenen is dat dit het mogelijk maakt om naar gerelateerde resources elders in de API te kunnen navigeren. Veelgebruikte zogenaamde ‘hypermedia controls’ zijn de _links.next en _links.prev objecten om door gepagineerde collecties te kunnen navigeren. Een ander voorbeeld is om met de BAG API van een nummeraanduiding naar een adresseerbaarObject te kunnen navigeren. GeoJSON kent hier niets voor, en het toevoegen van een _links object a la HAL zou invalid GeoJSON opleveren. De makkelijkste optie is om de hypermedia controls in de response headers mee te geven, dan krijg je dus zoiets in application/hal+json:

//application/hal+json
{
  "_embedded": {
    "items": [{
      "id": 1,
      "titel": "item 1",
      "_links": {
        "self": {
          "href": "https://.../items/1"
        }
      } 
    }, {
      "id": 2,
      "titel": "item 2",
      "_links": {
        "self": {
          "href": "https://.../items/2"
        }
      }
    }]
  }
  "_links": {
    "next": {
      "href": "https://.../items?page=3"
    },
    "prev": {
      "href": "https://.../items?page=1"
    }
  }
}

En zoiets in application/geo+json:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "id": 1,
        "titel": "item 1"
      }
    }, {
      "type": "Feature",
      "properties": {
        "id": 2,
        "titel": "item 2"
      }
    }
  ]
}

Met de Link response header krijg je dan Link: <https://.../items?page=3>; rel="next", <https://.../items?page=1>; rel="prev", echter moet je dan eigenlijk voor alle individuele resources ook relaties verzinnen en deze opnemen in de Link header: Link: <https://.../items?page=3>; rel="next", <https://.../items?page=1>; rel="prev", <https://.../items/1>; rel="items/1.self", <https://.../items/2>; rel="items/2.self".

Overigens is het laatstgenoemde een probleem dat ook speelt voor andere formaten die geen eigen hypermedia mechanisme hebben zoals plain JSON, maar die bieden we momenteel dan ook helemaal nog niet aan :wink:

Ik hoor graag jullie input/thoughts zodat we hier wellicht een welkome feature request (ook voor de DSO API Strategie) uit kunnen halen. Alvast bedankt voor het meedenken!

4 likes

Goed voorstel, wilde dit toevallig net bij de KDP-ers opbrengen :grinning:.

In eerste instantie lijkt mij het beste om de RFC aan te houden: RFC 7946 - The GeoJSON Format, die is zelfs stricter dan eerdere versies, bijv geen projectie-ondersteuning, alleen WGS84. Er is wel ruimte voor extensies, bijv extra velden, via “Foreign Members”.

Verder nog te bepalen:

  • Features zonder geometrie: (nog) niet duidelijk uit RFC ?
  • Navigatie: evt via “Foreign Members” ?
  • meerdere geometrieën: via GeometryCollection 3.1.8?

Maar goed, zolang de API data volgens de GeoJSON RFC teruggeeft, zal veel GIS-software, denk aan bijv QGIS, daarmee kunnen werken.

1 like

Hmm, interesting die GeometryCollection. Die is nieuw voor mij.

Wat betreft projectie merk ik dat daar veel onduidelijkheid over bestaat. Volgens mij zeggen ze gewoon dat ze zich helemaal niet met projectie willen bemoeien en dat (tenzij anders vermeld, op wat voor manier dan ook) een gebruiker er vanuit mag gaan dat het WGS84 is. Wij geven het nu aan als iets een ander CRS is, terwijl je gewoon de GeoJSON structuur kunt gebruiken om de coördinaten kwijt te kunnen. Daarmee gaat het (veelgehoorde) argument “We kunnen geen GeoJSON aanbieden want wij gebruiken RD” dus volgens mij ook niet op.

1 like

ha, wel goed om hier een opmerking bij te plaatsen, het idee van geo+json is om de werkelijkheid te versimpelen, zodat deze met niet al te veel kennis van zaken gebruikt kan worden (men dient de geintroduceerde onnauwkeurigheid voor lief te nemen). Volgens mij moet het antwoord op geo+json in RD dus ook zijn: niet doen. geo+json hoort in LatLon te zijn. Andere versimpelende conventies:

  • slechts 1 geometry per object
  • geen curves

Maar om nu een nieuwe syntax te gaan verzinnen om het niet te versimpelen lijkt me ook weer zoiets :slight_smile: Het werkt voor ons prima met andere CRS-en namelijk.

@dvh, hoe kijk jij vanuit #oas naar dit soort encodings? GeoJSONL: An optimized format for large geographic datasets - Interline Technologies (Streaming json)

OAS is niet echt geschikt om streaming APIs te beschrijven.