Google je (kot po navadi) napisal zelo dober API za upravljanje z zemljevidi, ki pa privzeto ni vključen v SDK in moramo knjižnico maps.jar dodati ročno. Pa si poglejmo, kako to storimo.

V prvem delu male šole smo si namestili Android SDK za različico Androida, za katero želimo razvijati aplikacije, hkrati pa smo namestili še Google API-je. Če kdo tega ni storil, lahko to napravi zdaj. Poženimo program < Android SDK> /tools/android oz. android.bat v Oknih. Odpre se nam že znani program Android SDK Manager, kjer smo namestili ustrezno različico Androida. Jaz sem namestil verzijo Android 2.3.3 (API 10). Različico API (v mojem primeru »10«) si zapomnimo za pozneje. Za prejšnje dele male šole je zadostovalo, da smo imeli nameščen modul SDK Platform, zdaj pa namestimo še Google APIs by Google Inc. Program bo z interneta potegnil dodatne knjižnice in jih shranil v mapo Android SDK-ja.

Namestimo Google APIs in si zapomnimo API-različico Androida

Ko imamo knjižnico na disku, lahko program Android SDK Manager zapremo in odpremo Idejo z našim projektom. Knjižnico v projekt dodamo tako, da kliknemo File > Project Structure in pod Project Settings izberemo Libraries. Nato nad drugim stolpcem kliknemo na »+« (New Project Library) > Java. Zdaj na disku poiščemo knjižnico < Android SDK> /add-ons/addon-google_apis-google_inc_-< API verzija> /libs/maps.java. Ideja nas vpraša, kateremu modulu želimo dodati novo knjižnico. Izberimo naš edini modul in kliknimo »OK«. Knjižnica je zdaj dodana projektu in se bo zapakirala skupaj z aplikacijo. Mi tega ne želimo, saj je knjižnica že na napravi, tako da pod Project settings izberimo Modules > MojMikroAndroid > Dependencies, kjer v razpredelnici vidimo knjižnico maps, ki ima izbran obseg (scope) Compile. Oseg spremenimo v Provided in kliknimo OK. Knjižnica bo zdaj prisotna pri prevajanju aplikacije, ta pa še ne bo vedela, da naj bi jo uporabila, zato ji to kar povejmo. Odprimo datoteko AndroidManifest.xml (lahko uporabimo bližnjico Ctrl+Shift+N in vpišemo ime datoteke) ter znački < application android:label="@string/app_name"> dodajmo podznačko:

< uses-library android:name="com.google.android.maps"/> .

Zdaj bo knjižnica prisotna ob prevajanju aplikacije in tudi ko bo aplikacija zagnana.

Pridobitev ključa

Google ima zaščito pred nenadzorovanim pridobivanjem njihovih zemljevidov, kar izvaja tako, da mora vsak razvijalec, ki hoče njihove zemljevide uporabiti, od njih dobiti edinstveno kodo, s pomočjo katere bo aplikacija lahko pridobila zemljevide, Google pa bo vedel, kdo jih uporablja.

Ključ za zemljevide mora sovpadati s certifikatom, s katerim je podpisana naša aplikacija. Ko aplikacijo naložimo na Play, jo moramo podpisati z lastnim certifikatom, med razvojem pa se uporablja razvijalski certifikat – mi trenutno potrebujemo njegov prstni odtis. Certifikat se nahaja v mapi < uporabnikova domača mapa> /.android/debug.keystore. Odprimo to mapo v terminalu (Linux) oziroma cmd-ju (Windows), nato pa napišimo naslednji ukaz:

keytool -list -alias androiddebugkey -keystore debug.keystore -storepass android -keypass android.

Ta ukaz izpiše prstni odtis certifikata, kar je videti nekako takole: 21:7B:DD:...:E4. Skopirajmo to vrednost v edino vnosno polje na strani http://code.google.com/android/maps-api-signup.html ter pritisnimo Generate API Key. Google nam izpiše ključ, s pomočjo katerega lahko dostopamo do njegovih zemljevidov z našo aplikacijo, hkrati pa nam izpiše še kodo, ki jo lahko uporabimo pri programiranju.

Zdaj je končno vse pripravljeno, da dodamo zemljevide v aplikacijo tako, kot smo dodajali vsebine do zdaj.

Pogled MapView

