Nu i stort format:
www.findvej.dk

Læs mere: Findvej-blog

Find et enkelt sted
Find alle adresser på en vej

Bag om map.ter.dk

Af Peter Brodersen

Hvordan er map.ter.dk lavet?

Indholdsfortegnelse

  1. Indledning
  2. Kort og Matrikelstyrelsen
    1. Dataformatet
    2. Koordinater
    3. Filer
    4. Webservice
  3. Google Maps
  4. Øvrig kode

Indledning

map.ter.dk, som er forgængeren til www.findvej.dk, er lavet af Peter Brodersen, og består af et par yderst simple sider, der kombinerer to stærke ressourcer: Google Maps og en fortegnelse fra Kort og Matrikelstyrelsen.

Data er blevet lagt ind i en MySQL-database og fylder samlet 219 MB. Selve databasen rummer 2.149.065 lokationer fordelt på 105.637 veje. Kortet er lavet med Google Maps' API, med PHP som serverside-sprog.

Websiden blev en realitet i juli 2005, efter jeg begyndte at eksperimentere med Google Maps og Google Earth. Jeg kom herefter i tanke om at jeg på et tidspunkt havde stødt på en online-fortegnelse af alle danske adresser, og med god hjælp fra altid flinke Christian Schmidt lykkedes det mig at finde og få fat i dataen - der dog vel at mærke altså er fra 2002. Det betyder, at nye lokationer (fx på Holmen), nye postnumre (fx 2870 Dyssegård) og den nye kommuneopdeling ikke er medtaget.

Både Google Maps og Kort og Matrikelstyrelsens fortegnelse kan bruges gratis, og kræver ikke meget mere end en database og et programmeringssprog. Styrken ligger i Google Maps samt adressefortegnelsen - hverken RDBMS'en (databasen) eller sproget er specielt vigtig i den forbindelse.

Kort og Matrikelstyrelsen

Kort og Matrikelstyrelsen stillede i 2001 og 2002 en fortegnelse over adresser med koordinater til fri rådighed. Et par år senere blev nyheden opdateret om at Kort og Matrikelstyrelsen ikke længere stillede data til rådighed, men at man kunne foretage enkelte udtræk eller købe adressedata på www.ois.dk - Den Offentlige InformationsServer. Denne rummer langt mere information om den enkelte lokation, men til gengæld er det så ikke længere muligt at få en samlet fortegnelse, som man selv kan bruge.

Jeg har ikke noget kendskab til hvordan Kort og Matrikelstyrelsen i første omgang har udarbejdet databasen. Nogle steder er flere adresser, fx for samme karré, placeret samme sted. Nogle steder henvises der til midt i karréen, andre steder til indgangen ved vejen.

Fortegnelsen rummer udelukkende informationer om enkelte punkter. Der er ingen overordnede geografiske informationer om vejes forløb, så det er ikke muligt at lave ruteberegning eller lignende.

Dataformatet

De oprindelige database-filer består af en række områder (København, Frederiksborg, Roskilde, etc.), der hver indeholder en række CSV-filer (adresse.txt, vejnavn.txt, postnr.txt, sogn.txt, kommune.txt, amt.txt, altvej.txt). Dataen er fuldt ud normaliseret og dataformatet kan læses i dataformat-specifikationen.

Eksempel på indhold af adresse.txt:

