Google Maps API jest świetne – bardzo proste, wręcz intuicyjne, rozszerzalne, a w dodatku nieźle udokumentowane. Po prostu świetna robota ze strony Google.
Trafiłem jednak ostatnio na nietypowy problem – tworzyłem stronkę dla firmy, której siedziba mieści się w dwóch odległych od siebie miastach. Nie chciałem tworzyć dwóch osobnych map dojazdowych – stworzyłem więc jedną z dwoma przyciskami wskazującymi na konkretne miasta. O tym jak umieścić kilka lokalizacji na jednej mapie Google Maps przeczytasz poniżej.
Jako, iż w projekcie wykorzystywałem jQuery, tak i w niniejszym przykładzie posłużę się tą biblioteką.
Google Maps ma świetnie udokumentowane API, jak i również kilkanaście podstawowych przykładów wykorzystania mapy.
Wykonana przeze mnie mapa z dwoma lokalizacjami była budowana głównie w oparciu o tutorial – niczego więcej mi nie było trzeba.
Tworzymy mapę z wykorzystaniem Google Maps
Na stronie (mowa tutaj o HTML) musimy wstawić tylko jeden znacznik, do którego będziemy później odwoływać się w JavaScript/CSS i w którym dynamicznie utworzymy mapę:
1 | <div id="map_canvas"></div> |
Przy pomocy CSS możemy określić wygląd mapy:
1 2 3 4 5 6 7 | #map_canvas { border: 1px solid gray; margin: 40px 10px 30px 0px; padding: 0; width: 600px; height: 480px; } |
I teraz najważniejsze – mapa jest tworzona dynamicznie w technologii Canvas, dlatego pozostały kod będzie napisany wyłącznie w JavaScript.
Google Maps JavaScript API
Całość kodu JavaScript będzie zamknięta w jednym obiekcie, by nie powodować kolizji z innymi przestrzeniami znakowymi dla funkcji, właściwości i obiektów. Tak to wygląda:
1 2 3 4 5 6 7 8 9 10 11 | var MyUI = { start: function() { this.maps.start(); }, maps: { // kod mapy } } |
Tak stworzony kod będzie wczytywany po załadowaniu się całej treści na stronie, na samym końcu:
1 | $(function() { MyUI.start() }); |
I to tyle słowem wstępu :-) Czas wczytać mapę i ją udekorować. Poniżej prezentuję kolejne metody obiektu MyUI.maps wraz z krótkim opisem:
1 2 3 4 5 6 7 8 9 10 11 | /** * Load Google Maps API Script and run callback method! **/ start: function() { $.ajax({ url: 'http://maps.google.com/maps/api/js?sensor=false&language=pl&callback=MyUI.maps.render', dataType: 'script' }); } |
W powyższym wywołaniu dzieje się najważniejsza rzecz – przy pomocy technologii AJAX wczytujemy skrypt Google Maps do pobrania (stąd dataType: ‚script’) i dodajemy funkcję zwrotną (callback), która zostanie wywołana po pozytywnym wczytaniu się pliku mapy.
Zamiast przerzucać wywołanie funkcji zwrotnej na Google Maps moglibyśmy skorzystać z metody success i wywołać odpowiednie akcje, np.:
1 2 3 4 5 6 7 8 9 10 11 | start: function() { $.ajax({ url: 'http://maps.google.com/maps/api/js?sensor=false&language=pl', dataType: 'script', success: function() { MyUI.maps.render(); } }); } |
Równie dobrze można by wczytać ten skrypt w sposób statyczny, tzn. dodając do kodu HTML znacznik script:
1 | <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&language=pl"></script> |
Będziemy teraz potrzebowali zmiennych do przechowywania informacji o mapie i pozycji naszych lokalizacji:
1 2 3 4 5 | // map object map: undefined, // positions positions: {}, |
Właściwość map będzie się odnosić do aktualnej mapy, co okaże się pomocne przy dodawaniu markerów, własnych przycisków, etc. Obiekt positions odpowiada natomiast za przechowywanie pozycji geograficznych naszych lokalizacji.
1 2 3 4 5 6 7 8 9 10 11 | descriptions: { wysoka: '<h4>BreTerm</h4>' + '<p><strong>Instalacje Elektryczne, Pomiary Termowizyjne</strong></p>' + '<p>ul. Świerczewskiego 4/11, 89-320 Wysoka</p>', bydgoszcz: '<h4>BreTerm</h4>' + '<p><strong>Instalacje Elektryczne, Pomiary Termowizyjne</strong></p>' + '<p>ul. Doktora Antoniego Jurasza 1, 85-090 Bydgoszcz</p>', }, |
Kolejny obiekt przechowuje tekst, który pojawi się w dymku po kliknięciu w marker na mapie dla wskazanej lokalizacji.
Teraz, gdy mamy już podstawowe dane przygotowane, zabierzmy się za renderowanie mapy.
Renderujemy mapę!
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | /** * Render map on page **/ render: function() { this.positions.wysoka = new google.maps.LatLng(53.182205, 17.076842); this.positions.bydgoszcz = new google.maps.LatLng(53.126586, 18.02865); this.map = new google.maps.Map(document.getElementById("map_canvas"), { scaleControl: false, zoom: 15, center: this.positions.wysoka, mapTypeId: google.maps.MapTypeId.ROADMAP, streetViewControl: false, mapTypeControlOptions: { mapTypeIds: [ google.maps.MapTypeId.ROADMAP, google.maps.MapTypeId.HYBRID ] } }); this.goToPositionButton( MyUI.maps.positions.wysoka, 'BreTerm Wysoka', 'Pokaż adres siedziby BreTerm w Wysokiej', MyUI.maps.setHomeWysoka ); this.goToPositionButton( MyUI.maps.positions.bydgoszcz, 'BreTerm Bydgoszcz', 'Pokaż adres siedziby BreTerm w Bydgoszczy', MyUI.maps.setHomeBydgoszcz ); this.addMarkers( MyUI.maps.positions.wysoka, this.descriptions.wysoka, 'BreTerm' ); this.addMarkers( MyUI.maps.positions.bydgoszcz, this.descriptions.bydgoszcz, 'BreTerm' ); }, |
Po kolei opiszmy co tutaj się dzieje:
- Ustawiamy pozycje naszych lokalizacji, wstawiając odpowiednią długość i szerokość geograficzną (longitude i latitude); wartości te podstawiamy do obiektu google.maps.LatLng.
Ustawione lokalizacje zapisujemy we wcześniej utworzonym obiekcie positions (nie musimy się ograniczać do dwóch pozycji, może być ich więcej).
- Tworzymy obiekt mapy, ustawiając swoje parametry (zbliżenie, przyciski na mapie, rodzaj mapy, wyświetlaną pozycję).
Obiekt mapy przypisujemy do wcześniej utworzonej właściwości map.
- Dodajemy do mapy dwa przyciski, wykorzystując metodę goToPositionButton, za której napisanie zaraz się zabierzemy.
- Dodajemy markery do mapy, wykorzystując kolejną metodę: addMarkers, która zostanie przedstawiona poniżej.
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 | /** * Add markers to map * * @param object position (google.maps.LatLng) * @param string text code in marker * @param string button alt value **/ addMarkers: function(position, text, title) { if (!text) return false; var marker = new google.maps.Marker({ map: this.map, position: position || this.map.getCenter(), title: title || 'punkt docelowy' }); var infowindow = new google.maps.InfoWindow(); infowindow.setContent(text); google.maps.event.addListener(marker, 'click', function() { infowindow.open(this.map, marker); }); }, |
Powyższa metoda odpowiada za dodawanie markerów do mapy.
- Jeśli do metody nie został dodany tekst markeru, akcja zostaje przerwana (po co komu marker bez żadnych informacji?).
- Wstawiamy marker z wykorzystaniem metody google.maps.Marker.
- Dodajemy dymek z informacją o punkcie – w dymku tym zostaje podana nazwa firmy, dokładny adres, etc.
- Obsługujemy zdarzenie click mapy – pojedyncze kliknięcie na markerze powoduje wyświetlenie dymku.
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | /** * Go To Position Button * insert button - go to selected position in the map * * @param object position (google.maps.LatLng) * @param string button value * @param string button alt title * @param string callback function name **/ goToPositionButton: function(position, text, title, callback) { var controlDiv = document.createElement('DIV'); controlDiv.style.margin = '0px 5px'; // set CSS for the control border var controlUI = document.createElement('DIV'); controlUI.style.backgroundColor = 'white'; controlUI.style.borderStyle = 'solid'; controlUI.style.borderWidth = '1px'; controlUI.style.borderColor = 'gray'; controlUI.style.cursor = 'pointer'; controlUI.style.width = '120px'; controlUI.style.textAlign = 'center'; controlUI.style.padding = '2px 8px'; controlUI.title = title || 'Wyświetl pierwotny adres'; controlDiv.appendChild(controlUI); // set css for the control interior var controlText = document.createElement('DIV'); controlText.style.fontFamily = 'Arial,sans-serif'; controlText.style.fontSize = '12px'; controlText.style.paddingLeft = '4px'; controlText.style.paddingRight = '4px'; controlText.innerHTML = text; controlUI.appendChild(controlText); // setup the click event listeners google.maps.event.addDomListener(controlUI, 'click', function() { // set center position MyUI.maps.map.setCenter(position); // callback function if (callback) callback(); }); controlDiv.index = 1; this.map.controls[google.maps.ControlPosition.RIGHT].push(controlDiv); }, |
Sporo kodu, niemniej bardzo prostego do zrozumienia:
- Tworzymy warstwę DIV (controlDiv), w której osadzimy przycisk, którą możemy dowolnie ostylować.
- Tworzymy kolejną warstwę DIV (controlUI), która posłuży za przycisk (również ustawiamy własne style CSS).
- Do naszego „przycisku” dodajemy pole tekstowe (controlText).
- Dodajemy obsługę zdarzenia click dla przycisku – po kliknięciu w przycisk wyświetlamy pozycję firmy na mapie oraz wywołujemy funkcję zwrotną, o ile taką ustawiliśmy.
- Na sam koniec wstawiamy wszystkie warstwy do mapy po prawej stronie (google.maps.ControlPosition.RIGHT).
1 2 3 4 5 6 7 | setHomeWysoka: function() { MyUI.maps.map.setZoom(15); }, setHomeBydgoszcz: function() { MyUI.maps.map.setZoom(16); } |
Powyższy kod to wspomniane funkcje zwrotne, które są wywoływane po kliknięcie w poszczególne przyciski. Jeśli klient kliknie na przycisk „Bydgoszcz” to mapa się powiększy (większe miasto = bardziej dokładna mapa), natomiast gdy kliknie w przycisk „Wysoka” to mapa się zmniejszy.
Słowem zakończenia
Jak widać powyżej, zadanie nie było najwyższych lotów i szczególnie trudne, choć trochę czasu na to zeszło. W związku z tym postanowiłem się podzielić – być może komuś się przyda.
Wszystko fajnie wytłumaczone. Link do zumi nie działa.
Parę uwag, może się przyda:
– goToPositionButton – widząc coś takiego w kodzie spodziewałbym się funkcji obsługującej jakiś event, addPositionButton byłoby bardziej na miejscu;
– @param string callback function name – nie string, tylko function; to z kolei wypadałoby sprawdzić chociażby przez $.isFunction.
Pamiętaj, że ktoś kiedyś może jeszcze pracować na Twoim kodzie, więc postaraj się nie uprzykrzać mu życia wprowadzaniem w błąd. ;)
mapa jest tworzona dynamicznie w technologii Canvas
Hm, czy aby na pewno?
Pozdrawiam.
@czesław:
jak nie działa jak działa. :D
@Pejotr:
Z tym nazewnictwem masz rację, mogłem się bardziej postarać. O @param function nie wiedziałem, szczerze mówiąc. Dziękuję za wartościowy komentarz :-)
Btw. jak nie canvas to co?
W wersji 2.0 api był wykorzystywany svg/vml, ale tylko do rysowania ścieżek i wielokątów. Sama mapa była złożona z divów i obrazków i, z tego co widzę, nadal tak jest.
Podejrzewam więc, że jeśli w v3 coś jest „tworzone dynamicznie w technologii Canvas”, to co najwyżej dodatkowe elementy rysowane „na mapie”, a tych akurat w powyższym przykładzie nie ma. ;)
Nie zagłębiałem się w sposób wykonania Google Maps, bardziej interesowało mnie API i to tutaj ma największe znaczenie :-)
W sumie opieramy wszystko na elemencie Canvas, więc pewnie działa to na tej zasadzie. Starsze wersje IE mają z tym problemy to być może jest przerzucane na VML. Rysunek PNG z mapą jest serwowany jako tło, przy pomocy ww. technologii nanosimy markery i inne elementy, na to jeszcze inne rzeczy można nanieść z HTML/CSS i gitara.
Bardzo pomocne informacje. Dzięki
Jako że niedawno produkowałem też takie rozwiązanie… nie ma jeszcze canvas’a – wciąż mapa jest składana z wielu obrazków.
Z tutorialka korzystałem, choć po chwili trzeba było przejść do googlowego API i na nim pracować.
Co ciekawe
2
3
4
5
6
7
url: 'http://maps.google.com/maps/api/js?sensor=false&language=pl',
dataType: 'script',
success: function() {
MyUI.maps.render();
}
});
Takiego myku pod prototypem nie zrobisz, same-orgin policy zablokuje.
@Michał:
jQuery rozpoznaje dataType i jeśli jest „script”, a nagłówki zwracają coś z „text/javascript, application/javascript, application/ecmascript, application/x-ecmascript” to wykonywany jest poniższy kod:
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
jQuery.ajaxTransport( "script", function(s) {
// This transport only deals with cross domain requests
if ( s.crossDomain ) {
var script,
head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
return {
send: function( _, callback ) {
script = document.createElement( "script" );
script.async = "async";
if ( s.scriptCharset ) {
script.charset = s.scriptCharset;
}
script.src = s.url;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function( _, isAbort ) {
if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
// Remove the script
if ( head && script.parentNode ) {
head.removeChild( script );
}
// Dereference the script
script = undefined;
// Callback if not abort
if ( !isAbort ) {
callback( 200, "success" );
}
}
};
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
// This arises when a base node is used (#2709 and #4378).
head.insertBefore( script, head.firstChild );
},
abort: function() {
if ( script ) {
script.onload( 0, 1 );
}
}
};
}
});
;)
Witam serdecznie. jestem laikiem informatycznym. chciałbym taką mapę wstawić do mojej strony w joomla. rozumiem, że część kodu wklejam do css ale resztę kodu gdzie wklejam? czy tworzę nowe pliki i gdzie je wtedy umieszczam. czy można prosić o rozszeżenie opisu.dzięuję
@Marek: wpis jest kierowany właśnie do początkujących webdeveloperów. Kopiujesz / wklejasz na stronie i już działa. W dodatku jest działający przykład, gdzie można podejrzeć co/jak funkcjonuje:)
Jeśli to nie jest jasne to zajrzyj proszę do jakiegoś podstawowego kursu HTML/JavaScript, np.:
https://developer.mozilla.org/pl/docs/
dziękuję, już się biorę do pracy :)