Z novo knjižnico smo pridobili tudi nov pogled MapView, za katerega lahko sklepamo, da skrbi za prikaz zemljevidov. Pogled je zelo preprost za uporabo, hkrati pa je izjemno prilagodljiv, tako da lahko z njim prikažemo skoraj vse, kar si zaželimo. Najbolje bo, da ga kar dodamo in ugotovimo, kaj nam lahko pokaže.

V Ideji ustvarimo novo datoteko s postavitvijo: res/layout/messages_map.xml in vanjo zapišimo naslednjo kodo (lahko jo prekopiramo s strani s ključem in samo dopišemo malenkosti):

< ?xml version="1.0" encoding="utf-8"?>
< com.google.android.maps.MapView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/messages_map"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:apiKey="< Maps API ključ> "
/>

Ker pogled MapView ni del osnovnega razvojnega okolja, moramo značko MapView zapisati skupaj s paketom com.google.android.maps., dodamo še identifikator, lastnost clickable dodamo, da bomo lahko s kliki uporabljali zemljevide, namesto < Maps API ključ> pa napišemo ključ, ki smo ga prej pridobili na spletni strani.

Zdaj ustvarimo še kodo, ki bo to postavitev uporabila. Znotraj mape src z desnim miškinim gumbom kliknimo na paket com.example in izberimo New > Android Component. Ob Name vpišimo MessagesMapActivity, vrednost Kind nastavimo na Activity, polji Label in Mark as startup activity pa pustimo prazni. Ustvarjen razred je razširitev razreda Activity: extends Activity, za prikaz zemljevidov pa mora razširjati razred MapActivity, zato to kodo zamenjamo z extends MapActivity. Ko to storimo, nam Idea to vrstico rdeče podčrta, ker je zasledila napako. Razred MapActivity ima namreč abstraktne metode, ki jih moramo implementirati. Ko smo s kurzorjem v tej vrstici, pritisnemo kombinacijo tipk Alt+Enter, da dobimo predlagane rešitve, in izberemo Implement methods. Izberemo metodo isRouteDisplayed():boolean in pritisnemo OK. Ustvari se nam metoda, ki vrača false, kar nam ustreza, saj ne bomo prikazovali podatov o poteh.

Zdaj moramo v metodi onCreate določiti postavitev in omogočiti povečevanje in pomanjševanje zemljevida. To storimo z naslednjo kodo:

setContentView(R.layout.messages_map);
MapView mapView = (MapView) findViewById(R.id.messages_map);
mapView.setBuiltInZoomControls(true);

Aktivnost je zdaj pripravljena za prikaz, dodati moramo le še zavihek, da jo bomo lahko videli. To naredimo tako, kot smo v prejšnjem delu dodali zavihka Moji podatki in Regije – odpremo razred MojMikroAndroidActivity in na konec metode onCreate dodamo kodo:

tabHost.addTab(tabHost.newTabSpec("map_tab")
.setIndicator(getString(R.string.map),
getResources().getDrawable(R.drawable.ic_tab_map))
.setContent(new Intent(this, MessagesMapActivity.class)));

Ne pozabimo ustvariti besedila z imenom map in ikone zavihka z imenom ic_tab_map. Zdaj lahko zaženemo aplikacijo in preizkusimo naš novi zemljevid.

Plasti

Sam zemljevid hitro postane nezanimiv in dobi pravo vrednost šele, ko na njem prikažemo podatke. Dodajmo torej na zemljevid ikone, vezane na lokacijo, ki bodo uporabniku omogočile, da nanje klikne in dobi dodatne podatke. Za to ustvarimo nov razred DialogOverlay, ki naj razširja razred ItemizedOverlay< OverlayItem> . ItemizedOverlay je razred, ki pomaga pri izrisovanju plasti na zemljevid, OverlayItem pa je osnovni element, ki ga ta plast prikazuje, saj vsebuje lokacijo, ikono in sporočilo.

Idea nas spet opozori na metode, ki jih je treba implementirati, in ponovno ji dovolimo, da to stori namesto nas. Vendar pa tokrat po implementaciji metod Idea še vedno ni zadovoljna in ponovni pritisk na tipki Alt+Enter na vrstici z napako nam razkrije, da nam manjka še konstruktor. Dovolimo Ideji da napravi tudi to:

public DialogOverlay(Drawable drawable) {
super(drawable);
}

