Indianin na szlaku, czyli o wykorzystaniu Google Maps z ...
Transcript of Indianin na szlaku, czyli o wykorzystaniu Google Maps z ...
Indianin na szlaku, czyli o wykorzystaniu Google Maps z Apache
Wicket słów kilka. Autor: Grzegorz Musiał
1. Wprowadzenie
W czasach kiedy samochód pozbawiony nawigacji GPS jest już odosobnionym wyjątkiem
nawet na polskich drogach. W czasach gdy człowiek zapomina jak korzystać z map
tradycyjnych. I wreszcie w czasach, w których nieodpłatnie udostępniane są
interaktywne mapy, nie sposób stworzyć firmowej strony internetowej, która nie
zaznacza dynamicznie adresu siedziby. Podążając tym tokiem rozumowania należy
stwierdzić, że zatem nie sposób się obejść bez map, a dokładnie omawianych w
niniejszym dokumencie map dostarczanych przez firmę Google, w większości
profesjonalnych (firmowych) zastosowań aplikacji webowych.
Zatem o ile zastosowanie wspomnianego gotowego rozwiązania na statycznych stronach
HTML z wykorzystaniem dostarczonego kodu Java Script, nie nastręcza żadnych
problemów. O tyle zastosowanie rozwiązania Google’a niestety wymaga większych i
bardziej skomplikowanych zabiegów w przypadku chęci zastosowania jednym z
najmłodszych dzieci w rodzinie frameworków Java EE – Apache Wicket. Całe szczęście z
pomocą przychodzi nam szerokie grono internautów i pasjonatów programowania,
które na stronie [1] udostępniło gotową bibliotekę implementującą Java Scriptowy kod
Google Maps. Biblioteka ta nosi nazwę wicket-contrib-gmap2 i opiera się na co prawda
nie najnowszym kodzie, bo w wersji drugiej (obecnie jest wersja nr 3). Jednakże z uwagi
na szerokie stosowanie starszej wersji na większości serwisów WWW, wersja trzecia
dopiero zyskuje sobie zwolenników i zapewne niedługo doczekamy się również wicket-
contrib-gmap3. Póki co, skupimy się na dostępnej na wspomnianej stronie, aktualnej
wersji.
2. Przygotowanie środowiska
2.1. Uzyskanie klucza
W celu używania Google Maps API poza serwerem lokalnym, tj. http://localhost/ ,
konieczne jest uzyskanie klucza, który podawany jest w momencie tworzenia obiektu
mapy.
Aby uzyskać wspomniany klucz, należy wejść na stronę
http://code.google.com/intl/pl/apis/maps/documentation/ , na której w prawym
górnym rogu wybieramy opcję Signup for an API key.
Następnie na wyświetlonej stronie, na samym dole wypełniamy następujący formularz:
Wykonujemy:
• Zaznaczamy checkboxa oznaczającego, że zapoznaliśmy się z warunkami
korzystania z biblioteki,
• Wpisujemy adres naszej strony. Uwaga: warto wpisywać najwyższy poziom
posiadanej domeny, tj. bez WWW, ani pozostałych przedrostków. Zastosowanie
domeny typu http://jakasdomena.pl pozwala później korzystać z tego samego
klucza zarówno na stronie http://poddomena.jakasdomena.pl jak i jakiejkolwiek
innej subdomenie podanej domeny.
Po wysłaniu formularza otrzymujemy w odpowiedzi poniższą stronę:
Widzimy na samej górze wygenerowany API Key i tylko on nas interesuje. Dostępne
poniżej przykłady w Java Script’cie nie są użyteczne z punktu widzenia Wicketa.
2.2. Zainstalowanie wicket-contrib-gmap2 w repozytorium Maven’a
Przed przystąpieniem do napisania pierwszego kodu wykorzystującego Mapy Google,
należy zainstalować wspomnianą bibliotekę w repozytorium Mavena. W tym celu ze
strony [1] pobieramy z SVN projektu najnowszą wersją biblioteki (na dzień 10.01.2010r.
1.4.0). W folderze do którego chekout’ujemy pobrane pliki, wchodzimy do folderu
gmap2-parent a następnie do folderu gmap2. Z linii komend uruchamiamy następnie:
mvn install
Po otrzymaniu komunikatu BUILD SUCCESSFUL możemy przystąpić do napisania
pierwszego kodu.
Zanim jednak kod JAVY musimy jeszcze dopisać poniższy kod w sekcji <dependencies>
pliku POM.xml naszego projektu:
<dependency>
<groupId>org.wicketstuff</groupId>
<artifactId>gmap2</artifactId>
<version>1.4-SNAPSHOT</version>
</dependency>
3. Wykorzystanie Google Maps w kodzie aplikacji
3.1. Umieszczenie kontrolki mapy na stronie
Wykorzystywana biblioteka zawiera w sobie definicje klasy GMap2. Klasa ta
charakteryzuje się tym, że dziedziczy z klasy nadrzędnej Component, dzięki temu jej
zachowanie w przypadku umieszczania na stronie jest analogicznie do znanych
komponentów, np. Label. Wykorzystując tę analogię oraz pusty projekt utworzony za
pomocą archetypu dostępnego na stronie [4], przedstawimy proste umieszczenie mapy
na stronie WWW.
W tym celu wyczyścimy kod klasa HomePage.java otrzymując poniższy kod:
Następnie analogicznie pozbędziemy się zbędnego kodu przykładowego z pliku
HomePage.html otrzymując:
Teraz przejdziemy do najciekawszej części, czyli właściwego umieszczania mapy na
stronie.
W tym celu przygotujemy na stronie HTML odpowiedni kontener. Założymy, że
początkowo nasza mapa ma mieć szerokość 800px a wysokość 400px, co
odzwierciedlimy odpowiednim stylem CSS umieszczonym inline w znaczniku <div>,
który zostanie zagnieżdżony bezpośrednio pomiędzy znacznikami <body>.
Następnie, aby w ogóle kompilacja aplikacji zakończyła się sukcesem, należy dodać
odpowiedni komponent w kodzie JAVY w pliku HomePage.java.
Zauważamy, że w porównaniu do poprzedniej wersji tego pliku pojawiły się dwie nowe
linijki.
• W pierwszej z nich utworzony zostaje obiekt mapy. Jest on wspomnianego wcześniej
typu GMap2. W konstruktorze widzimy dwa parametry:
a. Pierwszy: identyfikator komponentu umieszczanego na stronie, tj. dokładnie
to samo co umieszczono wcześniej w atrybucie wicket:id w pliku HTML.
b. Drugi: wygenerowany wcześniej API Key.
• W drugiej linijce natomiast dokonujemy powiązania stworzonej mapy z widokiem
HTML. W tym momencie potwierdzone zostaje wcześniejsze rozszerzanie przez
klase GMap2 klasy Component, gdyż sygnatura metody add jest następująca:
add(Component c).
Tak napisany kod, po wcześniejszym tymczasowym usunięciu klas testowych z aplikacji
archetypowej możemy uruchomić na lokalnym serwerze wykorzystując odpowiedni
goal Maven’owego builda, czyli:
mvn jetty6:run
Oczom naszym ukaże się poniższa strona:
Niestety w tym momencie wyświetlana jest domyślna lokalizacja, a dokładnie Mountain
View, USA. Ale kwestią wyświetlenia żądanego punktu ma mapie zajmiemy się w
kolejnym podpunkcie.
3.2. Zaznaczenie adresu na mapie (współrzędne)
Mając wiedzę i kod stworzony w poprzednim podpunkcie skupimy się teraz na
najbardziej podstawowym dostosowaniu mapy do wymogów klienta i aplikacji, tj.
wyśrodkujemy ją na wybranym przez nas adresie, a pod tym adresem umieścimy
domyślny marker.
Zanim jednak tego dokonamy, w tym podpunkcie będziemy potrzebować konkretnych
współrzędnych punktu na mapie, o którym mowa.
Zatem udamy się na stronę http://maps.google.com i tam w wyszukiwarce podając
Wrocław, Wybrzeże Wyspiańskiego 27 (adres Politechniki Wrocławskiej).
Następnie w calu wyłuskania informacji o dokładnych współrzędnych punktu:
• Klikamy lewym przyciskiem na pokazany marker i z podręcznego menu
wybieramy opcję Wyśrodkuj mapę tutaj.
• Klikamy na umieszczony w prawym górnym roku link Link i kopiujemy jego
wartość.
Skopiowany link powinien mieć postać zbliżoną do poniższej:
http://maps.google.com/maps?f=q&source=s_q&hl=pl&geocode=&q=Wro
c%C5%82aw,+wybrze%C5%BCe+wyspia%C5%84skiego+27&sll=37.0625,-
95.677068&sspn=39.184175,93.076172&ie=UTF8&hq=&hnear=wybrze%C5%
BCe+Stanis%C5%82awa+Wyspia%C5%84skiego+27,+Wroc%C5%82aw,+Dolno%
C5%9Bl%C4%85skie,+Polska&ll=51.107739,17.062175&spn=0.007598,0.
022724&z=16
Fragment, który nas interesuje z punktu poszukiwanych współrzędnych wpisanego
adresu, rozpoczyna się sekwencją „&ll=”, po której nastepują rozdzielone przecinkiem
dwie liczby zmiennoprzecinkowe. Jest to odpowiednio: długość i szerokość geograficzna.
Znając ten punkt, przejdziemy do kodu aplikacji i zrealizowania postawionego zadania.
W podpunkcie tym, modyfikacjom będzie ulegać wyłącznie kod HomePage.java gdyż
działać będziemy na kontrolce mapy, a nie na układzie strony. Po modyfikacjach kod
powinien wyglądać następująco:
Pierwszą modyfikacją kodu w porównaniu do jego wcześniejszej wersji było dodanie
poniższej linijki:
GLatLng wsp = new GLatLng(51.107739, 17.062175);
Odpowiada ona za utworzenie dostępnego również w bazowym Google Maps API
obiektu „punktu na mapie” a dokładnie pary współrzędnych: długości i szerokości
geograficznej. Jak łatwo zauważyć parametry konstruktora tego obiektu, są dokładnie
wyłuskanymi z wcześniejszego linka liczbami.
W kolejnej linijce:
mapa.setCenter(wsp);
Wykorzystując metodę obiektu GMap2 wyśrodkowaliśmy widok na wspomnianym,
utworzonym powyżej punkcie.
Ostatnim krokiem było utworzenie, wymaganego przez naszego klienta, domyślnego
markera znajdującego się również we wspomnianym punkcie WSP.
GMarker marker = new GMarker(wsp);
Ostatnim krokiem wieńczącym dzieło było dodanie utworzonego markera do mapy. Tym
razem warto zauważyć, że dodawanie nie odbywa się bezpośrednio do ciała strony
HTML przez metodę add, a przy użyciu metody obiektu GMap2 addOverlay.
mapa.addOverlay(marker);
Ostateczny efekt pracy wygląda następująco:
Poprzez porównanie do widoku uzyskanego na stronie http://maps.google.com łatwo
zauważyć niedociągnięcia otrzymanej mapy:
• Brak kontroli nad przybliżeniem (sterowania w lewym górnym rogu)
• Zbyt oddalony widok domyślny, tj. ten który użytkownik widzi zaraz po
załadowaniu się mapy.
Aby rozwiązać wspomniane problemy należy wykonać kolejno przedstawione operacje.
Dodanie kontrolki sterującej przybliżenie odbywa się poprzez dodanie następującego
kodu:
mapa.addControl(GControl.GLargeMapControl);
Natomiast przybliżenie mapy najwygodniej dostosować empirycznie wykorzystując
metodę obiekty GMap2 setZoom. W naszym przykładzie zastosujemy:
mapa.setZoom(15);
Ostateczny efekt prac zaprezentowano na poniższym ekranie.
Widzimy zatem jak przy pomocy kilku linii kodu łatwo jest wykonać najbardziej
podstawowe zadanie jakie spoczywa na twórcy strony WWW, czyli umieszczenie mapy z
zaznaczonym adresem siedziby firmy.
Nie spoczniemy jednak na laurach i następnym punkcie przejdziemy do bardziej
zaawansowanego rozwiązania, tj. zaznaczymy siedzibę firmy na mapie wykorzystując do
tego wbudowany w Google Maps API mechanizm wyszukiwania adresów na
podstawie frazy wyszukiwania (tak jak pokazano na stronie http://maps.google.com).
3.3. Zaznaczenie adresu na mapie (fraza wyszukiwania)
Nie zawsze mogąc, a przede wszystkim nie zawsze mając czas na przeglądanie strony
maps.google.com, wyłuskiwanie współrzędnych z linków, poszukamy alternatywnego
rozwiązania zaznaczania współrzędnych. Rozwiązanie to oprzemy na wbudowanym w
Google Maps wyszukiwania po frazie i na jego podstawie uzyskiwanie obiektu
współrzędnych GLatLng.
W niniejszym przykładzie wykorzystamy dostępny wraz z używaną biblioteką wicket-
contrib-gmap2 pakiet wicket-contrib-gmap2-examples, w którym umieszczono kod
klasy ServerGeocoder, która odpowiada za wykonywana przez serwer zapytania o
współrzędne kierowane do serwera Google Maps.
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import org.apache.wicket.util.io.Streams;
import wicket.contrib.gmap.api.GLatLng;
import wicket.contrib.gmap.util.Geocoder;
import wicket.contrib.gmap.util.GeocoderException;
/**
* A serverside Geocoder.
*/
public class ServerGeocoder implements Serializable
{
private static final long serialVersionUID = 1L;
// referencja do geocodera
private Geocoder geocoder;
/**
* @param gMapKey
* Gmap API key
* @throws IllegalArgumentException
* If the API key is <code>null</code>
*/
public ServerGeocoder(String gMapKey) {
if (gMapKey == null) {
throw new IllegalArgumentException("API key
cannot be null");
}
this.geocoder = new Geocoder(gMapKey);
}
/**
* @param address
* The address for which a coordinate must be
found.
* @return GLatLng point for the address found by the
Geocoder
* @throws GeocoderException
* If a error happened on the side of Google
* @throws IOException
* If a connection error happened
*/
public GLatLng findAddress(String address) throws
IOException {
URL url = this.getURL(address);
URLConnection connection = url.openConnection();
String content =
Streams.readString(connection.getInputStream());
return geocoder.decode(content);
}
protected URL getURL(String address) throws
MalformedURLException
{
return new URL(geocoder.encode(address));
}
}
Wykorzystując powyższą klasę zastąpimy dotychczasowe ręczne tworzenie obiektu wsp,
jego automatyczną wersją będącą wywołaniem metody klasy ServerGeocoder.
Główny kod aplikacji znajduję się teraz w bloku try {} catch, co jest podyktowane
koniecznością reagowania na ewentualny brak wyszukiwanego adresu lub też problemy
komunikacyjne z serwerem.
Widzimy zatem w nowej wersji kodu tworzenie obiektu sg typu ServerGeocoder, w
którego konstruktorze podajemy wykorzystywany już wcześniej API key, a następnie już
we wspomnianym bloku wywołujemy metodę owego obiektu – findAddress, której
przekazujemy dokładnie taka samą frazę wyszukiwania, jak na stronie
http://maps.google.com.
Efekt naszych działań przedstawia poniższy ekran.
Porównując nakład pracy wymagany do naniesienia na mapę poszukiwanego przez nas
adresu można by złudnie dojść do wniosku, że drugi pokazany sposób jest tym
najbardziej słusznym ponieważ wymaga najmniej pracy. Jednak jeżeli weźmiemy pod
uwagę każdorazową konieczność wyszukiwania adresu przy każdym załadowaniu mapy,
i to adresu, który dobrze znamy i który raz wprowadzony nie ulega zmianie, to narzut
spowodowany tym wyszukiwaniem może niekiedy rzutować na czas ładowania strony.
Dlatego w przypadkach kiedy chcemy zaznaczyć na mapie punkt, którego położenie nie
będzie ulegać zmianie, to mimo wszystko lepiej zastosować pierwszy sposób.
4. Podsumowanie
Ten krótki tutotial stanowi tylko niewielki pokaz możliwości Google Maps jako
środowiska do pisania aplikacji wykorzystujących mapy, a tym samym również niewielki
wycinek funkcjonalności dostępnych w bibliotece wicket-contrib-gmap2. Dlatego też
zachęca się Czytelnika do dalszego eksperymentowania.
5. Literatura
[1] http://wicketstuff.org/confluence/display/STUFFWIKI/wicket-contrib-gmap2
[2] http://wicket.apache.org/
[3] http://code.google.com/intl/pl/apis/maps/documentation/
[4] http://wicket.apache.org/quickstart.html