"Adrid";"KommNr";"VejKode";"ZhusNr";"HusNr";"PostNr";"SognNr";"Bynavn";"NklsBak";"RdatoBak";"EBak";"NBak";"RetnBak";"PlacBak";"Kn100Dk";"Kn250Dk";"Kn1kmDk";"Multi"
...
"1015092017_";"101";"5092";"017_";"17";"2500";"7026";"";"B";20011215;719779.29;6174497.76;200.00;"5";"100m_61744_7197";"250m_617425_71975";"1km_6174_719";1
"1015092019_";"101";"5092";"019_";"19";"2500";"7026";"";"B";20011215;719780.32;6174479.55;200.00;"5";"100m_61744_7197";"250m_617425_71975";"1km_6174_719";1
"1015096002_";"101";"5096";"002_";"2";"1203";"7002";"";"B";20011215;724894.70;6175945.86;200.00;"5";"100m_61759_7248";"250m_617575_72475";"1km_6175_724";2
"1015096002F";"101";"5096";"002F";"2F";"1203";"7002";"";"U";;0.00;0.00;0.00;"5";"";"";"1km_6175_724";0
"1015096004_";"101";"5096";"004_";"4";"1203";"7002";"";"B";20011215;724888.06;6175928.26;200.00;"5";"100m_61759_7248";"250m_617575_72475";"1km_6175_724";1
"1015096006_";"101";"5096";"006_";"6";"1203";"7002";"";"B";20011215;724877.26;6175924.53;200.00;"5";"100m_61759_7248";"250m_617575_72475";"1km_6175_724";1
"1015096008_";"101";"5096";"008_";"8";"1203";"7002";"";"B";20011215;724871.41;6175916.13;200.00;"5";"100m_61759_7248";"250m_617575_72475";"1km_6175_724";2
"1015096010_";"101";"5096";"010_";"10";"1203";"7002";"";"B";20011215;724866.30;6175908.61;200.00;"5";"100m_61759_7248";"250m_617575_72475";"1km_6175_724";2
"1015096012_";"101";"5096";"012_";"12";"1203";"7002";"";"B";20011215;724858.20;6175897.54;200.00;"5";"100m_61758_7248";"250m_617575_72475";"1km_6175_724";2
"1015096014_";"101";"5096";"014_";"14";"1203";"7002";"";"B";20011215;724846.99;6175879.98;200.00;"5";"100m_61758_7248";"250m_617575_72475";"1km_6175_724";2
"1015096016_";"101";"5096";"016_";"16";"1203";"7002";"";"B";20011215;724842.09;6175874.18;200.00;"5";"100m_61758_7248";"250m_617575_72475";"1km_6175_724";2
"1015096018_";"101";"5096";"018_";"18";"1203";"7002";"";"B";20011215;724837.45;6175868.37;200.00;"5";"100m_61758_7248";"250m_617575_72475";"1km_6175_724";2
"1015096020_";"101";"5096";"020_";"20";"1203";"7002";"";"B";20011215;724835.77;6175860.71;200.00;"5";"100m_61758_7248";"250m_617575_72475";"1km_6175_724";1
"1015096022_";"101";"5096";"022_";"22";"1203";"7002";"";"B";20011215;724826.64;6175854.76;200.00;"5";"100m_61758_7248";"250m_617575_72475";"1km_6175_724";2
"1015096024_";"101";"5096";"024_";"24";"1203";"7002";"";"B";20011215;724816.36;6175846.62;200.00;"5";"100m_61758_7248";"250m_617575_72475";"1km_6175_724";3
"1015096024A";"101";"5096";"024A";"24A";"1203";"7002";"";"B";20011215;724816.36;6175846.62;200.00;"5";"100m_61758_7248";"250m_617575_72475";"1km_6175_724";3
"1015096026_";"101";"5096";"026_";"26";"1203";"7002";"";"B";20011215;724806.11;6175838.20;200.00;"5";"100m_61758_7248";"250m_617575_72475";"1km_6175_724";3
"1015096026A";"101";"5096";"026A";"26A";"1203";"7002";"";"B";20011215;724806.11;6175838.20;200.00;"5";"100m_61758_7248";"250m_617575_72475";"1km_6175_724";3
"1015096028_";"101";"5096";"028_";"28";"1203";"7002";"";"B";20011215;724795.13;6175831.82;200.00;"5";"100m_61758_7247";"250m_617575_72475";"1km_6175_724";2
"1015096030_";"101";"5096";"030_";"30";"1203";"7002";"";"B";20011215;724785.58;6175827.48;200.00;"5";"100m_61758_7247";"250m_617575_72475";"1km_6175_724";3
"1015096032_";"101";"5096";"032_";"32";"1203";"7002";"";"B";20011215;724779.32;6175823.20;200.00;"5";"100m_61758_7247";"250m_617575_72475";"1km_6175_724";2
"1015100001_";"101";"5100";"001_";"1";"1760";"7019";"";"B";20011215;723104.01;6174514.65;200.00;"5";"100m_61745_7231";"250m_617450_72300";"1km_6174_723";3
"1015100002_";"101";"5100";"002_";"2";"1760";"7019";"";"B";20011215;723099.32;6174559.56;200.00;"5";"100m_61745_7230";"250m_617450_72300";"1km_6174_723";2
...

