Geolokalizacja z mapami OpenStreetMaps

Ostatnio w pracy miałem zadanie polegające na implementacji Mapy, na którą będą nanoszone markery przedstawiające konkretne ulice. Pierwsze co zrobiłem to wyświetliłem Mapę Google’a następnie zacząłem szukać jakiś informacji, w jaki sposób uzyskać z adresu ulicy jego współrzędne. Google oferuje taką usługę, ale niestety nie jest ona tak przyjemnie ograniczona jak sama mapa.

Szukając dalej znalazłem coś co nazywa się Nominatim. Jest to darmowa, otwarta baza pozwalająca na przetworzenie adresu ulicy na jego współrzędne. Podczas testów okazało się że Mapa Google’a co jakiś czas wyświetlała komunikat informujący iż przekroczyłem limit, co było rzecz jasna niedopuszczalne.

Postanowiłem przepisać kod, używając OpenStreetMap, jest to darmowa alternatywa dla Mapy Google’a.

Napiszmy teraz coś razem ;)

Zacznijmy od HTMLa:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<html>
<head>
<title>Test</title>
<meta charset="utf8">
<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<style type="text/css">
      html { height: 100% }
      body { height: 100%; margin: 0; padding: 0 }
      #form {height: 18%;  text-align: center;width: 50%;margin: 0 auto; margin-top: 1%;}
    </style>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">

</head>

<body>
<div id="form" class="">
    <form>
        <div class="form-group" >
        <input placeholder="Podaj adres.." class="form-control" type="text" name="add" id="val">
        <button id="spr" class="btn btn-default">Sprawdź</button>
    </div>
    </form>
</div>
<div id="map"></div>

</body>
</html>

Jak pewnie część z czytających zauważyła – dodałem style bootstrapa i jQuery. Ten pierwszy służy już teraz do ostylowania formularza a jQuery użyjemy do wysyłania żądań. A więc mamy coś takiego:
Clipboard02

Teraz czas na troszkę JS’a. Zacznijmy od pustego $.ready()

1
2
3
$(document).ready(function(){

}

Aby móc wyświetlić OSM (skrót od OpenStreetMap) musimy mieć do tego bibliotekę, najłatwiej zrobić to wykorzystując OpenLayers – http://openlayers.org ;)

A więc dodajmy jeszcze jeden plik *.js:

1
http://dev.openlayers.org/OpenLayers.js

Idźmy dalej. Mamy diva w który wyrysujemy mapę oraz biblioteki, a więc napiszmy „inita” czyli funkcję która wyrysuje ową mapę..

Może ona wyglądać np. tak:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$(document).ready(function(){

    var map, layer;
    function init(){
        map = new OpenLayers.Map( 'map');//1
        layer = new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png');//2
        map.addLayer(layer);//3
        map.setCenter(
            new OpenLayers.LonLat(21, 52).transform(
                new OpenLayers.Projection("EPSG:4326"),
                map.getProjectionObject()
            ), 7
        );  //4  
    }
}

Tworzymy dwie zmienne globalne (a przynajmniej globalne dla funkcji init()). Nasza funkcja realizuje 4 proste kroki:

1. Tworzymy obiekt OpenLayers, argumentem jest id diva w którym mapa się wyrysuje.
2. Tworzymy warstwę OSM, drugi argument to adres do pojedynczych „kwadratów” mapy.
3. Dodajemy warstwę do mapy.
4. Centrujemy mapę na 21 długości, 52 szerokości – czyli na Warszawę :) metoda transform przekształca owe współrzędne na odpowiedni dla mapy rzut, zagadkowa 7mka to zoom ;)

Zapisz to proszę i zobacz to samo co ja:
Clipboard04

Super, ale gdzie ten cały Nominatim? Ano jeszcze nie ma. Nominatim ogólnie jest zestwem skryptów i bazy danych które instaluje się na serwerze, ale dla przykładu, użyjemy otwartego API dostępnego na mapquestapi

