# PostGIS

## Vad det är

PostGIS är ett tillägg med öppen källkod till databasservern PostgreSQL. Det låter en kolumn rymma
en form, till exempel en leveransadress, en å eller en fastighetsgräns, och låter vanlig SQL räkna
ut var formerna ligger i förhållande till varandra. På svenska kallas sådana data oftast geodata
eller spatiala data, och en databas med PostGIS en spatial databas. Projektet började som ett
forskningsprojekt hos Refractions Research, ett konsultbolag inom GIS och databaser, och utvecklas
och släpps i dag som ett eget projekt med öppen källkod.

Det installeras per databas med en enda sats, `CREATE EXTENSION postgis;`, och tre saker följer
med.

- **Spatiala typer.** `geometry` beskriver former på ett plant plan, i det koordinatsystem som
  datan samlades in i. `geography` beskriver dem på jordens krökta yta och mäter i meter.
- **Spatiala funktioner.** En stor familj funktioner vars namn börjar med `ST_`: `ST_Intersects`
  och `ST_Contains` för samband, `ST_Area` och `ST_Length` för mätning, `ST_Transform` för att
  flytta mellan koordinatsystem och `ST_AsGeoJSON` för att lämna över resultatet till en webbkarta.
- **Spatiala index.** PostGIS bygger ett R-träd ovanpå PostgreSQL:s GiST-ramverk och stöder också
  SP-GiST och blockintervallindex (BRIN) för vissa sorters data.

