GPX med Kartverkets bakgrunnskart

OBS: En nyere versjon av eksempelet her er tilgjengelig på http://www.atlefren.net/post/2011/02/kartverket-openlayers/. Se gjerne på koden der!

OBSOBS (mai 2015): Denne bloggposten ble skrevet for 5 år siden, og mye har endra seg siden da. Det betyr at denne bloggposten er utdatert og koden ikke funker mer, men ta en titt på https://github.com/atlefren/kartverket-gpx

Du har sikkert en GPS, eller en smarttelefon med GPS innebygd? Du liker kanskje også å gå på tur og logge den? Men du synes ikke det er så forbanna kult å legge ut et track fra en tur i den norske villmark over Google Maps (la oss innse det) elendige turkart eller kornete satelittbilder?

Du ønsker kanskje ikke å vise frem noe slik som dette til turvenner? Du skulle heller ønske du kunne lagt inn ruta di over “indrefileten” av norske data? Nemlig kartverkets kart? Du har sikkert fått med deg at disse er tilgjengelige for gratis bruk for privatpersoner?

Du har kanskje til og med kommet så langt at du har fått satt opp OpenLayers med kartverkets WMS på din hjemmeside?

Men der stopper du kanskje? Du skjønner ikke helt hvordan du skal få lagt GPX fila di over kartet? Eller du får kanskje lasta den inn, men den havner helt riv, ruskende, galt avsted? Eller kanskje ser hele Norge helt på trynet ut? Frykt ikke, siden kongeriket Norge har fått meg utdanna som IKT-Geomatiker tenkte jeg jeg skulle prøve å hjelpe deg.

Jeg vet folk lurer på dette. Jeg så tidligere en tweet fra Martin Bekkelund om temaet, og når har også søkefraser som “turkart egne gpx-filer” begynnt å dukke opp i søkeloggene her på bloggen. Derfor tenkte jeg å yte en innsats.

Så, hva tenkte jeg å gå gjennom? Først av alt en liten intro til å sette opp OpenLayers med Kartverkets WMS i Openlayers. Så tenkte jeg å gå gjennom hvordan man får inn GPX-data i OpenLayers, før jeg forklarer magien bak transformasjoner og dermed får alt til å passe sammen.

Kartoppsett

Først må du ha opp et kart, det gjøres ved å gjøre ca følgende:

1. Lag en HTML-side med en div med høyde og bredde 100%. Gi denne diven en id

<html>
<head>
<title>Kart</title>

<!-- sett opp en div til kartet -->
<style type="text/css">
#map { width: 100%; height: 100%;}
</style>
</head>

<body>

<div id="map"></div>

</body>
</html>

2. Så må du legge inn OpenLayers js-biblioteket. Det greieste er å hente den fra serveren deres direkte.
Dette gjør du ved å legge til en <script>-tag i headeren:

<!-- last inn OpenLayers js -->
<script src="http://openlayers.org/api/OpenLayers.js"></script>

3. Så må vi skrive litt JavaScript selv. Vi ønsker å ha en metode som startes når siden listes, og vi velger å legge all javascript-koden direkte inn i en script-blokk i headeren: Da får vi følgende skjelett:

<html>
<head>
<title>Kart</title>

<!-- sett opp en div til kartet -->
<style type="text/css">
#map { width: 100%; height: 100%;}
</style>
<!-- last inn OpenLayers js -->
<script src="http://openlayers.org/api/OpenLayers.js"></script>
<script type="text/javascript">
//her kommer JS-koden vår
function init(){
}
</script>
</head>

<body onload="init()">

<div id="map"></div>

</body>
</html>

4. Da er grunnlaget lagt, vi har importert OpenLayers-biblioteket, og vi har en (foreløpig) tom init-funksjon. Da gjenstår det å lage et Map-objekt, et layer objekt og legge til litt kontroller.

Gjør vi dette blir js-blokka vår seende slik ut (med beskrivende kommentarer):


<script type="text/javascript">
//selve kartet
var map;