Eksempel på indhold af vejnavn.txt:

"KommNr";"VejKode";"VejNavn";"Opdat"
...
"101";"5077";"N-Vej";20000523
"101";"5080";"Ny Adelgade";19910923
"101";"5084";"Ny Blegdamsvej";19910923
"101";"5088";"Nyborggade";19910923
"101";"5092";"Nybovej";19910923
"101";"5096";"Nybrogade";19910923
"101";"5100";"Ny Carlsberg Vej";19910923
"101";"5104";"Nygade";19910923
"101";"5108";"Nygårdsvej";19910923
"101";"5112";"Nyhavn";19910923
"101";"5113";"Nyhavnsbroen";19910923
...

Alle filer (med undtagelse af adresse.txt) lagde jeg uden videre ind ved at oprette tabeller med felter som nævnt i dataformat-specifikationen, strippe filen for uhensigtsmæssige newlines og så udføre en simpel MySQL-import med LOAD DATA:

LOAD DATA LOCAL INFILE 'vejnavn.txt' INTO TABLE vej FIELDS TERMINATED BY ';' OPTIONALLY ENCLOSED BY '"' IGNORE 1 LINES;
LOAD DATA LOCAL INFILE 'kommune.txt' INTO TABLE kommune FIELDS TERMINATED BY ';' ENCLOSED BY '"' IGNORE 1 LINES;
LOAD DATA LOCAL INFILE 'altvej.txt' INTO TABLE altvej FIELDS TERMINATED BY ';' ENCLOSED BY '"' IGNORE 1 LINES;
...

Dertil kastede jeg en håndfuld passende indekser hen over de felter, der skal slås op på. Jeg har af performancehensyn ydermere valgt at lade alle felter være fixed width (dvs. char for tekstfelterne). Det gør at dataen fylder mere, men kan hentes hurtigere, idet hver record i hver tabel fylder et helt bestemt antal bytes.

Koordinater

Alle koordinater i adresse.txt (fx 724806.11 , 6175838.20) er i koordinatsystem EUREF 89 (WGS84) UTM zone 32. Dette er også nævnt i dataformat-specifikationen.

Der findes en række forskellige metoder at angive punkter på en kugle ud fra sæt af x,y-koordinater. Længde- og breddegrader er de mest velkendte. UTM-specifikationen er baseret på en række kvadranter, der specificerer et antal meter nord/syd og øst/vest fra et givent punkt.

"Ulempen" ved længde- og breddegrader er, at afstanden mellem længdekredsene (afstanden af en grad øst) varierer og bliver mindre, alt efter hvor langt væk fra ækvator, man befinder sig. Ved Ækvator er afstanden godt 110 km. Ved København er afstanden godt 60 km. I praksis betyder det blandt andet, at hvis man på den nordlige halvkugle bevæger sig 1 km nord, 1 km øst, 1 km syd og 1 km vest, så ender man ikke ved sit udgangspunkt, men derimod lidt øst for det.

Google Maps bruger imidlertid almindelige længde- og breddegrader, så jeg besluttede mig for at konvertere koordinaterne i adresse.txt tilsvarende én gang for alle. Til det formål brugte jeg perl-modulet Geo::Coordinates::UTM (der findes også en PHP-funktion) for at konvertere koordinaterne.