Ker pa bomo mi potrebovali še nekaj dodatnih elementov, dodajmo konstruktorju parameter Context context, ki ga dodelimo polju z istim imenom, napravimo pa tudi instanco seznama OverlayItem-ov. Ikono želimo prikazati tako, da bo geografska točka na spodnjem srednjem delu slikice. Konstruktor ter polja naj bodo videti tako:

private Context context;
private List< OverlayItem> items;
public DialogOverlay(Drawable drawable, Context context) {
super(boundCenterBottom(drawable));
this.context = context;
items = new ArrayList< OverlayItem> ();
}

Zdaj lahko konkretiziramo metodo createItem(int i), ki naj namesto null vrača i-ti element seznama items: return items.get(i);, medtem ko naj metoda size() namesto 0 vrača velikost seznama items: return items.size();.

Dodajmo še metodo, ki bo napolnila naš seznam elementov:

public void addItem(int latitude, int longitude, String title, String message) {
items.add(new OverlayItem(new GeoPoint(latitude, longitude), title, message));
populate();
}

Ta metoda bo ustvarila nov OverlayItem z geografsko točko na podani zemljepisni širini in dolžini ter z naslovom in sporočilom. Pomembno je, da na koncu dodajanja (ali pozneje odstranjevanja) točk kličemo metodo populate(), saj se v nasprotnem primeru točka ne bo prikazala na zemljevidu. Dodajmo pa še metodo, ki bo počistila vse točke z zemljevida:

public void clear() {
items.clear();
populate();
}

Prikaz sporočila

Ta plast torej omogoča prikaz vseh točk, dodanih z metodo addItem, zdaj pa je čas, da vključimo še interakcijo z uporabnikom – prikaz naslova in sporočila, če klikne na posamezno točko. Za to moramo povoziti metodo onTap(int i):

@Override
protected boolean onTap(int i) {
OverlayItem tappedItem = createItem(i);
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setTitle(tappedItem.getTitle());
dialog.setMessage(tappedItem.getSnippet());
dialog.setNeutralButton("Zapri", null);
dialog.show();
return true;
}

Preprosto sporočilo lahko prikažemo z AlertDialog-om, ki mu nastavimo naslov in sporočilo, dodamo gumb »Zapri« in na koncu še prikažemo. Da dialog ve, kje se mora prikazati, mu moramo podati contekst, ki smo ga prej nastavili v konstruktorju. Metoda naj vrne true, kar bo pomenilo, da smo klik obravnavali in da ni treba iti iskati po (morebitnih) preostalih plasteh, če smo kliknili še na kaj drugega.

Na sliki sta prikazani dve ikoni, ki smo ju dodali ročno, in naša lokacija.

Plast je zdaj pripravljena, da jo dodamo na zemljevid. Odprimo razred MessagesMapActivity, mu dodajmo polje private DialogOverlay dialogOverlay; in na koncu metode onCreate dodajmo kodo:

dialogOverlay = new DialogOverlay(
getResources().getDrawable(R.drawable.ic_map_marker), this);
mapView.getOverlays().add(dialogOverlay);

Pri tem ne pozabimo dodati ikone (slikice) ic_map_marker, za katero priporočam, da je enakih ločljivosti kot so ikone za zavihke.

Če bi zdaj pognali našo aplikacijo, bi se ta plast že izrisala, vendar mi ne bi videli ničesar, ker še nismo dodali nobenih podatkov za prikaz. Metodo za dodajanje točk smo že napravili, zdaj pa jo še pokličimo. V razredu MessagesMapActivity ustvarimo novo metodo, ki bo na zemljevidu prikazala dve točki:

public void reloadMarkers() {
dialogOverlay.clear();
dialogOverlay.addItem(46378883, 13834922, "Juhuuuu", "Jaz sem najvišji");
dialogOverlay.addItem(46120200, 14815438, "Center", "Jaz sem pa najbolj na sredini");
}

To metodo pokličimo na koncu metode onCreate v istem razredu in točke se bodo prikazale na zemljevidu. Samo poženemo aplikacijo in že vidimo prve ikone na zemljevidu, če pa jih kliknemo, dobimo še sporočila. Za izhod iz sporočila enostavno pritisnemo gumb nazaj.

Koordinate

