Spring til indhold

GeoJSON

geografi skrevet som JSON

Hvad det er

GeoJSON er et åbent format til at udveksle kortobjekter, altså kortets punkter, linjer og flader sammen med de oplysninger, der hører til dem, som ét almindeligt JSON-dokument. IETF standardiserede det som RFC 7946 i august 2016, og det afløste dermed den fællesskabsspecifikation fra 2008, som formatet begyndte med. En GeoJSON-tekst sendes med medietypen application/geo+json og gemmes som regel med filendelsen .geojson, selvom almindelig .json også er registreret.

Formatet har tre slags objekter, og hvert af dem oplyser i et “type”-felt, hvilken slags det er.

  • Geometry er en form. Der findes syv typer: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon og GeometryCollection. Det er formerne fra Simple Features-modellen fra Open Geospatial Consortium (OGC), de samme, som kodningerne WKT og WKB bærer i tekst og binært.
  • Feature er én ting på kortet: en geometri, et “properties”-objekt med tingens attributter og et valgfrit “id”. Geometrien må være null for et objekt, hvis placering er ukendt.
  • FeatureCollection rummer en liste af features i et “features”-felt. Det er den, som de fleste filer og API-svar indeholder.

Et enkelt objekt for en smart-måler ser sådan ud:

{
    "type": "Feature",
    "id": "meter-0417",
    "geometry": {
        "type": "Point",
        "coordinates": [12.5683, 55.6761]
    },
    "properties": {
        "name": "Meter 417",
        "installed": "2023-04-12"
    }
}

Hver position er et array af tal: længdegraden først, så breddegraden og til sidst en valgfri højde i meter. Specifikationen kræver præcis den rækkefølge for de to første, så punktet ovenfor ligger i det centrale København, 12,57 grader øst og 55,68 grader nord. Alle koordinater bruger ét referencesystem, længde og bredde på WGS 84 i decimalgrader, som OGC betegner CRS84. Versionen fra 2008 lod en fil angive et andet system i et “crs”-felt. RFC 7946 fjernede feltet, fordi blandede referencesystemer havde givet problemer med interoperabilitet, og tillader kun et andet system, hvor alle parter har aftalt det på forhånd.

Polygoner har deres egne regler. Hver ring er lukket, med mindst fire positioner, hvor den sidste gentager den første. Den første ring er den ydre grænse, og eventuelle senere ringe er huller. RFC 7946 tilføjer en højrehåndsregel, hvor ydre ringe løber mod uret og huller med uret, men fordi 2008-versionen intet sagde om omløbsretning, skal parsere acceptere polygoner, der bryder reglen.

Hvorfor det betyder noget

Alt, der kan læse JSON, kan læse GeoJSON. En browser parser det uden plugin, et webkortbibliotek tegner det direkte, og en dokumentdatabase gemmer det, som det er. RFC 7946 nævner JavaScript-biblioteker til webkort, JSON-baserede dokumentdatabaser og web-API’er som de steder, hvor formatet er udbredt. Den rækkevidde gør det til det sædvanlige valg, når kortdata forlader en desktop-GIS og skal ud på en webside eller gennem et API.

Standarder bygger også videre på det. OGC API – Features, OGC’s standard for at levere geografiske objekter over HTTP, anbefaler GeoJSON, hvor dataene kan repræsenteres i det. En server, der tilbyder GeoJSON, returnerer en FeatureCollection for en liste af objekter og en enkelt Feature for ét objekt, begge som application/geo+json, og serverens standardreferencesystem er den samme længde og bredde på WGS 84, som GeoJSON fastlægger.

De samme designvalg sætter også formatets grænser.

  • Ét referencesystem. Data, der ligger i et nationalt gitter, skal transformeres til længde og bredde, før de skrives, og transformeres tilbage af enhver modtager, der regner i meter.
  • Kun simple former. Geometrityperne dækker punkter, linjer af lige stykker og polygoner. Kurver, 3D-legemer som bymodeller og koordinater uden for WGS 84 falder uden for formatet, og OGC API – Features nævner netop de tilfælde som sine eksempler på data, GeoJSON ikke kan bære.
  • Størrelse. Hver koordinat skrives ud som decimalcifre, og en FeatureCollection er én JSON-værdi, som en almindelig JSON-parser læser til sidste byte, før den giver ét eneste objekt fra sig. RFC 8142 definerer GeoJSON Text Sequences, medietype application/geo+json-seq, med én GeoJSON-tekst pr. post, så vilkårligt store datasæt kan læses trinvis.
  • Ingen delte grænser. To nabopolygoner gemmer hver deres fælles grænse fuldt ud, så grænsen skrives to gange, og en rettelse i den ene kopi kan åbne et hul eller et overlap.

RFC 7946 er også klar i mæle om, hvad formatet overlader til andre. GeoJSON giver ingen beskyttelse af fortrolighed eller integritet i sig selv, så data under transport må hvile på TLS. Afsnittet om sikkerhed advarer om, at offentliggørelse af placeringen af sårbare personer, dyr, levesteder eller anlæg kan udsætte dem for sporing eller skade, og at sløring af positioner i et anonymiseret datasæt næppe kan stå imod et målrettet angreb.