En anden løsning kunne være at lægge koordinaterne ind i UTM32-format og så blot foretage konverteringer fra/til længde- og breddegrader on the fly. I så fald kan også adresse.txt importeres uden videre i stil med de øvrige filer.

Lidt yderligere læsning:

Filer

De samlede CSV-filer fylder omkring 350MB; ca. 40MB i zippet form. Jeg har lagt dem online i deres oprindelige distributionsform, hvis andre er interesserede.

Webservice

Kort- og Matrikelstyrelsen er i starten af 2006 begyndt at udbyde webservices i samarbejde med IT- og Telestyrelsen og Erhvervs- og Byggestyrelsen. Dette giver mulighed for langt mere opdateret data. De har tillige en række fora til rådighed i forbindelse med udviklingen af de services. Alle interesserede kan benytte sig af deres webservices.

Læs mere om de offentlige adresse-webservices.

Google Maps

Google Maps har satellitkort over hele verden. For en del lande - nu også inklusive Danmark - er der ydermere vejfortegnelser. Lige nu lider kortet under at der kun få steder i Danmark (primært i hovedstadsområdet) er satellitkort i høj opløsning. I det meste af resten af Danmark findes kun kort i en opløsning, der næsten er for lav til at være rigtig brugbar.

Google Maps har frigivet et API, så man kan have et Google Map på sin egen hjemmeside. Ydermere har de en vidunderlig dokumentation og eksempler til at lave sine egne tilføjelser, punkter, animationer, m.m. på sit kort. Det kan næsten ikke blive meget bedre.

Google Maps API Documentation nævnte, at de ikke beskæftigede sig med geocoding. Derfor var det spændende selv at kombinere de geografiske lokationer med Googles kort. Efterfølgende er der introduceret geocoding-muligheder i APIet, men endnu ikke for Danmark.

Ved hjælp af deres API er det blandt andet muligt at tilføje punkter til kortet og navigere til bestemte steder, blot ved yderst simpel javascript, hvor man blot opgiver koordinaterne til en række punkter, samt den tilhørende tekst i tekstboblerne:

..
// markers
map.addOverlay(createMarker(new GLatLng(55.6761965232,12.5755359823),'<b>Nybrogade 26, København</b>'));
map.addOverlay(createMarker(new GLatLng(55.6761965232,12.5755359823),'<b>Nybrogade 26A, København</b>'));
map.addOverlay(createMarker(new GLatLng(54.7736870057,11.8679974179),'<b>Nybrogade 26, Nykøbing Falster</b>'));
map.addOverlay(createMarker(new GLatLng(57.0550560315,9.9121082975),'<b>Nybrogade 26, Aalborg</b>'));

map.setCenter(new GLatLng(55.67,12.57),10,G_HYBRID_MAP)

Selve koordinaterne stammer så ganske enkelt blot fra adresse-databasen.

Øvrig kode

I øjeblikket rummer map.ter.dk ikke specielt mange funktioner. Jeg overvejer lidt, hvad jeg kan bruge dataen til. Forslag er velkomne.

En mulighed er at tilbyde en webservice, hvor man fx kan slå en adresse op og få returneret koordinaterne i et eller andet format. Jeg har allerede lavet en simpel, primitiv udgave af dette (ved at man tilføjer output=simple i URL'en), hvilket jeg selv bruger til et privat familie-website, hvor folks adresser bliver opdateret herfra. Derefter er det muligt at se hvor folk bor på et internt Google Map eller med en eksport til Google Earth.

Det kunne ligeledes være interessant at integrere mere information. Jeg tror dog desværre ikke, at fx adressefortegnelser, telefonnumre eller lignende tilsvarende er gratis tilgængeligt. God data har tendens til at være dyrt.

Diverse mere eller mindre relateret gøgl:

- Af Peter Brodersen
Sidst opdateret: 1. juli 2007 22:02:59