//her starter vi ting
function init() {

OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3; //reduserer problemet med "rosa" tiles (openlayers viser rosa tiles når den ikke får lastet en tile)

//sett opp kartet (EPSG:32633 er UTM sone 33N)
map = new OpenLayers.Map( 'map', {
projection: new OpenLayers.Projection('EPSG:32633'),
maxExtent: new OpenLayers.Bounds(-2500000.0,3500000.0,3045984.0,9045984.0),
units: "m",
maxResolution: 2708.0, // tilsvarer zoom level 3 (hele er 21664.0)
numZoomLevels: 18 // egentlig 21, men maxResolution tilsvarer zoom level 3 (følgelig er 0-3 skrudd av)
} );

//musenavigering
map.addControl(new OpenLayers.Control.MouseDefaults());

// Definer karttjenesten(e)
var topo2 = new OpenLayers.Layer.WMS(
"Topografisk norgeskart2","http://opencache.statkart.no/gatekeeper/gk/gk.open?",
{layers: 'topo2', format: 'image/png'},{attribution:''}
);

// Legg WMSen fra kartverket til kartet
map.addLayers([topo2]);

//sentrer kartet på besseggen
map.setCenter(new OpenLayers.LonLat(683589,7146336),0); //Besseggen

}

</script>

5. Har du kommet så langt skal du nå ha ett Norgeskart du kan zoome og panorere rundt i, slik som dette.

GPX-innlasting

Godt å vel det, men jeg ser jo fortsatt ikke noe track? Nei, det skal vi ta nå.

Første skritt er å lokalisere en .gpx-fil (min ser slik ut) og ligger i samme mappe som kartet mitt på webserveren.

En ting kan vi like godt notere oss først som sist: GPX filer er i et koordinatsystem kjent som WGS 84 Lat/Lon, mens kartet vårt er i WGS84 UTM Sone 33N. Derfor må vi transformere, men det kommer vi til. Først skal vi få lasta inn GPX-fila i JS-koden.

1. Innlasting. OpenLayers har støtte for GPX-formatet og støtte for å laste filer. I tilegg må vi ha et vector-layer å legge tracket vårt i.

Ved å legge inn dette får vi noe slikt som:

//openLayers GPX-leser
var format = new OpenLayers.Format.GPX();

//layeren vi hiver featurene fra GPX i
var featureLayer = null;

//selve kartet
var map;

//her starter vi ting
function init() {

//sett opp featurelayeren som en vector-layer
featureLayer = new OpenLayers.Layer.Vector("GPX");

//url til gpx-fila (må ligge på samme server)
var url = "track.gpx";

//OpenLayers har støtte for å lese inn filer
OpenLayers.loadURL(url, null, null, loadSuccess, loadFailure);

//sett opp kartet (EPSG:32633 er UTM sone 33N)
map = new OpenLayers.Map( 'map', {
projection: new OpenLayers.Projection('EPSG:32633'),
maxExtent: new OpenLayers.Bounds(-2500000.0,3500000.0,3045984.0,9045984.0),
units: "m",
maxResolution: 2708.0, // tilsvarer zoom level 3 (hele er 21664.0)
numZoomLevels: 18 // egentlig 21, men maxResolution tilsvarer zoom level 3 (følgelig er 0-3 skrudd av)
} );

//musenavigering
map.addControl(new OpenLayers.Control.MouseDefaults());

OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3; //reduserer problemet med "rosa" tiles (openlayers viser rosa tiles når den ikke får lastet en tile)

// Definer karttjenesten(e)
var topo2 = new OpenLayers.Layer.WMS(
"Topografisk norgeskart2","http://opencache.statkart.no/gatekeeper/gk/gk.open?",
{layers: 'topo2', format: 'image/png'},{attribution:''}
);

// Legg WMSen fra kartverket og featureLayeren vår til kartet
map.addLayers([topo2,featureLayer]);

//sentrer kartet på besseggen
map.setCenter(new OpenLayers.LonLat(683589,7146336),0); //Besseggen

}

//denne kalles når GPX-fila er lastet
function loadSuccess(request) {
//les responsen her som GPX
var features = format.read(request.responseText);

}

//denne kalles hvis/når noe går galt ved lesing av fila
function loadFailure(request) {
alert("Kunne ikke lese GPX-fila...");
}

Vi ser vi har gjort endel nye ting:

  • Vi har definert et format og en featureLayer
  • Vi kjører et kall til OpenLayers.loadURL
  • Vi legger til et layer til til kartet
  • Vi har en funksjon loadSuccess som laster GPX-fila over i et feature-array

Men vi kommer forsatt ikke så veldig langt.

Neste, logiske skritt, ville være å legge til featurene i featureLayeret og zoome inn på disse. Dvs å legge til litt i loadSuccess:

function loadSuccess(request) {
//les responsen her som GPX
var features = format.read(request.responseText);
//vi får da en samling features, legg disse til vector-layeret
featureLayer.addFeatures(features);
//zoom kartet, denne gangen til område de lastede featurene dekker
map.zoomToExtent(featureLayer.getDataExtent());

}

Men dette vil ikke fungere, det ser vi her. Problemet er, som nevnt før, at vi har to forskjellige koordinatsystem. Hadde kartet vært i WGS84 Lon/Lat hadde det fungert (men det vil vi ikke, da ser ikke kartet bra ut), hadde GPX-fila vært i WGS84 UTM Sone 33N ville det fungert (men det er aldri GPX-filer).

Dermed må vi transformere. Her kommer OpenLayers til kort, men den kan støtte seg på Proj4js. Du må laste ned kildekoden, og legge “lib/proj4js-compressed.js” et sted der javascriptet ditt når den, samt inkludere den på samme måte som med OpenLayers.js. Hvis du legger den i samme mappe legger du til:

<script src="proj4js-compressed.js"></script>

i headeren.

I tillegg må du definere for Proj4Js hvilke parametere som gjelder for WGS84 UTM Sone 33N, da den ikke kjenner det. Dette gjør du ved å legge inn følgende i begynnelsen av JavaScriptet:

//Proj4js definisjon for WGS84 UTM Sone 33N
Proj4js.defs["EPSG:32633"] = "+proj=utm +zone=33 +ellps=WGS84 +datum=WGS84 +units=m +no_defs";