Opazimo lahko, da so geografske koordinate zapisane v nenavadni obliki. Bolj smo navajeni na koordinate v obliki stopinje-minute-sekunde (na primer 46° 22' 43,98''), pri čemer so stopinje med -90 in 90 za zemljepisno širino ter med -180 in 180 za zemljepisno dolžino, minute in sekunde pa so med 0 in 60. Zgoraj pa so zapisane kot eno celo število. Gre za to, da jih je na ta način lažje zapisati v računalniški jezik. Klasično obliko zapisa pretvorimo v številko tako:

±x° y' z'' → ± (x + y/60 + z/3600) × 1.000.000
Kako je to videti za zemljepisno širino Triglava:

46° 22' 43,98'' → (46 + 22/60 + 43,98/3600) × 1000000 = 46378883,3 ~ 46378883
Ista formula velja za zemljepisno dolžino in širino.

Moja lokacija

Na zemljevidu je dobro prikazati tudi našo lokacijo, da si bomo lažje predstavljali, kje se nahajajo sporočila, prikazana na zemljevidu, glede na nas. Ker je to zelo uporaben podatek v praktično vseh lokacijskih aplikacijah, ima knjižnica z zemljevidi že vgrajeno možnost prikaza tega podatka.

Mali krog prikazuje center naše lokacije, veliki pa natančnost.

Če želimo v aplikaciji pridobiti podatke o naši lokaciji, ji moramo najprej dovoliti branje teh podatkov. To storimo tako, da odpremo datoteko AndroidManifest.xml in korenski znački < manifest ...> dodamo podznački:

< uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
< uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Prva dovoli uporabo približne lokacije (z uporabo omrežja GSM ali Wi-Fi), druga pa uporabo natančne lokacije (z uporabo GPS navigacije). Sedaj odpremo še datoteko

MessagesMapActivity.java in dodamo polje: private MyLocationOverlay myLocationOverlay; To polje predstavlja plast, ki na zemljevidu prikaže našo lokacijo in natančnost metode pridobivanja lokacije (GPS je najbolj natančna metoda, Wi-Fi malo manj, GSM pa najmanj). Na koncu metode onCreate pa naredimo stopnjo tega razreda in ga dodamo na seznam plasti na zemljevidu:

myLocationOverlay = new MyLocationOverlay(this, mapView);
mapView.getOverlays().add(myLocationOverlay);

Prikazovanje našega lokacijskega podatka je zdaj omogočeno, ni pa še uporabljeno pridobivanje. Glede tega moramo biti malo bolj previdni, saj branje GPS-koordinat porabi precej energije, tako da želimo vklopiti to možnost le, če je naprava prižgana in aplikacija zagnana ter prikazana. To storimo tako, da razredu MyLocationOverlay.java dodamo naslednji metodi:

Override
protected void onResume() {
super.onResume();
myLocationOverlay.enableMyLocation();
}

@Override
protected void onPause() {
super.onPause();
myLocationOverlay.disableMyLocation();
}

Ta koda je zelo jasna: ko se aplikacija ustavi (jo zapremo, odpremo drugo aplikacijo, ugasnemo napravo in podobno), se onemogoči pridobivanje naše lokacije, ko pa aplikacijo spet zaženemo, se pridobivanje vklopi.

Če zdaj zaženemo našo aplikacijo na napravi, nam prikaže lokacijo.

Zaključek

V tokratnem delu male šole smo v našo aplikacijo vključili zemljevide in na njih dodali celo svoje podatke. Bralci ste vabljeni, da poskusite na zemljevidu prikazati še satelitske posnetke Zemlje (metoda setSatellite(true) na razredu MapView) ali pa prikazati še kakšne zanimive podatke. Izvorno kodo tega članka lahko kot vedno najdete na spletni strani www.logica.pro/mojmikro, kjer ste vabljeni tudi, da kaj vprašate ali pa se pohvalite s svojimi aplikacijami.


Ker zemljevidi potrebujejo svojo knjižnico in dostopajo do GPS-lokacij, bi bilo treba za zagon aplikacije na emulatorju na tega namestiti dodatne funkcionalnosti. Lažja in tudi bolj uporabna pot pa je, da aplikacijo testiramo na naši Android napravi. Za to moramo na računalniku nastaviti povezavo do razvojne naprave – navodila najdemo tu: http://goo.gl/C8C4j. V Ideji pa moramo spremeniti zagon – V Run > Edit configuration izberemo Target device > USB device.

Moj mikro, junij 2012 | Rok Končina