Tilføj koordinater til en CSV-fil

Geokod en liste af adresser til x/y i WGS84 eller UTM32 med datavask og opslag på id, i ren Python eller med pandas.

4 min Niveau: Øvet ✓ Kode testet

Et kort over kunder, en ruteplan eller en analyse pr. område kræver koordinater på hver adresse. Har du en CSV med vejnavn, husnummer og postnummer, kan du tilføje kolonnerne x og y med to slags kald: datavask finder adressens id, og et opslag på id'et giver koordinaterne.

Koordinaterne hører til adgangsadressen, altså bygningens indgang. Alle lejligheder i samme opgang har derfor samme punkt, og du kan geokode på bygningsniveau uden etage og dør.

1. Find adgangsadressen med datavask

GET /datavask/adgangsadresser tager vejnavn, husnummer og postnummer (eller hele adressen i betegnelse) og returnerer det bedste match med id:

Shell#geo-curl
curl -s -G "https://api.danadresse.dk/datavask/adgangsadresser" \
  --data-urlencode "vejnavn=Strandvejen" \
  --data-urlencode "husnr=100" \
  --data-urlencode "postnr=2900" \
  -H "X-Api-Key: $DANADRESSE_API_KEY"

I svaret er resultater[0].adresse.id adgangsadressens id. Svaret har samme form som DAWA's datavask og indeholder ingen koordinater. Tag kun rækker med kategori A eller B. En C-række får tomme koordinater, så et menneske kan se på den.

2. Hent koordinaterne på id

GET /adgangsadresser tager flere id'er adskilt af |. Med struktur=mini får du et fladt objekt med x og y. Uden srid er de længde- og breddegrad i WGS84 med otte decimaler. Med srid=25832 er de i UTM32-meter, som er praktisk til afstande og GIS:

Shell#geo-ids
curl -s -G "https://api.danadresse.dk/adgangsadresser" \
  --data-urlencode "id=0a3f507c-0624-32b8-e044-0003ba298018|0a3f507a-ec01-32b8-e044-0003ba298018" \
  --data-urlencode "struktur=mini" \
  --data-urlencode "srid=25832" \
  -H "X-Api-Key: $DANADRESSE_API_KEY"

Svaret kommer ikke i samme rækkefølge som id'erne. Slå op på id, ikke på position. Hold dig til omkring 100 id'er pr. kald, så URL'en ikke bliver for lang.

Vil du have begge koordinatsystemer i ét kald, så tilføj format=csv. CSV-svaret har altid kolonnerne wgs84koordinat_længde, wgs84koordinat_bredde, etrs89koordinat_øst og etrs89koordinat_nord.

3. Hele flowet med csv-modulet

Eksemplet læser to rækker fra en tekststreng, vasker dem og henter koordinaterne i WGS84 og UTM32 med ét kald pr. koordinatsystem. Det bruger kun requests og standardbiblioteket:

Python#geo-csv
import csv
import io
import os
import sys
import requests

DATA = """kundenr,vejnavn,husnr,postnr
1001,Strandvejen,100,2900
1002,Rådhuspladsen,1,1550
"""

s = requests.Session()
s.headers["X-Api-Key"] = os.environ["DANADRESSE_API_KEY"]
API = "https://api.danadresse.dk"

rows = list(csv.DictReader(io.StringIO(DATA)))
for row in rows:
    r = s.get(f"{API}/datavask/adgangsadresser", timeout=10, params={
        "vejnavn": row["vejnavn"], "husnr": row["husnr"], "postnr": row["postnr"]})
    r.raise_for_status()
    svar = r.json()
    ok = svar["kategori"] in ("A", "B") and svar["resultater"]
    row["kategori"] = svar["kategori"]
    row["adgangsadresseid"] = svar["resultater"][0]["adresse"]["id"] if ok else ""

ids = [r["adgangsadresseid"] for r in rows if r["adgangsadresseid"]]

def punkter(srid):
    r = s.get(f"{API}/adgangsadresser", timeout=10, params={
        "id": "|".join(ids), "struktur": "mini", "srid": srid, "per_side": len(ids)})
    r.raise_for_status()
    return {a["id"]: (a["x"], a["y"]) for a in r.json()}

