Wiesz już, jak wstawiać proste mapy Google na swoją stronę oraz jak precyzyjnie dostosować ich kształt i wygląd do własnych potrzeb. Czas przejść do bardziej zaawansowanych funkcji oferowanych przez Google Maps API.
W drugiej części artykułu o Google Maps API odsłonimy kulisy nanoszenia kolorowych warstw na mapę, animowania przejść pomiędzy różnymi adresami oraz wyznaczania trasy przejazdu samochodem pomiędzy zadanymi punktami. Co więcej, zaprezentujemy także metodę wstawiania reklam Google AdSense na mapę, a nawet przepis na tworzenie całkowicie własnych mapek, wykorzystujących Google Maps. A więc zaczynamy!
Zmiana nazw typów widoków
W poprzednim artykule zaprezentowaliśmy kontrolki, które możesz dołączyć do swojej mapy. Była wśród nich również kontrolka odpowiedzialna za wybór widoku mapy przez użytkownika. Przypomnijmy, że możesz udostępnić taką kontrolkę w następujący sposób:
map.addControl(new GMapTypeControl());
Prezentowane typy mapy mają jednak anglojęzyczne
nazwy. Co zrobić, aby spolonizować kontrolkę?
var types = map.getMapTypes();
types[0].getName = function() {
return \"Satelita\";
}
types[1].getName = function() {
return \"Hybryda\";
}
types[2].getName = function() {
return \"Mapa\";
}
Za pomocą metody {stala}getMapTypes(){/stala} klasy
GMap2 zwracane są aktualnie dostępne widoki
w formie tablicy. Jeśli chcesz zmienić ich nazwę,
wystarczy podmienić dla każdego elementu tej
tablicy funkcję {stala}getName(){/stala} na własną.
Animowanie przejścia między punktami
Silnik Google Maps pozwala na płynne animowanie przejść pomiędzy różnymi punktami. Służy do tego metoda {stala}panTo(){/stala} klasy GMap2.
map.panTo(point);
Przesuwa ona \”kamerę\” do wskazanego
punktu. Jeśli punkt ten znajduje się w wyświetlanej
części mapy lub niedaleko jej obrzeży, nastąpi płynne
przejście. Jeśli wyszukiwany punkt znajduje się
dalej, wówczas nastąpi gwałtowny przeskok.
Oto, w jaki sposób możesz wykorzystać metodę {stala}panTo{/stala} w celu utworzenia animowanych i ładnych przejść po mapie. Na początek przygotujemy kilka przydatnych funkcji pomocniczych, które będą na potrzeby tego przykładu realizować zadanie przybliżania, oddalania oraz przesuwania widoku kamery do określonego punktu (animacja). Aby oczekiwane operacje wykonywane były sekwencyjnie, wykorzystamy funkcję {stala}setTimeout(){/stala} do ustalenia precyzyjnego czasu wywołania każdej z nich. Każda operacja przesuwa moment wyzwolenia kolejnej.
function zoomIn(text) {
// Zapamietaj czas wykonania
var time = tim;
// Zwieksz czas - nastepna operacja wykona sie 3 sekundy pozniej
tim += 3000;
// Utworz funkcje, ktora zostanie wyzwolona po czasie time
window.setTimeout(function() {
// Zmien zoom - przyblizenie
map.setZoom(16);
map.openInfoWindowHtml(map.getCenter(), text);
}, time);}
// Funkcja oddalajaca - analogicznie do powyzszej
function zoomOut() {
var time = tim;
tim += 1000;
window.setTimeout(function() {
map.closeInfoWindow();
map.setZoom(8);
}, time);
}
// Przejscie pomiedzy punktami
function animateTo(address, shortwait) {
var time = tim;
if (shortwait == 1)
tim += 500;
else
tim += 3000;
// Geolokalizacja jest znana z poprzednich skryptow
geocoder.getLatLng(address, function(point) {
window.setTimeout(function() {
map.panTo(point);
},time);
});
}
W powyższych przykładach zastosowane
zostały najprostsze metody sterowania animacją.
Ponieważ jednak szybkość przesuwania mapy oraz
zoomów może zależeć od szybkości komputera
oraz łącza internetowego, ustawiane na stałe czasy
nie są najlepszym pomysłem.
Lepiej byłoby więc
zaimplementować mechanizmy sprawdzające, czy określona operacja została wykonana i dopiero
wówczas zrealizować kolejną. Pozostawiamy to
jednak do samodzielnego dopracowania.
Gdy mamy już utworzone wszystkie funkcje
pomocnicze, czas na zainicjowanie animacji:
var geodecoder = null;
var map = null;
var tim = 1000;
function load() {
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById(\"map\"));
geocoder = new GClientGeocoder();
...
showAddress(\"Nawojki 11, Krakow, PL\", 8);
zoomIn(\"Uniwersytet Jagiellonski
Wydzial Matematyki i Informatyki\");
animateTo(\"Mickiewicza 30, Krakow, PL\");
zoomIn(\"Akademia Gorniczo-Hutnicza
Wydzial Fizyki i Informatyki Stosowanej\");
zoomOut();
animateTo(\"Bankowa 14, Katowice, PL\");
zoomIn(\"Uniwersytet Slaski
Wydzial Matematyki, Fizyki i Chemii\");
zoomOut();
animateTo(\"Akademicka 16, Gliwice, PL\");
zoomIn(\"Politechnika Slaska
Wydzial Automatyki, Elektroniki i Informatyki\");
zoomOut();
animateTo(\"Uniwersytet Wroclawski, PL\");
zoomIn(\"Uniwersytet Wroclawski
Wydzial Matematyki i Informatyki\");
zoomOut();
...
}
}
Powyższy kod prezentuje na mapie wybrane
uczelnie kształcące informatyków na południu Polski.
Pomiędzy poszczególnymi miastami i uczelniami
następuje płynne przejście. W każdym mieście
realizowane jest przybliżenie do widoku uczelni,
a następnie powrót do mapy w mniejszej skali.
Aby możliwe były animacje przejść na większych
obszarach, konieczne będzie spore oddalenie
oraz odpowiednio duża mapa (aby poszczególne
miejscowości znajdowały się w widocznej jej części
lub na obrzeżach). Dlatego pamiętaj, aby powiększyć
warstwę z mapą. Rozmiar 1000 x 500 pikseli
powinien wystarczyć:
Animowane przejścia można wykorzystać do
tworzenia ciekawych prezentacji. Na niektórych
przeglądarkach Google Maps umożliwia również
płynne skalowanie zoomu. Niestety częściej zauważalny
jest mniej ciekawy efekt \”skoku\” zoomu.
Kluczowym fragmentem funkcji {stala}drawCircle(){/stala}
jest wyliczenie współrzędnych punktów leżących
na okręgu o zadanym promieniu. Pętla zapewnia
równomierne odnalezienie punktów na całym
okręgu, ze skokiem 10 stopni. Łącznie wyliczonych
zostanie 36 punktów. Powinno to wystarczyć do
otrzymania płynnego okręgu, choć przy bardzo
dużych promieniach lub dużych zoomach może być
konieczne dodanie kolejnych punktów.
Samo obliczanie współrzędnych punktów
następuje na podstawie własności funkcji sinus
oraz cosinus – są to proste obliczenia trygonometryczne.
Sposób wywołania:
showAddress(\"Gdansk, PL\", 10);
drawCircle(10000);
Wywołana w ten sposób funkcja {stala}drawCircle{/stala} narysuje
okrąg z wypełnieniem, którego promień jest
równy 10 km, a środek znajduje się w Gdańsku.
Ponieważ do wyznaczania punktów użyte zostały
współrzędne geograficzne, narysowany okrąg jest
niezależny od stopnia przybliżenia mapy. Możesz
zwiększać lub zmniejszać zoom – za każdym razem
Google Maps dokona odpowiedniego przeliczenia
punktów.
Wytyczanie trasy
Od niedawna Google posiada zaimplementowane
również mechanizmy znane dotąd
z systemów nawigacyjnych. Może wyznaczyć trasę
samochodową pomiędzy podanymi lokalizacjami.
To bardzo ciekawa możliwość, którą możesz szeroko
zarządzać z poziomu Google Maps API. Marzy ci
się własne zumi.pl? Żaden problem!
Rozpocznijmy od wytyczenia najprostszej trasy.
Zanim jednak przejdziemy do tworzenia kodu Java-
Script, konieczne będzie utworzenie w sekcji BODY
strony dodatkowej warstwy, do której Google
wstawi tekstowy opis trasy:
Czas zacząć korzystać z Google Maps API.
Stwórz funkcję pomocniczą {stala}makeRoute{/stala}, która
będzie przyjmować dwa parametry: łańcuchy
z adresem startowym i docelowym.
var map = null;
function load() {
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById(\"map\"));
geocoder = new GClientGeocoder();
makeRoute(\'Dworzec Centralny, Warszawa, Poland\', \'Uniwersytet Warszawski, Warszawa, Poland\');
}
}
function makeRoute(fromAddress, toAddress){
directions = new GDirections(map, document.getElementById(\"directions\"));
directions.load(\"from: \" + fromAddress + \" to: \" + toAddress)
}
Powyższy przykład sprawi, że wytyczona
zostanie trasa z Dworca Centralnego w Warszawie
na kampus Uniwersytetu Warszawskiego. Wynik
zostanie wyświetlony na mapie jako graficznie wyróżniona
ścieżka, a także w nowej warstwie, gdzie
znajdzie się szczegółowy opis tekstowy trasy.
Zasada działania skryptu jest bardzo prosta.
Stworzona funkcja makeRoute wykonuje zaledwie
dwie instrukcje. W pierwszej tworzona jest instancja
klasy GDirections, która będzie odpowiadać
za wytyczanie trasy. Jako parametry konstruktora
przekazywana jest referencja do obiektu mapy
oraz opcjonalna referencja do obiektu, w którym
zostanie umieszczony tekst ze szczegółami trasy.
Obiektem tym jest zwykle warstwa DIV.
Następnie wykonana zostaje metoda load()
dopiero co stworzonego obiektu. Jako parametr
należy przekazać specjalnie sformatowany łańcuch
zapytania. Stworzysz go, trzymając się wzorca:
from: @adres_zrodlowy
to: @adres_docelowy
Zamiast {stala}@adres_zrodlowy{/stala} oraz {stala}@adres{/stala}
docelowy należy wstawić odpowiednie dane do
geolokalizacji (np. adres).
Jeśli chcesz wytyczyć trasę, która będzie
przechodzić przez punkty pośrednie, możesz to
zrobić poprzez dodawanie kolejnych punktów
docelowych:
from: @adres_zrodlowy to: @adres_posredni
to: @adres_docelowy
W tym miejscu należy zwrócić uwagę na fakt,
że parser adresów przy mechanizmie wytyczania
trasy jest dużo bardziej restrykcyjny niż standardowy
parser, używany w poprzednim artykule do odnajdywania
miejsc na mapie. Nie obsługuje on chociażby
skrótu PL jako nazwy kraju. Konieczne będzie podanie
pełnego angielskiego rozwinięcia, np.:
{stala}Wiertnicza 166, Warszawa, Poland{/stala}
System Google może również nie odnaleźć
wielu skróconych nazw, które z powodzeniem
można było podawać przy wyszukiwaniu miejsca
na mapie. Sytuacja ta ma najprawdopodobniej
charakter przejściowy i będzie dopracowywana.
Jeśli jednak chcesz uniknąć tej metody, możesz
zastosować alternatywny sposób wytyczania trasy,
z wykorzystaniem metody {stala}loadFromWaypoints(){/stala}.
Oto przykład:
function makeRoute(fromAddress, toAddress){
directions = new GDirections(map,document.getElementById(\"directions\"));
waypoints = Array(fromAddress,toAddress);
directions.loadFromWaypoints(waypoints);
}
Do metody {stala}loadFromWaypoints(){/stala} przekazujesz
tablicę punktów, przy czym mogą one być przekazywane
bądź jako współrzędne geograficzne, bądź
jako standardowy adres, w notacji analogicznej do
zwykłego wyszukiwania na mapie.
Na koniec dodamy jeszcze, że wywołując
metody {stala}load(){/stala} oraz {stala}loadFromWaypoints(){/stala}, możesz
przekazać dodatkowy parametr – obiekt {stala}GDirectionsOptions{/stala}.
Najważniejszą rzeczą, którą możesz
w ten sposób ustawić, to język używany przy tekstowych
opisach trasy. Domyślnym jest oczywiście
angielski, ale w prosty sposób możesz spróbować
go zmienić na francuski:
directions.load(\"from: \" + fromAddress + \" to: \" + toAddress, { \"locale\": \"fr\" })
Aktualnie nie jest dostępny język polski. Najpewniej
jednak jest to tylko kwestia czasu, dlatego
warto zapamiętać sposób zmiany języka.
Analizowanie danych z obsługą zdarzeń
Po wytyczeniu trasy możesz \”wyciągać\”
z obiektu {stala}Gdirections{/stala} wiele dodatkowych informacji.
Poniżej znajdziesz kod, dzięki któremu otrzymasz
informację o całkowitym dystansie trasy.
function makeRoute(fromAddress, toAddress) {
directions = new Gdirections(map, document.getElementById(\'directions\'));
GEvent.addListener(directions, \"load\",onGDirectionsLoad);
waypoints = Array(fromAddress,toAddress);
directions.loadFromWaypoints(waypoints);
}
function onGDirectionsLoad() {
alert(\"Dystans w metrach: \" + directions. getDistance().meters);
}
Zwraca uwagę nietypowa konstrukcja tej funkcji.
Korzystając z metody {stala}addListener(){/stala}, dodaliśmy
obsługę zdarzenia \”load\” dla obiektu directions.
Jest to konieczne, ponieważ klasa GDirections
jest asynchroniczna, podobnie jak sama metoda
geolokalizacji opisywana szerzej na początku
artykułu. Dzięki dodaniu obsługi zdarzenia \”load\”,
po pomyślnym załadowaniu mapy zostanie wywołana
wskazana funkcja – {stala}onGDirectionsLoad(){/stala}.
Ona z kolei wykona dodatkowe operacje, które
przed zakończeniem operacji wyznaczania trasy nie
miałyby sensu.
W pokazanym przykładzie wyświetlona zostanie
w oknie dialogowych długość trasy w metrach.
Nie można jednak zakładać, że wykonywanie
operacji zawsze zakończy się sukcesem. Istnieje
cały szereg czynników, które mogą stanąć temu na
przeszkodzie, począwszy od problemów z połączeniem
z serwerem, na błędnie wpisanym adresie
skończywszy. Jednak i z tym łatwo możesz sobie
poradzić, dodając obsługę zdarzenia error.
function makeRoute(fromAddress, toAddress) {
...
GEvent.addListener(directions, \"error\",onGDirectionsError);
...
}
function onGDirectionsError() {
if (directions.getStatus().code == G_GEO_UNKNOWN_ADDRESS)
alert(\"Adres niepoprawny\");
else if (directions.getStatus().code ==G_GEO_SERVER_ERROR)
alert(\"Problem połączenia z serwerem\");
else
alert(\"Niezidentyfikowany błąd\");
}
W trakcie obsługi błędów dokonujemy sprawdzenia,
jaki jest status operacji, co po dokonaniu
porównania z popularnymi kodami błędów pozwala
wyświetlić adekwatny komunikat. Popularnych
błędów jest więcej, możesz je znaleźć w dokumentacji
na stronach Google. W przykładzie znalazły
się przykłady dwóch najczęstszych.
Analogicznie do sposobu pobrania danych
o dystansie trasy, możesz również odczytać przewidywany
czas jej przejechania:
function onGDirectionsLoad(){
alert(\"Trase przejedziesz w czasie: \" + directions.getDuration().seconds + \"sekund\");
}
Rzecz jasna, przelicznik czasowy należy traktować
jako orientacyjny, szczególnie w warunkach
miejskich. Zerknij także do dodatkowych ramek,
gdzie prezentujemy sposób wykorzystania obliczeń
dystansu w praktyce.
Rozłożenie trasy na czynniki pierwsze
Oprócz ogólnych danych, po wyznaczeniu trasy
możemy również otrzymać z systemu Google bardzo
precyzyjne informacje dla każdego kolejnego
punktu trasy. Pomocne są tutaj dwie dostępne
klasy:
- {stala}GRoute{/stala} – prezentuje szczegóły odcinków pomiędzy
kolejnymi zadanymi punktami kluczowymi
naszej trasy, które zostały podane podczas jej
wytyczania. W najprostszym przypadku podajesz
tylko dwa punkty: początkowy i końcowy.
Wówczas trasa składać się będzie z jednego
odcinka GRoute, równoznacznego z jej całością. - {stala}GStep{/stala} – oznacza kolejne elementy opisowe
wchodzące w skład danego odcinka GRoute,
czyli zasadnicze zmiany w trasie. Jeśli odcinek
zaczyna się na dworcu kolejowym, a kończy
przy centrum handlowym, zbiór obiektów GStep
może informować, że należy kolejno: jechać proste,
skręcić w prawo, następnie w lewo i znów
3 km prosto.
Po co to wszystko? Faktem jest, że dane te są
wyświetlane domyślnie w dodatkowej warstwie i w najprostszych sytuacjach nie trzeba dokonywać
dodatkowej ich obróbki Czasem jednak istnieje
potrzeba przetworzenia trasy krok po kroku we
własnym zakresie.
Poniżej znajdziesz przykład wykorzystania klas
{stala}GRoute{/stala} i {stala}GStep{/stala}. Dokonamy analizy odnalezionej
trasy i sprawdzimy, który odcinek trasy jako
pierwszy przekracza dystans 5 km w linii prostej od
punktu startowego.
AdSense w Google Maps
Funkcja, która w tym miejscu zostanie umówiona,
nie jest jeszcze dostępna dla naszego kraju.
Ponieważ jednak jest to tylko kwestią czasu, nie
można przemilczeć tak ciekawego aspektu sterowania
Google Maps z poziomu API.
Chodzi o reklamy kontekstowe AdSense, które
od kilku miesięcy zyskały nową funkcjonalność.
Przy reklamach lokalnych można podać precyzyjne
dane teleadresowe firmy, co umożliwia naniesienie
takiej reklamy na mapę kartograficzną Google
Maps. Oczywiście tylko wtedy, gdy serwis, na
którym uruchomiona jest taka mapa, dopuścił
możliwość prezentacji reklam.
Reklama prezentowana jest jako \”dymek\”
w odpowiednim miejscu mapy. Dymek zawiera
krótki opis firmy, może zawierać zdjęcie lub logo.
Od każdego kliknięcia webmaster otrzymuje gratyfikację.
W jaki sposób działa to w praktyce? Zarządzanie reklamami na mapach odbywa się
dzięki klasie GAdsManager. Konstruktor tej klasy
przyjmuje dwa obowiązkowe parametry – referencję
do mapy oraz łańcuch będący identyfikatorem
AdSense. Oto przykład:
ads = new GAdsManager(map, \"pub-00000000000\");
Można również przekazać kilka dodatkowych opcji:
- {stala}maxAdsOnMap{/stala} – maksymalna liczba reklam
widoczna w danym momencie na mapie (domyślna
wartość wynosi 3) - {stala}channel{/stala} – określenie opcjonalnego kanału
spersonalizowanego – mechanizmu Google
AdSense stworzonego w celu badania skuteczności
określonej ekspozycji reklamy. Analogiczne
z wartością \”google_ad_channel\” w standardowym
kodzie AdSense. - {stala}minZoomLevel{/stala} – najmniejsza wartość przybliżenia,
dla której będą wyświetlane reklamy
(domyślnie jest to 6).
Przykład użycia opcji dodatkowych:
ads = new GAdsManager(map, \"pub-00000000000\", {maxAdsOnMap: 5});
Wypada mieć tylko nadzieję, że reklamy lokalne
Google Maps już wkrótce pojawią się także w Polsce.
Własne mapy
Google udostępnia szeroką możliwość nakładania
własnych warstw na mapę. W ten sposób
możesz tworzyć nowe typy map i tym samym rozszerzać standardowo dostępne: mapę drogową,
satelitarną oraz hybrydową. Najciekawsze jest to,
że otrzymujesz dzięki temu cały potężny mechanizm
Google Maps wraz z pełną jego funkcjonalnością.
We własnym zakresie zajmujesz się jedynie
dostarczeniem plików graficznych z mapą.
Możliwości wykorzystania tego mechanizmu
są praktycznie nieograniczone. Własne warstwy
czy całe mapy mogą prezentować na przykład
wygląd danego miejsca sprzed kilkudziesięciu lat
(mapy archiwalne), zdjęcia lotnicze, prezentacje
sieci ciepłowniczej w danym miejscu, warstwy
z zaznaczeniem rozmaitych danych geologicznych,
geograficznych czy statystycznych oraz wiele, wiele
innych inicjatyw. Zakres możliwości ogranicza
niemal wyłącznie własna wyobraźnia.
Praktycznym przykładem takiej mapy może być
plan nowojorskiego metra, dostępny pod adresem
http://www.onnyturf.com/subway/. Wchodząc na tę
stronę zauważysz, że plan metra ma swoją specjalną
ikonkę, przełączającą do trybu mapy \”Subway\”.
Użytkownik może przejść do innych wariantów mapy,
choć jak wiesz już z poprzedniego artykułu, można
zablokować wyświetlanie określonych kontrolek.
Nie będziemy w tym miejscu precyzyjnie opisywać,
w jaki sposób tworzyć własne mapy, wymaga
to bowiem znacznie więcej pracy w programie graficznym,
aniżeli w samym Google Maps. Skoncentrujemy
się jednak na przedstawieniu zasadniczej
idei takiej operacji.
Rozpoczynasz od prac przygotowawczych.
Tworzysz obiekty klas {stala}GCopyright{/stala} (definiujesz tutaj
licencję twojej mapy), GTileLayer (nowa warstwa
w Google Maps – puste miejsce do wstawiania
własnej grafiki) oraz {stala}GMapType{/stala} (łącznik warstwy
z interfejsem użytkownika). Na koniec dołączasz
warstwę do swojej mapy za pomocą metody
{stala}addMapType(){/stala}.
Teraz przygotuj swoją mapę i podziel ją na
kwadraty o długości boku 256. Jeśli przewidujesz
możliwość zmiany stopnia przybliżenia, będziesz
musiał przygotować osobną mapkę dla każdego
poziomu przybliżenia i zapisywać każdy jako
zbiór kwadratów 256×256. Dopuszczalny format
pliku to PNG, JPG lub GIF. Zaletą PNG jest obsługa
przezroczystości.
Co ważne, nie musisz zapełniać grafiką całego
obszaru oraz każdego poziomu przybliżenia. Jeśli
określony region nie zostanie odnaleziony, nie
zostanie dla niego wyświetlona mapa.
Istotne będzie nazewnictwo plików. Każdy
obrazek w Google Maps ma niejako swój numer
porządkowy. Tworzy się go poprzez określenie jego
kolejności względem lewego górnego rogu mapy za
pomocą dwóch współrzędnych: x oraz y. Wszystko
to odbywa się odrębnie dla każdego poziomu zoomu.
A więc obrazek o współrzędnych (1000, 765)
będzie znajdować się na lewo od obrazka (1001,
765). Po odpowiednim nazwaniu plików będziesz
jeszcze musiał zdefiniować funkcję, która będzie
zamieniać odpowiednie współrzędne x, y, zoom na
adres URL do twoich grafik. Jest to bardzo proste
i zapewnia pełną uniwersalność rozwiązania.
Najtrudniejszym zadaniem jest kalibracja
swoich obrazów z systemem Google Maps.
Pomocne może być do tego narzędzie dostępne
pod adresem http://open.atlas.free.fr/GMapsTransparenciesImgOver.php , które pozwala wskazać
współrzędne geograficzne interesującego nas
miejsca na mapie, następnie załadować własną
warstwę i precyzyjnie ją dokalibrować.
Pomaga w tym fakt, że załadowana warstwa będzie nieco
przezroczysta, co pozwoli precyzyjnie nałożyć ją
na właściwe miejsce na mapie Google. Narzędzie
to pozwala także dokonywać drobnych korekt
w wielkości warstwy.
Istnieje też kilka innych pomysłów na to, jak
rozwiązać problem kalibracji. Znajdziesz je, podobnie
jak precyzyjne porady z zakresu tworzenia
własnych map, w serwisie http://mapki.com/. Jest
to serwis typu Wiki, poświęcony wymianie wiedzy
na temat Google Maps.
Google Maps wciąż się rozwija…
Bez wątpienia Google wiąże ogromne nadzieje
ze swoim systemem kartograficznym. Programiści
tej kalifornijskiej firmy bardzo wiele uwagi poświęcają
rozwijaniu systemu Google Maps. Wystarczy
wspomnieć, że aż kilka z prezentowanych w tym
artykule nowinek został wprowadzonych w ostatnich
miesiącach.
Można spodziewać się więc, że ilość funkcji
będzie się zwiększać. Warto trzymać rękę na pulsie.
Pomocna w tym zakresie może okazać się szczególnie
strona developerska dla programistów korzystających
z systemów Google: http://code.google.com/.
Gdy przejrzysz dokumentację Google Maps API,
odnajdziesz tam jeszcze wiele ciekawych opcji,
które nie sposób było drobiazgowo omówić w tym
miejscu. Mamy jednak nadzieję, że po lekturze
tego artykułu wiesz już, jak rozpocząć swoją przygodę
z Google Maps.
Zanim jednak zaczniemy, zmodyfikujemy
funkcję {stala}makeRoute{/stala}, aby przekazywać do niej zbiór
większej liczby punktów (chcemy bowiem, by trasa
mogła mieć wiele punktów pośrednich):
function makeRoute(waypoints) {
directions = new Gdirections(map, document.getElementById(\'directions\'));
GEvent.addListener(directions, \"load\", onGDirectionsLoad);
GEvent.addListener(directions, \"error\",onGDirectionsError);
directions.loadFromWaypoints(waypoints);
}
Sposób wywołania:
makeRoute(Array(\'Dworzec Centralny,Warszawa, PL\', \'Czerska 8, Warszawa, PL\',\'Wiertnicza 166, Warszawa, PL\'));
Teraz wprowadzimy nową funkcjonalność do
funkcji obsługującej zdarzenie \”load\”.
function onGDirectionsLoad(){
startPoint = directions.getRoute(0).getStep(0).getLatLng();
alert(startPoint);
}
szukaj:
for (i = 0; i < directions.getNumRoutes(); i++) {
for (j = 0; j < directions.getRoute(i).getNumSteps(); j++) {
point = directions.getRoute(i).getStep(j).getLatLng();
if (point.distanceFrom(startPoint) >= 5000) {
alert(point.distanceFrom(startPoint) +\" \" + directions.getRoute(i).getStep(j).getDescriptionHtml());
break szukaj;
}
}
alert(i);
}
Czas na krótką analizę powyższego kodu.
Metoda {stala}getNumRoutes(){/stala} klasy {stala}GDirections{/stala}
dostarcza informację o tym, z ilu odcinków składa
się wytyczona trasa (w przypadku powodzenia
operacji powinna być to liczba o jeden mniejsza
od liczby zadanych punktów). Korzystamy z niej
w celu określenia warunku wyjściowego pierwszej
(zewnętrznej) pętli.
Klasa {stala}GDirections{/stala} posiada również metodę
{stala}getRoute(){/stala}, która jako parametr przyjmuje kolejny
numer odcinka, licząc od 0. Zwraca ona odpowiadający
danemu odcinkowi obiekt klasy {stala}GRoute{/stala}. Ta
z kolei posiada funkcję {stala}getNumSteps(){/stala}, która zwraca
liczbę kroków przejścia z początku do końca
odcinka. Wykorzystujemy tę wartość do określenia
warunku wyjściowego drugiej (wewnętrznej) pętli.
W ten sposób zapewniliśmy sobie przejście
przez każdy krok każdego odcinka trasy. Sprawdzamy
więc każdy kolejny krok z założeniem
początkowym (odległość 5 km w linii prostej od
punktu startu trasy). Metoda {stala}getLatLng(){/stala} klasy
GStep zwraca obiekt klasy {stala}GLatLng{/stala}, czyli w istocie
odwzorowanie współrzędnych punktu na Ziemi
– dla miejsca początkowego danego kroku.
Możemy teraz wykorzystać opisywaną we
wcześniejszej części artykułu metodę {stala}distanceFrom(){/stala} klasy {stala}GLatLng{/stala}. Jeśli otrzymany dystans
w metrach jest większy lub równy 5000, oznacza
to, że osiągnęliśmy poszukiwany krok. Wyświetlamy
okno dialogowe z wyliczonym dystansem
oraz opisem danego punktu. Opis ten w postaci
sformatowanej jest zwracany przez metodę {stala}getDescriptionHtml(){/stala}
klasy {stala}GStep{/stala}.
Tu nasz algorytm się kończy. Zwróć jeszcze
uwagę na ciekawe zastosowanie wyrażenia break.
Służy ono standardowo do wyjścia z jednej, najbardziej
wewnętrznej pętli. Ponieważ jednak wcześniej
nadaliśmy etykietę blokowi pętli zewnętrznej,
to podając tę etykietę przy funkcji break, osiągamy
efekt wyjścia z obu pętli jednocześnie. Koncepcja
etykietowania jest tu nieco podobna do stosowanej
w języku Basic.
Jak widzisz, Google Maps API udostępnia
potężne możliwości w zakresie wytyczania tras
w swoim systemie geolokalizacyjnym. Dla większości
polskich miast wprowadzono szeroki zakres
informacji o adresach. System uwzględnia nawet
dane o ulicach jednokierunkowych.
Co ciekawe, ponieważ odnajdywanie punktu
może zostać przeprowadzone nie tylko dla
zadanego adresu w postaci nazwy ulicy i numeru
domu, ale także przy podaniu bardzo precyzyjnych
współrzędnych geograficznych, nie ma przeciwskazań
do zintegrowania Google Maps z praktycznie
dowolnym systemem GPS. Jeśli tylko masz
możliwość nawiązania w urządzeniu przenośnym
odpowiednio stabilnego połączenia z internetem,
możesz pokusić się o stworzenie własnego systemu
nawigacji samochodowej.
Oznaczanie obszarów kolorem
Google Maps API jest wyposażona w możliwość
oznaczania pewnych fragmentów obszaru
kolorowymi liniami oraz wypełnieniem. Cel taki
osiągniesz poprzez użycie klasy {stala}GPolyline{/stala} (tworzy
obwódkę) lub {stala}GPolygon{/stala} (dodatkowo wypełnia
obwódkę kolorem).
Za pomocą obu tych klas możesz tworzyć
dowolne kształty. Odbywa się to poprzez łączenie
wszystkich podanych punktów – obiektów klasy
{stala}GLatLng{/stala}, przekazywanych do obiektu za pośrednictwem
konstruktora. Oto jak to działa:
// Rysuje tylko obwódkę
map.addOverlay(new GPolyline(points, \'#FD942D\', 4, 1));
Powyższy przykład spowoduje dodanie
warstwy złożonej z połączonych ze sobą punków
(tablica points). Utworzona na ich podstawie obwódka
będzie mieć kolor \”#FD942D\”, szerokość 4
piksele oraz będzie nieprzejrzysta. Możesz ustawić
określoną wartość przezroczystości z zakresu od 0
do 1 (0 – obwódka niewidoczna, 1 – całkowicie nieprzejrzysta).
// Rysuje obwódkę z wypełnieniem
map.addOverlay(new GPolygon(points, \'#FD942D\', 4, 1, \"#FFFF00\", 0.2));
W powyższym przypadku tworzona jest
instancja klasy {stala}GPolygon{/stala}, której przekazywane
są dwa dodatkowe parametry: kolor wypełnienia
(\”#FFFFFF\”) oraz stopień przezroczystości tegoż
wypełnienia (0.2).
Poniżej znajduje się przykład praktyczny.
Tworzymy wielokąt, którego wierzchołki stanowią
kilka polskich miast. Zostaną one połączone linią
z dodanym wypełnieniem.
var points = [];
function load() {
...
showAddress(\"Polska, PL\");
addPoint(\"Gdansk, PL\");
addPoint(\"Warszawa, PL\");
addPoint(\"Krakow, PL\");
addPoint(\"Wroclaw, PL\");
addPoint(\"Poznan, PL\");
addPoint(\"Gdansk, PL\");
window.setTimeout(function() {map.addOverlay(new GPolygon(points, \'#FD942D\', 4, 1, \"#FFFF00\", 0.2));}, 3000);
...
}
function addPoint(address) {
geocoder.getLatLng(address,function (point) {points.push(point);});
}
Funkcja {stala}addPoint(){/stala} dokonuje geolokalizacji określonego
adresu (miasta), a następnie dodaje odnaleziony
punkt do tablicy points. Następnie w ramach
funkcji {stala}load(){/stala} tworzony jest obiekt klasy {stala}GPolygon{/stala},
korzystający z points. Zwróć uwagę, że warstwa nanoszona
jest po odczekaniu 3 sekund (odpowiada za
to metoda {stala}setTimeout(){/stala}).
Musisz pamiętać, że geolokalizacja
jest czynnością wykonywaną asynchronicznie,
w niezależnym wątku – program wykonuje
go równolegle, nie wstrzymując działania głównego
skryptu do czasu zakończenia wątku pobocznego.
Co za tym idzie – musisz dać systemowi odrobinę
czasu na połączenie się z serwerami, opracowanie
danych oraz zapisanie ich do tablicy.
W dalszej części artykułu poznasz lepszy
sposób radzenia sobie z problemem asynchroniczności,
korzystając ze zdarzeń.
Rysowanie okręgów
Narzędzia {stala}GPolyline{/stala} oraz {stala}GPolygon{/stala} pozwalają
również tworzyć bardziej ambitne rzeczy. W kolejnym
przykładzie zobaczysz w jaki sposób można
zastosować funkcje matematyczne do narysowania
okręgu z wypełnieniem. Stworzymy funkcję {stala}drawCircle(){/stala}, która będzie rysować koło o promieniu
wynoszącym zadaną liczbę metrów.
Na wstępie jednak znów konieczne jest napisanie
funkcji pomocniczych. Potrzebujemy bowiem
funkcji, które przeliczą podany dystans w metrach
na wartość w stopniach w odniesieniu do długości
i szerokości geograficznej. Jest to konieczne,
ponieważ to te wartości są podstawową jednostką
obliczeniową w Google Maps. Zadanie to realizują
poniższe dwie funkcje:
function getRadiusInLat(distance) {
var point = map.getCenter();
var pointTmp = new GLatLng(point.lat()+1,point.lng());
var distanceTmp = point.distanceFrom(pointTmp);
return distance * 1 / distanceTmp;
}
function getRadiusInLng(distance) {
var point = map.getCenter();
var pointTmp = new GLatLng(point.lat(),point.lng() + 1);
var distanceTmp = point.distanceFrom(pointTmp);
return distance * 1 / distanceTmp;
}
Obie funkcje są bardzo podobne, a więc ograniczę
się do omówienia jedynie pierwszej z nich.
Funkcja {stala}getRadiusInLat(){/stala} przyjmuje jako parametr
długość promienia okręgu w metrach. Oczekujemy,
że zwróci wielkość promienia w przeliczeniu na
różnice szerokości geograficznej w danym miejscu
Ziemi. Co ważne, z uwagi na kształt naszego globu
jest to wartość różniąca się w różnych zakątkach
świata, stąd nie można przypisać jej na stałe.
Klasa GLatLng udostępnia metodę {stala}distanceFrom(){/stala},
która oblicza odległość w metrach
pomiędzy punktem symbolizowanym przez obiekt
tej klasy a innym punktem podanym w parametrze.
Nie ma wprost funkcji odwrotnej, ale możesz sobie
z tym problemem poradzić, używając proporcji.
W powyższym przykładzie obliczamy, ile wynosi
dystans w metrach do punktu, którego szerokość
geograficzna została przesunięta o jeden stopień.
Używając proporcji, można łatwo obliczyć, o jaką
część stopnia po szerokości geograficznej przesuniemy
się, pokonując dystans jednego kilometra.
Oczywiście będzie to wartość przybliżona, gubiąca
dokładność na większych obszarach.
Otrzymane z obu funkcji długości promieni,
wyrażone w stopniach po długości i szerokości
geograficznej, mogą zostać następnie wykorzystane
w funkcji {stala}drawCircle(){/stala}.
function drawCircle(distance){
// Promien rysujemy od srodka mapy
var point = map.getCenter();
// Zapamietaj wielkosc promieni
var radiusLat = getRadiusInLat(distance);
var radiusLng = getRadiusInLng(distance);
// Utworz pusta tablice punktow
var circlePoints = [];
// Korzystaj z klasy
Math.with (Math) {
for (var a = 0 ; a < 361 ; a += 10 ) {
// Przelicz stopnie <0; 360> na radiany <0; 2PI>
var rad = a*(PI/180);
// Szerokosc geograficzna punktu na okregu
lat = point.lat() + radiusLat * sin(rad);
// Dlugosc geograficzna punktu na okregu lng = point.lng() + radiusLng *cos(rad);
// Dodaj punkt do tablicy
circlePoints.push(new GLatLng(lat,lng));
}
}
// Rysuj kolo i dodaj warstwe
circleLine = new GPolygon(circlePoints,\'#FD942D\',4,1,\'#FFFF00\',0.2);
map.addOverlay(circleLine);
}