Formerna följer Simple Features, den modell som [Open Geospatial Consortium](https://www.ogc.org/)
har publicerat och som har antagits som ISO 19125: punkter, linjer (line strings) och polygoner,
deras flerdelade varianter och samlingar av blandade typer. Varje lagrat värde bär ett SRID, ett
nummer som pekar på en rad i tabellen `spatial_ref_sys` och talar om vad koordinaterna betyder. SRID
4326 betyder longitud och latitud i grader i referenssystemet WGS 84, det system som GPS rapporterar
i. Ett projicerat system mäter i stället i meter österut och norrut från en fast origo, och det är
exakt bara inom det område det konstruerades för. I Sverige använder tillämpningar på nationell nivå
SWEREF 99 TM, och för lokalt bruk finns tolv lokala projektionszoner.

Rasterdata, topologi och vissa 3D-operationer kommer som separata tillägg, `postgis_raster`,
`postgis_topology` och `postgis_sfcgal`, så att en databas bara bär de delar den faktiskt använder.

## Varför det spelar roll

Beslutet som PostGIS tvingar fram är var platsdata ska bo. I en databas utan PostGIS hamnar
koordinaterna gärna i två numeriska kolumner, medan det egentliga spatiala arbetet sker någon
annanstans: i applikationskod som loopar över rader för att räkna ut avstånd, i ett GIS-program på
skrivbordet som någon exporterar till varje måndag, eller i en separat geografisk tjänst med en
egen kopia av datan. Varje sådan kopia glider isär. Med PostGIS blir ”vilka kunder ligger inom två
kilometer från en depå som har varan i lager” en enda SQL-fråga som kopplar ihop lagersaldon,
kundregister och former, inom en och samma transaktion, under samma säkerhetskopior och samma
behörigheter som allt annat i databasen.

Det betyder också att spatiala data får samma garantier som resten av databasschemat redan har. Ett
CHECK-villkor kan avvisa en ogiltig polygon redan när den skrivs in. Säkerhet på radnivå
(row-level security) kan begränsa ett regionalt team till fastigheterna i den egna regionen. En
import som fallerar rullas tillbaka i stället för att lämna ett halvt kartskikt efter sig.

Det andra beslutet är geometry eller geography, och PostGIS-manualen är rak på den punkten. Data
som håller sig inom en stad eller ett land lagras oftast bäst som `geometry` i en projektion vald
för området, eftersom hela funktionsuppsättningen finns och aritmetiken är snabb. Data som spänner
över kontinenter, eller vars användare vill ha avstånd i meter mellan punkter var som helst på
jorden, är lättare att få rätt som `geography`. Den typen sköter själv matematiken på den krökta
ytan, till priset av en mindre uppsättning funktioner.

Och ibland är svaret att PostGIS är mer än jobbet kräver. En app som bara lagrar latitud och
longitud per butik och sätter ut nålar på en karta klarar sig med två numeriska kolumner. PostGIS
gör sig förtjänt av sin plats när det du behöver veta gäller avstånd, inneslutning eller
överlappning, eller när datan innehåller linjer och polygoner snarare än enstaka punkter.

## Så ser det ut i praktiken

PostGIS var skälet till ett databasbyte i det ena projektet och arbetshästen under en upprensning i
det andra.

En kartapplikation som besvarade spatiala databasfrågor i realtid för många användare körde på en
Microsoft SQL Server-databas utan inbyggd geospatial indexering, och latensen gick ut över både
kartan och tjänsterna bakom den.
[Lösningen blev en flytt till PostgreSQL med PostGIS](/sv/portfolio/improved-geographical-map-application-performance-by-10x-through-6/),
med ett schema utformat för spatiala data, GiST- och SP-GiST-index på geometri- och
geografikolumnerna och villkor på de spatiala koordinaterna. Över 50 miljoner spatiala poster gick
genom ETL-pipelines som transformerade dem till EPSG:4326 på vägen in. Efter flytten tog ett
genomsnittligt svar under 250 millisekunder i stället för 2,5 sekunder, de spatiala frågorna
presterade 10 gånger bättre och backenden använde 65 % mindre CPU.

Det andra projektet började med data snarare än hastighet. Ett stort geospatialt analysprojekt
hittade över 1 000 inkonsekvenser i geografiska dataset och tidsseriedataset. GDAL, QGIS och ArcGIS
användes för att hitta och rätta de spatiala felen, medan
[PostGIS körde de spatiala databasfrågorna och den spatiala indexeringen](/sv/portfolio/resolved-1-000-issues-in-geographical-data-and-7/),
med procedurer i PL/pgSQL som transformerade datan inne i PostgreSQL, och det rensade resultatet
visades i Mapbox. Körtiderna för spatiala SQL-frågor sjönk med 35 %.

## Var det går fel

**Avstånd kommer tillbaka som pyttesmå decimaltal.** En SQL-fråga ber om avståndet mellan två orter
och får `0.08`. Kolumnen är `geometry` i SRID 4326, så svaret är i grader, och en längdgrad krymper
från ca 111 kilometer vid ekvatorn till ingenting vid polerna. Konvertera till `geography` för att
få svaret i meter, eller flytta datan med `ST_Transform` till ett projicerat system som passar
området innan du mäter.

**En radiesökning läser varenda rad.** Tabellen har ett spatialt index, men planen visar en
sekventiell skanning. Den vanliga orsaken är ett filter skrivet som `ST_Distance(a, b) < 500`.
Manualens lista över funktioner som använder indexet av sig själva tar med `ST_DWithin` men inte
`ST_Distance`. Att linda in den indexerade kolumnen i en typkonvertering eller en transformering
har samma effekt, eftersom ett index på `geom` inte säger någonting om `geom::geography`.

```sql
-- en geography-kolumn, indexerad som den lagras, mätt i meter
CREATE INDEX depots_geog_idx ON depots USING GIST (geog);

SELECT id FROM depots
WHERE ST_DWithin(geog, ST_MakePoint(12.57, 55.68)::geography, 500);
```

Om datan måste stanna i en `geometry`-kolumn i 4326 kan du antingen lägga till en
`geography`-kolumn som den här och indexera den, eller skapa indexet på samma uttryck som SQL-frågan
använder. Kör `ANALYZE` efter en massinläsning, så att planeraren vet att indexet är värt att ta.

**Punkterna hamnar i havet.** Varje form behåller sitt mönster men ligger på fel plats, ofta på en
annan kontinent. Två misstag ger det här. `ST_SetSRID` märker bara om en geometri och låter dess
tal vara som de är, så om den används där `ST_Transform` var avsedd stämplas meter som grader. Det
andra är att longitud och latitud har bytt plats någonstans uppströms: PostGIS förväntar sig,
precis som GeoJSON, x före y, alltså longitud före latitud. Använd `ST_SetSRID` bara för att
deklarera vad talen redan är och `ST_Transform` för att räkna om dem, och testa varje import med en
punkt vars läge du känner till.

**Area- och överlappsberäkningar kastar fel.** En import från en shapefil går igenom, och senare
fallerar `ST_Intersection` med ett topologiundantag. PostGIS lagrar ogiltig geometri med avsikt, så
att den kan hittas och lagas, och en polygon vars gräns korsar sig själv är ogiltig. Kontrollera
med `ST_IsValid`, laga med `ST_MakeValid`, som behåller varenda ursprunglig hörnpunkt, och lägg
till ett CHECK-villkor så att en dålig form avvisas redan i dörren i stället för att upptäckas i en
rapport.

## Läs mer

- [PostGIS Documentation](https://postgis.net/documentation/) — projektets egen ingång till
  manualen, FAQ:n och utbildningsmaterialet.
- [Data Management](https://postgis.net/docs/using_postgis_dbmanagement.html) — geometry och
  geography, SRID, giltighetsregler och de tre indextyperna, så som manualen definierar dem.
- [Spatial Indexing](https://postgis.net/workshops/postgis-intro/indexing.html)
  — workshopens kapitel om omslutande rektanglar och om de funktioner som använder ett index utan
  att bli tillsagda.
- [ST_DWithin](https://postgis.net/docs/ST_DWithin.html) — referenssidan för avståndstestet, med
  enheterna för varje typ.
- [Simple Feature Access – Part 1](https://www.ogc.org/standards/sfa/) — OGC-standarden bakom de
  geometrityper som PostGIS lagrar.
- [SWEREF 99, projektioner](https://www.lantmateriet.se/sv/geodata/gps-geodesi-och-swepos/referenssystem/tvadimensionella-system/sweref-99-projektioner/)
  — Lantmäteriets beskrivning av SWEREF 99 TM och de tolv lokala projektionszonerna.

<https://engineer.company/sv/glossary/postgis/>