wgs84, utm = punkter(4326), punkter(25832)
for row in rows:
    aid = row["adgangsadresseid"]
    row["lon"], row["lat"] = wgs84.get(aid, ("", ""))
    row["x_utm"], row["y_utm"] = utm.get(aid, ("", ""))

out = csv.DictWriter(sys.stdout, fieldnames=list(rows[0].keys()))
out.writeheader()
out.writerows(rows)

Til en rigtig fil skal du udskifte io.StringIO(DATA) med open("kunder.csv", encoding="utf-8-sig"), dele id-listen op i bidder af 100 og håndtere 429 som beskrevet i Grænser, kvoter og 429.

4. Samme med pandas

Med pandas er det praktisk at vaske de unikke adresser én gang, hente koordinaterne i bunker af 100 id'er og flette resultatet tilbage. Mange CRM-eksporter har den samme adresse på flere kunder:

Python#geo-pandas
import os
import pandas as pd
import requests

s = requests.Session()
s.headers["X-Api-Key"] = os.environ["DANADRESSE_API_KEY"]

df = pd.read_csv("kunder.csv", dtype=str, encoding="utf-8-sig")
unikke = df[["vejnavn", "husnr", "postnr"]].drop_duplicates()

def vask(r):
    svar = s.get("https://api.danadresse.dk/datavask/adgangsadresser", timeout=10,
                 params={"vejnavn": r.vejnavn, "husnr": r.husnr, "postnr": r.postnr}).json()
    ok = svar["kategori"] in ("A", "B") and svar["resultater"]
    return pd.Series({"kategori": svar["kategori"],
                      "adgangsadresseid": svar["resultater"][0]["adresse"]["id"] if ok else None})

unikke = unikke.join(unikke.apply(vask, axis=1))

ids = unikke["adgangsadresseid"].dropna().unique().tolist()
punkt = {}
for i in range(0, len(ids), 100):
    bunke = ids[i:i + 100]
    r = s.get("https://api.danadresse.dk/adgangsadresser", timeout=30, params={
        "id": "|".join(bunke), "struktur": "mini", "per_side": len(bunke)})
    r.raise_for_status()
    punkt.update({a["id"]: (a["x"], a["y"]) for a in r.json()})
unikke["x"] = unikke["adgangsadresseid"].map(lambda i: punkt.get(i, (None, None))[0])
unikke["y"] = unikke["adgangsadresseid"].map(lambda i: punkt.get(i, (None, None))[1])
df = df.merge(unikke, on=["vejnavn", "husnr", "postnr"], how="left")
df.to_csv("kunder_geokodet.csv", index=False)
print(df["kategori"].value_counts())

Læs alle kolonner som tekst (dtype=str). Ellers mister koder med foranstillet nul, som kommunekoden 0101, deres nul, og husnumre som 12A blandes med tal.

Hvilket koordinatsystem?

sridSystemEnhedBrug
4326 (standard)WGS84grader (længde, bredde)Webkort, GPS, Google Maps, Leaflet
25832ETRS89 / UTM zone 32Nmeter (øst, nord)Afstande, GIS, danske kortdata

Bemærk rækkefølgen: x er længdegrad og y er breddegrad. Mange værktøjer vil have lat, lon, så byt om, når du skriver til dem. Se Koordinater og srid.

Kanttilfælde

  • Ingen koordinater. Nogle få adresser har ikke et adgangspunkt. Så er x og y null.
  • Nøjagtighed. Det fulde svar fra /adgangsadresser/{id} har adgangspunkt.nøjagtighed: A er målt, B er beregnet, U er usikkert. Filtrer på den, hvis præcisionen betyder noget for dig.
  • Semikolon-filer. Danske Excel-eksporter bruger tit semikolon. Brug csv.DictReader(f, delimiter=";") eller pd.read_csv(..., sep=";").
  • Allerede kendte id'er. Har du adresse-id'er fra en tidligere vask, så spring datavask over og hent koordinaterne direkte med GET /adresser?id=a|b|c&struktur=mini.

Næste skridt

Relaterede guides

Markdown-kilde Fejl i guiden? Skriv til os API-reference (Swagger)

Prøv det med en gratis nøgle

2 000 kald/måned på gratisplanen. Intet kreditkort.

Opret konto →