Sådan ser det ud i praksis

På tværs af mere end tredive GIS-projekter, der blev bygget, udrullet og supporteret i produktion, var GeoJSON et af de formater, GDAL/OGR flyttede data imellem, sammen med shapefiles, GeoTIFF, KML og vector tiles. PostgreSQL med PostGIS rummede de rumlige data nedenunder, QGIS og JOSM blev brugt til selve dataarbejdet, og frontenden var webkort bygget på Mapbox GL og Leaflet.

Et referencesystem skal ligge fast, før noget af det kan ske. I en migrering fra MSSQL til PostgreSQL med PostGIS-udvidelsen blev over 50 millioner rumlige poster flyttet gennem ETL-pipelines, hvis transformationstrin bragte dem til SRID-standarden EPSG:4326. Den kode betegner det samme WGS 84-datum i grader, som GeoJSON fastlægger, og PostGIS-manualen oplyser, at output fra funktionen ST_AsGeoJSON overholder RFC 7946 for geometri i EPSG:4326, uden at der skal transformeres.

Hvor det går galt

Punkterne lander i det forkerte hav. Et lag med danske adresser dukker op midt i Det Arabiske Hav, og alle intervalkontroller består stadig, fordi begge tal er gyldige grader. Bredde og længde blev byttet om på vej ind. EPSG:4326 angiver formelt breddegraden før længdegraden, mange datakilder følger den orden, og GeoJSON vil have længdegraden først. Byg hver position ud fra navngivne felter frem for kolonnernes rækkefølge, og hold et testobjekt på en kendt adresse, så en ombytning fanges af en test og ikke først ved en gennemgang af kortet.

Koordinaterne kommer i meter. Et lag mangler på kortet eller afvises af klienten, og filen har koordinater i hundredtusinder, hvor der burde stå grader. Geometrien var gemt i et projiceret nationalt gitter og blev eksporteret, som den var. Transformér den først til længde og bredde, med ST_Transform(geom, 4326) i PostGIS. Uden transformation tilføjer ST_AsGeoJSON som standard et “crs”-felt, netop det felt, RFC 7946 fjernede.

Polygoner tegnes på vrangen i én klient. Et forsyningsområde fylder hele kortet undtagen sin egen form, mens andre værktøjer tegner den samme fil korrekt. Den klient bruger hver rings retning til at skelne inde fra ude, og ringene løber den anden vej, hvad 2008-versionen tillod. PostGIS-manualen bemærker, at nogle klienter kræver højrehåndsreglen, så anvend den før eksport med ST_ForcePolygonCCW(geom), og afprøv resultatet i hver klient, der bruger det.

Filerne bærer flere cifre, end kortet behøver. Et lag med detaljerede polygoner tager sekunder at hente på en telefon, og koordinaterne har ni eller flere decimaler. ST_AsGeoJSON skriver som standard op til ni decimaler, og RFC 7946 bemærker, at et spring fra seks til femten decimaler næsten kan fordoble en teksts størrelse, mens seks allerede opløser omkring 10 cm. Angiv en mindre maxdecimaldigits, og reducér præcisionen med ST_ReducePrecision først, fordi PostGIS-manualen advarer om, at det at skære cifre af alene ved output kan gøre en geometri ugyldig.

Interne kolonner sendes med i properties. Et offentligt endpoint returnerer ejernavne, omkostningstal eller noter, der var ment til at blive i databasen. Får ST_AsGeoJSON en hel række, lægger den hver kolonne undtagen geometrien i “properties”. Vælg kun de kolonner, der er beregnet til offentliggørelse, i en underforespørgsel, og navngiv, i PostGIS 3.5 eller nyere, nøglekolonnen, så den bliver objektets “id”:

SELECT json_build_object(
  'type', 'FeatureCollection',
  'features', json_agg(ST_AsGeoJSON(m.*, id_column => 'id')::json)
)
FROM (SELECT id, name, geom FROM meters) AS m;

Læs mere

  • RFC 7946: The GeoJSON Format — IETF-standarden med objekttyperne, koordinatrækkefølgen, omløbsreglen, rådene om præcision og sikkerhedsovervejelserne.
  • RFC 8142: GeoJSON Text Sequences — søsterformatet, der deler et stort datasæt op i én GeoJSON-tekst pr. post.
  • ST_AsGeoJSON — PostGIS-manualens side om at eksportere geometri og hele rækker som GeoJSON, med parametrene for præcision og valgmuligheder.
  • EPSG:4326 WGS 84 — registerposten, hvis akserækkefølge sætter breddegraden først, det modsatte af GeoJSON.
  • OGC API – Features – Part 1: Core — OGC-standarden, der anbefaler GeoJSON til at levere objekter over HTTP.

Ordliste