# GeoJSON

## 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:

```json
{
    "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](/da/portfolio/led-the-development-deployment-and-support-of-over-35/),
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](/da/portfolio/improved-geographical-map-application-performance-by-10x-through-6/)
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":

```sql
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](https://www.rfc-editor.org/rfc/rfc7946) —
  IETF-standarden med objekttyperne, koordinatrækkefølgen, omløbsreglen, rådene
  om præcision og sikkerhedsovervejelserne.
- [RFC 8142: GeoJSON Text Sequences](https://www.rfc-editor.org/rfc/rfc8142) —
  søsterformatet, der deler et stort datasæt op i én GeoJSON-tekst pr. post.
- [ST_AsGeoJSON](https://postgis.net/docs/ST_AsGeoJSON.html) — PostGIS-manualens
  side om at eksportere geometri og hele rækker som GeoJSON, med parametrene for
  præcision og valgmuligheder.
- [EPSG:4326 WGS 84](https://epsg.org/crs_4326/WGS-84.html) — registerposten,
  hvis akserækkefølge sætter breddegraden først, det modsatte af GeoJSON.
- [OGC API – Features – Part 1: Core](https://docs.ogc.org/is/17-069r4/17-069r4.html)
  — OGC-standarden, der anbefaler GeoJSON til at levere objekter over HTTP.

<https://engineer.company/da/glossary/geojson/>