(Denne definisjonen finner du under “Proj4js” på http://spatialreference.org/ref/epsg/32633/.)

Da er vi nesten i mål, nå gjenstår bare selve transformasjonen. Det vi må gjøre i loadSuccess er å loope gjennom alle featurene og transformere geometriene fra WGS84 LatLon (som har kode EPSG:4326) til WGS84 UTM Sone 33N (som har kode EPSG:32633). Dette gjør vi med transform-metoden til geometri-objektet, og funskjonen vår blir slik:


function loadSuccess(request) {
//les responsen her som GPX
var features = format.read(request.responseText);

//loop gjennom alle features og transformer fra WGS84 Lat/Lon (EPSG4326) til WGS84 UTM Sone 33N (EPSG:32633)
for(var i = 0; i < features.length; i++){
features[i].geometry.transform(new OpenLayers.Projection('EPSG:4326'), new OpenLayers.Projection('EPSG:32633'));
}

//vi får da en samling features, legg disse til vector-layeret
featureLayer.addFeatures(features);
//zoom kartet, denne gangen til område de lastede featurene dekker
map.zoomToExtent(featureLayer.getDataExtent());
}

Nå ser vi et spor slynge seg oppover Skogshorn, men det er litt blasst. Vi må endre hvordan det tegnes!

Flikking og pussing

Vi har nå ting på plass, men det ser kanskje litt sært ut? Vi kan mens vi er i gang fikse litt på utseendet på lina vår, via et StyleMap på vektor-layeret. Dette gjør vi ganske enkelt slik (og tegner linja med 2px grønn strek):


//angi styling av features
var styleMap = new OpenLayers.StyleMap(OpenLayers.Util.applyDefaults({
strokeColor: "green",
strokeWidth: 2},
OpenLayers.Feature.Vector.style["default"]));

//sett opp featurelayeren som en vector-layer
featureLayer = new OpenLayers.Layer.Vector("GPX", {styleMap: styleMap});

Og får dette. (For mer om styling, se OpenLayers.Feature.Vector).

Eller kanskje du foretrekker å bruke N50-kartet som bakgrunn, og la brukeren få velge? Mulighetene er mange!

Konklusjon

Dette var bare en quick-and-dirty gjennomgang av prinsippene, koden fungerer greit til enkel bruk, men den kan lett kombineres med opplastingsrutiner for å laste opp GPX-filer via f.eks. php, eller du kan utvide ting til å bruke en romlig database for å lagre GPS-tracket? Muligehtene er mange.

Og, skulle du oppdage feil eller mangler, eller har spørsmål er det bare å si fra!

25 thoughts on “GPX med Kartverkets bakgrunnskart

  1. Pingback: Tweets that mention HC SVNT DRACONES » GPX med Kartverkets bakgrunnskart -- Topsy.com

  2. Atle Post author

    Takk for kommentar, godt etter å ha brukt litt tid på slikt.

    Glemte helt å nevne at du, og andre, også har laget tutorials som dreier seg rundt kartverkets tjenester, det får eventuelt bli en ny samlepost!

  3. Martin Bekkelund

    Kjenner at jeg har litt lyst til å gi deg en klem. 😉 Dette har jeg, som du så riktig har merket deg, lurt på lenge. Dessverre kokte det bort i kålen før ferien, så da var det ekstra hyggelig å se denne artikkelen da jeg kom tilbake.

    Tusen, tusen takk!

    Neste prosjekt blir kanskje å lage et lite PHP-skript som automatiserer opplasting og visning? 😛

  4. Atle Post author

    Takker, hyggelig med sånne tilbakemeldinger.

    Jeg synes jo denne appropachen var en smule mer elegant enn det jeg skisserte før sommeren, så for min del var det artig å få det til. Så håper jeg både du og andre får brukt den til noe fornuftig.

    Nå er ikke jeg noen racer på PHP, men jeg vil tro det er en enkel sak å lage et standard upload-script, og det å programmatisk gi js-koden en fil som input er vel heller ikke rocket-science. Men for at jeg skal sette meg ned å gjøre noe sånnt tror jeg jeg må ha bruk for det selv 🙂

    Dog, du har vel litt PHP-peil selv også, har du ikke? :p

  5. Martin Bekkelund

    Jeg har allerede brukt dette til noe nyttig. Moro å kunne lage en egen, privat turportal hvor jeg publiserer alle GPX-rutene mine.

    Det er jo, som du skriver, ikke vanskelig å lage et slikt PHP-skript som bare genererer en ny HTML-fil med ny GPX-informasjon. Jeg er ingen guru på PHP, men det kunne vært moro å lage. Men det var denne tiden, da. 🙂

  6. Pingback: Garmin Forerunner 305 « Martin Bekkelund

  7. Atle Post author

    Hyggelig å høre at det kommer folk til nytte, og at det faktisk er mulig å bruke oppskriften for omtrent menigmann 😉

  8. Pingback: HC SVNT DRACONES » GPX med Kartverkets bakgrunnskart, del 2

  9. marius rohde johannessen

    Flott post! Dette skal testes. Finnes det måter å legge til bilder fra turer i kartet, f.eks fra panoramio? Hender man har noen fine geotaggede mobilbilder man gjerne vil dele med andre.

  10. Pingback: HC SVNT DRACONES » Hjelp, jeg vil lære meg OpenSource WebGIS?

  11. Geir A Granviken

    Endelig fant jeg en løsning som faktisk fungerer, nemlig din!! Jeg har ønsket nettopp dette, å kunne vise GPS-spor på Statkarts fantastiske norgeskart, i stedet for på det sørgelige kartet hos Google. (Og det var etter å ha sett de flotte kartene på Ut.no, at jeg skjønte at det kanskje var mulig å gjøre dette mye mye bedre!)

    Jeg har kastet bort flere dager på å teste ut løsninger med utgangspunkt i eksemplene på Statkarts utviklersider (som forøvrig er fulle av feil, som tok særdeles lang tid å debugge for en som bare kan vanlig HTML, før jeg i det hele tatt fikk vist et kart på skjermen…), samt eksempel-nettstedene det var linket til. Men ikke pokker om jeg fikk opp noe spor fra gpx-filene på det samme kartet, uansett hva jeg prøvde av anvisninger på hvordan man skulle gjøre det med OpenLayers. Før jeg gav opp gjorde jeg et siste enkelt Google søk, tror det var gpx+statkart, – og vips havnet jeg her!

    Jeg tok meg den frihet å kopiere hele kildefilen i det ferdige eksemplet ditt, for å bruke som utgangspunkt, og da fikk jeg jammen meg både kart OG gpx-spor gitt! 🙂 Hjertelig takk for dette kompetente stykket arbeide! Du kan se min foreløpige test-side her: http://www.ggranvik.no/tur/tur_snoehetta.htm. Jeg ser du har laget en videreutviklet versjon, som jeg sikkert kommer til å prøve også, men foreløpig er jeg happy bare for det å kunne vise et GPS-spor på Statkarts norgeskart! Forøvrig burde jeg vel legge inn en attribution til din kilde når jeg skal skal publisere en ferdig side?

  12. Atle Post author

    Så bra du fant noe som endelig fungerte!

    Hvis det er slik at Kartverkets eksempler har feil så ville jeg sendt dem en mail, de vil sikkert rette opp i ting hvis de får høre hva som ikke fungerer.
    Har du forresten sett http://www.mineturer.org ? Det kan også være en løsning, hvis du kun er interessert i å se turene dine på kart. Dog, jeg skjønner godt at man vil ha ting på egen side.

    Når det gjelder attribution er det ikke noe krav i det hele tatt, jeg anser dette eksempelet som “public domain”, men om du på en eller annen måte vil hive inn en link til denne bloggposten er det bare fint, det sørger jo bare for at Google-ranken til denne siden havner høyere, noe som vil glede andre i samme situasjon som deg.

  13. Geir A. Granviken

    Ja, jeg burde vel maile Statkart om disse eksempel-kodelinjene deres. Jeg prøver gjerne mineturer.org etterhvert. Nå har man vel faktisk muligheten for å gjøre omtent det samme (tror jeg da) på norgeskart.no også. Hvis man oppretter en brukerkonto og logger inn, kan man nemlig laste opp egne GPX-filer, og vise de som egne lag. Veldig sparsomt med dokumentasjon riktignok. Og med funksjonen hvor man kan lage en link for embedding, er det tydeligvis IKKE mulig å få med egne lag (i hvert fall har ikke jeg fått til det). Derfor er koden din fortsatt nødvendig hvis man vil vise en gpx-rute til andre enn seg selv ser det ut til!

    Ellers putter jeg inn en attribution til koden din, for som du sier vil jo det bare bidra til å øke rankingen på Google.

  14. olem

    Hei

    Det ser ut som du har hardkoda gpx filene inn i index.html, er dette riktig?

    type:
    //definer opp GPX-filene, alle felter må med
    var files = [
    {file:”files/Sylene1.gpx”,color: “#ff0000”, width: 3, title: “Sylene 1”, desc: “Beskrivelse”},
    {file:”files/Sylene2.gpx”,color: “#0000ff”, width: 3, title: “Sylene 2”, desc: “Beskrivelse…”}

    kartet ser ut til å laste veldig tregt også, men det er vel tilkoblignen til kartjensten sikkert. tanker om det?

    Tenkte jeg skulle se litt på koden din etterhvert og gjøre det mer “brukervennlig” om jeg får litt tid her og der.

    Noe spesielt som burde “ryddes” opp i?

  15. Pingback: Pappa holder meg opptatt « slashdir

  16. Atle Post author

    hei!

    Hardkodinga er riktig ja, mest fordi jeg (som jeg skrev på bloggen din) fokuserte på å vise den på kart, ikke “alt rundt”.

    Tanken var vel at folk skulle plukke opp ideen og klare å lage et upload-script eller lignende selv.

    Det at kartet er tregt skyldes nok kartserveren ja, det har du rett i.

    Vel, om du har satt deg som mål å lage en mer “brukervennlig” versjon ville jeg fokusert på å ta det du selv ser på som vanskelig, er en stund siden jeg har sett på dette nå, så har ikke helt oversikten. En opplastingsfunksjon er nok en god start. Ellers så har jeg jo mineturer.org, som et eksempel på hvordan man kan gjøre GPX-filer rimelig brukervennlig (dog uten mulighet til å kjøre på egen server).

    Artig å se at folk fortsatt snubler over denne posten!

  17. olem

    Kan gjøres sånn, så slipper man å hente alle filene manuelt (du vet vel det sikkert, men kanskje andre som tar en titt kunne tenke seg det), beskrivelsen kan evnt leses inn i en form, upload script finnes det nok av tilgjengelig på nettet.

    *husk å rename index.html til index.php da, eller så vil det ikke fungere

    //Henter alle .gpx filer fra ./files mappen

  18. olem

    Ser ut som du enten tilatter php i kommentarfeltet (ikke lurt) eller så syntes det bare ikke, anyway, hadde litt bugs uansett. her er en som fungerer


    //Henter alle .gpx filer fra ./files mappen
    var files = [

    ];

    Til opplasting har jeg bare valgt å bruke http://www.phphq.net/scripts.php?script=phUploader#phUploader
    støtter passordinnlogging osv, kjører den på en egen side slik at kartet kan ta hele skjermen (fint om det skal vises frem på telefon / tablets)

  19. Steve

    Heisan,
    Noen år siden du laget denne artikkelen og kartene virker ikke.
    Hadde du hatt mulighet å sett lit på eksemplet og fått det til å fungere? Er meget interessert å inkludere egene GPX filer på kart på egen nettside.. =)

Leave a Reply

Your email address will not be published. Required fields are marked *