Napiszmy teraz co ma się stać gdy kliknie się na przycisk:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    $("#spr").click(function(e){
        e.preventDefault();//1
        var formVal = encodeURIComponent($("#val").val());//2
        console.log(formVal);//3
        var link = 'http://open.mapquestapi.com/nominatim/v1/search.php?format=json&q='+formVal;//4
        console.log("Query sending..");
        $.get(link, function(data){ //5
            console.log("Query sended..");
            var js = (data);
            console.log(js);
            try{
                var lon = js[0].lon; //6
                var lat = js[0].lat; //6
            }
            catch(x){

                alert("brak sensownej odpowiedzi");
                return;

            }
            layerMarkers = new OpenLayers.Layer.Markers("Markers"); //7
            map.addLayer(layerMarkers); //8
            console.log('Received lat='+lat+', long='+lon);
            var lonLat = new OpenLayers.LonLat(lon, lat).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject()); //9
            var size = new OpenLayers.Size(21, 25); //10
            map.setCenter(lonLat, 7); //11
            var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);//12
            var icon = new OpenLayers.Icon('http://www.openstreetmap.org/openlayers/img/marker.png',size,offset);//13
            layerMarkers.addMarker(new OpenLayers.Marker(lonLat,icon));//14

        });
    });

1. Zatrzymujemy domyślne zachowanie przycisku.
2. Pobieramy z pola formularza adres i funkcją encodeURIComponent encodujemy adres do formy jaką można dokleić do URLa.
3. W kodzie jest sporo console.log() – wyświetlam na konsoli całość procesu, potem Ci pokażę screena.
4. Kleję link do API Nominatim. Jak widzisz, dostanę w odpowiedzi JSON’a.
5. Wysyłam do API żądanie.
6. Analizując odpowiedź:
Clipboard06

dochodzimy do wniosku że API zwraca kilka dopasować. Posortowanych celnością. A więc bierzemy element zerowy i pola lon i lat. Dokładnie tak jest w kodzie.
7. Tworzymy nową warstwę, która będzie zawierać markery.
8. Dodajemy ją do mapy.
9. Tworzymy obiekt z danych dostarczonych z API.
10. Tworzymy obiekt Size, czyli wielkość naszego markera.
11. Centrujemy mapę na wpisany w pole formularza adres.
12. Obliczamy przesunięcie markera względem punktu na który wskazuje.
13. Tworzymy ikonę na podstawie linku do obrazka, wielkości i przesunięcia.
14. Do warstwy zawierającej markery dodajemy tworzony Marker.

Zobaczmy jak nasz skrypt poradzi sobie ze zlokalizowaniem mojej uczelni :) Wchodzimy się na stronę wydziału i kopiujemy

1
Bohdana Stefanowskiego 18/22 Łódź

Wpisujemy taki adres do naszego formularza i…
Clipboard08

I działa! ;) Mega, nie? Niestety w pracy to aż takie proste nie było i to co Wam teraz pokazałem było jedynie czyś co można nazwać „proof of concept„.

Kilka słów na zakończenie.. Nominatim nie jest nieomylny. Najlepiej jak będziecie wpisywać pełne nazwy ulic, czyli „Stefana Żeromskiego” a nie „Żeromskiego” – w ten sposób można podnieść niezawodność aplikacji. Jeśli mimo wszystko nie uda się mu znaleźć współrzędnych to po prostu odpowiedź API będzie pusta..

Czy jesteś w stanie powiedzieć co się stanie w naszej aplikacji jeśli będzie taka sytuacja? ;)

Matt.

O i jeszcze demo: Demo – pełny kod w źródłem dema.

Dzięki za wizytę,
Mateusz Mazurek
Podziel się na:
    Facebook email PDF Wykop Twitter

Dodaj komentarz

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subskrybuj  
Powiadom o