tanulas_navigacios_szoftverek_es_digitalis_terkepek_fejlesztese_Androidra - Fehér Krisztián honlapja

Fehér Krisztián weboldala
Fehér Krisztián weboldala
Tartalomhoz ugrás

tanulas_navigacios_szoftverek_es_digitalis_terkepek_fejlesztese_Androidra

Fehér Krisztián: Navigációs szoftverek és digitális térképek fejlesztése Androidra

Tartalomjegyzék
Előszó
1. A navigációról
1.1. A hagyományos koordináták
1.2. A GPS rendszer
1.3. GPS vevő az okostelefonokban
2. A navigációs szoftverek típusai
2.1. Helymeghatározó alkalmazások
2.2. Pozíciókövető alkalmazások
2.3. Útvonalrögzítő alkalmazások
2.4. Helyalapú szolgáltatást nyújtó alkalmazások
2.5. Térképmegjelenítő alkalmazások
2.6. Útvonaltervező alkalmazások
3. A GPS vevő kezelése
3.1. A GPS modul elérése
3.2. Lekérdezhető GPS adatok
4. Haladó ActionScript programozás
4.1. Konstans értékek használata
4.2. Osztályok létrehozása
4.3. A tömbkezelés alapjai
4.4. Időzítők használata
5. A Flash Builder haladó szintű használata
5.1. A Debugger használata
5.2. A Flash Builder tuningolása
6. Külső térképek használata – Google Maps API
6.1. Statikus térképek használata  Google Maps API-val
6.2. Dinamikus térkép-megjelenítés
7. GPS koordinátarendszer képpont alapú levetítése kijelzőkre
7.1. Egy kis koordinátageometria
8. A vektoros rajzolás alapjai
8.1. A rajzvászon
8.2. A rajzecset
8.3. Ecsetbeállítások
8.4. Színek megadása RGB kódokkal
8.5. Képpontok rajzolása
8.6. Vonalak rajzolása
8.7. Kör és ellipszis rajzolása
8.8. Kitöltőszínek használata
8.9. Poligon rajzolása
8.10. Szöveg kiíratása
8.11. A rajzvászon törlése
8.12. Színátmenetek létrehozása
9. Bittérkép-alapú rajzolás és képforgatás
10. Navigáció és matematika
10.1. GPS koordináták távolsága
10.2. Szomszédsági kapcsolatok ábrázolása
10.3. A legrövidebb út – Dijkstra algoritmusa
10.4. Útvonalszámítás a gyakorlatban
11. Képernyőforgatás és navigáció
11.1. Az alkalmazás terve
11.2. Megvalósítás
12. Útvonalrögzítő alkalmazás készítése
13. Digitális térképek készítése és felhasználása
13.1. A térképtervezés menete
13.2. Pár szó a domborzat szintkülönbségeiről
13.3. A megvalósítás
13.4. Egyszerűbb és fotorealisztikus megjelenítés
13.5. Még gyorsabb térképmegjelenítés
13.6. A magyarországi autópályák kirajzolása
13.7. A Balaton kirajzolása és a pozíció valós idejű megjelenítése
13.8. További ötletek, javaslatok
14. A NASA domborzati adatainak használata
14.1. Az SRTM adatok bemutatása
14.2. Az adatok feldolgozása
15. Függelék
15.1. Dijkstra algoritmusa C nyelven
15.2. SRTM adatokból előállított képek

Előszó

Ez a leírás egy régebbi írásom alapján lett elkészítve, időközben sokat változott a világ, elsősorban az Adobe AIR/Flex eltűnését emelném ki. A leírásnak ezért elsősorban értékmegőrző szerepe van. Ennek megfelelően tessék olvasni!

A navigációról lehet nagyon bonyolultan és nagyon egyszerűen is beszélni. Mi ez utóbbit tűztük ki célul.
Ez a leírás azoknak szól, akik saját helyalapú alkalmazásokat akarnak készíteni androidos mobileszközeikre és meg szeretnék érteni az ilyen típusú alkalmazások működését, készítésének mikéntjét.
A leírás valóban minden ismeretet megad ehhez. Segítségével az alapoktól indulva, nem kevesebb, mint 16 mobilalkalmazás ismertetésén keresztül az Olvasó képes lesz elkészíteni saját helyalapú- és térkép alkalmazásait.
Hogyan férünk hozzá a mobileszközök által látott GPS koordinátákhoz? Hogyan ábrázolhatunk ilyen koordinátákat a mobileszköz képernyőjén? Hogyan érdemes elkezdeni digitális térképeket készíteni?
Sok más érdekes kérdés mellett ezekre is választ adunk, egyszerűen, érthetően.

Mit tanulhatunk meg a leírásból?

A leírás célja olyan ismeretek átadása, melyek segítségével a magyar olvasók elkészíthetik saját helyalapú mobilalkalmazásaikat és digitális térképeiket.

Három fő témakört mutatunk be:

1. GPS koordináták felhasználását helyalapú alkalmazásokban.
2. A saját digitális térkép készítésének módjait.
3. Az útvonaltervezést.

Számos képlettel és algoritmussal fogunk megismerkedni, melyeket az Olvasó saját igényeinek megfelelően, szabadon felhasználhat és továbbfejleszthet. A felsőoktatásban tanuló hallgatók is haszonnal forgathatják a leírást.

Szükséges ismeretek

A leírás Adobe AIR és Apache Flex technológiával készített példaprogramokat mutat be, melyek Adobe Flash Builder-rel készültek. Az ActionScript alapszintű ismerete a leírás szempontjából ezért alapkövetelmény.
Az ActionScript és Flex alapú mobilprogramozás alapjait szintén a fentebb említett első leírásból lehet elsajátítani.

C nyelvű forráskódok a leírásban

A leírás két C nyelvű segédprogram forráskódját is bemutatja, mellyel a C és az Actionscript szintaktikája közti nagyfokú hasonlóságot is demonstrálni kívánjuk és célunk az is, hogy a C nyelvben már jártas, de az ActionScript-et mindezidáig nem használó programozók is kedvet kapjanak a Flash alapú mobilfejlesztésekhez.

A leírás felépítése

A navigációról, navigációs szoftverek készítésének általános bemutatásáról szól a leírás első tematikus blokkja.
A második blokkban a Flash Builderről és az ActionScriptről meglévő ismereteinket mélyíthetjük tovább.
A harmadik blokkban példaalkalmazások elkészítésén keresztül megismerhetjük a GPS-alapú tájékozódás és a navigáció különféle felhasználási lehetőségeit, fejlesztői szemszögből, példaalkalmazások forráskódjának ismertetésével, elmagyarázásával.
Az összes példaalkalmazás forráskódja áttanulmányozható és szabadon felhasználható. Az alkalmazások a Flash Builder 4.6-os és a Flex SDK 4.6-os verziójával készültek, így a régebbi Flash Builder változatok tulajdonosai is garantáltan fogják tudni használni, csakúgy, mint az újabb, 4.7-es Flash Builder tulajdonosai.
A C nyelvű forráskódokat bármely szabványos C fordítóval lefordíthatjuk és - gyakorlati szempontokat is figyelembe véve – szintén megjegyzésekkel láttuk el.
Az egyes fejezetek egyszerűen, közérthető módon vannak megfogalmazva. Bár egy leheletnyi matematikáról a téma jellegéből adódóan természetesen nem mondhatunk le, ám az Olvasónak nem kell bonyolult matematikai tételekkel és egyenletekkel hadakoznia. Egyszerűen is lehet.
Az egyes fejezetek egymás után értelmezhetőek a legkönnyebben. A fejezetek végig fogják az Olvasó kezét és így vezetik, egyik fejezetről a másikra.

A leírás ismeretanyagának elsajátításához jó kedvet és sok-sok sikerélményt kívánok!



1. A navigációról

A navigáció szó a latin navigare (utazni, hajózni) szóból ered és mindazon eljárások összességét jelenti, melyek a hajózás és a repülés során használatosak a helymeghatározáshoz, ill. a választott utazási irány tartásához. A szó eredetileg tehát nem szárazföldi közlekedésre utalt.
Napjaink elterjedt „navigáció” kifejezése alatt a helymeghatározás modern formáját értjük, a közlekedés konkrét módjától függetlenül.

1.1. A hagyományos koordináták

A Föld nem tökéletesen gömb alakú, hanem ellipszoid, azaz enyhén „lapos” az északi és a déli pólusainál. A London egyik kerületében található Greenwich-től keletre eső földrészeket, egészen körülbelül a Csendes-óceánig, a Föld keleti részének, a Greenwich-től nyugatra esőket egészen az amerikai kontinens nyugati határáig a Föld nyugati részének nevezzük. Az egyenlítőtől északra eső területek alkotják az északi féltekét, míg a délre esők a délit.
A hagyományos térképészetben egy hely konkrét meghatározásához ún. szélességi (észak-dél) és hosszúsági (kelet-nyugat) fokokat használnak.
Természetesen a valóságban a Föld felülete nem kétdimenziós sík, hanem magassággal is rendelkezik. A tengerszint magasságát 0 méternek alapul véve, ahhoz viszonyítva adják meg a térképeken a magasságot (pl. egy hegy magasságát), vagy éppen a mélységet (pl. egy mélyföld, vagy tengeri árok mélységét).
A hosszúsági fokok nyugatra és keletre 180-180 fokra, a szélességi fokok északra és délre 90-90 fokra osztják fel a Földet. A fokokat tovább osztják fokperce és fokmásodpercekre, így egészen pontos helymeghatározás lehetséges.
A fokok törtszámok, melyeket egyenként a következő képlettel számolhatunk ki:

fok ,  (fokperc/60) + (fokmásodperc/3600)

A Magyar Országház helyének koordinátái például:

47.507122, 19.045937

Pontosan ilyen formátumban szolgáltatják az okostelefonok GPS vevői is a földrajzi koordinátákat.

1.2. A GPS rendszer

A GPS szó az angol Global Positioning System („Globális Pozícionáló Rendszer”) elenevezés rövidítése. A rendszert az USA Védelmi Minisztériuma üzemelteti és eredetileg katonai célokra fejlesztették ki. Napjainkra azonban a polgári célú használata is a mindennapok részévé vált.
A GPS rendszer működése 24, a Föld körül keringő műhold működésén alapul, melyek mindegyike egyedi, speciális pályán kering. A pályák úgy vannak beállítva és összehangolva, hogy adott időpillanatban a Föld mindegyik pontjáról egyszerre több műhold jeleit is fogni lehessen. A szélesség, hosszúság és tengerszint feletti magasság megállapításához legalább 4 műhold egyidejű láthatóságára van szükség.
Az első műholdat 1978-ban állították pályára, de az első teljes „flotta” csak 1994-ben állt össze.
A műholdak kb. 20.000 km-es magasságban keringenek a Föld körül és egy nap alatt kétszer is megkerülik azt. Egy GPS műhold élettartama kb. 10 év, emiatt folyamatosan újakat állítanak pályára.
A kommunikáció rádióhullámok segítségével történik, így hasonló vételi tulajdonságok jellemzőek rájuk, mint a hagyományos rádióvevők esetében.
Noha ez nem feltétele a helyes működésnek, léteznek földfelszínen található állomások is, melyek segítségével tovább lehet pontosítani a pozicionálást. például katonai célokra is léteznek ilyen állomások.

1.3. GPS vevő az okostelefonokban

A GPS vevőegység egy kisméretű elektronikus modul, a telefonok és tabletek házán belül helyezkednek el, egy viszonylag kis méretű eszközről van szó. CPU-ba integrálva is találkozhatunk vele, például androidos eszközök esetében.
Az androidos eszközökben általában egy ún. A(ssisted)-GPS vevőegység található, mely számos adatot nem maga számol ki, hanem a mobilszolgáltatók bocsájtják rendelkezésre és ezt használják fel a telefonok. Ez elsősorban a műholdak helyzetének megtalálási és követési idejét gyorsítja fel, illetve a vevők energiafogyasztása is jóval kisebb ezáltal.
Kaphatóak olyan telefonok és tabletek, melyek nem rendelkeznek GPS vevővel. Ebben az esetben sem feltétlenül kell lemondanunk a helymeghatározás lehetőségéről, ugyanis az eszközök még így is képesek meghatározni helyzetüket pusztán mobilhálózati adótornyok, vagy WIFI hozzáférési pontok adatait felhasználva.
Az adótornyok által biztosított lefedettség viszonylag nagy, ám a pontosságuk kevésbé, illetve a sikeres jelfeldogozáshoz szükséges, hogy az eszköz „rálásson” a rádiótornyokra. A pontosság ebben az esetben igen alacsony, kb. 500-1000 méteres szórást mutat, viszont legalább működik.
A WIFI pontok elvileg nagyon pontos helymeghatározást tesznek lehetővé, viszont csak korlátozott mértékben hozzáférhetőek, elsősorban városokban. A WIFI-s megoldás végtelenül egyszerűen működik: a hozzáférési pontok helyzetét egyszerűen adatbázisokban tárolják és a helymeghatározás során a WIFI jelerősségét veszik alapul. A módszer bár működik, a pontos működés egyik feltétele a folyamatos adatbázis-frissítés és persze a WIFI hozzáférési pontok folyamatos elérhetősége. Ilyen adatbázisokat működtet pl. a Google.
Az A-GPS vevővel rendelkező, de mobilszolgáltató hálózatára nem csatlakozó eszközöknél (például a szolgáltatás által le nem fedett területen, vagy SIM kártya használata nélküli használat esetén) a műholdak helyzetének megtalálása és követése is jóval több időt vehet igénybe, ez általában 5-10 perc.
Viszonylag ritkák a teljesértékű GPS vevőt tartalmazó androidos eszközök, melyek önállóan, mobilszolgáltatók hálózatai nélkül is képesek a műholdas jelek gyors feldolgozására, de találkozhatunk ezzel is. Ezek az eszközök adott esetben jóval drágábbak is lehetnek, mint az A-GPS rendszert használók, viszont gyors és stabil működésűek.
A telefonok GPS vevője által szolgáltatott adatok megfelelő jelerősség esetén pontosak és megbízhatóak. A minőség egyik ismérve az is, hogy milyen gyorsan találja meg a vevő a jelet, ill. milyen stabilan tudja megtartani azt. Ez készülékenként változhat. Egy A-GPS rendszer esetében a saját hely meghatározása általában 5-10 másodpercen belül lezajlik és stabil műholdkapcsolat, ill. műholdkövetés marad fenn.
Általánosságban elmondható, hogy a fentiek mellett a mobileszközök helymeghatározó képessége erősen hardverfüggő is: bizonyos eszközök nehezebben, mások könyebben birkóznak meg a feladattal.
A jó hír: a programozónak nem kell foglakoznia a helyadatok fizikai előállításának mikéntjével, a szoftveres oldalon a helykoordináták ugyanúgy látszanak bármelyik fenti esetben.
Itt jegyezzük meg, hogy egyes androidos eszközök képesek az orosz GLONASS navigációs hálózat használatára is. Ez a GPS rendszer orosz megfelelője. Ebben a leírásban csak a GPS rendszer használatát feltételezzük.
A GPS-es helymeghatározás meglehetősen energiaigényes folyamat, ezért az Android lehetőséget biztosít arra, hogy a felhasználó manuálisan engedélyezze, vagy letiltsa a GPS használatát.
A GPS be- ill. kikapcsolását az Android operációs rendszer Beállítások – Vezeték nélküli és mobilhálózatok beállítási lehetőségénél végezhetjük el.


 

A helymeghatározás engedélyezése GPS vevő nélküli(bal oldalon) és GPS vevővel rendelkező (jobb oldalon) androidos okostelefonon.



2. A navigációs szoftverek típusai

Ebben a fejezetben kísérletet teszünk a navigációs szoftverek típusainak meghatározására. Az osztályozás szubjektív, mégis úgy gondoljuk, hogy a leggyakoribb felhasználási módokat lefedi.
A különféle felhasználási módokat többnyire kombinálni szokták egymással.

2.1. Helymeghatározó alkalmazások

A saját hely meghatározása klasszikus és a legalapvetőbb navigációs tevékenység, sokszor ez is elegendő egy adott fel-adat megoldásához, például saját tartózkodási helyünk, vagy tereptárgyak helyének meghatározásához, és/vagy GPS koor-dinátáinak megjelenítéséhez.

2.2. Pozíciókövető alkalmazások

A pozíciókövetés (angolul: tracking) klasszikus és gyakori formája a flottakövetés, például a logisztikában.
Az ilyen alkalmazások segítségével pontosan, és ami még fontosabb: távolról, akár valós időben is követni lehet egy esz-köz/jármű helyzetét.
A közforgalmi alkalmazás szintén gyakori és kézenfekvő, noha többnyire speciális GPS-célhardverek, nem pedig telefo-nok használatával valósítják meg.

2.3. Útvonalrögzítő alkalmazások

A GPS koordinátákat szabályos időközönként rögzítve út-vonalrögzítő alkalmazások készíthetőek, melyekkel megtett útvonalak térképezhetőek fel, elemezhetőek ki valós időben, akár utólagosan is.
Például egy kellemes túrázás után nagyon hasznos lehet a megtett út utólagos megjelenítésének a lehetősége.

2.4. Helyalapú szolgáltatást nyújtó alkalmazások

Napjainkban még messzemenően kihasználatlan területet je-lentenek a helyfüggő, vagy a mobileszköz közelében elérhető szolgáltatásokkal kapcsolatos felhasználási módok, ám nagy jövő előtt áll ez a terület.
Képzeljünk el például egy autós navigációs szoftvert, ami időben figyelmezteti a sofőrt az üzemanyag csökkenésére és automatikusan felajánlja a legközelebbi benzinkúthoz vezető útvonalat!

2.5. Térképmegjelenítő alkalmazások

A tájékozódás régóta létező formája a térképalapú tájékozó-dás. A digitális technika a hagyományos megjelenésű két-, vagy a mostanság gyakori háromdimenziós térképmegjelení-tést is lehetővé teszi.
A megjelenített térkép lehet statikus térkép, mely egyszerű-en csak saját helyzetünket, vagy egy előre meghatározott stati-kus helyet jeleníti meg, de lehet dinamikus is, mely helyze-tünktől és beállításoktól függően mindig más útvonalakat, léte-sítményeket, intézményeket jelenít meg.

2.6. Útvonaltervező alkalmazások

Egy adott ponthoz, például egy településhez vezető legrö-videbb út meghatározása alapvető navigációs feladat, elsősor-ban a közlekedésben használt navigációs alkalmazásokban.
Általában egy bizonyos településtől, vagy egyik címtől egy másikig vezető legrövidebb út, ill. a köztes állomások megjele-nítése, ill. megtalálása a cél.



3. A GPS vevő kezelése

Ebben a fejezetben megismerkedünk a GPS vevő kezelésének alapjaival ActionScript nyelven, Adobe AIR platformon.
A GPS vevő által szolgáltatott adatok lekérdezése az ActionScript GeoLocation osztályán keresztül történik.

3.1. A GPS modul elérése

Mindenekelőtt az alkalmazás GPS vonatkozású jogosultságait kell beállítanunk az –app.xml alkalmazásleíró fájlban.

A legfontosabb a helyadatokhoz történő hozzáférés engedélyezése:

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

Elsősorban városi környezetben ajánlott (bár nem kötelező) a WIFI hálózatokon keresztül történő helymeghatározás engedélyezése is. Ehhez a következő engedélyek szükségesek:

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

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

Példaalkalmazásainkban a telefon állapotáról is le fogunk kérdezni adatokat, ehhez az alábbi jogosultság engedélyezése szükséges:

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

A GPS vevőegység kezeléséhez programunk elején importálni kell a GeoLocation osztályt.

import flash.sensors.Geolocation;

Ezután létrehozunk egy osztálypéldányt.

public var geoLocation:Geolocation;

Ennek az osztálypéldánynak a tulajdonságain keresztül fogjuk kiolvasni a GPS vevő által szolgáltatott adatokat.
Alkalmazásaink indulásakor mindenképpen ellenőrizni kell, hogy az adott eszköz rendelkezik-e GPS vevőegységgel! Ezt az osztály isSupported tulajdonságával lehet lekérdezni (értéke true, vagy false lehet).
A GPS adatok frissítési gyakoriságát szintén be kell állítanunk. Minden frissítéskor a GeolocationEvent.UPDATE esemény váltódik ki.
A GPS adatokhoz egy eseménykezelő függvényen keresztül fogunk hozzáférni, mely a beállított frissítés minden egyes alkalmával lefut. Ennek aktiválása az eseményfigyelő beállításával történik.
A fentiek illusztrálására szolgál az alábbi kódrészlet, mely 1 másodperces frissítési intervallum beállításával aktiválja a GPS egységet és a frissítéshez a GPS_frissites nevű függvényt rendeli.

if(Geolocation.isSupported == true)
{
geoLocation = new Geolocation();

geoLocation.setRequestedUpdateInterval(1000);

geoLocation.addEventListener(GeolocationEvent.UPDATE,GPS_frissites);

}

else
{
// Nincs GPS támogatás;
}

Az eseménykezelő függvény váza:

public function GPS_frissites(event:GeolocationEvent):void
{
//GPS adatok lekérése, feldolgozása stb.
}

Az egység aktiválásakor az Android operációs rendszerének értesítési sorában (a képernyő legtetején) megjelenik a GPS szimbólum, mint például az alábbi ikonok egyike:



A vevőegységnek először stabil műholdkapcsolatot kell keresnie, létrehoznia és azt tartania is kell. Az adatok csak ezután lesznek hozzáférhetőek. A folyamatról az operációs rendszer értesítési sorában látható GPS ikon megjelenése is folyamatosan tájékoztatást ad.
Megjegyezzük, hogy a GPS vevők a legutóbbi vételi adatokat eltárolják és ezeket szolgáltatják a következő bekapcsolásukkor, egészen az adatok frissítéséig.
A frissítési gyakoriságot ezredmásodperces pontossággal be lehet állítani ugyan, de az intervallum tartásának pontossága nem garantált. Ha a frissítést 10-15 másodpercnél nagyobb időközökre állítjuk, elképzelhető, hogy az eszköz a frissítések között a GPS kapcsolatot megszakítja, majd a következő ütemezett frissítéskor újra felépíti.
A túl kis időközökre beállított frissítés ugyanakkor intenzívebb energiafogyasztáshoz vezet.
Az 500-10000 ezredmásodperc közötti értékek a legtöbb esetben megfelelőek és az eszköz tartani is tudja ezt a frissítési gyakoriságot.
A GPS vevő kikapcsolását szintén programból végezhetjük el, az eseménykezelő függvény eltávolításával.

geoLocation.removeEventListener(GeolocationEvent.UPDATE,GPS_frissites);

A vevő ismételt bekapcsolása az eseménykezelő újbóli beállításával végezhető el (ilyenkor tehát nem kell a fentebb ismertetett teljes inicializálási procedúrát ismét végigcsinálni).
Előfordulhat, hogy a GPS egységet a felhasználó az alkalmazáson kívül, magában az Android operációs rendszeren keresztül kikapcsolja, vagy éppen bekapcsolja. Ezt az eseményt az alábbi kódrészletben bemutatott módon kezelhetjük.
A példában a valtozas függvényt rendeljük a GPS egység állapotváltozásának bekövetkezését jelző eseményhez.

public var geoLocation:Geolocation;

geolocation.addeventlistener(StatusEvent.STATUS,valtozas);

public function valtozas(event:StatusEvent): void
{
// státuszváltozás kezelése
}

A létrehozott Geolocation osztálypéldány muted tulajdonsága a vevőegység aktivált ill. deaktivált státuszáról ad tájékoztatást true, vagy false értékeket felvéve. Ezt feldolgozva tudjuk kezelni az állapotváltozást.

3.2. Lekérdezhető GPS adatok

A vevőegység által szolgáltatott adatokhoz a Geoloca¬tionEvent.UPDATE eseményhez rendelt eseménykezelő függvény event paraméterének, mint osztálynak a tulajdonságaiként férhetünk hozzá.
Az egyes, lekérdezhető tulajdonságok a következők.

event.timeStamp
Ezredmásodpercben megadja a GPS inicializálása óta eltelt időt. Ritkán használt érték.

event.code
Szöveges érték, mely a GPS állapotáról tájékoztat. Deaktivált állapotban például ”Geolocation.Muted” értéket vesz fel.

event.latitude
Szélességi GPS koordináta.  Number típusú lebegőpontos szám.

event.longitude
Hosszúsági koordináta. Number típusú lebegőpontos szám.

event.altitude
Magasságérték, méterben megadva.

event.speed
Az eszköz mozgási sebessége m/s-ban.
A sebességérték alapján a km/h és mp/h értékeket a következőképpen számolhatjuk ki:
km/h: event.speed * 3.6
mp/h: event.speed * 2.23693629

event.heading
Az eszköz iránya. Egész típusú számérték.
A leírás írásakor az AIR for Android nem támogatja ennek az értéknek a lekérdezését. A haladási irányt viszont ettől függetlenül is kiszámíthatjuk, a hosszúsági és a szélességi koordináták folyamatos elemzésével: azt kell folyamatosan mérnünk, hogy a helyváltoztatás során az egyes koordináták milyen irányba (pozitív, negatív) változnak és egymáshoz képest is milyen arányban. A módszer hátránya, hogy az égtáj megállapításához legalább egy rövid ideig mozogni kell az eszközzel az adott irányba.

event.horizontalAccuracy
Horizontális vételi pontosság, méterben.

event.verticalAccuracy
Vertikális vételi pontosság, méterben.

A vételi pontosság értéke azt adja meg, hogy az eszköz hány méteres pontossággal képes meghatározni saját helyzetét. Minél nagyobb ez az érték, a koordináták annál nagyobb távolságra lehetnek a tényleges helyzettől. Ez akár 1000 méteres szórást is jelenthet a gyakorlatban.
A 60-as érték már elfogadhatónak számít, ezt stabil GPS kapcsolat esetén általában gond nélkül tudják a mobileszközök nyújtani. Ebben az esetben a gyakorlatban kb. 5-6 méteres pontossággal tudja az eszköz meghatározni saját helyzetét.
8-10-es vételi pontossági értéket is elérhetünk, ami már kiválónak számít, ez 1-2 méteres pontosságot is jelenthet a gyakorlatban.
A vétel pontosságát épületek által okozott vételi árnyékolás, jelvisszaverődés, ill. tereptárgyak is befolyásolhatják.
Az alábbi tesztalkalmazás (Pelda_01) a főbb GPS adatok megjelenítését végzi el egy egyszerű felületen és bemutatja a GPS vevőegység által szolgáltatott adatok lekérdezését a gyakorlatban.


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationDPI="160"
applicationComplete="init()">
<fx:Script>
<![CDATA[

import flash.sensors.Geolocation;
import mx.events.FlexEvent;
 
public var geoLocation:Geolocation;
 
public function init():void
{

if(Geolocation.isSupported==true)
{
 geoLocation = new Geolocation();

 geoLocation.setRequestedUpdateInterval(1000);

geoLocation.addEventListener(GeolocationEvent.UPDATE,GPS_frissit);
}
else
{
 info.text="Nem érzékelhető GPS egység.";
}
  
}
 
public function GPS_frissit(event:GeolocationEvent):void
{
info.text="GPS Test\n\n"
+ "Szélesség: "+ event.latitude.toString() + "\n"
+ "Hosszúság: "+ event.longitude.toString() + "\n"
+ "Magasság: "+ event.altitude.toString() + "\n"
+ "Sebesség: "+ event.speed.toString() + "\n"
+ "(Mi/h): "+ (event.speed*2.23693629).toString() + "\n"
+ "(Km/h): "+ (event.speed*3.6).toString() + "\n"
+ "Irány: "+ event.heading.toString() + "\n"
+ "Horizontális pontosság: "+ event.horizontalAccuracy.toString() + "\n"
+ "Vertikális pontosság: "+ event.verticalAccuracy.toString();
}

//Kilépés az alkalmazásból
public function Kilepes():void
{
NativeApplication.nativeApplication.exit();
}

]]>
</fx:Script>
<s:TextArea id="info" top="0" width="100%" height="260" fontSize="16" />

<s:Button label="Bezár" top="265" width="100%" click="Kilepes()" />

</s:Application>





4. Haladó ActionScript programozás

Ebben a fejezetben olyan további programozási eszközökkel ismerkedünk meg, melyekkel a navigációs szoftverek készítésekor elkerülhetetlenül szembesülünk, és a leírás ismeretanyagának a megértéséhez is szükségesek.

4.1. Konstans értékek használata

A konstansok, más néven állandók értéke nem változik a program futása során. Létrehozásuk nagyon hasonlít a változókéhoz, azzal a különbséggel, hogy a var kulcsszó helyett a const kulcsszót kell használnunk.
Konstansok értékeit alapesetben nem tudjuk változtatni futásidőben, ezért mindenképpen a létrehozásukkor kell értéket adnunk nekik.
Bár nem kötelező, az állandók neveit csupa nagybetűvel szokás írni.
Az alábbi példában egy egyszerű állandót hozunk létre.

public const KONSTANS_ERTEK:int=1024;

4.2. Osztályok létrehozása

Az ActionScript beépített lehetőségeket tartalmaz a modern programozási paradigmák egyik legfontosabb eszközének, az osztályoknak a gyors és hatékony létrehozására is, melyeket projektjeinken belül használhatunk.
Az osztályok az objektumorientált programozás magját jelentik. Egy osztályra úgy gondolhatunk, mint komplex adatszerkezetre, mely az adatokat és a rajtuk végezhető műveleteket egyetlen objektumba foglalja. Ez az objektum az osztály.
Az osztályok használata nagyban segíti a forráskód áttekinthetőségét, strukturálását és a forráskód újrafelhasználását is. A fejlesztési munka sebességét pedig megnöveli azáltal, hogy az osztályok külön lefordítódnak és csak akkor lesznek ismét lefordítva, ha módosítjuk őket. Ennek nagyméretű alkalmazások esetén van jelentősége.
Az osztályok létrehozásának és alkalmazásának demonstrálására egy példaprogramot fogunk készíteni. Az általunk létrehozott osztály egy szám értékét fogja növelni és visszaadni.
Hozzunk létre egy új Flex Mobile projektet Pelda_02 néven!
Ezt követően a 'File' menü 'New' almenüjéből válasszuk ki az 'ActionScript Class' menüpontot!



A megjelenő párbeszédablakban kényelmesen megadhatjuk az osztály főbb jellemzőit.



A legfontosabb az osztály neve ('Name' tulajdonság) és a tartalmazó csomag ('Package') megadása. Ha nem írunk semmit a csomag mezőbe, akkor osztályunkat az

import OSZTALY_NEV;

utasítással tudjuk az MXML forráskódban importálni.

Ez a megoldás azonban nem javasolt. Ezért adjunk meg egy csomagnevet is: 'Sajat_csomag'. Osztályunk neve pedig ’Pelda_osztaly’ legyen!



Ezt követően a 'Finish' gombra kattintva létrejön az osztály a projektünkben. Az osztály fizikailag is külön forrásfájlban lesz tárolva, melynek neve  'PELDA_OSZTALY.AS' lesz.



A Flash Builder Code Assist szolgáltatása dinamikusan elemzi projektjeink forráskódjait, így a kódkiegészítő szolgáltatás már fel fogja kínálni osztályunk nevét is a beépített osztályok nevei mellett.



Az osztály importját kétféleképpen is megadhatjuk:

import sajat_csomag.*;
import sajat_csomag.Pelda_osztaly ;

Az első módon egy adott csomag összes osztályát egy lépésben importálhatjuk. Akkor érdemes így eljárnunk, ha több osztályt is tartalmaz a csomagunk.
A második formát akkor érdemes használni, ha csak egyetlen osztályt akarunk importálni.

Újdonsült osztályunk egyelőre csak egy üres kódvázból áll:


package sajat_csomag
{
public class Pelda_osztaly
{
 public function Pelda_osztaly()
 {

 }
}
}

Automatikusan létrejön egy, az osztály nevével megegyező metódus (tagfüggvénynek is szokás nevezni), ez az osztály ún. konstruktora lesz. A konstruktor az osztály létrehozásakor automatikusan lefutó metódus, mely kiválóan alkalmas az osztályok által használt adatok (más néven osztálytulajdonságok) inicializálására.
Példánkban az osztályok hozzáférhetősége public lesz, azaz projektünkön belül bárhol hivatkozható lesz.
A példaalkalmazásunk osztályához négy dolgot kell implementálnunk:
1. Kell egy szám, ennek az értékét fogjuk mindig 10-el növelni. Ennek neve szam lesz és egy egyszerű egész típusú változó.
2. A szám értékét az osztály létrehozásakor nullára állítjuk a konstruktorban.
3. Kell egy metódus, mely növeli az osztály 'szam' tulajdonságát 10-el. a metódus neve novel lesz.
4. Kell egy újabb metódus, amely visszaadja a szám értékét, hogy programunkban felhasználhassuk. A metódus neve get_szam lesz.

Osztályunk forráskódja az alábbi lesz:

package sajat_csomag
{
publicclass Pelda_osztaly
{
 public var szam:int;

 public function Pelda_osztaly()
 {
  szam=0;
 }
 
 public function novel():void
 {
  szam+=10;
 }
 
 public function get_szam():int
 {
  return szam;
 }
}
}

Az alkalmazásunk kimenetének szerepét a kimenet elnevezésű TextArea Flex komponens látja el, a szám léptetését pedig egy nyomógombbal végezzük el.
A nyomógomb lenyomásához rendelt muvelet nevű függvény növeli az osztály szám tulajdonságának értéket és sztringgé alakított formáját átadja a szövegdoboznak (azaz megjeleníti):

public function muvelet(event:MouseEvent):void
{
osztaly_peldany.novel();

kimenet.text=osztaly_peldany.get_szam().toString();
}

A teljes MXML alkalmazás forráskódja az alábbiakban tanulmányozható:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160">

<fx:Script>
<![CDATA[

import sajat_csomag.*;
 
public var osztaly_peldany:Pelda_osztaly = new Pelda_osztaly();
  
public function muvelet(event:MouseEvent):void
{
osztaly_peldany.novel();
kimenet.text=osztaly_peldany.get_szam().toString();
}

public function Kilepes():void
{
NativeApplication.nativeApplication.exit();
}

]]>
</fx:Script>
<s:TextArea id="kimenet" width="100%" height="200" top="0"/>

<s:Button bottom="50" label="Kiszámít" width="100%" click="muvelet(event)" />

<s:Button bottom="0" label="Kilépés" width="100%" click="Kilepes()" />
</s:Application>




Valahányszor a 'Kiszámít' feliratú gombot lenyomjuk, a szövegmezőben kiírt szám értéke nőni fog 10-zel.

4.3. A tömbkezelés alapjai

Amikor programokat készítünk, sokszor előfordul, hogy nagy mennyiségű, ugyanolyan típusú változót kell kezelnie alkalmazásunknak.
Könnyen belátható, hogy például többezer változó egyenként történő feldolgozásának a leprogramozása kilátástalan szélmalomharccá tenné a programozók életét. Szerencsére van megoldás.
A matematikában ismeretes halmazokhoz hasonlóan a programozási nyelvek is lehetővé teszik nagy mennyiségű adat egyetlen adathalmazként történő kezelését. Ennek a programozási eszköznek a neve: tömb.
A gyakorlatban mindez úgy működik, hogy egy tömböt létrehozunk egy egyedi névvel (akárcsak egy változót) és onnantól kezdve a tömb nevén keresztül érjük el a tömb elemeit úgy, hogy az elemek sorszámát is megadjuk. Az elemeket tehát sorszámaikra hivatkozva érhetjük el, hogy műveleteket végezzünk velük, vagy éppen rajtuk. A sorszámokat indexenek, a sorszámozást pedig indexelésnek, tömbindexelésnek is nevezik.
A tömbök ún. dimenziókkal is rendelkeznek (legalább eggyel). A dimenziók alatt a tömbök méretét, logikai kiterjedését, egyszerűbben fogalmazva a tömb befogadóképességét értjük. Az egydimenziós tömb tulajdonképpen egy felsorolásszerű lista, egy lineáris adatsorozat. Az egydimenziós tömböket szokás vektoroknak is nevezni.
Például a SAJAT_TOMB elnevezésű tömb 15. elemét a SAJAT_TOMB(15) hivatkozással érhetjük el.
A kétdimenziós tömb leginkább egy táblázathoz hasonlítható, melyben az első dimenzió a sorokat, a második az oszlopokat jelenti.
A gyakorlatban ritkán használunk két dimenziónál többet, de elvi akadálya nincsen.
Az ActionScript nagyon kényelmes tömbkezelést tesz lehetővé. Mint minden ActionScript adattípus, úgy a tömb is egy objektum (pontosabban: egy osztály, melynek típusa az általános Object osztály).

Az ActionScript tömbkezelésének legfontosabb tulajdonságai, melyek egyben el is térnek a legtöbb programozási nyelv gyakorlatától, a következőek:

• A tömb elemeinek számozása alapesetben 0-tól kezdődik, de ez nem kötelező, sőt, a tömbelemek sorszámozása teljesen kötetlen, nem kötelező a folytonos indexeléshez sem ragaszkodni.
• A tömbök indexelése nem csak számokkal, hanem nevekkel is történhet. (Ezzel az ún. asszociatív tömbkezeléssel ez a leírás nem foglalkozik.)
• Egyazon tömb elemei különböző típusúak is lehetnek.
• A tömbök dinamikusan nyilvántartják saját méreteiket, melyet dimenziónként lekérdezhetünk a length tulajdonsággal.
• A tömböket futásidőben bővíthetjük.

4.3.1. Egydimenziós tömbök

Egy 10 elemű egydimenziós tömböt a következőképpen hozhatunk létre Actionscript-ben:

public var egydimenzio:Array= new Array(10);

Ebben az esetben a tomb elemeinek sorszáma 0-9-ig terjed, az első elem sorszáma tehát 0 lesz.

A tömb elemeit létrehozásuk helyén azonnal meg is adhatjuk. Ilyenkor nem a tömb méretét adjuk meg zárójelek között, hanem vesszővel elválasztva a tömb elemeit. A tömb méretét a tömb maga fogja beállítani, a megadott elemek száma alapján.
Az alábbi példában az imént megismert tömböt azonnal fel is töltjük értékekkel:

public var egy_dimenzio:Array= new Array(1,2,3,4,5,6,7,8,9,10);

Említettük, hogy a tömb elemei különböző típusúak is lehetnek. Tegyük fel, hogy a tízedik elem (azaz a 9. sorszámú elem) értéke nem szám, hanem szöveg lesz.

public var egy_dimenzio:Array= new Array(1,2,3,4,5,6,7,8,9,"Tíz");

Más programozási nyelvben efféle megoldás elképzelhetetlen, ActionScript-ben ez nem probléma.
Amennyiben a tömb elemeihez a tömb létrehozása után akarunk értékeket rendelni, azt a következőképpen tehetjük meg:

public var egydimenzio:Array= new Array(10);

...

egy_dimenzio[0]=1;
egy_dimenzio[1]=2;
egy_dimenzio[2]=3;
egy_dimenzio[3]=4;
egy_dimenzio[4]=5;
egy_dimenzio[5]=6;
egy_dimenzio[6]=7;
egy_dimenzio[7]=8;
egy_dimenzio[8]=9;
egy_dimenzio[9]=10;

Megjegyezzük, hogy a fenti példában a tömb kezdeti méretének megadását akár el is hagyhattuk volna.  Ebben az esetben dinamikusan bővítenénk a kezdetben 0 elemű tömböt (pár sorral lejjebb még szót ejtünk erről).
A gyakorlatban hatékonyabb (és helytakarékosabb) megoldás a tömbök kezelését algoritmizálni és erre for ciklusokat használni.
A fenti sorokat az alábbi kóddal kiváltva ugyanazt az eredményt kapjuk:

public var egydimenzio:Array= new Array(10);

for(i=0;i<10;++i)
egydimenzio[i]=i+1;

Mint fentebb utaltunk rá, egy tömb hosszát dimenziónként a length tulajdonságán keresztül kérdezhetjük le. Ezáltal sokkal általánosabb kódot tudunk írni a tömbjeinket kezelő ciklusokra.
Az alábbi kód a tömb méretétől függetlenül mindig helyesen fut le, hiszen mindig egydimenziós tömbünk aktuális méretét veszi alapul. Az eredmény ugyanaz lesz, mint az előző kódrészletben.

for(i=0;i<egydimenzió.length;++i)
egydimenzio[i]=i+1;

ActionScriptben a tömbök indexelése nem kötelezően 0-tól kezdődik és az indexeknek nem is kötelező folytonosnak lenniük. Az alábbi kódrészlet egy kissé szokatlan indexelésű, de teljesen „legális” tömböt eredményez:

public var egy_dimenzio:Array= new Array(4);

egy_dimenzio[1]=0;
egy_dimenzio[2]=1;
egy_dimenzio[4]=2;
egy_dimenzio[7]="Három";

A fenti példát alapul véve felmerülhet a kérdés: mi történik, ha mondjuk a 0. sorszámú elemre hivatkozunk? A válasz kézenfekvő: futásidejű programhiba keletkezik, ugyanis 0. sorszámú tömbelem nem jön létre a memóriában. Amennyiben tehát egyedi sorszámozás használata mellett döntünk, mindig nagyon körültekintően kell eljárnunk a felhasználás során!
Az ActionScriptben létezik egy még kényelmesebb módja is a tömbök feldolgozásának. A for ciklus egy speciális fajtája ActionScript-ben tömbök kezelésére kifejezetten ideális. Ez az ún. for each ciklus, mely egy adott tömb összes elemén végiglépked.

Lássunk egy példakódot erre:

for each (var i:int in egy_dimenzio)
i=i+10;

A fenti példa az egydimenziós tömbünk minden eleméhez hozzáad 10-et.
Vegyük észre, hogy a cikluson belül az éppen feldolgozás alatt álló tömbelem teljesen másképpen jelenik meg a korábbi példákhoz képest! A for each ciklusokban egy ún. alias (azaz helyettesítő) nevet kell megadnunk (a fenti példában az „i”-t) a tömbelemek kezelésére. Az egydimenzio[i]kifejezést így az „i” fogja helyettesíteni a for each ciklusban.
Amennyiben tömbünk különböző típusú adatokat tartalmaz, úgy a ciklus fejlécében az ”:int” helyett ”:*” -ot kell írnunk:

for each (var i:* in egy_dimenzio)

A for each ciklus óriási előnye, hogy nem kell törődnünk a tömb sorszámozásával, a ciklus garantáltan minden elemen végigszalad és csak létező indexeket használ.

Az Array osztály a számobjektumokhoz hasonlóan rendelkezik egy toString() metódussal, mely az egész tömböt szöveggé alakítja. A szövegben a tömb elemei vesszővel lesznek elválasztva:

egydimenzio.toString();

Bővíthetjük-e tömbjeinket futásidőben, azaz dinamikusan? Igen. Ehhez semmi mást nem kell tennünk, mint megadnunk a kívánt új elemet, a kívánt tömbindexszel hivatkozva.
Amennyiben fenti egydimenziós tömbünkhöz egy 11. elemet is hozzá akarunk adni, azt a következőképpen tehetjük meg:

egy_dimenzio[10]=”Új elem”.

A tömbök ily módon történő kezelése nagyon kényelmes és az emberi logikához is közel áll.

4.3.2. Kétdimenziós tömbök

A kétdimenziós tömbök is ugyanúgy kezdik pályafutásukat, mint az egydimenziós tömbök.
Létrehozásukra három példát mutatunk.

Az első példában létrehozunk egy egyszerű tömböt:

public var ketdimenzio:Array = new Array();

Ebben a formában tömbünk még egydimenziós. Az alábbi sorokkal létrehozunk egy-egy új tömbelemet és egyúttal a második dimenziót is hozzájuk rendeljük úgy, hogy az egydimenziós tömbünk minden eleme egy újabb tömb lesz. A tömbelemeket (melyek a második tömbdimenzióba „kerülnek bele”) közvetlenül megadjuk:

ketdimenzio[0]= [1,2,3];
ketdimenzio[1]= [4,5,6];

A második módszerrel egy programsorral intézünk el mindent:

public var ketdimenzio:Array = new Array (new Array (1,2,3),new Array(4,5,6));

A végeredmény mindkét fenti módszerrel ugyanaz.

A harmadik módszerrel anélkül hozhatunk létre tetszőleges méretű kétdimenziós tömböket, hogy előtte feltöltenénk őket értékekkel. Egy 50x50-es elemszámú, „üres” tömb létrehozása például így történhet:

public var i:int;

public var ketdimenzio:Array = new Array(50);

for(i=0;i<50;++i)
ketdimenzio[i]=new Array(50);

A kétdimenziós tömbök tartalmát is könnyedén sztringekké alakíthatjuk a toString() metódussal:

ketdimenzio.toString();

A tömbök mint osztályok még számos „beépített” lehetőséget tartalmaznak a tömbkezeléshez.

A tömbök kezelésének elsajátítására egy olyan példaalkalmazás (Pelda_03) forráskódját közöljük le, mely a tömbök feltöltésének és feldolgozásának fentebb ismertetett módjait demonstrálja három egydimenziós és egy kétdimenziós tömb kezelésének példáján keresztül.
A tömbök tartalmát minden esetben egy 'kimenet' nevű szövegdobozban íratjuk ki:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160"
creationComplete="init(event)">
<fx:Script>
<![CDATA[

import mx.events.FlexEvent;
  
public var egydimenzio:Array= new Array(10);

public var egydimenzio_2:Array=new Array(1,2,4,6,8,"Száztizenegy");   
public var egydimenzio_3:Array= new Array(4);

public var ketdimenzio:Array = new Array (new Array (1,2,3),new Array(4,5,6));
  
protected function init(event:FlexEvent):void
{
var i:int;
 var j:int;
   
 //tömb feltöltése ciklussal
 for(i=0;i<10;++i)
 egydimenzio[i]=i;
   
 //tömb kiíratása ciklus nélkül
 kimenet.text+="Ciklus nélkül:\n";
 kimenet.text+=egydimenzio.toString();
 kimenet.text+="\n\n";
 
 //tömb kiíratása ciklussal, előre rögzített ciklusszámmal
 kimenet.text+="Ciklussal:\n";

 for(i=0;i<10;++i)
 {
 kimenet.text+=(egydimenzio[i]).toString();
 kimenet.text+=",";
 }

 kimenet.text+="\n\n";
   
 //tömb kiíratása ciklussal, dinamikus méretlekérdezéssel
 kimenet.text+="Ciklussal, dinamikusan:\n";

 for(i=0;i<egydimenzio_2.length;++i)
 {
  kimenet.text+=(egydimenzio_2[i]).toString();
  kimenet.text+=",";
 }    
   
 kimenet.text+="\n\n";
  
//tömb feltöltése futásidőben
egydimenzio_3[1]=0;
egydimenzio_3[2]=1;
egydimenzio_3[4]=2;
egydimenzio_3[7]="Három";    
   
//tömb kiíratása for each ciklussal
kimenet.text+="for each ciklussal:\n";

for each (var k:* in egydimenzio_3)
{
kimenet.text+=k.toString();
kimenet.text+=",";
}

kimenet.text+="\n\n";
//kétdimenziós tömb kiíratása egymásba ágyazott ciklusokkal
kimenet.text+="Kétdimenzió, ciklussal:\n";

for(i=0;i<ketdimenzio.length;++i)
 for(j=0;j<ketdimenzio[0].length;++j)
 {  
  kimenet.text+=(ketdimenzio[i][j]).toString();
  kimenet.text+=",";  
 }

kimenet.text+="\n\n";
   
   
//kétdimenziós tömb kiíratása ciklus nélkül
kimenet.text+="Kétdimenzió, ciklus nélkül:\n";
kimenet.text+=ketdimenzio.toString();
}
   
//Kilépés az alkalmazásból
public function Kilepes(event:MouseEvent):void
{
NativeApplication.nativeApplication.exit();
}
   
]]>
</fx:Script>
<s:TextArea id="kimenet" width="100%" height="90%" />

<s:Button label="Bezárás" bottom="0" width="100%" height="10%" click="Kilepes(event)"/>
</s:Application>


Az alkalmazás képernyőképe:



4.4. Időzítők használata

A felhasználói interakcióra adott válaszként végrehajtott műveletek mellett gyakorta lehet szükség szabályos időközönként automatikusan végrehajtódó műveletekre is.
Egy navigációs alkalmazás például a háttérben bizonyos időközönként mérheti a haladás sebességét, vagy statisztikát vezethet a megtett útról stb.
Az automatizált ciklikus végrehajtás eszközei az ún. időzítők. Ezt Actionscript-ben a Timer osztály valósítja meg.
A Timer osztályt is alapvetően ugyanúgy kell létrehozni, mint bármely más objektumot, ám a létrehozáskor meg kell adnunk az időzítés kívánt intervallumát is:

public var idozito:Timer=new Timer(5000,0);

A fenti példa egy olyan időzítőt hoz létre, mely minden 5. másodpercben aktivizálódik. Az intervallumokat ezredmásodpercben kell megadnunk.
Az időzítők önmagukban nem csinálnak semmit, amíg függvényt nem rendelünk hozzájuk.
A hozzárendelés egy eseményfigyelő létrehozásával történik.
Az alábbi példa az imént létrehozott időzítőhöz hozzárendeli az auto_fuggveny nevű függvényt.

idozito.addEventListener("timer",auto_fuggveny);

A függvény váza a következő:

public function auto_fuggveny(event:TimerEvent):void
{
//műveletek
}

Nagyon fontos, hogy a függvény argumentuma egy TimerEvent típusú, nem pedig egyszerűen Event típusú objektum legyen.
Az eseményfigyelés hozzárendelése után az időzítő „élesítve” van, de még külön el kell elindítani a .start() metódusával is.
Az eseményfigyelés végleges eltávolítása az eseménykezelő eltávolításával történhet, ha már nincsen szükség rá:

idozito.removeEventListener("timer",auto_fuggveny);

Amennyiben (például erőforrás-kímélés végett, vagy más okból) csak átmenetileg akarjuk kikapcsolni az időzítőt, azt az alábbi utasítással tehetjük meg:

idozito.stop();

Ilyenkor az eseményfigyelő eltávolítása nem történik meg.
Az időzítő újraindítása a .start() metódus ismételt meghívásával történhet:

idozito.start();

Az időzítők használatára egy olyan példaprogramot (Pelda_04) mutatunk be, mely egy szöveges mezőbe ír ki automatikusan egy növekvő számsorozatot, másodpercenként egy számjegyet kiírva:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160"
creationComplete="init(event)">
 
<fx:Script>
<![CDATA[

import mx.events.FlexEvent;

public var szam:int;

public var idozito:Timer=new Timer(1000,0);
 
//Az időzítő előkészítése
public function init(event:FlexEvent):void
{
szam=0;    
   idozito.addEventListener("timer",auto_fuggveny);
idozito.start();
}   
  
//Az időzítő által végrehajtandó függvény kódja
public function auto_fuggveny(event:TimerEvent):void
{
kimenet.text+=szam.toString()+" ";
++szam;
}
  

//Kilépés az alkalmazásból
public function Kilepes(event:MouseEvent):void
{
NativeApplication.nativeApplication.exit();
}
  
]]>
/fx:Script>
<s:TextArea id="kimenet" width="100%" height="90%" text="Számsorozat: " />

<s:Button label="Bezárás" bottom="0" width="100%" height="10%" click="Kilepes(event)"/>
</s:Application>



Az alkalmazás képernyőképe néhány másodperces futtatás után:





5. A Flash Builder haladó szintű használata

Ebben a fejezetben a Flash Builder haladó szintű felhasználási módjaival ismerkedünk meg.
A navigációs alkalmazások sok számadattal dolgoznak, előfordulhat, hogy valami nem úgy működik, ahogy azt előzőleg elképzeltük. Ilyenkor jól jöhet, ha meg tudjuk nézni, mi történik alkalmazásunk belsejében annak futása alatt.
Megismerjük, hogyan tudjuk megvizsgálni és elemezni alkalmazásaink adatait futásidőben, hogy könnyebben megtaláljuk az esetleges programhibákat, ezt nevezzük hibakeresésnek, angolul debugging-nak.
Bemutatjuk továbbá, hogy milyen speciális beállításokkal tudunk extrém nagy méretű projekteket is lefordítani.

5.1. A Debugger használata

A modern fejlesztőeszközök lehetőséget biztosítanak arra, hogy a fejlesztés alatt álló alkalmazásokat egyfajta felügyelt módban futtassuk. Ez azt jelenti, hogy az adott fejlesztőeszköz képes akár soronként megállva is futtatni egy alkalmazást és közben például az alkalmazás változóit valós időben nyilvántartani és tartalmukat megjeleníteni.
Ezt a funkciót hibakeresésnek nevezik, angol szóval debugging-nak. Ennek az alapjaival ismerkedünk most meg.
A hibakeresés első lépéseként egy ún. töréspontot (angolul: 'breakpoint') kell adnunk az alkalmazás forráskódjának egy adott sorához. A töréspontok, mint ahogyan az elnevezésük is mutatja, az alkalmazás futásának ideiglenes megtörésére, megszakítására használatosak.
A hibakereső használatának demonstrálására a 4.2 fejezetben bemutatott példaalkalmazást (Pelda_02) fogjuk használni.
A Flash Builderben töréspontot úgy tudunk legkönnyebben létrehozni, ha a forráskód bal oldalán, a kívánt programsor magasságában duplán kattintunk az egér bal gombjával.
A töréspontokat egy kék kör szimbolizálja. Az alábbi képen az látható, ahogy a 12. sorhoz rendelünk egy töréspontot. Töréspontokból akármennyit megadhatunk.
Töréspontokat úgy tudunk eltávolítani, hogy duplán ismét rájuk kattintunk.
A töréspontokon a jobb egérgombbal kattintva a megjelenő helyi menüben további beállításokat végezhetünk, ezeknek a tárgyalásától eltekintünk.



A Debugger a töréspont sorának végrehajtása után megállítja, felfüggeszti a program futását (de nem fejezi be, nem állítja le a programot) és lehetőségünk nyílik az alkalmazás által, a töréspont eléréséig létrehozott összes memóriabeli objektum áttanulmányozására. Lényegében egy pillanatképet kapunk alkalmazásaink belső állapotáról.
A hibakereső futtatási módot mobileszközön történő futtatáskor is használhatjuk, ilyenkor természetesen a mobileszköznek csatlakoztatva kell lennie számítógépünkhöz.
A Debugger-t egy külön Debug futtatási gombbal tudjuk aktiválni és az alkalmazást ebben a módban elindítani. (Amennyiben az alkalmazásunkat a normál futtatás módban indítjuk el, semmi különös nem fog történni, még akkor sem, ha a törésponttal megjelölt programsor végrehajtódik.)
A hibakeresési futtatási a gomb a Flash Builder ikonsorában a normál módú futtatás gomb mellett közvetlenül balra található és a képe egy zöld bogárra hasonlít. A hibakereső módban történő futtatáshoz ugyanúgy rendelkezésünkre állnak a futtatási beállítások, mint a normál futtatási mód esetében.



Kattintsunk tehát erre a gombra! Az alkalmazás elindul. Nyomjuk le a 'Kiszámít' feliratú gombot alkalmazásunk felületén! Ekkor a végrehajtás eléri a töréspontot, a vezérlés pedig átmenetileg visszakerül a Flash Builderhez.
A vezérlés visszakerül a Flash Builder-hez és egy párbeszédablak jelenik meg, mely felajánlja, hogy azonnal az ún. 'Flash Debug' perspektívára válthatunk. Mielőtt azonban így tennénk, pipáljuk ki a 'Remember my decision' lehetőséget, így a párbeszédablak legközelebb már nem fog megjelenni. Kattintsunk most a 'Yes' nyomógombra!



A nézet változását a Flash Builder jobb felső sarkában található nézetváltó gomb is jelzi.



Debug nézetben új szerkesztőablakokkal is találkozunk, melyek közül számunkra most a képernyő (alapértelmezés szerinti) jobb felső negyedében elhelyezkedő 'Variables' címsorú lesz érdekes.



Az ablak egy lenyitható szerkezetű listában jeleníti meg az adatokat.
A 'this' név alkalmazásunkat jelenti. Ezt kibontva megkereshetünk minden objektumot, mely az aktuális töréspontig létrejött. Az alábbi képernyőképen például azt láthatjuk, hogy a „kimenet” nevű szövegdobozunk egyelőre még üres, a 'text' tulajdonságának értéke egy üres sztring (””).
Az [inherited] csoportban található tulajdonságok a vizsgált objektum azon tulajdonságait mutatják, melyek az objektum szülő osztályának tulajdonságai. (Az AIR alkalmazások osztályai általában más osztályok ún. leszármazottai, melyeket ezek ’szüleinek’ nevezünk. A tulajdonságok és a metódusok átvételét a szülőktől ’öröklődésnek’ nevezik. A példaalkalmazásainkban itt nem alkalmazunk explicit leszármaztatást.)



A listában könnyedén megtalálhatjuk azokat az objektumokat is, melyek kifejezetten abban a függvényben/metódusban léteznek, ahol a töréspontot elhelyeztük.
Ezek az objektumok a this csoporton kívül helyezkednek el és szürke kör áll előttük.
Az alábbi képen a muvelet eseménykezelő függvényben elérhető event objektum (amit paraméterként kap meg a függvény) pillanatnyi tulajdonságait listázzuk ki. Az objektum ebben az esetben az egérhez tartozó eseménytulajdonságokat tartalmazza. (Ha függvény további, lokális változókat is tartalmazna, azokat is ugyanitt találnánk meg.)



Amennyiben további töréspontot is tartalmaz alkalmazásunk, vagy az alkalmazást tovább akarjuk futtatni anélkül, hogy leállítanánk, akkor a képernyő középső, felső részén található 'Resume' gombra kattintsunk, mely egy zöld színű, lejátszást szimbolizáló gomb!



Amennyiben ki akarunk lépni a hibakeresési módból, kattintsunk a piros, 'Terminate' gombra!
Ezután a perspektívát visszaállítva a Flash szerkesztő üzemmódjára, a kódszerkesztő nézetbe juthatunk vissza:



5.2. A Flash Builder tuningolása

A navigációs szoftverek, a térképeket előállító alkalmazások meglehetősen sok adattal dolgoznak, nagy méretűre nőhetnek. Ilyen alkalmazás fejlesztése során előfordulhat, hogy (legkésőbb az alkalmazás lefordításakor) egyszerűen „kifutunk” a memóriából.

5.2.1. A memóriahasználat beállítása

A Flash Builder bizonyos mennyiségű területet automatikusan pluszban lefoglal számítógépünk memóriájából, alapértelmezés szerint ennek mérete néhány száz megabájt körül van. Ezt a területet például alkalmazásasink lefordításakor is használja a fejlesztőkörnyezet.
Jó tudni, hogy ennek méretét manuálisan is beállíthatjuk. Az engedélyezett felhasználható memóriaterület méretének növelésével gyorsíthatjuk a FlashBuilder működését és elkerülhetjük, hogy a fejlesztőkörnyezet lelassul, vagy túl kevés memóriára hívja fel a figyelmünket.
A finomhangoló beállítást egy konfigurációs fájl módosításával végezhetjük el, ennek a fájlnak a neve FlashBuilder.ini  (nem összekeverendő a FlashBuilderC.ini fájllal, mely szintén ebben a könyvtárban található!) és a következő könyvtárban találhatjuk (a Flash Builder 64 bites változata esetén):

C:\Program Files\Adobe\Adobe Flash Builder 4.7\FlashBuilder.ini

A fájl egy egyszerű, formázatlan szövegfájl, akár a Windows Jegyzettömb alkalmazásával is szerkeszthetjük. Ha korábban már elindítottuk a Flash Buildert, akkor előbb azonban mindenképpen lépjünk ki belőle!
A fájlban az alábbihoz hasonló bejegyzéseket keressük:

-Xms256m
-Xmx512m
-XX:MaxPermSize=256m
-XX:PermSize=64m

Az Xmx és a MaxPermsize értékek növelésével növelhetjük a gyorsítótár méretét. Elsősorban a MaxPermsize méretének a növelésére koncentráljunk!
Milyen értékeket érdemes beállítani? Természetesen figyelembe kell vennünk, hogy számítógépünk operációs rendszere, ill. az egyéb alkalmazások szintén igényelnek valamennyi memóriát. Ezért csak megfelelő értékekkel van értelme kísérleteznünk.
Egy 2GB RAM-mal felszerelt számítógép esetén például érdemes 1024MB, azaz 1GB méretű gyorsítótárat beállítani. Ez a legtöbb esetben bőségesen elegendő.
Ezt leszámítva, minél több memóriát tudunk biztosítani a FlashBuildernek, annál jobb. Amennyiben biztosan tudjuk, hogy alkalmazásunk nagy mennyiségű statikus adatot fog tartalmazni és azokkal fog dolgozni, akkor érdemes a használt számítógépet is bővíteni.
A legtöbb feladat egy kétmagos processzorral és 4GB memóriával rendelkező számítógéppel megoldható.
Amennyiben messze ki akarjuk tolni mozgásterünket, legalább négymagos processzorral és 16GB, vagy még több memóriával felszerelt számítógépet állítsunk csatasorba! Ilyen konfiguráció esetén, 14-15 GB memóriát hozzárendelve a Flash Builderhez, akár több százezer soros forrásfájlokat is le tudunk fordítani.
Ez utóbbi megoldásra mindazonáltal ritkábban lehet szükség.

5.2.2. Az automatikus fordítás kikapcsolása

Amikor elmentjük változtatásainkat a projektjeinkben, akkor a Flash Builder a háttérben alapértelmezés szerint automatikusan lefordítja alkalmazásunkat.
Ez nagyban meggyorsíthatja az alkalmazás kipróbálását, ám szerényebb konfigurációjú számítógépeken megnövelheti a fejlesztőkörnyezet válaszidejét.
A Windows – Preferences menüben elérhetőek a Flash Builder részletes beállításai. Az automatikus fordítást a General – Workspace pont alatt kapcsolhatjuk ki és be. Az opció neve ’Build automatically’.



5.2.3. Előfordulások kiemelésének kikapcsolása

A FlashBuilder teljesítményének növelésére akkor is szükség lehet, ha alkalmazásunk forráskódjának mérete több ezer sorra duzzad. Régebbi számítógépek használata esetén ilyenkor előfordulhat, hogy a fejlesztőkörnyezet működése kis mértékben szintén lassul.
Ezt csökkenthetjük különböző valósidejű szerkesztési funkciók kikapcsolásával, melyek fontos erőforrásokat vehetnek igénybe.
Az egyik ilyen funkció a ’Mark occurences’, az azonos kifejezések előfordulásának kiemelése a forrásszövegben. Ezt a szolgáltatást a FlashBuilder ikonsorában is kikapcsolhatjuk.



5.2.4. Kódblokkok összezárásának kikapcsolása

A FlashBuilder forráskódjainkat a háttérben folyamatosan elemzi és olyan szolgáltatásokat is alkalmaz, melyek a forráskód jobb olvashatóságát, kezelhetőségét segítik elő.
Az egyik ilyen szolgáltatás a ’Folding’, mely a szemantikailag összetartozó kódbolokkok (pl. egy függvény kódja)  egyfajta becsukását teszi lehetővé egyetlen sorrá, hasonlóan ahhoz, ahogyan a Windows Intézőben a könyvtárak struktúráját zárhatjuk be és nyithatjuk ki.



A szolgáltatás kikapcsolásával erőforrásokat szabadíthatunk fel. Ezt a kódszerkesztő ablak bal szélén előhívható helyi menüben tehetjük meg, az ’Enable Folding’ opció kikapcsolásával.
A FlashBuilder működésének teljeskörű testreszabását a Windows – Preferences menüponttal elérhető beállítópanelen végezhetjük el.



6. Külső térképek használata – Google Maps API

A 3. fejezetben megismertük, hogyan tudjuk lekérdezni mobileszközünk pillanatnyi helyadatait.
Itt az ideje megismerkednünk a koordináták megjelenítésének lehetőségeivel!
Amennyiben nem saját térképeket akarunk készíteni és megjeleníteni, akkor harmadik fél által elérhető térképeket és térképszolgáltatásokat is használhatunk a megjelenítés során.
Népszerű, nyilvános térképszolgáltatást nyújt például a Google, a Bing, a Yahoo, vagy az OpenStreetMap.
Egyes szolgáltatók rendelkezésre bocsájtanak kifejezetten Flash-alapú terképmegjelenítő programozási eszközöket is térképeik használatához.
Ebben a leírásban a lehető leguniverzálisabb, kiegészítők használatát nem igénylő megoldásokat fogunk bemutatni.
A következőkben a Google néhány egyszerűen elérhető és használható térképszolgáltatásának használatát fogjuk megismerni.
Egy sor Google térképszolgáltatást használhatnak külső fejlesztők is, az ezen szolgáltatások elérését biztosító felületet nevezik API-nak, azaz alkalmazásfejlesztői csatoló felületnek (Application Programming Interface), ami egy szolgáltatáshívási gyűjtemény.
Ezeket a szolgáltatásokat ingyenesen is igénybe lehet venni, ám a felhasználás keretei kötöttek.
Általában pár ezer (kb. 2000-2500) szolgáltatáshívás engedélyezett naponta (üzleti szerződés keretében ez a korlátozás jóval feljebb kitolható).
További fontos korlátozás, hogy a szolgáltatott adatokat kizárólag a Google térképszolgáltatásaival együtt szabad felhasználni. A szolgáltatott adatok tárolása és a Google térképeitől független felhasználása szintén tilos. A felhasználás részletes feltételeiről a Google fejlesztői webhelyén találhatunk további információkat.
A Google térképmegjelenítését statikus és dinamikus megjelenítési módban is igénybe vehetjük.
A statikus megjelenítés gyorsabb, mint a dinamikus, ugyanis csak egy képfájlt kapunk vissza megjelenítés céljából, ami ugyanúgy néz ki, mint a „megszokott” dinamikus térkép, ám önmagában mégiscsak egy egyszerű képfájl. A generáltatható kép maximális mérete korlátozva van ugyan (640x640 pixel, ezt interpoláltathatjuk, de az már nem igazi nagyítás), a legtöbb hétköznapi feladatra azonban ez is elegendőnek bizonyulhat.
A dinamikus megjelenítésű térképszolgáltatás külön beépített kezelőfelületet is tartalmaz, például a térkép nagyítására stb. A dinamikus térképek megjelenítése viszont nagyobb adatforgalommal jár és több időt is vesz igénybe.
Mi most egy általános alapmegoldás bemutatására koncentrálunk, mely mindössze egyszerű, http alapú térkép-megjelenítő lekérdezésekre támaszkodik. Ezek használatát mutatjuk most be.

6.1. Statikus térképek használata Google Maps API-val

A statikus térképek megjelenítését akár asztali számítógépen is kipróbálhatjuk egy böngészőprogrammal.
A térképeket a Google szerverei egy képfájlba renderelik és csak ezt a képfájlt kapjuk vissza.
A megjelenítendő térkép tartalmát paraméterek megadásával befolyásolhatjuk, melyeket ”&” ill. ”|” jellel kell elválasztanunk egymástól. A teljes kérés egy „http://” kezdetű link formájában adható meg.
A link kötelező elemei, sorrendben:

A link első része mindig ugyanaz:

http://maps.googleapis.com/maps/api/staticmap?

A térkép közepének megadása:

center=

Az egyenlőség jel után vagy GPS koordinátákat adunk meg (latitude, longitude sorrendben), vagy szövegesen helyeket, címet (Pl.: Budapest, Hősök tere). Idézőjeleket nem kell használni.
Megjegyezzük, hogy az API a GPS koordinátákat az első hat tizedes jegyig dolgozza fel, az azután következő számjegyeket figyelmen kívül hagyja.

A térkép nagyításának megadása:

zoom=

Értéke általában 0 és 20 között van. A kisebb érték a kisebb nagyítást jelenti.

Képméret megadása:

size=

Kötelező paraméter, az eredményül visszaadandó képfájl dimenziói, pixelben. Megadási módja : SZÉLESSÉG x MAGASSÁG.

A térkép típusának megadása:

maptype=

Négyféle értéke lehet: terrain, satellite, roadmap, hybrid.

Jelölők hozzáadása:

markers=

Egy jelölő (angolul: location pin) grafikusan mutat egy adott pontot a térképen és akár többet is használhatunk belőlük.
A jelölők színét is beállíthatjuk és egy egykarakteres szöveges azonosítót is megjeleníthetünk rajtuk.
Az alábbi példában egy kék színű jelölő van megadva, mely Budapestre mutat:

markers=color:blue|Budapest

Szenzorhasználat megadása:

sensor=

Kötelező paraméter, értéke true, vagy false, attól függően, hogy a statikus térkép használatakor a felhasználó helyzetének megállapításához szenzort használtak-e vagy sem. A gyakorlatban nyugodtan lehet az értéke false is, a végeredmény szempontjából nincsen jelentősége (a Google a saját statisztikáihoz használja fel ezt a paramétert).

Az alábbi link segítségével domborzati megjelenítésben kérünk le egy 300x300 pixeles képet Budapest központjáról, megjelenítve egy kék színű jelölőt is:

http://maps.googleapis.com/maps/api/staticmap?center=47.498323,19.040285&zoom=14&size=300x300&maptype=terrain&markers=color:blue|Budapest&sensor=false



A képfájl formátuma alapesetben PNG, mérete a fenti példában megközelítőleg 46KB.
A visszaadott térképek bal alsó sarkában mindig látható a Google vízjele.
A valóságban sokkal több paraméter is megadható, megjeleníthetünk például útvonalakat is, stb.

Amennyiben térképmegjelenítési igényeinket a Google szolgáltatása kielégíti, roppant egyszerűen és könnyen készíthetünk olyan alkalmazásokat, melyeket térképként használhatunk valós időben.
A feladat csak annyi, hogy dinamikusan előállítsunk egy, a fentihez hasonló linket, mely a saját helyzetünk adatait tartalmazza.
Az alábbiakban egy komplett példaalkalmazás (Pelda_05) forráskódját közöljük le, melynek segítségével könnyedén megállapíthatjuk és megjeleníthetjük saját helyzetünket.
A térkép betöltésére és megjelenítésére egy StageWebView osztálypéldányt fogunk használni, azaz egy integrált böngészőfelületet.
A GPS adatok 10 másodpercenként frissülnek, ilyenkor a térkép frissítése is megtörténik. A képernyő nagy részét a térkép foglalja el.
Az alkalmazást a 'Befejezés' gombbal tudjuk bezárni.

Az alkalmazás indulásakor az init függvénnyel inicializáljuk a GPS vevőt és a megjelenítendő térkép tulajdonságait is.

public function init():void
{
...

Először a GPS vevő detektálása és előkészítése történik meg. A GPS adatok frissítését 10 másodperces időközökre állítjuk.

if(Geolocation.isSupported==true)
{
geoLocation = new Geolocation();

geoLocation.setRequestedUpdateInterval(10000);
...

Előkészítjük a térkép megjelenítéséhez használt StageWebView komponenst.

terkep.stage= this.stage;

A térkép függőleges maximális méretének kiszámolásánál a 'Gomb' elnevezésű nyomógomb magasságát szintén figyelembe vesszük, nehogy a térképmegjelenítés „kitakarja” a nyomógombot.

terkep_szelesseg=stage.width;

terkep_magassag=stage.height-Gomb.height-30;

A térkép magasságát és szélességét a képernyő rendelkezésre álló területéhez igazítjuk, de ügyelünk arra is, hogy 640 pixelnél ne legyen nagyobb egyik kiszámolt érték sem, mivel ennél nagyobb méretű képet nem lehet lekérni.

if(terkep_szelesseg > 640) terkep_szelesseg=640;
if(terkep_magassag > 640) terkep_magassag=640;

Ezt követően ténylegesen meg is jelenítjük a StageWebView komponensünket.

terkep.viewPort=new Rectangle(0,0,terkep_szelesseg,terkep_magassag);

Amennyiben mobileszközünk nem rendelkezik GPS vevővel, vagy az le van tiltva, akkor egy hibaüzenet fog megjelenni a térkép helyén. A hibaüzenet megjelenítéséhez egy egyszerű Label objektumot használunk.

hibauzenet.visible=true;

A GPS adatok minden frissítéséhez a GPS_frissit nevű függvényt rendeljük. Ez a függvény kéri le a GPS koordinátákat és ezeket felhasználva azonnal előállít egy http linket is, melyet egy egyszerű szöveges változóban (terkep_link) tárolunk. Ezt a linket töltjük majd be és jelenítjük meg a loadURL metódussal a StageWebView objektummal.

geoLocation.addEventListener(GeolocationEvent.UPDATE,GPS_frissit);

public var terkep_link:String;

Minimális hibakezeléssel ellátjuk a böngészőmegjelenítőt: amennyiben a betöltés folyamán hiba lépne fel (pl. nincs internetkapcsolat), rendszerüzenet, vagy egyéb nem kívánatos hibajelenség helyett a hibakezelo nevű függvény fog végrehajtódni.

terkep.addEventListener(ErrorEvent.ERROR,hibakezelo);

terkep.addEventListener(IOErrorEvent.IO_ERROR,hibakezelo);

Az alkalmazás motorja a GPS_frissit függvény, mely először is kiolvassa a GPS vevő által szolgáltatott koordinátákat:

public function GPS_frissit(event:GeolocationEvent):void
{
latitude=event.latitude;

longitude=event.longitude;


A kapott adatok alapján már könnyedén fel tudjuk építeni a szolgáltatáskérésünk URL-jét. A link a térkép közepének mindig készülékünk pillanatnyi tartózkodási helyét állítja be.

terkep_link="http://maps.googleapis.com/maps/api/staticmap?center=";

terkep_link+=latitude.toString()+","+longitude.toString()+"&zoom=14&size=";

terkep_link+=terkep_szelesseg.toString()+"x"+terkep_magassag.toString();

terkep_link+="&maptype=terrain&markers=color:blue|";

terkep_link+=latitude.toString()+","+longitude.toString();

terkep_link+="&sensor=false";

Utolsó lépésként pedig kezdeményezzük az elkészített link betöltését:

terkep.loadURL(terkep_link);
}

Az alkalmazás teljes forráskódja a következő:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationDPI="160"
applicationComplete="init()">
<fx:Script>
<![CDATA[

import flash.sensors.Geolocation;   
import mx.events.FlexEvent;
  
public var geoLocation:Geolocation;

public var terkep:StageWebView = new StageWebView();

public var terkep_link:String;

public var terkep_szelesseg:int;

public var terkep_magassag:int;

public var latitude:Number;

public var longitude:Number;
 
//Az alkalmazás előkészítése
public function init():void
{    
if(Geolocation.isSupported==true)
{
 geoLocation = new Geolocation();

 geoLocation.setRequestedUpdateInterval(10000);

geoLocation.addEventListener(GeolocationEvent.UPDATE,GPS_frissit);
    
 terkep_link="";

 terkep.stage= this.stage;

 terkep.addEventListener(ErrorEvent.ERROR,hibakezelo);

 terkep.addEventListener(IOErrorEvent.IO_ERROR,hibakezelo);

 terkep_szelesseg=stage.stageWidth;

 terkep_magassag=stage.stageHeight-Gomb.height-30;
    
 if(terkep_szelesseg > 640)
  terkep_szelesseg=640;

 if(terkep_magassag > 640)
  terkep_magassag=640;

terkep.viewPort=new Rectangle(0,0,terkep_szelesseg,terkep_magassag);

}
else
{
 hibauzenet.visible=true;
}   
}
//Hibakezelő függvény  
public function hibakezelo(event:ErrorEvent):void
{
;
}
//A GPS vevő adatauinak kiolvasása
//és a szolgáltatáskérés linkjének előállítása  
public function GPS_frissit(event:GeolocationEvent):void
{
latitude=event.latitude;

longitude=event.longitude;
   terkep_link="http://maps.googleapis.com/maps/api/staticmap?center=";
terkep_link+=latitude.toString()+","+longitude.toString()+"&zoom=14&size=";
terkep_link+=terkep_szelesseg.toString()+"x"+terkep_magassag.toString();
terkep_link+="&maptype=terrain&markers=color:blue|";
terkep_link+=latitude.toString()+","+longitude.toString();
terkep_link+="&sensor=false";
terkep.loadURL(terkep_link);
}
  
//Kilépés az alkalmazásból
public function Kilepes():void
{
NativeApplication.nativeApplication.exit();
}

]]>
</fx:Script>
<s:Button id="Gomb" label="Befejezés" bottom="5" width="100%" click="Kilepes()"/>

<s:Label id="hibauzenet" text="Nem érzékelhető GPS!" top="10" horizontalCenter="0" visible="false" />
</s:Application>




A példaalkalmazást jelentősen továbbfejleszthetjük igényeinknek megfelelően: a zoomolás mértékét, a térkép-megjelenítést stb. egyedi kezelőfelületen, például nyomógombokkal módosíthatóvá tehetjük.
Az alkalmazás nem kezeli a képernyő elfogatását. (A 10. fejezetben ennek a módját is bemutatjuk majd.)
A térkép jobb és bal alsó részén megjelenhetnek görgetősávok is, ha a térkép-megjelenítő méreteit nem pontosan állítjuk be. Androidos eszközön a megjelenített kép két ujjal végzett nagyítása automatikusan lehetséges.

6.2. Dinamikus térkép-megjelenítés

A dinamikus Google térképmegjelenítés hasonlóan kezdeményezhető, mint a statikus.
A dinamikus térképek használata során viszont nem csupán térkép-megjelenítés történik, hanem automatikusan megjelenik egy mobileszközökre optimalizált felhasználói felület is, melyen például a térkép nagyítását/kicsinyítését is elvégezhetjük, vagy szövegesen is begépelhetünk címeket. Ezt a Google biztosítja és a felület betöltése automatikus.
Hatalmas előny az előző módszerhez képest, hogy sokkal szabadabban nézegethetjük a térképeket és GPS vevő hiánya esetén interaktív módon használhatunk szabadszöveges keresést is. Internetkapcsolat persze ebben az esetben is szükséges.
A kiindulási link ebben az esetben:

http://maps.google.com/?q=

A ”q=” után GPS koordinátákat, vagy szöveges utcacímet, településnevet, földrajzi nevet is megadhatunk.
Az alábbi link egy Magyarországra mutató jelölővel tölti be a Google térképet. A linket asztali számítógépen is kipróbálhatjuk, hogy lássuk a működését, bár a megjelenítés más lesz, mint Androidon:

http://maps.google.com/?q=Magyarország

Magyarország lesz következő példaalkalmazásunk (Pel¬da_06) alapértelmezett „kezdőoldala”. Mobilalkalmazásunk felületén egy plusz nyomógombot is elhelyezünk, mellyel automatikusan saját helyzetünkre állíthatjuk a térkép-megje¬lení¬tést. Minden másról a Google által biztosított felület gondoskodik.
A megjelenítés ezúttal is egy StageWebView komponensen történik, alkalmazásunkba integrálva.
Az alkalmazás forráskódja nagyon hasonlít az előző fejezetben megismert kódhoz, ám annál jóval egyszerűbb.
A GPS vevő ütemezéséhez itt is hozzárendelünk egy GPS_frissit nevű függvényt, ám itt nem frissítjük automatikusan a térképet, csupán a készülék helyzetét kérdezzük le. Ezt azért tesszük így, mert a dinamikus térkép betöltése némi időt (pár másodpercet) vehet igénybe és a folyamatos frissítés lassú internetkapcsolat esetén a használhatatlanságig lelassítaná az alkalmazást.

public function GPS_frissit(event:GeolocationEvent):void
{
latitude=event.latitude;

longitude=event.longitude;
}

A "Saját hely" feliratú gombra lenyomásával tudjuk frissíteni a térképet. A gomb lenyomásakor végrehajtódó függvény felépíti a szükséges linket és betölti az oldalt a terkep elnevezésű webes megjelenítőbe.

public function sajat_hely():void
{

terkep_link="http://maps.google.com/?q=";

terkep_link+=latitude.toString()+","+longitude.toString();

terkep.loadURL(terkep_link);

}

Az alkalmazás teljes forráskódja:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationDPI="160"
applicationComplete="init()">

<fx:Script>
<![CDATA[

import flash.sensors.Geolocation;   
import mx.events.FlexEvent;

public var geoLocation:Geolocation;   

public var terkep:StageWebView = new StageWebView();

public var terkep_link:String;   

public var terkep_szelesseg:int;   

public var terkep_magassag:int;   

public var latitude:Number;   

public var longitude:Number;


//Az alkalmazás előkészítése   
public function init():void
{    
latitude=47.5;

longitude=19.3;

if(Geolocation.isSupported==true)
{
 geoLocation = new Geolocation();

 geoLocation.setRequestedUpdateInterval(10000);

geoLocation.addEventListener(GeolocationEvent.UPDATE,GPS_frissit);
    
terkep.stage= this.stage;

terkep.addEventListener(ErrorEvent.ERROR,hibakezelo);

terkep.addEventListener(IOErrorEvent.IO_ERROR,hibakezelo);
    
terkep_szelesseg=stage.stageWidth;

terkep_magassag=stage.stageHeight-Gomb_01.height-30;
    
if(terkep_szelesseg > 640)
 terkep_szelesseg=640;

if(terkep_magassag > 640)
 terkep_magassag=640;
   
terkep.viewPort=new Rectangle(0,0,terkep_szelesseg,terkep_magassag);

terkep_link="http://maps.google.com/?q=Magyarország";
    
terkep.loadURL(terkep_link);
}
else
{
 hibauzenet.visible=true;
}
}
//Hibakezelő függvény  
public function hibakezelo(event:ErrorEvent):void
{
;
}
//A helyadatok lekérése  
public function GPS_frissit(event:GeolocationEvent):void
{
latitude=event.latitude;

longitude=event.longitude;
}
//A térképszolgáltatás linkjének előállítása  
public function sajat_hely():void
{
terkep_link="http://maps.google.com/?q=";

terkep_link+=latitude.toString()+","+longitude.toString();

terkep.loadURL(terkep_link);
}
 
//Kilépés az alkalmazásból
public function Kilepes():void
{
NativeApplication.nativeApplication.exit();
}

]]>
</fx:Script>
<s:Button id="Gomb_01" label="Befejezés" right="5" bottom="5" width="40%" click="Kilepes()"/>

<s:Button id="Gomb_02" label="Saját hely" left="5" bottom="5" width="40%" click="sajat_hely()"/>

<s:Label id="hibauzenet" text="Nem érzékelhető GPS!" top="10" horizontalCenter="0" visible="false" />
</s:Application>


Az alkalmazás elindítása után egy ehhez hasonló kép fogad minket:



A ’Saját hely’ gombra kattintva pedig megjelenik pillanatnyi helyzetünk:





7. GPS koordinátarendszer képpont alapú levetítése kijelzőkre

Külső térképszolgáltatást ugyan nagyon kényelmes igénybe venni, ám meglehet, hogy nagyobb mértékben szeretnénk befolyásolni a térkép-megjelenítést, ill. egyedi megjelenítést szeretnénk kölcsönözni a térképeknek.
Bemutatjuk, hogyan lehet egyedi, saját térkép-megjelenítést használó alkalmazásokat készíteni. Olyan technikákat és példákat mutatunk be, melyek nem vesznek igénybe harmadik fél által nyújtott szolgáltatásokat, kiegészítőket, könyvtárakat, osztályokat stb.. Csak és kizárólag a saját kódjainkat fogjuk használni.
Ebben a fejezetben a legáltalánosabb módszert mutatjuk be, mely egyben a leírásban bemutatott három módszer legbonyolultabbika. Ez egy projekciós motor és mindössze néhány függvényből áll, melyek segítségével teljesen vektoros, ill. képpont alapon tudunk koordinátákat megjeleníteni.
A 13. fejezetben két további módszert is bemutatunk, melyek jóval egyszerűbbek és az ActionScript beépített képmegjelenítési lehetőségeit is kihasználják.

7.1. Egy kis koordinátageometria

A leírás elején megismertük, hogy a hosszúsági fokok nyugatra és keletre 180-180 fokra, a szélességi fokok 90-90 fokra osztják fel a Földet.
Tehát -180-tól 180 és -90-től 90 fokig terjed a fokhálózat beosztása.
Ez a felosztás nagy hasonlóságot mutat egy kétdimenziós koordinátarendszerrel, melyben minden pont helyét egyértelműen megadhatjuk két koordinátával, a koordinátarendszer X és Y tengelye mentén.
A koordinátarendszer középpontja (más néven: origója) az X és az Y tengely 0,0 koordinátáin található.



Szinte az összes kijelző, legyen az LCD televízió, vagy tablet PC, egy ehhez hasonló koordinátarendszerben jeleníti meg a képernyőtartalmakat, egy lényeges különbséggel: az Y koordináták többnyire lefelé nőnek.
A kijelzők esetében a képernyő bal felső sarka szokta jelenteni az origó helyét.



Amennyiben GPS koordinátákat akarunk megjeleníteni, például egy telefon kijelzőjén, a következő dilemmákkal találjuk magunkat szemben:
1. A GPS koordináták törtszámok, míg a kijelzők koordinátarendszere csak egész számokat tud kezelni.
2. A GPS koordináták értékhatárai és a képernyők felbontásának értékei még csak köszönő viszonyban sincsenek egymással.
3. A különböző kijelzők képaránya változatos.
4. A koordináták egy görbült felület pontjainak feleltethetőek meg, míg a kijelzők sík felületek.
5. Az egyes képpontok (pixelek) nem tökéletes négyzet alakúak.

A 4. és 5. pontban megfogalmazott kihívásokkal nem foglalkozunk, ellenben az első három kezelése nélkül nem lehet élvezhető digitális megjelenítést megvalósítani, ezért ezekkel mindenképpen foglalkoznunk kell. Először ezeknek a nehézségeknek az elméleti megoldásait tekintjük át.

7.1.1. Törtszámok és koordinátarendszer

A „törtszám kontra egész szám” dilemma egy huszáros vágással feloldható.
Mind a hosszúsági, mind a szélességi koordináták törtszámok. A tizedes vessző után álló hatodik helyiértéken álló szám megfeleltethető (megközelítőleg) a méteres léptéknek. (Számunkra a megközelítőleg méteres pontosság már elegendő...)
A trükk az, hogy a GPS koordinátákat (a hosszúsági és szélességi koordinátákat külön-külön) megszorozzuk 100.000-rel és csak az egész értékeiket (ha több mint 6 tizedesjegyet tartalmazott az eredeti koordináta) dolgozzuk fel a továbbiakban. A 100.000-rel történő szorzás segítségével a méteres léptéket beemeljük az egész értékek szintjére, melyet már meg fogunk tudni jeleníteni.
Voilá! Egyetlen szorzással a GPS koordinátákból egy Greenwich origójú, (hatalmas méretű) koordinátarendszer érvényes, egész számú koordinátáit kaptuk meg.

7.1.2. A koordinátarendszerek szinkronizálása

A második pontban megfogalmazott kihívás megoldását fordított észjárással lehet a legkönnyebben megtalálni: ne a kijelzők felbontásértékeit próbáljuk ráerőszakolni a GPS koordinátarendszerre, éppen ellenkezőleg: a GPS koordinátarendszer egy meghatározott területét vetítsük le a kijelző felbontására, mint koordinátarendszerre!
Nézzünk egy példát! Tegyük fel, hogy Magyarország digitális térképét akarjuk megjeleníteni egy telefon kijelzőjén! Ez egy jól körülhatárolható, négyzetes területre illeszthető térképrészlet, aminek van egy bal felső és egy jobb alsó koordinátája. Akárcsak a telefon kijelzőjének!
Tegyük fel továbbá, hogy egy 800x480 pixel képernyőfelbontású telefonon akarjuk megjeleníteni a koordinátákat!
Első lépésben meg kell adnunk a térképünk két átellenes sarkának GPS koordinátáit.



A bal felső koordináták legyenek a következők:

48.306582, 15.982910

A jobb alsók pedig:

45.853290, 23.365722

Mint már tudjuk, a GPS koordinátákat először meg kell szoroznunk 100.000-el. Így viszont olyan hatalmas számokat kapunk, melyek jóval meghaladják a kijelzők felbontását. Le kell tehát arányosan kicsinyíteni ezeket a számokat.
Ehhez meg kell tudnunk, hogy a szélességi koordinátákból kapott számértékeink különbségének abszolútértékében, illetve a hosszúsági koordináták különbségének abszolútértékében, miután megszoroztuk 100.000-el, hányszor vannak meg a képernyő szélességi-, ill. magasságértékei. (Mivel abszolútértéket számolunk, mindegy, hogy melyik értéket melyikből vonjuk ki.)
Figyeljünk arra, hogy a hosszúsági értékek a 'longitude' koordináták, a szélességiek pedig a 'latitude' koordináták!
Végezzük el a tehát következő számításokat:

ABS (15,982910 – 23,365722) * 100.000= 738281

ABS (48,306582 – 45,853290) * 100.000 = 245329

Ezután a kapott értékeket elosztjuk a megfelelő képernyőfelbontás-értékkel, azaz:

738281 / 800 = 923

245329 / 480 = 511

Osszuk el 1-et mindkét számmal, külön-külön:

1 / 923 = 0,00108342

1/ 511 = 0,00195694

Válasszuk ki a kisebbik értéket! Ez a 0,00108342 lesz.
A fenti számítássorozattal egy olyan „mágikus” számot kaptunk, amellyel az átalakított (100.000-el megszorzott) GPS koordinátákat megszorozva, azok garantáltan a látható képernyőterület valamelyik pontján helyezkednek majd el.
Ezzel a GPS koordinátarendszer levetítését végezhetjük el a kijelző koordinátarendszerére egy egyszerű szorzás művelettel. A kapott mágikus szám pedig a kicsinyítés arányszáma.
Ezt az arányszámot felhasználhatjuk arra is, hogy – értékét növelve, ill. csökkentve – nagyítást, ill. kicsinyítést eszközöljünk térképmegjelenítésünknél.

7.1.3. Koordinátaleképezés ActionScript kóddal

Példáinkban mindig fekvő téglalap alakú térképterületeket dolgozunk fel, így a gyakorlatban elegendő a vízszintes arányszám kiszámolása.
Ehhez először a terület nyers szélességét kell kiszámolnunk.

var nyers_szeles:Number;

nyers_szeles=Math.abs(long1-long2)*100000;

A nyers szélesség ismeretében már könnyedén kiszámíthatjuk a szükséges nagyítási-kicsinyítési arányszámot:

var rajz_nagyitas:Number;

rajz_nagyitas=1 / (int)((nyers_szeles)/rajzvaszon.width));

Az „(int) ( KIFEJEZÉS )” használatával kikényszerítjük, hogy egész számot kapjunk eredményül, akkor is, ha lebegőpontos számokkal dolgozunk.
A nagyítási arányszámot használva meglehetősen nagy nagyítást érhetünk el, ezért érdemes kisebb értéket használni, például úgy, ha elosztjuk a kapott eredményt. Ha negyedére csökkentjük az értékét, az általában már megfelelő a normál nagyítású térkép-megjelenítéshez.

rajz_nagyitas=1 / (int)((nyers_szeles)/rajzvaszon.width)) / 4;

7.1.4. A kijelző képarányának figyelembe vétele

Az előző két alfejezet már majdnem tökéletes megoldást nyújt, ám sajnos csak négyzetes képarányú kijelzőkön adna helyes arányú képet.
A valóságban azonban sokféle képarányú kijelzővel találkozhatunk. A helyes arányú megjelenítéshez ki kell számolnunk egy, a kijelző képarányát kifejező törtszámot.
Ehhez először ellenőriznünk kell, hogy a képernyő szélességi, vagy magassági értékei a nagyobbak-e. Ez korántsem triviális kérdés. Attól függően ugyanis, hogy vízszintes, vagy függőleges tájolásban tartjuk-e a mobileszközt, a két számérték felcserélődhet.
A dolgokat tovább nehezíti, hogy tabletek esetében az alapértelmezett, ún. PORTRAIT tájolás az eszköz vízszintes tartását jelenti, míg telefonoknál a függőlegeset.
A lényeg viszont ugyanaz mindkét esetben: a kijelző vízszintes és függőleges méretei közül a kisebb értéket osszuk el a nagyobbal!
Amennyiben például a képernyő szélessége kisebb, mint a magassága (pl. függőleges tartású telefon), a következő számítást végezzük el:

képarány = szélesség / magasság

A kapott arányszám mindig egy 1-nél kisebb törtszám lesz. Ezt a számot kell függőlegesen tartott állásban egy adott hosszúsági GPS pont X képernyő-koordinátává alakított értékével megszorozni, vízszintesen tartott helyzetben pedig az átalakított szélességi GPS koordinátáéval.

7.1.5. Képaránykezelés ActionScript kóddal

A képarány kezelésére legjobb, ha szintén bevezetünk egy segédváltozót:

var kep_arany:Number;

kep_arany=szeles/magas;

A nagyítási és a képarányt megadó segédváltozók segítségével már ki tudjuk számítani egy adott GPS koordináta pontos helyét a képernyőn.

A számolás logikája a következő:

Hosszúsági GPS koordináta X koordinátává alakítása

Az általunk feldolgozott terület abszolút hosszúsági középpontjából (mint hosszúsági fokból) kivonjuk a megjeleníteni kívánt hosszúsági fokot, megszorozzuk 100.000-rel, majd a nagyítási arányszámmal és végül a képarányt kifejező számmal. Végezetül pedig az egészet kivonjuk 0-ból. Erre az utolsó lépésre azért van szükség, mert különben az ábrázolt pont a terület vízszintes középpontjához képest tükrözötten jelenne meg (térkép esetén egy vízszintesen tükrözött térképet kapnánk). Ezt a műveletet a keleti hosszúsági koordináták esetében lehet és kell alkalmazni, mivel keleti irányba nőnek az értékeik.

Szélességi GPS koordináta Y koordinátává alakítása

Az általunk feldolgozott terület abszolút szélességi középpontjából (mint szélességi fokból) kivonjuk a megjeleníteni kívánt szélességi fokot, megszorozzuk 100.000-rel, majd a nagyítási arányszámmal és végül a képarányt kifejező számmal.
Mivel a szélességi koordináták a (de csak az északi féltekén!) dél felé haladva csökkennek, így nincs szükség a kapott szám 0-ból történő kivonására.

Egy adott GPS koordinátának megfeleltetett képpont X és Y képernyő-koordinátáit a következő két Actionscript függvénnyel számolhatjuk ki.

public function get_point_x(long:Number):int
{    
return (int)((0)- (((GPS_LONG_ABS_KOZEPPONT-long)*100000) * rajz_nagyitas) * kep_arany );
}

public function get_point_y(lat:Number):int
{
return (int)((((GPS_LAT_ABS_KOZEPPONT-lat)*100000) )*rajz_nagyitas)*kep_arany_2;
}

A GPS_LONG_ABS_KOZEPPONT és GPS_LAT_ABS_KOZEPPONT helyére vagy konkrét koordinátákat írunk be, vagy egy olyan változót, melynek értékét dinamikusan számoljuk ki. Ebben a leírásban előre kiszámított értékekkel fogunk dolgozni.
A két függvény két különböző képarányszámot (nem összekeverendő a nagyítási arányszámunkkal, melynek neve: rajz_nagyitas) használ fel a számításhoz. Ez azért van  így, mert ezt az arányszámot vagy csak az egyik függvényben, vagy csak a másikban kell csak használni. Ha az egyikben használjuk, a másikban nem kell.
Hogy mikor melyiket kell használni, az a képernyő szélességétől és magasságától függ. Ha a képernyő szélessége nagyobb, mint a magassága, akkor csak az Y képernyő-koordináták kiszámításánál kell használnunk, ellenkező esetben pedig csak az X koordináták kiszámításánál.
A megfelelő segédváltozók előkészítésére egy külön inicializációs függvényt fogunk használni példaalkalmazásainkban, mindössze arra kell figyelnünk, hogy mindig a megfelelő értékekkel dolgozzunk.

public function display_init(lat1:Number,long1:Number,lat2:Number,long2:Number,szeles:int,magas:int):void
{   
kep_arany=kep_arany_2=szeles/magas;

nyers_szeles=Math.abs(long1-long2)*100000;

rajz_nagyitas=1/(int)(nyers_szeles/rajzvaszon.width);

if(stage.stageWidth >= stage.stageHeight)
 kep_arany=1;

if(stage. stageWidth < stage. stageHeight)
 kep_arany_2=1;
}

A lat1, long1 paraméterei a térképünk „bal felső” GPS koordinátái, a lat2,long2 paraméterei pedig a „jobb alsó” GPS koordinátái lesznek.
A szeles és magas elnevezésű paraméterek a megjelenítés szélessége és magassága lesznek, például a képernyő szélessége és magassága.

Mindössze három rövidke függvény megírásával elkészítettünk egy általános projekciós megjelenítő motort.
Megjegyezzük, hogy többféle szabványos módszer is létezik koordináták, térképek levetítésére, megjelenítésére, az egyik legelterjedtebb a WGS-84 elnevezésű.
A leírásban ismertetett módszerek a Föld görbületét, illetve ellipszoid alakját nem veszik figyelembe, ezért az északi és a déli pólusokhoz közeli koordináták, illetve térképek ábrázolását némi pontatlansággal képesek elvégezni. Ez a pontatlanság azonban inkább elméleti jelentőségű, mint gyakorlati.
A GPS koordináták további érdekes felhasználási lehetőségeire hamarosan visszatérünk, most azonban (mintegy pihenésképpen) megismerkedünk az AIR alkalmazásokban használható alapvető rajzolási technikákkal, melyekre nagy szükségünk lesz saját térképeink megjelenítésekor.



8. A vektoros rajzolás alapjai

Az ActionScript egyik erőssége többek között fejlett rajz- és animációs képessége, melyet nyelvi szinten közvetlenül is programozni tudunk.
Ebben a fejezetben megismerkedünk a legfontosabb vektoros rajzolási metódusokkal és használatukkal.
Az AIR teljes mértékben támogatja a grafikus gyorsítással ötvözött két- és háromdimenziós rajzolást is a Starling, Stage3D és Away3D API-kon keresztül.
Példaalkalmazásainkhoz ezek az API-k nem szükségesek, de a teljesség kedvéért felhívjuk a figyelmet ezekre a lehetőségre is.

Az alapvető rajzolási rutinok használatához importálnunk kell a következő osztályt:

import flash.display.Graphics;

8.1. A rajzvászon

A klasszikus ActionScript rajzolásban a Stage objektumra történik a rajzolás. Ebben a leírásban egy általánosabb, ha lehet úgy mondani: rugalmasabb technikát mutatunk be.
A rajzolást nem a Flash ’színpad’ objektumára, hanem egy Group komponensen fogjuk elvégezni.
Miért jobb ez a módszer? Group komponensből több is lehet és úgy használhatjuk őket, akár a rajzrétegeket a grafikus szerkesztőprogramokban. Az egyes rétegek ki-bekapcsolásával ráadásul nagymértékben testreszabhatóvá tehetjük alkalmazásaink megjelenítését.
A Group komponens önmagában nem jelenít meg semmit és teljesen átlátszó, ezért üres rajzvászonként kiválóan használható.
Létrehozása nagyon egyszerű Flex-ben. Az alábbi sorral például egy, a teljes rendelkezésre álló képernyőfelületet kitöltő „rajzvásznat” hozunk létre:

<s:Group id="rajzvaszon" width="100%" height=”100%” visible="false" />


 

Rétegek használata komplex rajzoláshoz

8.2. A rajzecset

A rajzoláshoz minden esetben egy képzeletbeli rajzecsetet használhatunk, amelyre úgy gondolhatunk, akár egy valódi ecsetre. Az ecset vastagsága és színe is különböző lehet, adott esetben az ecset kiindulási helyzetét is megadhatjuk, illetve meg is kell adnunk.

8.3. Ecsetbeállítások

Az ecset színét a lineStyle metódussal tudjuk beállítani.
A metódus változó hosszúságú paraméterlistát használ, ami azt jelenti, hogy nem muszáj az összes paramétert megadni, csak azt, amit be akarunk állítani.
Az első paraméter a rajzecset vastagságát állítja be, a második a színét, hexadecimális formátumban.
Az alábbi példában a rajzvaszon objektumon használt rajzecsetet egy pixel szélességűre és élénkvörös színűre állítjuk be.

rajzvaszon .graphics.lineStyle(1, 0xFF0000);

A metódus általános alakja:

lineStyle(thickness:Number=null, color:uint=0, alpha:Number=1.0, pixelHinting:Boolean=false, scaleMode:String="normal", caps:String=null, joints:String=null, miterLimit:Number=3):void

Elsősorban vonalak rajzolásakor szükség lehet az ecset pozíciójának a beállítására is, erre a moveTo metódus szolgál.
Az alábbi példában az ecsetet a képernyő bal felső sarkába állítjuk:

rajzvaszon.graphics.moveTo(0,0);

A metódus általános alakja:

moveTo(x:Number, y:Number):void

8.4. Színek megadása RGB kódokkal

ActionScriptben és általában is a modern programozási nyelvekben, 32 bites integer értékeket használnak színek megadására.
A színeket hexadecimális formában közvetlenül is megadhatjuk, pl. a fekete szín a 0x000000, vagy a fehér 0xFFFFFF.
Mivel azonban a számítógépek a színeket vörös, zöld, kék színek keverésével állítja elő és ez a fajta színmegadás egyszerűbb, ezért kényelmes volna, ha RGB értékekkel is tudnánk dolgozni. Noha ilyen beépített rajzmetódus nincsen az ActionScriptben, könnyedén írhatunk magunknak egyet.
Az alábbi példa pontosan ezt a feladatot végzi el:

public funtcion RGBtoINT(r:int,g:int,b:int):int
{
return r << 16 | g<<8 | b;
}

A függvény a megadott színkódokra biteltolási műveleteket alkalmaz és ezekből állítja elő az int típusú színértéket, melyet egész értékként ad vissza. Használatával könnyebben értelmezhető módon tudunk megadni színeket alkalmazásainkban.
Az r, g, b argumentumok 0-255-ig vehetnek fel értékeket, így összesen 16777216 lehetséges színt állíthatunk elő.
Élénkzöld színkódot például így tudunk előállítani:

RGBtoINT(0,255,0);

8.5. Képpontok rajzolása

ActionScriptben kifejezetten egyedi képpont rajzolására nincsen beépített rajzmetódus, helyette egy meglévő másik rajzrutint használhatunk.
Egy fekete képpont kirajzolásához például használhatunk egy egyetlen képpont szélességű négyzetet kirajzoló metódust.
Az alábbi kódrészlet a képernyő 50,50 pixelkoordinátájába rajzol ki egy képpontot:

rajzvaszon.graphics.lineStyle(1, 0x440000);
rajzvaszon.graphics.drawRect(50,50,1,1);

8.6. Vonalak rajzolása

A vonalrajzolás kezdetén általában ki kell jelölni a vonal (rajzolás) kezdőpontját, utána pedig a vonal végpontját.
Egymás után több végpontot megadva hosszabb, összefüggő törtvonalat is rajzolhatunk a lineTo metódussal.
Az alábbi kóddal egy átlós vonalat rajzolhatunk a fentebb létrehozott „rajzvásznunkra”, annak két átellenes sarkát összekötve:

rajzvaszon.graphics.lineStyle(2, 0x00FF00);
rajzvaszon.graphics.moveTo(0,0);
rajzvaszon.graphics.lineTo(rajzvaszon.width,rajzvaszon.height);

A lineTo metódus általános alakja:

lineTo(x:Number, y:Number):void

8.7. Kör és ellipszis rajzolása

A kör és ellipszis rajzolása abban tér el egymástól, hogy kör esetében csak a kör sugarának a méretét kell megadnunk (pixelben), míg ellipszis esetén a vízszintes és függőleges sugarat (azaz a vízszintes és függőleges arányokat) külön is meg kell adnunk.
A két metódus a drawCircle és a drawEllipse.

Példák:

rajzvaszon.graphics.drawCircle(100,100,50);

rajzvaszon.graphics.drawEllipse(100,100,50,30);

A metódusok általános alakjai:

drawCircle(x:Number, y:Number, radius:Number):void

drawEllipse(x:Number, y:Number, width:Number, height:Number):void

8.8. Kitöltőszínek használata

A lineStyle metódus segítségével valójában csak a rajzolás körvonalának megjelenését tudjuk szabályozni.
Amennyiben egy alakzatot ki is akarunk tölteni valamilyen színnel, a beginFill()..endFill() metóduspárost kell használnunk.
A használatuk roppant egyszerű: a két metódushívás között megrajzolt, kitölthető objektumok automatikusan ki lesznek töltve a beállított színnel.
A kitöltést a beginFill() metódus paramétereivel állíthatjuk be. A metódus első paramétere a kitöltési színt, a második az átlátszóságot szabályozza (ez valójában egy törtszám, 1-es értéke a 100%-os megjelenítést jelenti, míg 0.5-ös értékkel félig átlátszóvá tehetjük a kitöltött területet.
Egy kört például az alábbi kódrészlettel tudunk kék színnel kitöltve megjeleníteni:

rajzvaszon.graphics.beginFill(0x0000FF,1);
rajzvaszon.graphics.drawCircle(0,rajzvaszon.height/4,rajzvaszon.width/10);
rajzvaszon.graphics.endFill();

A beginFill() és endFill() metódusok általános alakjai:

beginFill(color:uint, alpha:Number=1.0):void

endFill():void

8.9. Poligon rajzolása

Poligont legegyszerűbben úgy rajzolhatunk, ha vonalakat rajzolunk közvetlenül egymás után a lineTo metódussal.
Amennyiben ezt a beginFill()..endFill() metóduspárossal kombinálva tesszük, a poligont színnel is kitölthetjük.
Egy kitöltött háromszöget például a következő módon rajzolhatunk ki:

rajzvaszon.graphics.lineStyle(1, 0x000000);
rajzvaszon.graphics.beginFill(0x0000FF,1);
rajzvaszon.graphics.moveTo(100,100);
rajzvaszon.graphics.lineTo(150,150);
rajzvaszon.graphics.lineTo(50,150);
rajzvaszon.graphics.lineTo(100,100);
rajzvaszon.graphics.endFill();

8.10. Szöveg kiíratása

Szövegrajzolásra nincsen külön rajzmetódus, helyette a rajzvászonhoz adhatunk Label típusú ActionScript objektumokat.
Mivel ezúttal nem Flex-en keresztül, hanem közvetlenül ActionScriptből hozunk létre vizuális komponenst, ennek az osztályát külön importálnunk kell alkalmazásunk elején:

import spark.components.Label;

A szövegcímke objektum létrehozása egyszerű:

var cimke:Label = new label();

A szöveges objektum tulajdonságának a beállítása már kevésbé egyszerű. ActionScript kódban ugyanis nem adhatunk meg MXML kódrészleteket és nem hivatkozhatunk közvetlenül a szövegobjektum tulajdonságaira sem. Helyette a címke komponens SetStyle metódusát kell használnunk.

Például a felhasznált betűtípus méretének a beállítása a következő módon történik:

cimke.setStyle("fontSize", "26");

A leggyakrabban használt tulajdonságok a következők:
x
A szöveg kezdetének vízszintes koordinátája.
y
A szöveg kezdetének függőleges koordinátája.
fontFamily
A betűtípus neve.
fontSize
A betűk mérete.
fontWeight
A betűk vastagsága (értéke ”normal”, vagy ”bold”).
fontStyle
A betűk döntöttsége (értéke ”normal”, vagy ”italic”).
color
A betűk színe.
backgroundColor
A kiírt szöveg háttérszíne (ha nem állítjuk be, a szöveg háttere átlátszó lesz).
textAlign
A kiírt szöveg vízszintes elrendezése. Lehetséges értékei: „center”, „end”, „justify”, „left”, „right”, „start”.
verticalAlign
A megjelenített szöveg függőleges elrendezése az alapvonalához képest. Lehetséges értékei: „bottom”, „justify”, „middle”, „top”.
Felhívjuk a figyelmet a fenti tulajdonságok megadásánál a kisbetűs/nagybetűs írásmód betartására. Amennyiben helytelenül adunk meg egy nevet, a tulajdonság nem lesz alkalmazva a szövegre.
A szöveget a rajzvászonhoz az addElement metódussal tudjuk hozzáadni, azaz megjeleníteni:

rajzvaszon.addElement(cimke);

8.11. A rajzvászon törlése

A kirajzolt rajzalakzatok törlése a rajzvászonról egyetlen metódussal elvégezhető:

rajzvaszon.graphics.clear();

A pixelalapú rajzolás (vonalak, körök stb.) és az objektumszintű hozzáadás (lásd szövegobjektumok hozzáadása fentebb) között van egy lényegi különbség: az objektumok a kirajzolásuk után is megmaradnak.
Amennyiben el akarjuk távolítani ezeket az objektumokat is, arra külön metódust kell meghívnunk.

rajzvaszon.removeAllElements();

A fenti két törlőmetódust együtt használva tehát már valóban minden, korábban kirajzolt elemet eltávolíthatunk a rajzvásznunkról.
A fentiek illusztrálására készítettünk egy példaalkalmazást (Pelda_07), melynek kódját alább tanulmányozhatjuk. Az alkalmazás minden bemutatott alakzat- ill. objektumtípust kirajzol a képernyőre:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160"
creationComplete="init(event)">

<fx:Script>
<![CDATA[

import mx.events.FlexEvent;
import flash.display.Graphics;
import spark.components.Label;
public function init(event:FlexEvent):void
{
var szoveg:Label;

//rajzvászon láthatóvá tétele
rajzvaszon.visible=true;
//képpont rajzolása
rajzvaszon.graphics.lineStyle(1, 0x440000);
rajzvaszon.graphics.drawRect(50,50,1,0);
//átlós vonal rajzolása
rajzvaszon.graphics.lineStyle(2, 0x00FF00);
rajzvaszon.graphics.moveTo(0,0);
rajzvaszon.graphics.lineTo(rajzvaszon.width,rajzvaszon.height);
//körvonal rajzolása
rajzvaszon.graphics.lineStyle(2, 0x0000AA);
rajzvaszon.graphics.drawCircle(rajzvaszon.width/2,rajzvaszon.height/4,rajzvaszon.width/10);
//ellipszis rajzolása
rajzvaszon.graphics.lineStyle(2, 0x0000AA);
rajzvaszon.graphics.drawEllipse((rajzvaszon.width/2)+(rajzvaszon.width/4),rajzvaszon.height/4,50,30);
//kitöltött kör rajzolása
rajzvaszon.graphics.lineStyle(2, 0x000044);
rajzvaszon.graphics.beginFill(0x0000EE,1);
rajzvaszon.graphics.drawCircle(0,rajzvaszon.height/4,rajzvaszon.width/10);
rajzvaszon.graphics.endFill();

//kitöltött négyzet rajzolása
rajzvaszon.graphics.lineStyle(4, 0xEE2222);
rajzvaszon.graphics.beginFill(0x888888,1);
rajzvaszon.graphics.drawRect(rajzvaszon.width/3,(rajzvaszon.height/2)+(rajzvaszon.height/4),150,100);
rajzvaszon.graphics.endFill();
//poligon rajzolása
rajzvaszon.graphics.lineStyle(1, 0x000000);
rajzvaszon.graphics.beginFill(0xCCCCCC,1);
rajzvaszon.graphics.moveTo(100,200);
rajzvaszon.graphics.lineTo(110,210);
rajzvaszon.graphics.lineTo(120,230);
rajzvaszon.graphics.lineTo(118,250);
rajzvaszon.graphics.lineTo(103,240);
rajzvaszon.graphics.lineTo(20,190);
rajzvaszon.graphics.endFill();

//szöveg megjelenítése
szoveg = new Label();
szoveg.setStyle("fontFamily", "Courier New");
szoveg.setStyle("fontSize", "12");
szoveg.setStyle("color", "0x000000");
szoveg.text="Egyszerű";
szoveg.x=200;
szoveg.y=200;
rajzvaszon.addElement(szoveg);
//szöveg kitöltött háttérszínnel
szoveg = new Label();
szoveg.setStyle("fontFamily", "Arial");
szoveg.setStyle("fontSize", "26");
szoveg.setStyle("fontWeight", "bold");
szoveg.setStyle("color", "0x0000FF");
szoveg.setStyle("backgroundColor", "0xBBBBFF");
szoveg.setStyle("textAlign", "center");
szoveg.text="Háttérrel";
szoveg.x=200;
szoveg.y=220;
rajzvaszon.addElement(szoveg);
}

//Kilépés az alkalmazásból
public function Kilepes():void
{
NativeApplication.nativeApplication.exit();
}

]]>
</fx:Script>
<s:Group id="rajzvaszon" width="100%" height="100%" visible="false" />

<s:Button label="Kilépés" bottom="0" width="100%" click="Kilepes()"/>
</s:Application>




8.12. Színátmenetek létrehozása

A színek előállításának egy haladó módja a színátmenetek létrehozása. Erre például domborzat megjelenítésekor, a különböző magasságú területek élethű ábrázolásához lehet szükség.
Színátmenetek létrehozása nem triviális feladat. Először is meg kell határoznunk, hogy mely kiindulási színből mely célszínbe történő színátmenetet színeit akarjuk előállítani. Ehhez a két szélső szín RGB kódjaira lesz szükségünk.
Egy olyan algoritmust mutatunk be, mely a színátmenet egy adott színét képes kiszámítani, a két szín közötti átmenet százalékosan megadott értéke alapján. Tehát az 50% megadásával pontosan a két szín között „félúton” ábrázolható átmeneti szín RGB értékeit fogjuk tudni kiszámolni.
Tegyük fel, hogy a fekete (0,0,0) és a fehér (255,255,255) közötti színátmenetek skálájának tetszőleges színei közül a 25%-os átmenet színét (sötétszürke) szeretnénk kiszámítani!
A százalék értéke tehát 25. Ez ActionScript kóddal:

var szazalek:Number;

szazalek =25;

A szazalek változó típusa lebegőpontos szám, ezáltal egészen finom átmeneteket is elő tudunk állítani.
Egy konkrét RGB színkomponens meghatározásához minden komponens esetén az alábbi számítást kell elvégeznünk:

R_kezdoszin * (1.0 – szazalek) + R_befejezőszin * szazalek

A számításhoz a fekete (0,0,0) és fehér (255,255,255) színeket alapul véve, ActionScript kóddal leírva ez így néz ki:

r = 0 * (1.0- szazalek) + 255 * szazalek;
g = 0 * (1.0-szazalek) + 255 * szazalek;
b = 0 * (1.0-szazalek) + 255 * szazalek;

Domborzatmegjelenítésnél a százalékérték előállítására érdemes figyelnünk. A cél mindig az legyen, hogy a lehető legtöbb színt jelenítsük meg, a lehető legegyenletesebben.
Egy gyakorlati példával élve, ha a 200-500 méteres magasságsávhoz rendelünk színátmenetet, akkor a százalékérték kiszámításához a következő számítás helyett

százalék = magasság / 500

a következő számítást használjuk:

százalék = (magasság-200) / 500

Így biztosak lehetünk benne, hogy a 200 méterhez tartozó szín lesz az átmenet alsó határához rendelt szín, míg az 500 méterhez tartozó a felsőhöz. Minden más szín pedig arányosan a kettő közötti színként fog megfelelni, egyenletes színátmenetet biztosítva.
A színátmenetek alkalmazására a 13. fejezetben látunk még példát.



9. Bittérkép-alapú rajzolás és képforgatás

A vektoros rajzolás adott esetben igen erőforrásigényes feladattá válhat. Szerencsére lehetőség van arra, hogy statikus képeket rajzoljunk. Ez azt jelenti, hogy a rajzolás elemei a kirajzolt képbe „bele lesznek égetve”.
A módszer hátránya egyben az előnye is, mert adott esetben sokkal gyorsabb kirajzolást és felhasználást tesz lehetővé.
Ez a rajzolási technika a BitmapData, Bitmap és a SpriteVisualElement objektumok együttes használatával kivitelezhető a legkönnyebben.
A bittérkép-alapú rajzoláshoz sajnos sokkal kevesebb előre elkészített metódust használhatunk, mint vektoros rajzolás esetén és ezek is kifejezetten négyzetes területek kezelésére alkalmasak.
Az alábbiakban megismerjük azt is, hogyan tudunk elforgatni kirajzolt egyszerű alakzatokat, képeket.

A rendelkezésre álló metódusok:
• fillRect()
• floodFill()
• setPixel()
• setPixel32()
• setPixels().

Természetesen saját függvények / metódusok írása minden további nélkül lehetséges.
Az alább bemutatott alkalmazás egy vektoros és egy bitképes alakzatot forgat, a szemléltetés kedvéért ellenkező irányokba.
A bittérkép alapú rajzolás négyzetes területeket kezel és pár metódus Rect típusú paramétert vár. A Rect osztályt külön importálnunk is kell:
  
import spark.primitives.Rect;
  
A kirajzolt alakzatok forgatása folyamatosan animált lesz, amihez egy időzítőt fogunk használni.

public var idozito:Timer=new Timer(10,0);

A kétféle alakzat forgatási szögét külön változókban tároljuk:

public var forgatasi_szog:int;

public var forgatasi_szog2:int;

A bittérképes rajzoláshoz először is szükségünk lesz egy BitmapData típusú objektumra, aminek a kep nevet adjuk. Ezt felfoghatjuk egyfajta absztrakt rajzterületnek is. Méretét 300x300 pixelre állítjuk be.

  
public var kep:BitmapData=new BitmapData(300,300,false,0xdddddd);

Ahhoz, hogy rajzunk meg is jelenjen, a kep objektumot hozzá kell rendelnünk egy Bitmap típusú objektumhoz.

public var bmp:Bitmap = new Bitmap(kep);

A képet ezután sajnos még nem fogjuk látni, noha már rajzolhatunk rá. A tényleges vizuális megjelenítést vektoros rajzolás esetében egy Group típusú vezérlőelemmel valósítottuk meg. A bitképes rajzoláshoz egy SpriteVisualElement típusú Flex objektumra is szükségünk lesz:

<s:SpriteVisualElement width="500" height="500" id="spr"/>

A bitképet még hozzá kell rendelnünk ehhez az objektumhoz, mielőtt rajzunk ténylegesen láthatóvá is válna.

spr.addChild(bmp);

 
Példaalkalmazásunk inicializációs függvénye előkészíti a forgatás mértékének megadásához használt változókat és kirajzol pár alakzatot mind a vektoros, mind a bitképes megjelenítést végző objektumra, majd beállítja és elindítja az időzítőt.


forgatasi_szog=0;

forgatasi_szog_2=360;


vektor_rajzvaszon.graphics.lineStyle(1, 0x000000);

vektor_rajzvaszon.graphics.beginFill(0xCCCCCC,1);

vektor_rajzvaszon.graphics.moveTo(100,200);

vektor_rajzvaszon.graphics.lineTo(110,210);

vektor_rajzvaszon.graphics.lineTo(120,230);

vektor_rajzvaszon.graphics.lineTo(118,250);

vektor_rajzvaszon.graphics.lineTo(103,240);

vektor_rajzvaszon.graphics.lineTo(20,190);

vektor_rajzvaszon.graphics.endFill();

   
bmp.x=0;

bmp.y=0;


kep.fillRect(new Rectangle(50,50,80,20),0x006622);

bitkep_rajzvaszon.addChild(bmp);


idozito.addEventListener("timer",auto_fuggveny);

idozito.start();


Az időzítőnek megadott auto_fuggveny nevű függvény végzi az animálást. Mivel az egyik alakzatot balra, a másikat jobbra forgatjuk, ezért a forgatási szögeket is növelni, ill. csökkenteni fogjuk.
A forgatási szögek értékei a 0-360 foktól kisebb és nagyobb értékek is lehetnek. Ha folyton növelnénk és csökkentenénk a szögeket, akkor ’túlforgatnánk’ az objektumokat, ami a gyakorlatban azt eredményezné, hogy az alakzatok egyre gyorsabban forognának. Ezt megelőzendő, az értékeket mindig 0 és 360 fok között tartjuk:


++forgatasi_szog;

--forgatasi_szog2;

if(forgatasi_szog == 360)
forgatasi_szog=0;

if(forgatasi_szog == 0)
forgatasi_szog=360;


Az alakzatok elforgatását a transformAround metódusokkal végezzük el. Megjegyezzük, hogy mind a Group, mind a VisualSpriteElement objektum rendelkezik ilyen metódussal.
A metódus általános alakja:


transformAround(transformCenter:Vector3D, scale:Vector3D=null, rotation:Vector3D=null, translation:Vector3D=null, postLayoutScale:Vector3D=null, postLayoutRotation:Vector3D=null, postLayoutTranslation:Vector3D=null, invalidateLayout:Boolean=true):void


Alkalmazásunkban elegendő az első három paramétert megadnunk, melyek az elforgatás középpontját, a nagyítást és a forgatás vektorát adják meg.
A vektorok háromdimenziós vektorok, melyeket a Vector3D nevű beépített osztályon keresztül érhetünk el.

Általános alakja:

Vector3D(x:Number=0.0, y:Number=0.0, z:Number=0.0, w:Number=0.0)

Az első három paraméter segítségével a 3d-s koordinátarendszer tengelyeihez viszonyított elforgatás mértékét adhatjuk meg. A negyedik paraméterrel opcionális és járulékos adatokat adhatunk meg. Példaprogramunkban csak az első három paraméter használatára szorítkozunk.
A forgatás középpontja a rendelkezésre álló képernyőterület közepe lesz, a forgatási szögek pedig a korábban már létrehozott forgatasi_szog  és forgatasi_szog2 nevű változóink.


rajzvaszon.transformAround(new Vector3D(stage.stageWidth/2,stage.stageHeight/2,0),null,new Vector3D(0,0,forgatasi_szog));
  
spr.transformAround(new Vector3D(stage.stageWidth/2,stage.stageHeight/2,0),null,new Vector3D(0,0,forgatasi_szog2));

}


Példaalkalmazásunk egyben arra is példa, hogy a rajzolás során Flex komponensek egymásra történő helyezésével rajzrétegeket valósíthatunk meg, melyek igen bonyolult grafikák elkészítését teszik lehetővé.


A példaalkalmazás (Pelda_08) teljes forráskódja:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160"
creationComplete="init(event)">

<fx:Script>
<![CDATA[

import flash.display.*;
import mx.events.FlexEvent;
import spark.primitives.Rect;
  
public var idozito:Timer=new Timer(10,0);

public var forgatasi_szog:int;

public var forgatasi_szog_2:int;
  
public var kep:BitmapData=new BitmapData(300,300,false,0xdddddd);

public var bmp:Bitmap = new Bitmap(kep);
 
//Az alkalmazás előkészítése
public function init(event:FlexEvent):void
{
forgatasi_szog=0;
forgatasi_szog_2=360;
   
//poligon rajzolása    
vektor_rajzvaszon.graphics.lineStyle(1, 0x000000);
vektor_rajzvaszon.graphics.beginFill(0xCCCCCC,1);
vektor_rajzvaszon.graphics.moveTo(100,200);
vektor_rajzvaszon.graphics.lineTo(110,210);
vektor_rajzvaszon.graphics.lineTo(120,230);
vektor_rajzvaszon.graphics.lineTo(118,250);
vektor_rajzvaszon.graphics.lineTo(103,240);
vektor_rajzvaszon.graphics.lineTo(20,190);
vektor_rajzvaszon.graphics.endFill();
   
bmp.x=0;
bmp.y=0;
//Négyszög rajzolása   
kep.fillRect(new Rectangle(50,50,80,20),0x006622);
bitkep_rajzvaszon.addChild(bmp);
   
idozito.addEventListener("timer",auto_fuggveny);
idozito.start();
}
//A forgatást végző függvény  
public function auto_fuggveny(event:TimerEvent):void
{
++forgatasi_szog;

--forgatasi_szog_2;

if(forgatasi_szog == 360)
 forgatasi_szog=0;

if(forgatasi_szog == 0)
 forgatasi_szog=360;
   
vektor_rajzvaszon.transformAround(new Vector3D(stage.stageWidth/2,stage.stageHeight/2,0),null,new Vector3D(0,0,forgatasi_szog));

  
bitkep_rajzvaszon.transformAround(new Vector3D(stage.stageWidth/2,stage.stageHeight/2,0),null,new Vector3D(0,0,forgatasi_szog_2));
}

//Kilépés az alkalmazásból
public function Kilepes():void
{
 NativeApplication.nativeApplication.exit();
}

]]>
</fx:Script>
<s:SpriteVisualElement width="500" height="500" id="bitkep_rajzvaszon"/>

<s:Group id="vektor_rajzvaszon" width="100%" height="100%" />

<s:Button label="Kilépés" bottom="0" width="100%" click="Kilepes()"/>
</s:Application>






10. Navigáció és matematika

Ebben a fejezetben megismerkedünk a GPS koordináták feldolgozásának különböző lehetőségeivel.
10.1. GPS koordináták távolsága

A pontos matematikai képlet, melynek segítségével a távolságot kilométerben kapjuk meg és figyelembe veszi a Föld ellipszoid alakját és sugarát is (mely kb. 6371 km), a következő:

tavolsag=
acos(cos(radians(90-lat1)) *
cos(radians(90-lat2)) +
sin(radians(90-lat1)) *
sin(radians(90-lat2)) *
cos(radians(long1-long2)))  * 6371

A számítások elvégzéséhez az ActionScript Math osztályának metódusait fogjuk tudni használni, a radiáns kiszámításának kivételével, ehhez ugyanis nincsen beépített ActionScript metódus.

A radiáns kiszámítása:

radians = fok * PI / 180

A fokot pedig a következő képlettel tudjuk kiszámítani a radiáns ismeretében:

fok = radians  - 180 / PI

Ennek ismeretében már ActionScriptben is fel tudjuk írni a távolságkiszámítás teljes függvényét:

tavolsag = Math.acos(Math.cos( ( (90-lat1) * (Math.PI /180) ) ) * Math.cos(( (90-lat2) * (Math.PI /180) )) + Math.sin(( (90-lat1) * (Math.PI /180) )) * Math.sin(( (90-lat2) * (Math.PI /180) )) * Math.cos(( (long1-long2) * (Math.PI /180) ))) * 6371;

Mivel a számítás eredménye egy lebegőpontos szám, méterben is kiszámíttathatjuk a pontos értéket, ha az eredményt megszorozzuk 1000-rel.
A képlet meglehetősen bonyolult, de nem kell fejből tudni. Egyszerűen csak használjuk!

10.2. Szomszédsági kapcsolatok ábrázolása

Hogyan tároljuk és dolgozzuk fel különböző pontok, például települések kapcsolatát?
Az egyik lehetséges megoldás ún. szomszédsági mátrixok, vagy útmátrixok használata, illetve kapcsolt listás szerkezetek alkalmazása.
Programozási szemszögből előzőeket egyszerű kétdimenziós tömbökként, utóbbiakat pedig dinamikusan létrehozott kapcsolt listákkal is meg lehet valósítani.
Ebben a fejezetben megismerjük azokat a számítástechnikában is használt alapvető módszereket, melyek földrajzi helyek kapcsolatainak leírására használhatóak.

10.2.1. A Bool mátrix

Adott három pont: A, B és C.
A következő kapcsolat áll fenn közöttük.



A három pont kapcsolatát egy ún. Bool mátrix segítségével nagyon egyszerűen ábrázolhatjuk, ami egy egyszerű kétdimenziós tömb. A módszer előnye az egyszerűsége, hátránya viszont, hogy a kapcsolatok meglétén kívül semmilyen más többletinformációt nem tárol.
A nemlétező kapcsolatokat 0-val jelöljük, a meglévőeket pedig 1-el.
A kapcsolatok mátrixa a következőképpen néz ki:



Két pont között tehát ott van kapcsolat, ahol egy 1-es áll a táblázatban. Nullával pedig a nemlétező kapcsolatokat jelöljük.

10.2.2. Élsúlyozott kapcsolatok útmátrixa

Jogos az igény, hogy a kapcsolatokat súlyozzuk, azaz értékkel lássuk el. Ezek az ún. súlyozott élek minden további nélkül felfoghatóak a két pont közötti útvonalak hosszúságainak is.



Az ún. útmátrix felépítése a következő:



A Bool mátrixok és a fenti élsúlyozott szomszédsági mátrixok tagadhatatlan előnye az egyszerűség, mégis sok hátrányuk is van.
A legsúlyosabb hátrány a helypazarlás. A 0-ák száma (ahol nem létezik kapcsolat) rendkívül magas lehet a tényleges kapcsolatot jelölő helyek számához képest. A sok 0-át tartalmazó mátrixokat ún. ritka mátrixoknak nevezi a matematika.
További hátrány, hogy ezek a mátrixok nehezen módosíthatóak. Ha megváltozik a tárolt kapcsolatok szerkezete (például, ha egy új csomópontot viszünk a rendszerbe), a mátrixot csak tetemes mennyiségű munkával lehet frissíteni.
Amennyiben a kapcsolatok lehetséges iránya lényegtelen (tehát: hogy egy kapcsolat csak egy irányba, vagy visszafelé is fennáll), akkor szintén feleslegesen tárolódnak az oda-vissza érvényes útvonalkapcsolatok is.
A következőkben még hatékonyabb ábrázolási módokkal ismerkedünk meg.

10.2.3. Irányított és élsúlyozott kapcsolatok

Irányított éleket akkor alkalmazunk, amikor külön jelezni akarjuk, hogy egyik csomópontból a másikat (és vissza) el lehet-e érni.
Úgy gondolhatunk erre, mint az egyirányú és kétirányú utcákra. Az alábbi illusztráció bemutatja ezt a fajta ábrázolást.



A fenti kapcsolatokat a következő mátrix-szal írhatjuk le.



Ezúttal már a csomópontok azonosítóit és a tényleges súlyokat (távolságokat) tároljuk a mátrixban.
Mint látjuk, ezúttal nincsenek „lyukak” a mátrixban, csak a tényleges kapcsolatokat tároljuk.
Eddig nem említettük, de most eláruljuk, hogy a fenti példákban valójában gráfokkal dolgoztunk és azok ábrázolását kezdtük el vizsgálni. A gráf matematikai fogalom.
A gráfok szerkezetét ún. faszerkezettel lehet vizuálisan ábrázolni, ami leginkább egy fa gyökeréhez hasonlít (vagy faágakhoz, ha történetesen fejjel lefelé nézzük az ábrázolást).

Nézzünk egy komplexebb példát!



A fenti fastruktúra már ránézésre is igen sok hasonlóságot mutat egy valódi úthálózattal. Mivel éleket írunk le, ezért élmátrixnak is nevezhetjük ezeket a mátrixokat.
Az élmátrix a következőképpen írható le:



Ha az útkapcsolatok automatikusan érvényesek oda-vissza (ami két települést összekötő út esetében általában azért igaz szokott lenni), akkor a mátrix tovább egyszerűsíthető, ha kitöröljük a redundáns élek leírását. Például az alábbi két sor ugyanazt a kapcsolatot írja le, ezért redundáns:



A redundanciák megszűrésével egy sokkal helytakarékosabb mátrixhoz jutunk:



10.2.4. Gráf kapcsolt szerkezettel

A mátrix-alapú adatszerkezet megvalósítása mellett létezik egy szintén elterjedt, hatékonyabbnak mondott, ámde sokkal nehezebben megvalósítható módszer is: a kapcsolt listák alkalmazása.
A kapcsolt listákat a dinamikus memóriahasználattal kombinálva kétségkívül hatékony módszerhet juthatunk.
Kapcsolt listás megvalósításoknál kétféle listát fűznek össze egy adathalmazzá a memóriában. Az egyik egy ún. pontlista, a másik pedig egy éllista.
A pontlista elemei minimálisan a következő mezőket tartalmazzák:



A PONT egy csomópont neve.
A PTR a következő csomópontra mutató mutató (ha létezik következő csomópont).
Az ÉL-PTR a PONT csomópontból kiinduló élek további végpontjainak (tehát a szomszédos csomópontoknak) a listájára (éllista) mutató mutató.

Az éllista elemeinek felépítése:



A PTR egy, a pontlistában található csomópontra mutató közvetlen mutató.
Az ÉL-PTR a kiindulási csomópontból a következő szomszédos csomópontra mutat (ha létezik következő szomszédos csomópont).

A gyakorlatban az alábbi ábrán láthadó módon kapcsolódnak össze ezek a listák:



A leírásban az élmátrixok használatát fogjuk bemutatni egyszerűségük és könnyebb érthetőségük miatt.

10.2.5. Rendezetlen koordinátahalmazok

A GPS koordinátáknak elképzelhető egy olyan szélsőséges felhasználási módja is, mely nem definiál a koordináták között semmiféle kapcsolatot.
Ilyen esetben koordinátahalmazokkal dolgozunk.
Példa erre a felhasználásra tereptárgyak helyének tárolása és annak megállapítása, hogy mi van a közelben.
A módszert adatbázisok használatával is könnyedén kombinálhatjuk.

10.3. A legrövidebb út – Dijkstra algoritmusa

Izgalmas és örökzöld téma a navigációs szoftverek világában az útvonalkeresés.
Ennek többféle variációja lehetséges: legrövidebb út, alternatív útvonalak, érintőleges pontok figyelembe vétele stb.
A leírásban a legrövidebb út megkeresésének programozási eszközökkel történő egyik lehetséges, klasszikus megoldását mutatjuk be.
A legrövidebb útvonal problémájára Edsger Dijkstra holland matematikus-informatikus dolgozott ki egy megoldást. Többnyire az ő algoritmusának módosított változatait használják a navigációs szoftverekben is.

Az algoritmus működésének menete a következő:

1. Meg kell adnunk a kiindulópontot és a célállomást

2. A kiindulópontból elérhető összes csomópont távolságát egy tömbben tároljuk. A távolságok kezdetben ismeretlenek, ezt egy olyan értékkel jelöljük, ami biztosan nem lehetséges (például egy nagyon magas számmal, amit tekinthetünk végtelennek is).

3. Külön tömbben tároljuk, ha egy csomópont összes csomópontját leellenőriztük.

4. Egy harmadik tömböt a bejárt útvonal nyilvántartására használunk.

5. Megkeressük a kiindulópontból elérhető összes csomópontot és a távolságukat rögzítjük a távolságokat nyilvántartó tömbben. Először mindig a kiindulási csomóponthoz legközelebbi csomóponttal kezdjük és sorrendben vesszük a többit.

6. A távolságokat nyilvántartó tömbben kiválasztjuk a kiindulóponthoz legközelebbi csomópontot és annak az összes szomszédos csomópontjának a távolságát frissítjük a távolságokat nyilvántartó tömbben, ha a tömbben az adott csomóponthoz korábban nyilvántartott távolság nagyobb (alapesetben ez mindig fennáll a 2. lépésben beállított nagy értékeknek köszönhetően.)

7. Ha egy adott csomópont összes szomszédos csomópontjáig vezető távolságot feldolgoztuk, a csomópontot feldolgozottnak jelöljük az ezt nyilvántartó tömbünkben. Közben pedig frissítjük az útvonal-nyilvántartó tömbünket is.

8. A 5-7. pontokat ismételjük, egészen addig, amíg az összes csomópontot fel nem dolgozzuk, vagy meg nem találjuk közben a legrövidebb utat.

9. Amennyiben a távolságokat nyilvántartó tömbben a célállomástól mért távolság a 2. pontban beállított nagyon nagy számérték, akkor az algoritmus nem talált útvonalat. Ellenkező esetben a megfelelő távolságadat az útvonal hossza.

Dijkstra algoritmusa is irányított, élsúlyozott gráfokkal dolgozik, melyeket a fentebb már bemutatott szomszédsági (él)mátrixokkal is ábrázolni lehet a memóriában.
A leírásban bemutatott példaprogram (Pelda_09) is így jár el, ún. elsőbbségi, vagy prioritásos sorokat használva. A prioritásos sor használatával nem kell külön nyilvántartani a csomópontok feldolgozottságát: ha egy csomópont feldolgozásával végeztünk, egyszerűen eltávolítjuk a sorból.
Az alábbi kapcsolati hálózatot vesszük alapul az útvonaltervezéshez:



Példánkban a 4. és a 3. számú csomópont közötti legrövidebb utat fogjuk kiszámolni.
Az egyszerűség kedvéért a csomópontok távolságát egységesen 1-nek vesszük.
A szomszédsági mátrixunk a GRAF_TOMB nevű kétdimenziós tömb, felépítése megegyezik a fentebb megismert táblázatokéival.
Itt jegyezzük meg, hogy az alábbi ActionScript példaprogram C nyelvű, parancssorban futtatható változata megtalálható a leírás Függelékében.
Az alkalmazás forráskódjában külön megjegyzésekkel jelöljük az algoritmus főbb lépéseit a könnyebb érthetőség kedvéért.
Az útvonalak megadásával kapcsolatban nagyon fontos megjegyeznünk, hogy a csomópontok közötti kapcsolatokat oda-vissza külön is szerepeltetnünk kell, mert csak így tudunk tetszőleges irányú keresést kezdeményezni.
Az alkalmazásunk egy összetettebb adatszerkezetet is használ külön osztályként a csomópontok ábrázolására, melyre az útvonalszámítás folyamatában lesz szükség.

Az osztály forráskódja:


package sajat_csomag
{

//Ez az osztály egyetlen csomópontot definiál
//ezekből építjük fel a kapcsolati hálózatot.
//Az elemek tömböt alkotnak és tömbindexük alapján hivatkozhatunk rájuk.

publicclass Minta_osztaly
{
//Ez a tulajdonság adja meg, hogy hány másik elemhez kapcsolódik a csomópont
//maximum 20 kapcsolódási pont lehetséges
public var alelemek_szama:int;

//Az adott szomszéd elem tömbindexe.
public var end_elem:Array;

//A távolság egy-egy csomóponttól
public var cost_elem:Array;
 
public function Minta_osztaly()
{
var i:int;
  
//Eelőkészítjük egy csomópont tulajdonságait
alelemek_szama=0;
end_elem=new Array(20);
cost_elem=new Array(20);

for(i=0;i<20;++i)
{

//-1 értékkel jelölünk egy érvénytelen elemet
end_elem=cost_elem[i]=[-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1];

}
}
 
}
}


Az alkalmazás kimeneteként egy TextArea típusú szövegdobozt használunk. Az alkalmazás 100 csomópont kezelésére alkalmas, melyek mindegyike maximum 20 másik csomóponthoz kapcsolódhat.

Az MXML alkalmazás teljes forráskódja a következő:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="init()">

<fx:Script>
<![CDATA[

import mx.events.FlexEvent;

//importáljuk a csomópontokat leíró alaposztályt
import sajat_csomag.Minta_osztaly;
  
//Ez a változó egy végtelen távolságot ad meg
//segédváltozóként használjuk a tömbök előkészítésekor.
public const VEGTELEN:int=999;

//A maximális elemszám
public const MAX_ELEMSZAM:int=100;

//A maximális kapcsolatok száma
public const MAX_SZOMSZED:int=20;   

//Segédváltozók ciklusokhoz
public var i:int;
public var j:int;
  
//Segédtömb, a tömbök előkészítésekor használjuk.
public var csere_prio_sor:Array;

//Elsőbbségi sor
public var prio_sor:Array;

//Éleket leíró tömb
public var elek:Array;

//Segédtömb, a tömbök előkészítésekor használjuk.
public var csere_elek:Array;
public var check_multi_elek:int;

//Távolságokat tároló tömb
public var tavolsag:Array;

//Az útvonalat tároló tömb
public var elozo:Array;

//A végleges útvonal tárolására használt tömb
public var utvonal:Array;
  
//Segédváltozók
public var elek_elemszam:int;

public var csere_elek_elemszam:int;

public var csere_prio_sor_elemszam:int;

public var prio_sor_elemszam:int;

public var szomszedok_elemszam:int;

public var utvonal_elemszam:int;
  
//A kapcsolódásokat leíró élmátrix
public var graf_tomb:Array;

//Segédtömb, szomszédcsomópontok tárolására
public var szomszedok:Array;   
//A kiindulási és az elérni kívánt csomópont sorszáma
public var start:int;
public var cel:int;
  
//További segédváltozók
public var min:int;
public var u:int;
public var alt:int;
  

protected function init():void
{
var i:int;
   
//Kezdeti értékek beállítása
//Tömbők létrehozása a memóriában
csere_prio_sor=new Array(MAX_ELEMSZAM);
prio_sor=new Array(MAX_ELEMSZAM);
elek=new Array(MAX_ELEMSZAM);
csere_elek=new Array(MAX_ELEMSZAM);
tavolsag=new Array(VEGTELEN+1);
elozo=new Array(VEGTELEN+1);
utvonal=new Array(MAX_ELEMSZAM);
 
graf_tomb=new Array();
szomszedok=new Array();
   
// A start és a cél megadása
start=4;
cel=3;
   
//Segédváltozók alapértékei
csere_elek_elemszam=0;
utvonal_elemszam=0;
prio_sor_elemszam=0;
elek_elemszam=0;
szomszedok_elemszam=0;    
   
//Az élmátrix feltöltése
graf_tomb[0]= [0,1,1];
graf_tomb[1]= [1,4,1];
graf_tomb[2]= [0,1,1];
graf_tomb[3]= [1,5,1];
graf_tomb[4]= [5,6,1];
graf_tomb[5]= [0,2,1];
graf_tomb[6]= [2,3,1];
graf_tomb[7]= [4,1,1];
graf_tomb[8]= [1,0,1];
graf_tomb[9]= [5,1,1];
graf_tomb[10]= [6,5,1];
graf_tomb[11]= [2,0,1];
graf_tomb[12]= [3,2,1];
   
//A távolságokat és az útvonalakat nyilvántartó tömbök előkészítése
for(i=0;i<(VEGTELEN+1);++i)
{
//-1 -el jelöljük, ha érvénytelen egy elem
elozo[i]=-1;
tavolsag[i]=-1;
}    

for(i=0;i<MAX_ELEMSZAM;++i)
{
szomszedok[i]=new Minta_osztaly();
}
   
for(i=0;i<graf_tomb.length;++i)
{

elek[elek_elemszam]=graf_tomb[i][0];
++elek_elemszam;
elek[elek_elemszam]=graf_tomb[i][1];
++elek_elemszam;   

szomszedok[graf_tomb[i][0]].end_elem[szomszedok[graf_tomb[i][0]].alelemek_szama]=graf_tomb[i][1];    

szomszedok[graf_tomb[i][0]].cost_elem[szomszedok[graf_tomb[i][0]].alelemek_szama]=graf_tomb[i][2];     
++szomszedok[graf_tomb[i][0]].alelemek_szama;
++szomszedok_elemszam;
}
   
//A redundáns élek eltávolítása
csere_elek_elemszam=0;
for(i=0;i<elek_elemszam;++i)
{
check_multi_elek=0;   
for(j=0;j<csere_elek_elemszam;++j)
{
 if(csere_elek[j] == elek[i]) check_multi_elek=1;
}
 

  
if(check_multi_elek == 0)
{
 csere_elek[csere_elek_elemszam]=elek[i];
 ++csere_elek_elemszam;}
}
 
for(i=0;i<elek_elemszam;++i)
{
 elek[i]=csere_elek[i];
}    
   
elek_elemszam=csere_elek_elemszam;    
   
for(i=0;i<elek_elemszam;++i)
{
 tavolsag[elek[i]]=VEGTELEN;
}  
   
tavolsag[start]=0;
  
for(i=0;i<elek_elemszam;++i)
{
 prio_sor[i]=elek[i];
}
   
prio_sor_elemszam=elek_elemszam;
 
//Az adatok előkészítése befejeződött
   
//Amíg van feldolgozatlan elem a prioritásos sorban,
//addig folyamatosan feldolgozzuk a kapcsolati hálót
while (prio_sor_elemszam > 0)
{

//A legkisebb távolságú él kiválasztása
// a prioritásos sorból
min=VEGTELEN;
for(i=0;i<prio_sor_elemszam;++i)
{
 if (tavolsag[prio_sor[i]] < min)
 {
  min=tavolsag[prio_sor[i]];
  u=prio_sor[i];
 }
}
  
//A prioritásos sorból eltávolítjuk a legkisebb távolságú élt
csere_prio_sor_elemszam=0;   
for(i=0;i<prio_sor_elemszam;++i)
{
 if(prio_sor[i] != u)
 {
  csere_prio_sor[csere_prio_sor_elemszam]=
  prio_sor[i];          
  ++csere_prio_sor_elemszam;
 }
}
    for(i=0;i<csere_prio_sor_elemszam;++i)
{
 prio_sor[i]=csere_prio_sor[i];
}
prio_sor_elemszam=csere_prio_sor_elemszam;

//Ha elfogytak az élek, vagy megtaláltuk a célállomást,
//akkor befejeződik az algoritmus működése
if((tavolsag[u] == VEGTELEN) || (u == cel))
 break;

//Az összes szomszédos csomópont feldolgozása
//a távolságok és az útvonal (elozo tömb) frissítésével
if(szomszedok[u].end_elem[0] != -1)
{
     for(i=0;i<szomszedok[u].alelemek_szama;++i)
{
 alt=tavolsag[u]+szomszedok[u].cost_elem[i];

 if(alt < tavolsag[szomszedok[u].end_elem[i]])
 {
  tavolsag[szomszedok[u].end_elem[i]]=alt;
  elozo[szomszedok[u].end_elem[i]]=u;
 }
}
}
}
  
//Az utvonal előállítása
u=cel;
while(elozo[u] != -1)
{
utvonal[utvonal_elemszam]=u;
++utvonal_elemszam;
u=elozo[u];
}
utvonal[utvonal_elemszam]=u;
++utvonal_elemszam;

//Az utvonal előállítása   
for(i=0;i<utvonal_elemszam;++i)
{
 kimenet.text+=utvonal[utvonal_elemszam-i-1].toString()+" ";
}
}

//Kilépés az alkalmazásból
public function Kilepes():void
{
 NativeApplication.nativeApplication.exit();
}
 
]]>
</fx:Script>
<s:TextArea id="kimenet" width="100%" height="80%"/>

<s:Button label="Kilépés" bottom="0" width="100%" click="Kilepes()"/>
</s:Application>





10.4. Útvonalszámítás a gyakorlatban

Az útvonalak kiszámítása memória- és számításigényes feladat, ezért nagymennyiségű adat feldolgozása helyett bizonyos útvonalakat nem számolnak ki újra és újra, hanem a már korábban kiszámolt változatukat veszik elő. Ezzel a módszerrel drámaian le lehet csökkenteni az útvonaltervezés idejét.
Mindazonáltal ilyen léptékű optimalizálásra csak igazán nagy mennyiségű adat esetében van szükség. Egy néhány száz csomópontot tartalmazó kapcsolati hálóban a legrövidebb út megtalálása a másodperc tört része alatt végbemegy.
A keresést gyorsíthatjuk a kapcsolatok tárolásának optimalizálásával. Ezt úgy érhetjük el, hogy a kapcsolati listában az egymás mellett levő csomópontok közötti kapcsolatokat az élmátrixban közvetlenül egymás után tároljuk.



11. Képernyőforgatás és navigáció

A leírás ezen fejezetében bemutatjuk, hogyan lehet dinamikusan kezelni a megjelenítést a mobileszközök elforgatásakor.
A képernyő tájolásának kezelése jogos igény egy tájékozódásra és megjelenítésre használt alkalmazás esetében.
A tájolás kezeléséhez mindenképpen engedélyeznünk kell a telefon állapotának lekérdezését az -app.xml fájlban:

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

11.1. Az alkalmazás terve

Az alkalmazás (Pelda_10) Magyarország megyeszékhelyeit jeleníti meg és pillanatnyi helyzetünket egy zöld háromszöggel jelöli.
A mobileszköz koordinátái 10 másodpercenként frissülnek.
A térkép a képernyő elforgatásakor újrarajzolódik.

11.2. Megvalósítás

Az alkalmazás motorja a 7. fejezetben ismertetett algoritmusokon alapul. A get_point_x és a get_point_y függvényeken apró módosításokat hajtottunk végre, annak érdekében, hogy a képernyő közepéhez igyekezzenek igazítani a koordinátákat.
A városok adatait egy kétdimenziós tömbben tároljuk.
A városnevek kiírásához Label objektumokat használunk, bal oldalukon pedig egy-egy kör jelzi az adott város tényleges helyét.
Amennyiben a futtatásra használt eszköz nem rendelkezik GPS vevővel, akkor a "Nincs GPS támogatás." szövegcímkét jelenítjük meg.
Magyarázó szövegeket ezúttal az MXML alkalmazás forráskódjában helyeztünk el, megjegyzések formájában:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationComplete="init()"
backgroundColor="#f1efe9">
<fx:Script>
<![CDATA[

import flash.events.Event;
import flash.sensors.Geolocation;
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import spark.components.Label;
import spark.events.IndexChangeEvent;
  
public var geoLocation:Geolocation;   

public var adat_long:Number;   

public var adat_lat:Number;  
public var varoslista:Array = new Array();
 
public var kep_arany:Number;  
public var kep_arany_2:Number;
 
public var hatar_lat1:Number;  
public var hatar_lat2:Number;  
public var hatar_long1:Number;
  
public var hatar_long2:Number;
 
public var nyers_szeles:int;  
public var rajz_nagyitas:Number;
 
//Az alkalmazás előkészítése
public function init():void
{  
//A városadatok megadása  
varoslista[0]= ["Békéscsaba",46.6736,21.0877];
varoslista[1]= ["Budapest",47.4979,19.0402];
varoslista[2]= ["Debrecen",47.5316,21.6273];
varoslista[3]= ["Eger",47.9025,20.3772];
varoslista[4]= ["Győr",47.6874,17.6504];
varoslista[5]= ["Kaposvár",46.3593,17.7968];
varoslista[6]= ["Kecskemét",46.8964,19.6897];
varoslista[7]= ["Miskolc",48.0964,20.7624];
varoslista[8]= ["Nyíregyháza",47.9495,21.7244];
varoslista[9]= ["Pécs",46.0727,18.2323];
varoslista[10]= ["Salgótarján",48.0935,19.7999];
varoslista[11]= ["Szeged",46.253,20.1414];
varoslista[12]= ["Székesfehérvár",47.186,18.4221];
varoslista[13]= ["Szekszárd",46.3474,18.7062];
varoslista[14]= ["Szolnok",47.1621,20.1825];
varoslista[15]= ["Szombathely",47.2307,16.6218];
varoslista[16]= ["Tatabánya",47.5692,18.4048];
varoslista[17]= ["Veszprém",47.1028,17.9093];
varoslista[18]= ["Zalaegerszeg",46.8417,16.8416];
//A GPS vevő előkészítése
if(Geolocation.isSupported==true)
{
 geoLocation = new Geolocation();

 geoLocation.setRequestedUpdateInterval(10000);

geoLocation.addEventListener(GeolocationEvent.UPDATE,handleLocationRequest);

 stage.addEventListener(Event.RESIZE,forgatas);

 stage.dispatchEvent(new Event(Event.RESIZE));
    
 rajzvaszon.visible=true;

 rajzvaszon_2.visible=true;
}
else
{
 info.visible=true;
}
}
  
//A forgatás kezelése
//A készülék tájolásának változásakor újrarajzoljuk az egész
//térképet.
public function forgatas(event:Event):void
{
rajzvaszon.graphics.clear();

rajzvaszon.removeAllElements();

display_init(48.306582,15.0,45.853290,24,
rajzvaszon.width,rajzvaszon.height);   

varosok_megjelenit();
}
  
//A GPS adatok beolvasásakor frissítjük a megjelenítést
public function handleLocationRequest(event:GeolocationEvent):void
{
adat_lat=event.latitude;

adat_long=event.longitude;

rajzvaszon.graphics.clear();

rajzvaszon.removeAllElements();

display_init(48.30,15.0,45.85,23.0,
rajzvaszon.width,rajzvaszon.height);   
   
varosok_megjelenit();

rajzvaszon_2.graphics.clear();

rajzvaszon_2.graphics.lineStyle(3,0x00BB00);

rajzvaszon_2.graphics.moveTo(get_point_x(adat_long),get_point_y(adat_lat));

rajzvaszon_2.graphics.lineTo(get_point_x(adat_long)+10,get_point_y(adat_lat)-20);   
rajzvaszon_2.graphics.lineTo(get_point_x(adat_long)-10,get_point_y(adat_lat)-20);  
 rajzvaszon_2.graphics.lineTo(get_point_x(adat_long),get_point_y(adat_lat));
}
  
//A rajzolás előkészítése
public function display_init(lat1:Number,long1:Number,lat2:Number,long2:Number,szeles:int,magas:int):void
{   
kep_arany=kep_arany_2=szeles/magas;

nyers_szeles=Math.abs(long1-long2)*100000;
   rajz_nagyitas=1/(int)(nyers_szeles/rajzvaszon.width);

if(stage.stageWidth > stage.stageHeight) kep_arany=1;

if(stage. stageWidth < stage. stageHeight) kep_arany_2=1;
}

//GPS koordináta átváltása X képernyőkoordinátává
public function get_point_x(long:Number):int
{    
return (int)( (rajzvaszon.width/2) - (((19.5-long)*100000) * rajz_nagyitas) * kep_arany );
}
 
//GPS koordináta átváltása X képernyőkoordinátává
public function get_point_y(lat:Number):int
{
return (int)((rajzvaszon.height/2)) + (((rajzvaszon.height/2)+((47.079936-lat)*100000) )*rajz_nagyitas)*kep_arany_2;
}

//A városok kirajzolása
public function varosok_megjelenit():void
{
var i:int;
var szoveg:Label;
for (i = 0; i<19; ++i)
{
    rajzvaszon.graphics.lineStyle(2,0x000000);
 rajzvaszon.graphics.drawCircle((get_point_x(varoslista[i][2])),get_point_y(varoslista[i][1]),2);
    
rajzvaszon.graphics.lineStyle(1, 0xFF0000);

szoveg = new Label();
szoveg.setStyle("fontSize", "10");
szoveg.setStyle("color", "0x000000");
szoveg.setStyle("textAlign", "center");
szoveg.setStyle("verticalAlign", "middle");
szoveg.text=varoslista[i][0];
szoveg.x=get_point_x(varoslista[i][2])+5;
szoveg.y=get_point_y(varoslista[i][1])-5;
    
rajzvaszon.addElement(szoveg);
}
}

//Kilépés az alkalmazásból
public function Kilepes():void
{
 NativeApplication.nativeApplication.exit();
}

]]>
</fx:Script>
<s:Group id="rajzvaszon" width="100%" height="100%"/>

<s:Group id="rajzvaszon_2" width="100%" height="100%" />
<s:Label id="info" text="Nincs GPS támogatás." visible="false" click="Kilepes()"/>
</s:Application>







12. Útvonalrögzítő alkalmazás készítése

Komoly gyakorlati igény lehet a helyváltoztatás időben történő rögzítésére.
Az alábbiakban egy többfunkciós útvonalrögzítő alkalmazást fogunk elkészíteni.
A program aktuális helyzetünkről képes „pillanatképet” készíteni, vagy folyamatosan rögzíteni a készülék koordinátáit.
Mindkét esetben a rögzített koordinátá(ka)t e-mailben elküldhetjük, mondjuk ismerősöknek, vagy akár saját magunknak is, ha később fel akarjuk használni az adatokat.
Folyamatos rögzítés esetén kb. 40 percnyi „felvétel” készítésére van lehetőség. A rögzítés ezután automatikusan leáll. A rögzítéshez két egyszerű, egyenként 500 elemet tartalmazó egydimenziós tömböt használunk: egyik a hosszúsági, a másik a szélességi koordinátákat rögzíti. Amikor időben rögzítünk koordinátákat, a kezdetben 'Útvonalrögzítés' feliratú gomb felirata 'Stop'-ra változik. Ha a tömbök betelnek, akkor a rögzítés automatikusan is leáll.
A GPS vevő által szolgáltatott adatokat 5 másodpercenként olvassuk ki és a vételi pontosságot vizuálisan is jelezzük.

Egy „GPS” szöveget jelenítünk meg, melynek színe visszajelzést ad a GPS vevő állapotáról:
• szürke: a GPS vevő nincs bekapcsolva, vagy az eszköz nem rendelkezik GPS vevővel
• piros: a pontosság 100 méternél nagyobb szórást mutat
• sárga: a pontosság 10 és 100 méter között van
• zöld: 10 méter alatti mérési pontosság.

A koordináták e-mailben történő elküldésekor a mobileszközre telepített email kliens programok közül választhatunk egyet.
Az e-mail előre kitöltve tartalmazza a feladó címét, a levél tárgyát („Levél a mobilomról”), és törzsét, mely az 'Éppen itt jártam' szöveg után a koordinátákat tartalmazza útvonalrögzítő módban.
Útvonalrögzítő módban legalább egyetlen koordináta rögzítése után lehetséges az e-mailküldés.
Amennyiben csak a pillanatnyi helyzetünket rögzítjük a ’Helyzet rögzítése’ feliratú gombbal, akkor a koordináták egy dinamikus Google térkép-linkbe lesznek beágyazva, amit a levél címzettje meg tud nyitni és így egy böngészőben azonnal megtekinthető a Google térképén a koordináta.
Ha útvonalrögzítés közben megnyomjuk a ’Helyzet rögzítése’ gombot, az útvonalrögzítés automatikusan leáll.

Az alkalmazás (Pelda_11) teljes forráskódja:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationDPI="160"
backgroundColor="#000000"
applicationComplete="init()">
<fx:Script>
<![CDATA[

import flash.sensors.Geolocation;   
import mx.events.FlexEvent;
  
public var geoLocation:Geolocation;

public var rogzites_tipus:int;//0-aktuális pozíció, 1-útvonal rögzítése

public var gps_allapot:int;

public var adat_long:Number;

public var adat_lat:Number;

public var lat_tomb:Array = new Array(500);

public var long_tomb:Array = new Array(500);

public var log_status:int;

public var szamlalo:int;
  
//Az alkalmazás előkészítése
public function init():void
{
rogzites_tipus=0;

gps_status.setStyle("color","#AAAAAA");

if(Geolocation.isSupported==true)
{
 gps_allapot=1;

 geoLocation = new Geolocation();

 geoLocation.setRequestedUpdateInterval(5000);

geoLocation.addEventListener(GeolocationEvent.UPDATE,GPS_frissit);
}
else
{
  gps_allapot=0;
}    
}
 
//Egyetlen koordináta rögzítése
public function egyszeri_lekerdezes():void
{
if(rogzites_tipus == 1)
 log_bekikapcs();

GPS_levelkuldes();
}

//Az email tartalmának előállítása és a levélküldés kezdeményezése
public function GPS_levelkuldes():void
{
var link:String="";
var i:int;
var j:Number;
   
if(gps_allapot == 0)
 return;

if(rogzites_tipus == 0)
{
link+="mailto:?subject=Levél a mobilomról&body=http://maps.google.com/?q="+adat_lat.toString()+","+adat_long.toString();
}
elseif(rogzites_tipus == 1)
{
link+="mailto:?subject=Levél a mobilomról&body=Éppen itt jártam\n";

for(i=0;i<szamlalo;++i)
{
 j=lat_tomb[i];

 link+=(i+1).toString()+".\n"+j.toString()+"\n";
 
j=long_tomb[i];

 link+=j.toString()+"\n\n";
}
}
navigateToURL(new URLRequest(link));
}
 
//A GPS koordináták és a vételi pontosság kijelzésének frissítése
public function GPS_frissit(event:GeolocationEvent):void
{
adat_lat=event.latitude;

adat_long=event.longitude;

if(event.horizontalAccuracy > 100)
{
 gps_status.setStyle("color","#ea0000");
}
elseif(event.horizontalAccuracy < 10)
{
 gps_status.setStyle("color","#00AA00");
}
elseif(event.horizontalAccuracy < 101)
{
 gps_status.setStyle("color","#eaec39");
}

//az útvonalrögzítést végző kód
if(log_status == 1)
{
 if(szamlalo == 0)
  return;
 else if(szamlalo == 500)
 {
  log_bekikapcs();
  return;
 }
else
{
 lat_tomb[szamlalo]=(Number)(adat_lat);

 long_tomb[szamlalo]=(Number)(adat_long);

 ++szamlalo;
}
}
}
  
//Az útvonalrögzítés be/kikapcsolása
public function log_bekikapcs():void
{
if(gps_allapot == 0)
 return;

if(log_status == 0)
{
 szamlalo=0;

 lat_tomb[0]=(Number)(adat_lat);

 long_tomb[0]=(Number)(adat_long);

 log_status=1;

 track_gomb.label="Stop";

 rogzites_tipus=1;
}
else
{
 rogzites_tipus=0;

 log_status=0;

 track_gomb.label="Útvonalrögzítés";
}
}
  
//Kilépés az alkalmazásból
public function Kilepes():void
{
NativeApplication.nativeApplication.exit();
}

]]>
</fx:Script>
<s:Button label="Helyzet rögzítése" width="70%" top="10" horizontalCenter="0" click="egyszeri_lekerdezes()" />

<s:Button id="track_gomb" label="Útvonalrögzítés" width="70%" top="60" horizontalCenter="0" click="log_bekikapcs()" />

<s:Button label="Küldés" width="70%" top="110" horizontalCenter="0" click="GPS_levelkuldes()" />

<s:Button label="Kilépés" width="70%" top="170" horizontalCenter="0" click="Kilepes()" />

<s:Label id="gps_status" text="GPS" top="220" horizontalCenter="0"  color="#AAAAAA" fontSize="18" fontWeight="bold" />
</s:Application>


   





13. Digitális térképek készítése és felhasználása

Egy valamire való navigációs alkalmazás képes a környezetéről is információkat megjeleníteni: utakat, folyókat, hegyeket, dombokat stb.
A legfogósabb kérdés kétségkívül a domborzatmegjelenítés.

A megjelenítés három módját fogjuk bemutatni:
1. univerzális megoldás, mely tisztán vektoros és képpontrajzolás-alapú megjelenítésre ideális
2. kombinált, félig a Flash Bitmap objektumára támaszkodó megoldás
3. egyszerűsített, statikus megjelenítés.

Mint látni fogjuk, akármelyik megoldást választjuk is, a következő háromféle adatra mindig szükségünk lesz:

- a térkép valamelyik sarkának koordinátái
- a térkép szélessége és magassága (lehetőleg fokban megadva)
- a domborzati adatokat tartalmazó adatrács (idegen szóval: raszter) lépésköze fokban (az 1. és 2. módszer esetén), vagy pixelben (3. megoldás esetén).

A térképek készítésének egyik kulcsfontosságú mozzanata az adatgyűjtés és az adatok rendszerezése.
Az adatgyűjtésnek alapvetően kétféle módja létezik.
A legegyszerűbb, ám leginkább időigényes módszer a kézi adatgyűjtés. Nagy mennyiségű adatnál ez igen nagy kihívást jelenthet, ezért alapos felkészülést és előkészítést igényel. A 12. fejezetben bemutatott alkalmazást is átalakíthatjuk ilyen feladatra.
Külső, szabadon felhasználható adatbázisok használata lehetséges, ám ebben az esetben nagyon körültekintőnek kell lennünk, ugyanis a legtöbb, ingyenesen is elérhető térképszolgáltatáson keresztül kinyerhető nyers adatok saját rendszerben történő tárolása tilos. Külső térképszolgáltatások használatakor tehát előzetesen mindig tájékozódjunk az adatokat rendelkezésre bocsátó harmadik fél által megszabott felhasználási feltételekről!
A 14. fejezetben publikus NASA adatok felhasználását fogjuk bemutatni.

13.1. A térképtervezés menete

Először is azt kell tisztáznunk, hogy mely területet kell lefednie térképünknek!
Következő lépésként tervezett térképünk ”felbontását” kell meghatároznunk. A felbontás annak a képzeletbeli rácsnak a felosztását jelenti, melyet a magasságadatok gyűjtéséhez használni fogunk (lásd lentebb). Ez tehát a magasságadatok mintavételezésének a léptéke.
Bár minél nagyobb a felbontás, annál szebb lesz a végeredmény, azért ésszerű kereteket kell szabnunk ennek, mert az adatgyűjtés idejét, az adatok tárolását és megjelenítését minden nagyságrendű változtatás exponenciális mértékben befolyásolja. Már egy 50x50-es térképrács is 2500 adat előállítását követeli meg!
Mi befolyásolhatja a döntést?A ráfordítás mennyisége és a megjelenítő eszköz felbontása.
Kis térképek esetén bőségesen elegendő, ha a rács felső határa 100x100. Egy ilyen térkép kicsit kinagyítva egy átlagos felbontású kijelzőn is (pl. 480x800 pixel) gyors és még élvezhető.
Amennyiben nagyfelbontású kijelzőket is számításba veszünk, vagy térképünket asztali számítógépen is meg akarjuk jeleníteni, érdemes jóval nagyobb méretekben gondolkodni, legalább 400x300, vagy 800x600-as rácsfelbontások szükségesek.
A gyakorlatban kiválóan alkalmazható léptékek méterben kifejezve a 80-1000 méter közöttiek. A 200-500 méteres léptékek is jól használhatóak a gyakorlatban.
Ha túlságosan nagy léptéket választunk, térképünk könnyen pontatlanná válhat, kevésbé fogja tükrözni a valódi domborzati viszonyokat.
Nagyobb felbontás esetén térképünk nagyobb nagyításokat is lehetővé tesz élvezhető megjelenítésben, ám a tároláshoz szükséges hely és a megjelenítéshez szükséges idő jóval nagyobb lesz. Ennek a dilemmának az áthidalására a 13.5 fejezetben látunk egy példát.
A rács meghatározásához egyszerűen a kijelölt négyzet, vagy téglalap alakú terület két átellenes csúcsának a koordinátáját kell meghatároznunk, mint azt az alábbi ábrán is láthatjuk.



Ezután a szélességi és a hosszúsági koordináták különbségének abszolútértékét elosztva tervezett rácsfelbontásunk értékeivel, megkapjuk a szükséges „letapogatási” léptékeket fokokban.
Példánkban (Pelda_12) a Kárpát-medence területét fedjük le 144x60-as felbontással.

A területet határoló koordináták:
50.0, 15.0 (a térkép bal felső sarka)
és
45.0, 27.0 (a térkép jobb alsó sarka)

Itt valójában egy kis trükköt alkalmazunk. A terület pontosan kétszer annyi foknyi széles, mint magas. Ez némi egyszerűsítésre ad lehetőséget, ugyanis mind a szélességi, mind a hosszúsági lépték 0.08333, ami kb. 8 kilométernek felel meg. Ennek ellenére a léptékeket külön változókban fogjuk tárolni alkalmazásunkban, hogy más térképekkel könnyen testre szabhassa az Olvasó.
Más arányú területek esetén tehát a léptékek különbözőek is lehetnek természetesen.
Példaalkalmazásunk a Kárpát-medence domrozatát, valamint Budapest és Szeged helyét fogja megjeleníteni.



Iskolai tanulmányinkból emlékezhetünk arra, hogy a következő magassági szinteket különböztetjük meg:

- síkság: < 200m
- dombság: 200-500m
- középhegység: 500-1500m
- magashegység: > 1500m

Ha csak ezt a négy felosztást különböztetnénk meg egy-egy színnel és így ábrázolnánk a domborzatot, akkor egy viszonylag sematikus képet kapnánk, például egy ehhez hasonlót (Budai-hegység):


Ezt elkerülendő, finomabb lépésközökkel, például 100 méterenként haladva, különböző színeket fogunk alkalmazni. A színeket egyelőre előre meg fogjuk adni.

13.3. A megvalósítás

Mivel elég sok és bonyolult számítást kell programunknak végrehajtania, ezért a GPS koordinátákat és a térképrács léptékeit nem közvetlenül, hanem beszédesebb változók formájában fogjuk megadni:

adat_long_felso=15.0;
adat_lat_felso=50.0;
adat_long_also=27.0;
adat_lat_also=45.0;
lat_leptek=0.08333;
long_leptek=0.08333;

A léptékeket a következők szerint számolhatjuk ki:

TÉRKÉP_SZÉLESSÉGE FOKOKBAN / KÍVÁNT VÍZSZINTES FELBONTÁS

TÉRKÉP_MAGASSÁGA FOKOKBAN / KÍVÁNT FÜGGŐLEGES FELBONTÁS

Konkrét számadatokkal:

lat_leptek = (adat_lat_felso - adat_lat_also) / 60;

long_leptek = (adat_long_felso - adat_long_also) / 144;

A domborzati adatokat tartalmazó mátrix dimenzióit lekérdezzük két változóba. Ez csupán a kód olvashatóságát hivatott könnyíteni.

//Segédváltozók beállítása
matrix_oszlopszam=domborzat.matrix[0].length;

matrix_sorszam=domborzat.matrix.length;

Az előzőekben létrehozott segédváltozókat felhasználva már a megjelenítőmotorunk inicializálása is egyszerűbbé válik:

display_init(adat_lat_felso,adat_long_felso,adat_lat_also,adat_long_also,kepernyo_w,kepernyo_h);

Mivel kisfelbontású térképet használunk, ezért a megjelenített pixeleket nagyítani fogjuk.
Ehhez egy segédváltozót vezetünk be:

pixelnagyitas=5;

Bármilyen más módszert is bevezethetünk a képpontok ideális nagyításának meghatározására (például képernyőméret figyelembe vétele). Mi most megelégszünk egy rögzített érték használatával.
A domborzati adatok megfelelő színű, képpontonként történő kirajzolásának alapja a keppont_kirajzol függvény.


public function keppont_kirajzol(x:int,y:int,mag:int):void

Nincs is más dolgunk, mint a domborzati adatokon végighaladva megjeleníteni azt:

for(i=0;i<matrix_sorszam;++i)
{
for(j=0;j<matrix_oszlopszam;++j)
{
 for(k=0;k<pixelnagyitas;++k)
 {
 for(l=0;l<pixelnagyitas;++l)
 {
keppont_kirajzol(k+get_point_x(adat_long_felso+((j*pixelnagyitas)*long_leptek)),l+get_point_y(adat_lat_felso-((i*pixelnagyitas)*lat_leptek)),domborzat.matrix[i][j]);
 }
 }
}
}


A két legbelső for ciklus a képpontok megfelelő nagyításáért felelős.

A városnevek megjelenítéséhez külön függvényt hozunk létre:

public function varosnev_kirajzol(lat:Number,long:Number,nev:String):void

A függvény legfontosabb feladata annak megállapítása, hogy a városok koordinátái a térképrács mely beosztására illeszkednek.
Ezt két segédváltozóval végezzük el (s1 és s2):

s1=(long-adat_long_felso)/long_leptek;

s2=(adat_lat_felso-lat)/lat_leptek;

Így a mindenkori rácshálózattól függetlenül használható megoldást kapunk. A kiszámolt s1 és s2 segédváltozók birtokában a városnevek kirajzolása már szinte gyerekjáték:

szoveg.x=get_point_x(adat_long_felso+(s1*pixelnagyitas*long_leptek) )+5;

szoveg.y=get_point_y(adat_lat_felso-((s2*pixelnagyitas)*lat_leptek));

A nyers domborzati adatokat külön osztályban tároljuk, mivel rengeteg adatról van szó és így áttekinthetőbb lesz a forráskód is.
Az osztály egyetlen, kétdimenziós tömbben tárolja a magassági adatokat, ennek neve matrix. Az osztály neve Terkepadat. Nem kell megijedni, a magasságadatokat tartalmazó osztályfájl szintén megtalálható a Kiadó oldalán letölthető mellékletben, nem kötelező kézzel begépelni azokat.
Az alkalmazásból a megjelenített kép megérintésével tudunk kilépni.

package terkepadat
{
public class Terkepadat
{
public var matrix:Array = new Array();
 
 public function Terkepadat()
 {   
  matrix[59]=[853,611,585,696,760,552,619,528,378,359,308,340,495,321,229,285,123,217,154,166,128,170,300,625,512,371,224,117,102,100,116,187,281,199,170,100,108,143,324,202,95,90,86,84,85,96,84,78,94,90,80,91,74,78,75,81,86,93,98,125,100,87,81,80,67,68,71,73,74,95,139,139,155,140,111,87,84,87,103,144,178,569,671,629,656,614,460,410,372,664,786,1178,812,458,337,246,241,200,171,181,203,273,239,260,437,423,319,399,421,294,242,212,293,483,429,543,501,343,503,446,449,516,528,423,479,431,350,327,354,301,281,230,220,187,224,210,151,127,104,86,72,78,72,64];
  matrix[58]=[839,750,708,778,416,371,412,375,320,372,381,296,285,299,430,185,133,224,331,389,270,230,359,232,157,129,100,92,90,90,92,213,560,94,85,86,83,85,85,150,87,84,82,82,86,88,89,78,96,76,79,79,86,93,106,154,146,180,176,163,143,126,103,96,70,73,74,77,85,122,134,126,73,74,77,84,125,160,152,102,130,421,767,663,1140,893,795,515,531,606,1036,866,874,550,815,402,279,248,260,252,245,261,319,323,477,398,457,464,501,442,416,366,429,358,381,566,378,522,560,477,385,563,617,483,481,433,418,399,582,348,357,277,246,239,318,146,374,375,234,150,105,96,79,73];
  matrix[57]=[702,513,735,396,473,371,296,309,311,223,193,213,206,432,453,318,352,229,323,314,220,133,103,189,285,165,97,87,87,103,85,86,94,109,98,91,85,88,90,82,82,80,81,89,80,79,83,88,81,78,94,144,212,281,226,196,209,279,320,207,149,69,233,71,69,76,76,76,105,123,111,85,75,77,74,75,88,127,174,177,160,326,360,599,751,1357,1069,823,530,1075,1169,1417,1428,734,962,976,520,1005,968,536,422,560,445,432,461,540,522,640,560,648,492,428,402,461,599,494,440,535,470,442,465,570,608,634,670,753,673,725,615,623,580,416,392,341,415,354,293,462,271,316,270,106,87,100];
  matrix[56]=[1109,838,652,319,237,225,243,258,173,243,296,176,250,265,204,302,367,357,145,195,162,104,106,95,105,107,94,120,105,115,148,251,234,115,120,341,219,279,226,119,97,84,77,78,81,78,78,96,104,106,115,78,76,79,75,77,76,78,79,72,77,76,109,124,73,76,79,75,74,76,75,73,73,74,78,88,83,98,194,209,463,207,295,439,730,844,852,646,462,1049,1199,1455,1908,1535,1436,1480,1488,1382,1451,843,963,814,1454,1338,868,1275,1154,1111,1283,1291,849,763,374,392,646,631,592,852,588,783,564,833,624,1037,1038,723,966,895,811,776,613,598,554,413,558,435,289,304,203,508,327,133,189,152];
  matrix[55]=[1225,532,386,269,252,194,189,185,224,235,228,142,127,112,201,206,394,230,162,100,91,96,112,109,170,304,270,349,570,249,342,188,146,129,164,127,140,201,161,134,111,104,106,107,89,85,87,94,97,79,79,80,80,80,81,78,78,80,79,79,77,74,69,70,73,75,79,77,69,75,74,72,74,76,81,87,111,89,128,201,189,403,349,280,372,731,526,300,372,773,1130,1958,1715,1717,1778,1886,1347,912,775,1044,1357,987,1454,1601,1826,2081,1934,1607,1169,1377,923,332,1116,689,742,842,741,810,964,947,850,784,632,1794,1569,1544,1155,1312,1055,1230,1319,1300,733,805,579,647,427,510,351,419,266,285,289,320];
  matrix[54]=[477,639,275,331,192,205,147,175,178,218,283,116,151,154,135,141,152,100,96,93,92,107,99,134,150,251,161,654,602,694,281,250,196,206,194,217,297,206,207,129,98,91,85,86,84,82,81,82,84,75,79,92,80,78,77,78,77,78,74,79,74,78,70,71,77,78,74,74,75,75,75,74,74,78,80,97,98,109,140,141,139,269,291,287,215,294,267,252,453,926,734,1390,1294,1501,1414,1399,1583,1073,921,778,702,774,1252,1639,1634,1473,1546,1092,802,765,985,806,486,881,1221,1083,1047,1508,1187,1244,1492,1310,953,1119,1337,2122,2029,1144,1426,1068,1391,1080,1025,1156,1024,781,661,767,501,356,522,271,402,504];
  matrix[53]=[443,365,249,263,240,228,228,105,105,106,145,104,121,117,111,99,99,98,105,91,96,147,168,112,129,127,165,219,460,586,651,636,650,639,476,236,175,126,112,93,93,98,98,82,85,85,84,95,89,83,78,80,80,78,79,78,77,77,78,75,78,71,71,76,76,75,75,74,75,77,74,71,77,77,81,85,100,120,140,149,131,209,188,264,238,230,173,214,243,341,503,643,700,615,474,560,491,470,819,1231,1026,1043,1378,1663,1474,1979,1607,1867,1383,1685,1795,921,721,845,1770,1001,1382,1915,1719,1363,2247,1814,1200,1254,801,943,1055,1075,1366,1083,1095,1579,1351,1090,874,629,1224,840,915,663,576,439,449,238];
  matrix[52]=[699,715,157,164,145,453,115,182,121,113,113,149,135,185,118,103,94,92,95,104,142,235,139,156,108,160,161,212,388,516,524,420,173,171,149,120,108,95,106,100,90,91,87,85,87,94,75,84,76,80,85,83,81,80,80,79,82,82,79,79,76,73,73,74,77,76,74,71,74,73,74,75,79,81,81,85,90,102,115,169,287,213,199,206,170,147,258,810,782,675,544,1090,749,471,379,342,342,864,912,1120,1144,1714,1968,1472,1569,1824,1819,1474,1161,1314,1069,508,1385,1981,2121,1988,1931,2230,2282,2048,2147,1446,944,818,791,642,881,1219,1250,855,722,927,832,961,828,1095,931,1351,939,1197,1042,476,585,343];
  matrix[51]=[934,705,487,253,227,281,197,150,126,123,157,137,129,103,98,96,96,96,100,96,305,245,150,150,136,132,122,134,233,236,187,212,202,164,119,99,95,106,93,90,103,91,94,82,83,84,77,80,85,82,83,82,83,81,98,103,98,92,86,78,77,68,78,79,75,75,72,73,71,73,75,76,76,82,84,86,88,92,101,118,152,142,117,122,159,299,292,667,1071,1210,1071,941,668,660,584,455,258,435,601,1082,1247,1338,1193,1261,1360,1409,1268,1216,1082,885,651,382,399,556,684,775,925,1077,1010,946,1010,1290,1018,698,742,666,556,546,564,590,578,909,760,794,985,1022,1358,1322,1328,1065,1116,782,590,449];
  matrix[50]=[822,283,256,386,958,404,483,635,438,214,131,113,110,102,99,98,116,129,138,101,141,171,138,124,131,127,174,197,173,191,186,127,105,105,98,96,91,92,93,88,104,83,90,86,100,78,78,93,80,85,85,84,97,107,107,101,105,100,92,82,83,76,73,74,76,74,74,76,73,74,78,79,80,81,84,88,89,92,94,98,102,106,111,134,231,196,192,307,479,529,1076,1003,670,687,372,289,235,308,311,430,877,937,1200,965,713,1016,842,775,486,490,456,386,460,381,398,452,487,505,495,516,512,531,523,609,587,618,522,504,494,499,507,782,837,618,840,1100,1132,1352,1236,1232,834,523,437,281];
  matrix[49]=[336,277,243,206,200,152,617,304,297,126,261,205,159,126,161,109,104,104,111,119,110,110,106,113,132,140,161,191,134,110,110,100,100,97,98,96,110,93,90,90,90,94,99,101,91,82,143,78,90,82,84,99,105,107,94,102,106,100,98,96,89,76,75,74,75,76,76,76,78,80,80,81,82,84,86,95,110,111,175,153,155,163,164,149,126,132,165,202,289,367,645,411,514,610,380,249,213,194,253,329,423,556,505,740,539,502,445,547,466,414,426,522,536,407,471,484,492,551,417,421,428,428,465,510,728,796,545,484,605,677,526,514,521,530,586,854,1291,1400,1136,1305,698,602,816,522];
  matrix[48]=[292,435,499,191,180,177,154,146,141,207,132,442,801,397,175,146,127,158,117,157,116,137,121,134,229,227,144,123,105,101,102,105,107,114,97,98,98,106,129,173,135,134,144,120,81,91,79,80,84,86,95,112,117,107,109,100,101,99,101,101,91,80,72,75,74,75,75,78,83,82,86,88,90,103,117,133,133,154,214,174,214,229,174,212,198,225,226,239,317,308,171,168,305,179,187,200,431,690,445,201,224,286,378,361,328,334,429,428,517,376,487,519,546,462,445,544,573,532,555,521,507,535,468,461,588,692,542,505,969,711,543,836,634,541,552,860,1051,1034,987,1003,562,516,427,262];
  matrix[47]=[487,486,309,422,176,257,217,528,217,188,161,145,263,218,332,205,171,142,154,179,160,183,159,201,211,128,122,108,107,119,131,144,126,105,104,106,110,115,223,163,180,118,181,136,90,82,82,89,86,94,96,114,120,116,112,108,103,104,101,98,79,75,77,71,73,74,75,78,85,88,90,89,95,105,96,130,170,144,202,228,224,189,244,179,220,144,148,153,151,160,343,426,290,353,424,436,564,547,529,505,616,415,223,441,280,327,417,421,330,477,393,471,464,529,543,537,463,488,533,550,578,479,537,529,469,707,796,490,626,790,576,1039,709,579,552,577,945,1073,962,1086,528,548,341,382];
  matrix[46]=[683,1043,332,600,673,641,607,384,250,158,161,158,226,173,199,162,199,165,169,152,208,206,280,179,122,115,110,123,121,132,143,165,139,122,147,196,168,227,325,163,154,209,167,156,121,115,83,91,93,109,105,119,113,130,124,119,108,103,94,87,79,73,75,75,73,75,77,80,83,82,89,92,95,111,120,128,138,150,154,121,122,169,130,133,138,265,265,269,407,462,562,603,393,489,629,611,561,577,676,533,518,423,685,227,295,331,298,407,288,264,433,449,548,447,520,557,476,607,478,698,540,744,613,514,545,689,644,491,496,673,659,942,685,864,637,764,887,906,1069,545,569,501,502,454];
  matrix[45]=[575,391,356,353,609,455,309,451,284,263,268,261,345,696,457,303,210,295,445,256,217,269,135,123,122,130,115,147,136,125,163,163,156,160,150,221,303,165,291,290,325,259,242,271,155,83,100,86,109,138,119,117,122,130,132,131,123,112,102,92,79,79,77,73,75,77,79,83,85,88,95,105,94,101,103,113,105,108,115,115,212,494,565,452,366,506,424,451,624,719,486,381,327,261,419,682,606,748,546,1287,893,776,433,266,229,255,232,283,489,446,363,420,301,325,434,375,393,429,562,568,480,529,554,672,507,559,802,590,769,863,1052,771,1035,643,1078,1009,1189,793,643,431,420,369,351,282];
  matrix[44]=[325,278,257,240,255,327,376,243,255,392,432,296,265,223,217,221,173,185,200,155,147,145,132,126,122,130,138,146,149,136,152,165,215,264,202,189,253,228,222,245,311,226,146,122,125,85,94,91,115,156,147,135,127,133,130,124,122,109,100,91,87,78,78,75,75,75,81,82,87,87,95,102,103,98,99,101,104,108,114,110,289,193,256,202,322,312,469,252,180,743,609,238,361,395,712,638,772,916,862,1265,959,695,693,444,307,244,396,276,338,275,269,456,497,438,442,425,410,432,362,363,441,689,547,494,619,677,771,950,868,1345,709,652,825,828,1084,1036,1359,958,535,352,267,207,196,485];
  matrix[43]=[404,424,467,407,486,378,290,315,267,264,393,303,250,264,188,180,168,162,156,156,143,136,134,181,210,160,164,157,164,135,159,179,165,164,210,218,173,186,150,158,174,220,149,101,136,83,88,86,85,117,143,135,151,132,134,126,127,110,100,87,87,74,75,74,78,77,80,83,88,90,93,95,97,96,98,98,104,103,108,107,105,111,121,124,174,189,328,187,289,425,281,339,940,996,798,1000,615,917,779,935,1269,807,983,630,307,303,330,315,319,455,401,353,291,361,446,418,502,429,483,566,449,507,647,597,489,840,836,935,1055,843,724,925,998,1187,1234,820,722,465,399,572,371,215,671,373];
  matrix[42]=[615,512,695,700,1096,902,703,365,251,254,228,222,210,199,241,219,202,190,173,153,141,143,160,173,176,211,159,167,150,144,150,155,166,158,160,153,138,140,135,106,210,136,166,151,88,96,86,90,85,86,87,123,139,129,124,128,119,105,95,87,84,80,76,78,79,78,80,83,86,90,90,94,93,94,95,97,96,99,98,98,97,102,106,119,120,123,172,434,796,741,423,581,1352,1419,1087,903,1037,751,1026,1216,668,602,1188,641,459,347,313,258,332,299,399,299,361,409,375,459,407,333,355,468,478,710,749,740,936,852,947,1353,1036,670,739,969,1368,1064,1048,1502,668,447,626,473,347,325,427,246];
  matrix[41]=[772,419,985,1379,1177,1200,1217,635,268,237,297,223,310,271,234,219,231,180,155,153,215,223,296,180,188,169,126,122,153,136,135,148,145,171,141,175,163,175,160,143,134,162,190,139,106,101,88,89,85,88,90,107,113,128,123,119,108,102,97,87,90,74,78,77,77,77,80,82,84,89,92,91,92,93,93,91,93,93,93,92,95,100,101,110,127,204,437,831,709,407,315,685,1238,1059,1154,799,1417,1494,1597,1704,1102,775,782,607,391,407,381,316,314,370,413,332,333,443,345,419,381,331,405,398,473,889,541,943,843,790,866,1274,870,692,813,1149,1320,1144,1040,743,785,1025,485,421,279,290,489,180];
  matrix[40]=[417,585,410,734,528,737,707,398,309,245,241,300,215,227,183,173,167,172,268,158,172,262,195,191,234,149,107,124,169,127,107,142,152,206,237,241,212,212,118,113,161,101,168,161,119,172,100,88,91,89,89,93,105,107,116,111,106,98,92,89,83,76,75,80,77,77,80,83,86,86,88,91,91,92,84,85,85,87,89,93,98,96,103,123,202,381,723,503,384,220,295,514,1151,1304,1461,1098,1353,1320,1310,1390,1208,778,549,561,499,400,377,387,344,289,404,387,322,367,441,311,337,429,469,472,625,475,699,980,1049,1461,1288,1070,963,893,940,1310,794,895,1532,1009,976,863,567,443,337,264,209,162];
  matrix[39]=[1151,858,800,498,465,359,365,302,292,311,303,220,213,194,190,180,239,209,172,182,221,203,166,151,188,188,115,105,218,106,101,105,162,210,231,170,196,203,138,131,139,98,235,146,99,148,157,93,92,90,90,90,98,109,110,111,106,100,97,87,78,83,75,77,78,81,80,83,84,85,85,86,85,88,81,83,84,86,86,88,94,96,102,125,216,202,276,190,174,237,408,772,1273,1399,1063,1128,1089,1197,1119,637,1048,590,571,733,591,516,470,377,405,306,333,340,428,340,409,350,402,454,467,532,578,1035,1215,1594,1034,787,742,755,1051,1321,1224,1133,1137,1363,1203,1284,1025,734,509,286,324,253,181,160];
  matrix[38]=[1543,1426,824,399,334,306,299,259,261,263,246,231,280,309,250,323,262,227,190,211,242,184,199,152,152,178,166,100,106,100,100,101,117,119,148,256,221,160,162,142,146,117,96,94,134,153,163,118,91,95,90,91,96,103,103,110,117,104,95,87,85,86,76,76,78,74,78,81,78,81,86,85,85,81,84,81,83,87,87,91,94,99,104,109,122,127,185,156,213,272,478,866,1323,1199,1201,851,715,936,875,929,513,390,362,462,340,356,338,487,360,373,337,377,439,363,441,379,377,426,501,541,753,629,985,1126,1113,736,738,839,984,1439,976,904,1211,1013,843,983,949,651,324,237,281,442,238,165];
  matrix[37]=[1708,1218,707,337,330,293,276,274,388,316,268,415,337,262,303,263,283,256,207,246,252,186,172,240,207,205,167,299,298,105,206,145,100,100,105,187,181,128,132,142,149,136,142,90,102,144,103,104,91,93,90,91,96,101,104,119,112,107,101,96,78,81,85,77,80,79,78,81,81,82,84,82,81,81,83,82,84,84,86,89,92,99,129,139,220,233,200,147,349,531,756,888,586,926,964,756,615,604,590,620,474,443,581,462,443,295,301,414,431,359,391,350,393,378,488,422,453,380,463,536,884,1140,1484,1421,1047,756,767,1036,1281,1380,914,802,1213,1267,868,962,458,366,266,267,332,308,222,182];
  matrix[36]=[1162,1386,864,353,368,353,398,325,358,373,309,324,376,276,298,313,309,268,250,242,233,237,227,170,151,118,197,238,152,187,162,354,213,270,101,100,100,110,107,135,138,127,128,99,153,136,123,115,96,93,94,94,96,100,114,124,116,113,105,102,90,78,76,81,79,80,79,81,80,80,82,79,79,82,83,84,82,86,88,87,92,101,127,222,182,298,250,489,487,660,770,454,489,637,538,505,400,414,405,414,466,449,495,402,406,389,309,286,470,281,413,356,342,388,393,434,437,521,428,650,1097,708,1103,965,721,777,1123,1240,931,925,1022,1117,1101,470,416,584,501,330,341,346,436,308,205,182];
  matrix[35]=[884,789,702,347,347,339,332,371,392,362,325,385,286,279,231,265,297,277,197,189,226,246,175,224,158,147,142,139,193,196,253,272,275,268,336,269,102,100,108,112,138,146,96,106,122,134,143,137,96,95,93,93,104,118,123,130,126,118,110,99,87,85,80,81,82,80,80,79,82,81,81,80,80,83,82,83,84,86,87,91,94,109,136,153,176,200,194,291,427,502,265,304,648,741,566,830,524,429,377,285,432,369,409,416,468,440,268,336,275,498,400,353,427,375,399,415,543,790,980,794,1022,958,1110,974,931,848,903,972,810,1417,669,778,879,483,863,601,657,403,410,339,355,394,206,191];
  matrix[34]=[1319,595,646,481,547,359,424,547,450,412,351,285,274,267,301,269,250,222,221,220,204,180,169,231,164,169,146,158,148,184,201,358,411,464,330,278,214,134,167,192,157,105,121,128,120,116,137,93,96,94,92,92,96,116,132,138,131,116,111,96,88,83,84,85,84,82,82,81,80,80,80,80,81,81,84,85,86,89,88,89,94,99,106,126,221,148,164,271,281,315,521,678,559,411,379,356,742,425,287,353,261,437,465,463,519,593,461,240,429,322,446,368,421,566,391,402,554,839,1314,1380,1195,1143,1139,1766,1284,1354,1036,1462,1419,1735,1083,1066,711,864,961,713,482,573,429,290,229,251,224,193];
  matrix[33]=[1216,917,578,967,403,719,570,528,395,364,383,424,320,315,287,307,288,309,268,215,191,182,171,163,179,132,136,138,156,171,257,298,330,412,415,317,213,149,106,106,101,106,115,108,139,120,121,99,98,97,93,96,105,120,128,141,124,109,99,90,87,85,90,83,84,84,80,83,85,81,81,81,82,83,83,84,84,88,88,90,92,97,101,118,137,203,168,143,416,472,588,311,275,306,309,249,291,343,364,347,309,399,466,427,362,403,409,242,234,314,275,311,301,456,434,424,602,845,855,815,1330,1232,1706,1183,1594,1122,1431,1218,1199,816,784,1146,1019,850,678,706,398,348,360,267,350,237,219,283];
  matrix[32]=[1629,1518,931,812,628,876,649,765,845,622,731,394,324,321,316,293,291,259,250,217,209,191,169,155,184,133,129,142,132,159,206,285,400,337,467,416,434,424,263,253,113,159,158,256,119,147,117,92,99,90,93,96,107,125,139,128,173,100,92,99,87,83,83,84,81,81,80,84,85,81,82,80,82,83,84,87,88,89,90,92,93,95,98,102,120,108,115,162,214,158,236,232,277,210,340,254,274,319,227,219,186,321,380,321,274,369,420,524,311,428,280,328,374,487,552,527,686,800,736,935,1061,1015,1147,1538,953,1173,1181,1217,928,699,1371,1283,847,808,768,481,395,324,285,354,320,203,313,289];
  matrix[31]=[568,693,1016,966,667,1174,1126,929,884,763,694,747,372,387,385,413,445,575,381,246,239,213,166,153,144,161,167,122,121,129,148,176,203,294,405,285,253,237,221,145,184,136,173,176,169,118,133,104,97,100,106,124,118,127,163,188,161,105,98,92,86,85,81,84,82,81,83,83,82,83,85,84,81,84,85,86,86,91,91,92,95,100,108,129,103,102,142,129,128,194,273,209,310,256,166,195,195,253,298,287,277,257,325,231,388,450,420,516,622,796,362,571,543,657,482,624,656,815,1061,769,1006,944,864,1031,972,846,1189,937,1039,1080,1009,983,830,742,558,433,351,310,356,312,265,290,411,155];
  matrix[30]=[715,1161,796,703,1004,1293,1258,1324,1021,989,865,634,554,507,598,576,426,413,304,273,225,181,160,152,140,137,132,129,120,121,134,169,154,158,278,230,253,240,216,249,408,269,165,183,179,189,171,216,177,102,113,120,126,180,207,165,124,110,100,102,87,86,84,83,83,85,84,87,82,85,86,84,84,85,85,88,88,88,95,95,102,111,118,115,124,105,105,157,175,200,185,184,215,173,281,168,251,241,254,194,313,372,408,478,328,396,399,526,511,626,577,576,566,600,1084,979,582,759,608,967,1235,1187,1030,1227,1184,1128,1251,805,946,664,817,547,766,497,507,355,349,391,269,302,325,413,331,128];
  matrix[29]=[1087,778,910,1074,914,613,1042,1285,1028,859,1332,1146,990,844,682,570,435,337,289,237,252,199,183,153,125,127,122,122,117,116,127,122,166,191,187,171,175,190,200,192,183,329,214,230,177,156,186,213,304,116,126,172,177,242,174,148,126,102,102,96,89,96,87,86,83,85,86,83,83,84,88,85,83,84,85,86,92,105,106,108,112,113,133,123,131,118,127,119,108,114,120,130,143,155,159,192,309,287,238,167,166,259,327,375,296,334,505,369,428,596,776,1309,750,777,679,1214,1745,903,1189,1757,1109,1558,1060,889,1128,984,1138,660,918,818,720,658,429,421,400,286,348,411,248,230,363,378,521,106];
  matrix[28]=[1558,1479,1470,1096,1104,1097,780,1022,723,1177,1052,1419,854,612,701,553,699,302,272,213,211,156,136,132,118,116,120,114,114,112,111,123,220,132,136,132,139,148,167,185,167,310,287,185,243,197,379,384,124,106,128,208,220,280,173,140,107,106,106,95,91,93,100,100,90,90,89,84,87,90,87,88,89,87,85,86,88,102,126,126,124,128,139,135,150,137,149,134,129,118,112,119,123,127,145,154,231,366,212,250,160,155,216,168,268,397,618,657,629,881,1118,673,752,739,1639,1551,1488,1497,1587,1561,1261,1118,1424,1119,972,883,1140,845,612,638,707,622,518,398,299,333,351,254,252,276,303,139,211,143];
  matrix[27]=[802,890,687,1223,957,1319,902,831,978,899,1002,580,534,492,528,532,495,517,332,267,150,113,112,114,112,112,113,113,109,110,111,110,116,137,127,156,151,142,128,129,131,421,393,234,159,256,253,465,272,103,155,171,207,129,190,133,119,128,112,104,98,98,102,104,102,97,93,89,87,88,89,87,88,87,86,86,87,94,116,155,148,140,149,158,155,152,150,146,130,137,131,117,116,119,126,128,133,150,219,194,158,150,264,245,485,717,856,1136,1313,884,520,435,600,829,810,818,772,1345,1274,1529,1316,1405,1218,1088,1149,1267,801,653,831,804,576,423,366,349,408,277,351,304,248,240,177,189,94,90];
  matrix[26]=[898,957,1262,1315,1063,983,883,1159,1100,622,1408,795,713,392,327,292,352,251,216,198,121,111,116,119,113,113,114,114,114,115,111,110,111,109,110,111,113,120,104,107,110,113,113,121,104,156,367,344,498,98,112,235,228,256,143,133,126,182,188,129,169,40,124,115,125,113,103,98,95,88,89,87,87,88,87,87,88,95,106,120,124,127,134,144,153,164,146,135,121,114,112,111,113,116,121,122,127,128,135,135,430,582,465,683,867,1020,500,569,701,421,362,519,764,570,899,1329,1262,1065,1244,1253,1077,1169,1085,988,990,973,757,816,754,660,483,427,354,315,484,433,290,256,300,176,153,127,133,188];
  matrix[25]=[745,1309,1232,835,1137,879,1025,721,749,1005,590,1035,677,448,305,266,243,213,182,137,121,110,114,122,121,119,118,116,115,113,116,118,110,108,110,106,105,106,107,120,193,135,122,162,107,221,110,225,171,191,416,288,260,304,244,148,153,257,292,294,533,191,163,176,138,134,151,121,108,97,93,93,93,88,90,89,88,93,102,115,117,136,122,135,142,149,145,141,124,111,108,112,115,116,118,124,124,132,131,271,153,226,739,1109,1127,948,832,440,321,494,459,545,774,539,915,1595,1036,1491,1510,1433,1001,1142,1058,1061,1212,776,995,854,789,487,416,372,334,341,492,342,270,280,172,124,174,184,205,241];
  matrix[24]=[681,642,686,747,1057,957,673,572,786,806,728,641,503,408,380,262,227,207,198,311,203,112,110,166,157,126,123,127,119,119,116,111,112,107,108,107,111,107,110,125,126,147,206,150,120,120,393,754,396,263,192,159,280,367,395,212,214,314,559,934,366,245,157,244,223,271,280,232,139,135,102,96,91,93,89,88,88,95,100,110,112,116,112,134,128,148,146,139,118,109,112,112,112,113,118,118,123,126,130,139,151,196,420,820,735,840,440,275,343,818,403,1044,1833,1196,858,1345,1339,1445,1154,1041,1094,1014,839,818,1001,802,723,495,455,425,393,374,394,364,285,326,242,226,135,175,127,93,209,230];
  matrix[23]=[402,450,363,794,779,609,716,658,797,544,558,735,662,573,502,221,197,185,177,172,169,166,160,175,135,130,129,125,123,119,116,115,112,110,108,107,108,108,117,113,173,169,178,153,126,188,188,412,487,210,194,163,171,299,292,276,410,273,187,208,240,280,201,261,434,337,450,548,257,136,102,102,101,87,89,90,93,98,90,103,102,103,115,114,132,128,138,135,110,108,110,105,110,111,114,120,124,124,157,302,271,501,346,264,227,264,253,373,659,731,769,718,814,1004,1505,1240,929,1256,1046,769,799,616,984,840,806,744,714,468,501,381,473,424,305,334,413,293,206,164,183,142,113,97,96,97];
  matrix[22]=[297,289,359,315,340,363,352,459,588,589,552,834,456,463,428,321,190,166,199,175,241,244,155,182,176,132,138,127,126,119,119,114,113,112,108,112,107,114,119,123,228,185,159,144,136,188,198,128,195,138,155,140,138,144,146,195,219,303,466,342,263,342,274,339,337,635,806,652,454,225,110,106,130,116,88,92,91,93,95,102,94,96,100,99,103,109,134,124,108,106,122,108,111,111,117,123,127,129,149,424,288,180,217,267,266,313,514,467,737,626,952,745,903,1009,1084,1617,1176,831,853,797,656,687,891,956,643,683,498,434,399,479,329,374,379,330,349,307,269,199,232,197,155,126,199,242];
  matrix[21]=[401,217,234,343,365,295,269,323,314,263,430,429,349,486,483,271,211,174,159,151,148,145,140,139,197,201,135,130,125,126,119,116,117,116,114,113,119,128,125,167,214,169,160,151,147,182,180,255,304,452,276,203,178,204,156,338,210,227,379,342,264,255,340,203,391,260,259,333,249,205,113,113,206,155,136,102,169,92,97,99,104,91,93,93,100,101,108,113,114,117,109,108,112,113,114,119,126,245,175,298,166,273,292,305,382,458,474,712,1219,1150,1039,857,1251,1850,1687,1158,1479,779,833,744,991,427,802,624,460,446,474,424,357,413,323,222,283,190,201,187,194,240,241,198,183,152,254,193];
  matrix[20]=[459,750,641,411,269,440,554,355,302,228,251,233,233,291,292,439,243,157,156,151,149,147,146,144,154,324,433,127,182,121,122,124,120,121,120,140,202,212,130,192,156,209,166,164,220,249,345,262,283,353,377,559,457,336,232,182,274,280,206,247,280,196,349,220,196,188,296,224,129,123,164,132,120,206,124,344,259,128,95,91,94,93,93,97,94,99,111,105,105,105,104,109,106,105,115,120,122,208,257,680,215,265,410,527,600,438,987,695,841,990,1392,1264,744,832,1048,880,1264,1342,805,870,668,586,338,446,415,422,400,390,342,308,304,271,218,146,138,187,183,194,153,115,108,105,160,239];
  matrix[19]=[881,695,733,704,952,360,501,512,381,270,184,180,175,171,168,399,163,191,160,155,162,160,142,139,154,236,558,337,205,185,151,136,126,129,163,200,227,171,219,231,176,186,233,444,525,259,447,507,377,296,538,536,443,314,353,240,185,198,185,275,256,213,222,195,157,287,251,164,237,163,240,147,188,153,134,195,409,183,375,108,101,95,95,93,96,103,114,102,101,102,106,113,112,106,144,148,242,186,284,273,302,422,413,990,1308,783,1152,646,840,1511,1146,1281,983,968,788,737,958,1059,595,583,561,409,313,286,284,283,293,256,237,309,168,164,287,181,208,196,241,225,165,163,168,227,201,188];
  matrix[18]=[856,870,824,872,735,677,408,383,194,189,185,181,214,207,179,220,262,275,183,243,256,241,165,148,154,193,244,534,413,192,182,154,152,136,160,218,180,149,294,308,234,214,588,606,198,720,708,465,604,390,450,723,662,401,577,247,300,289,261,296,298,278,230,233,179,336,256,290,268,184,244,252,211,196,205,197,719,397,448,323,103,184,97,96,98,105,104,102,100,103,105,112,114,230,177,356,411,919,644,402,391,572,523,446,862,639,1037,1042,1125,1031,992,843,1237,1011,985,674,814,602,416,580,331,407,325,284,336,272,223,198,257,247,261,199,251,337,220,243,188,248,206,192,206,217,238,264];
  matrix[17]=[779,740,781,733,716,636,610,480,289,323,324,324,295,242,272,217,251,298,247,231,229,204,196,144,164,176,203,183,520,304,219,209,179,145,271,195,177,245,168,383,559,490,511,613,312,449,607,573,458,366,757,640,555,517,364,522,398,507,582,438,408,308,338,233,260,329,457,444,210,377,229,263,255,302,271,157,254,466,218,252,246,135,96,109,105,100,100,101,101,102,134,108,121,218,239,642,537,351,300,476,676,778,415,1010,1138,1254,1224,1170,1098,1222,1171,1403,1168,893,819,642,534,549,425,361,281,263,251,251,240,282,303,312,229,233,219,300,228,292,288,197,169,264,228,172,179,220,236,266];
  matrix[16]=[689,663,628,591,534,523,501,535,324,442,405,249,332,235,240,267,361,422,217,260,190,293,154,153,156,167,219,245,216,272,436,343,211,157,163,397,374,225,194,175,344,312,386,853,732,332,283,299,428,323,340,361,394,509,592,772,1082,743,429,386,459,371,325,258,284,523,602,331,206,254,314,192,191,200,206,202,167,510,523,163,123,97,97,101,98,105,103,109,113,248,262,242,588,561,655,619,309,527,682,578,1051,549,692,827,1165,1466,1110,1284,1517,1094,821,1003,1077,579,524,575,486,441,347,335,310,318,311,361,283,266,266,255,251,264,244,240,192,195,224,203,230,234,226,214,196,218,239,157];
  matrix[15]=[616,639,613,564,543,574,457,374,305,423,316,280,278,278,256,193,202,211,225,214,204,198,177,158,150,182,234,178,189,264,310,376,228,208,167,354,465,326,220,182,226,301,224,423,990,758,481,441,696,544,404,413,715,1033,1164,914,978,825,627,436,1017,830,281,524,443,404,587,460,785,1032,681,635,277,275,282,224,183,225,350,231,160,101,115,102,100,99,101,129,227,331,862,876,394,241,310,417,519,894,1105,1166,601,822,1049,774,1265,943,1477,1106,1017,841,964,767,572,530,413,393,376,375,357,343,329,318,287,310,270,290,286,273,152,147,153,237,248,194,288,162,238,226,314,230,248,296,319,303];
  matrix[14]=[546,536,519,529,545,570,530,570,479,496,422,300,229,285,240,191,180,175,197,303,223,180,183,153,152,176,220,313,267,397,291,406,347,373,182,344,807,437,243,207,298,796,269,255,446,430,741,833,1164,467,424,362,630,761,679,839,711,794,710,1003,708,598,1052,916,498,370,534,1153,952,729,819,763,608,732,514,211,191,270,318,304,128,108,195,114,126,106,120,187,825,532,212,267,336,724,581,932,500,433,650,737,826,778,1064,866,986,957,1222,1140,805,725,542,589,609,436,361,344,327,308,279,289,345,330,241,283,311,313,326,267,290,256,315,251,218,236,256,234,287,282,236,226,331,278,286,292];
  matrix[13]=[542,557,608,530,565,548,465,442,464,473,448,398,374,211,200,228,179,185,174,204,347,162,158,197,177,160,161,222,381,520,470,494,633,406,240,193,226,476,332,394,680,885,438,466,389,693,521,659,951,929,931,981,723,732,562,654,766,648,709,705,748,787,892,1184,717,838,667,831,607,1049,622,430,455,465,622,393,286,323,842,386,159,114,123,219,421,206,231,708,478,396,320,316,495,772,832,650,1228,708,767,1034,845,729,723,983,696,1059,899,764,620,659,487,455,511,452,343,311,289,282,271,280,279,313,267,307,299,332,334,309,355,304,184,267,211,271,275,284,276,281,233,238,273,314,267,270];
  matrix[12]=[523,600,598,505,465,567,486,491,458,438,426,408,311,353,202,211,221,198,211,175,185,174,186,239,208,192,206,188,170,202,271,378,553,459,629,422,254,211,376,607,768,648,525,483,440,682,454,563,1053,1271,822,939,961,1194,1711,1544,1621,1024,1072,1219,1365,1239,1356,887,913,752,670,447,414,462,594,481,694,492,350,220,391,697,486,198,140,127,202,188,258,638,835,948,295,270,506,519,795,505,655,931,864,648,711,986,857,645,678,944,1218,962,629,452,504,396,376,416,375,438,430,378,250,246,236,303,316,226,216,338,370,325,355,315,324,315,199,288,223,274,268,284,265,277,320,287,274,309,276,290];
  matrix[11]=[487,582,642,542,576,591,533,457,447,474,409,390,324,370,349,304,321,214,185,198,176,247,285,188,223,234,194,277,183,170,257,243,217,319,410,589,514,492,231,262,492,629,507,652,916,1027,473,436,525,929,829,951,916,793,1054,830,1017,749,868,857,1018,1039,844,653,580,591,564,488,490,435,629,602,561,358,320,272,440,668,341,168,338,160,185,231,206,376,265,289,534,429,757,868,484,484,797,857,640,697,718,856,1189,642,995,711,741,679,434,396,423,365,347,315,290,272,319,363,275,304,301,238,257,278,282,342,386,343,357,324,329,320,293,283,271,274,245,279,271,310,311,246,272,301,290,326];
  matrix[10]=[489,579,628,668,592,488,575,491,538,489,441,444,460,393,375,377,223,298,274,227,191,200,281,322,264,322,461,400,270,212,213,258,262,395,426,406,439,812,547,406,262,352,509,507,525,657,1168,395,484,727,802,688,478,557,555,566,677,747,827,888,862,900,801,708,650,681,773,783,858,787,719,1037,724,339,356,334,334,368,251,379,208,279,238,440,270,378,354,562,718,722,857,809,777,1184,807,682,699,712,1047,1061,994,778,614,400,469,491,377,344,377,365,364,327,293,270,304,295,246,237,304,334,266,358,305,364,365,361,354,346,338,275,334,289,281,306,275,266,285,349,311,232,319,351,303,326];
  matrix[9]=[477,517,561,568,664,512,566,585,653,524,530,552,514,403,380,453,394,365,278,254,231,236,254,246,242,297,291,361,448,404,264,319,380,423,501,448,443,534,771,375,408,381,303,467,442,479,522,711,1336,662,949,621,763,877,1106,830,1159,2017,1951,1078,1919,2204,2129,1090,786,643,705,847,1144,728,558,404,489,789,945,419,320,266,318,287,216,411,306,262,463,549,494,750,815,719,899,708,700,661,717,732,620,699,827,774,485,518,675,537,418,328,326,360,298,283,356,304,337,302,267,265,216,222,307,317,348,387,382,375,353,365,356,347,337,318,340,310,279,270,316,383,328,353,310,247,329,324,296,253];
  matrix[8]=[571,511,568,644,726,634,610,656,598,587,495,454,470,455,460,489,471,352,308,264,288,398,515,318,319,337,284,341,251,280,186,263,286,318,375,415,406,673,741,682,750,663,532,451,370,330,488,558,481,800,659,790,688,586,817,988,1067,1353,1392,1250,1481,1295,1408,1247,912,714,598,719,768,607,552,628,941,624,648,326,468,215,354,235,240,326,514,314,593,570,697,818,938,578,546,712,673,640,504,590,547,603,678,677,641,748,444,388,337,326,302,310,303,279,265,246,340,280,240,326,256,291,293,338,299,333,358,376,331,340,346,341,316,316,320,311,332,307,379,259,333,301,300,328,291,308,306,290];
  matrix[7]=[656,628,684,624,620,557,607,598,587,511,653,527,486,559,555,475,397,334,273,388,373,511,540,416,366,243,206,195,190,189,201,235,486,585,626,529,345,607,525,572,683,959,661,719,738,577,460,570,743,775,955,960,794,911,699,665,779,846,833,889,741,917,837,702,646,788,1016,810,652,746,431,555,775,493,399,483,323,380,348,304,495,553,472,529,678,500,672,527,705,428,453,596,560,677,516,499,405,620,555,395,398,328,287,279,304,304,278,272,264,253,247,238,267,246,300,246,339,317,327,360,258,408,342,380,340,335,334,314,360,312,329,319,290,326,348,343,323,272,335,284,328,295,356,316];
  matrix[6]=[588,588,528,625,663,643,536,497,554,593,518,593,563,588,510,412,449,446,470,346,485,578,586,603,404,249,232,198,190,200,240,264,350,432,540,399,338,468,713,689,613,729,665,588,486,594,395,650,814,1091,877,914,733,759,607,596,649,700,702,702,646,720,635,764,629,829,521,735,673,618,841,829,749,585,711,546,505,554,534,541,586,569,579,481,538,552,521,459,370,529,482,508,562,568,526,700,368,400,381,367,331,322,310,307,261,285,283,268,259,248,248,273,246,267,292,242,336,356,404,373,266,344,375,346,360,346,328,335,362,312,334,329,315,357,332,314,325,312,309,333,303,299,318,307];
  matrix[5]=[499,496,489,502,520,634,593,523,489,512,638,554,525,530,524,575,342,558,481,354,393,649,620,417,340,231,226,235,208,262,221,307,389,312,358,271,383,571,762,638,751,569,707,813,1004,542,457,644,849,510,732,870,1044,741,674,744,653,747,718,707,662,809,772,832,831,575,494,617,333,637,741,698,562,628,435,466,581,511,491,436,556,478,509,526,425,411,372,364,313,453,501,523,594,434,476,518,340,356,298,309,319,277,271,262,256,253,264,251,335,331,369,282,258,316,398,316,369,389,398,300,292,370,368,361,334,342,340,302,321,329,321,349,339,309,269,307,324,314,348,321,297,285,326,337];
  matrix[4]=[542,492,482,464,591,585,500,463,487,510,539,630,637,686,678,544,677,580,546,482,478,479,592,457,314,240,235,213,237,317,419,553,493,292,288,290,302,388,563,427,457,656,656,779,548,375,721,628,754,595,750,776,723,824,1341,1064,777,671,525,498,813,644,965,825,538,514,432,332,305,371,457,549,545,524,473,425,793,589,341,470,421,337,328,385,281,422,286,404,356,424,445,432,523,441,352,307,323,350,310,289,268,262,262,264,270,290,290,261,326,321,352,358,345,370,350,258,353,364,287,394,398,384,368,376,366,351,346,325,317,324,339,367,319,325,306,307,302,316,341,302,316,312,293,305];
  matrix[3]=[469,444,381,436,391,401,452,520,571,576,633,707,685,625,710,577,596,554,495,606,472,318,489,428,402,239,220,217,280,321,556,545,552,589,289,293,240,243,276,302,305,327,384,381,335,498,544,622,1082,514,375,373,689,643,869,521,624,532,537,635,594,402,552,791,603,715,570,445,324,427,423,333,368,462,280,291,296,288,279,305,305,269,292,294,322,293,374,273,464,340,409,407,356,270,269,284,286,263,258,286,281,276,287,274,266,278,287,264,361,350,355,377,443,357,333,297,391,342,347,399,396,349,342,357,396,330,338,348,324,319,340,370,345,305,306,297,308,314,314,313,312,294,277,284];
  matrix[2]=[431,400,441,551,537,464,467,514,410,577,558,646,661,660,649,564,546,509,482,572,387,396,439,282,250,234,232,287,567,539,600,562,504,542,446,343,319,239,220,271,272,281,290,329,303,344,390,796,1004,416,615,620,745,652,511,379,462,647,528,623,478,548,406,455,491,341,445,374,475,322,379,407,325,309,317,316,255,283,227,238,260,324,567,286,365,391,373,298,440,407,245,342,311,262,206,249,259,225,246,247,234,278,232,265,274,303,278,324,328,333,344,264,276,259,259,287,403,415,363,361,380,360,389,373,337,309,338,343,364,367,363,351,327,296,311,310,289,315,311,306,314,319,290,290];
  matrix[1]=[448,498,478,460,378,400,317,331,479,552,487,428,437,486,445,395,388,489,445,415,404,528,487,260,302,323,277,544,597,714,622,516,487,518,331,475,445,289,259,214,247,255,239,283,288,268,278,295,341,339,489,386,360,415,334,378,410,375,507,333,360,404,322,306,323,288,432,410,266,271,303,327,263,349,362,330,344,240,426,256,363,288,228,361,357,431,366,236,315,308,308,371,359,257,207,193,198,226,198,219,256,245,242,293,273,289,293,299,312,319,255,234,260,239,227,248,227,239,250,266,383,291,366,321,320,378,372,335,330,374,329,327,283,280,291,296,294,267,272,287,302,280,301,306];
  matrix[0]=[397,416,422,312,241,233,280,459,422,331,269,268,302,409,293,343,414,502,573,392,508,481,431,285,355,539,693,620,624,556,493,490,445,358,318,254,235,223,265,273,198,199,234,261,256,252,262,252,257,265,278,276,302,275,242,386,315,326,298,311,305,292,264,218,302,310,255,254,291,375,201,326,208,299,326,279,196,259,314,387,307,321,288,361,351,233,276,301,280,281,245,228,249,226,201,185,190,217,236,217,246,257,247,299,337,282,291,297,298,232,234,235,238,232,230,217,219,231,259,363,418,401,327,380,349,372,351,344,363,349,280,265,287,258,242,265,262,276,272,283,285,268,283,277];
 }
}
}

Az MXML alkalmazás forráskódja:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
applicationComplete="init(event)">
<fx:Script>
<![CDATA[

import flash.events.Event;   
import mx.events.FlexEvent;   
import spark.components.Label;   
import terkepadat.*;
  
public var domborzat:Terkepadat = new Terkepadat();

public var matrix_oszlopszam:int;

public var matrix_sorszam:int;
  
//A térképmegjelenítéshez használt terület előkészítése
public var kep:BitmapData=new BitmapData(800,800,false,0xdddddd);
public var bmp:Bitmap = new Bitmap(kep);   
 
//Általános segédváltozók a rajzoláshoz
public var nyers_szeles:int;
public var rajz_nagyitas:Number;
public var kepernyo_w:int;
public var kepernyo_h:int;
  
//Segédváltozók a térképadatok arányos megjelenítéséhez
public var lat_leptek:Number;
public var long_leptek:Number;
public var adat_long_felso:Number;
public var adat_lat_felso:Number;
public var adat_long_also:Number;
public var adat_lat_also:Number;
public var pixelnagyitas:int;
public var kep_arany_2:Number;
public var kep_arany:Number;
  
//Az alkalmazás inicializálása   
public function init(event:FlexEvent):void
{
//Segédváltozók létrehozása ciklusok kezeléséhez
var i:int;
var j:int;
var k:int;
var l:int;    
   
//A rendelkezésre álló képernyőméretek lekérdezése
kepernyo_w=stage.stageWidth;
kepernyo_h=stage.stageHeight;
   

//A térképadatok megadása
adat_long_felso=15.0;
adat_lat_felso=50.0;
adat_long_also=27.0;
adat_lat_also=45.0;
lat_leptek=0.08333;
long_leptek=0.08333;
//Segédváltozók beállítása
matrix_oszlopszam=domborzat.matrix[0].length;
matrix_sorszam=domborzat.matrix.length;
  
//A rajzolás előkészítése
display_init(adat_lat_felso,adat_long_felso,adat_lat_also,adat_long_also,kepernyo_w,kepernyo_h);
pixelnagyitas=5;

//A rajzvászon előkészítése
bmp.x=0;
bmp.y=0;
bitkep_rajzvaszon.addChild(bmp);
   
//A magasságadatok kirajzolása
for(i=0;i<matrix_sorszam;++i)
{
for(j=0;j<matrix_oszlopszam;++j)
{
 for(k=0;k<pixelnagyitas;++k)
 {
  for(l=0;l<pixelnagyitas;++l)
  {
keppont_kirajzol(k+get_point_x(adat_long_felso+((j*pixelnagyitas)*long_leptek)),l+get_point_y(adat_lat_felso-((i*pixelnagyitas)*lat_leptek)),domborzat.matrix[i][j]);
  }
 }
}
}
   
//A konkrét városok megejelnítése
varosnev_kirajzol(46.253,20.1414,"Szeged");
varosnev_kirajzol(47.4979,19.0402,"Budapest");
}
  
//A városnevek kirajzolása
public function varosnev_kirajzol(lat:Number,long:Number,nev:String):void
{
//Segédváltozók
var s1:Number;
var s2:Number;
var szoveg:Label;

s1=(long-adat_long_felso)/long_leptek;

s2=(adat_lat_felso-lat)/lat_leptek;

rajzvaszon.graphics.lineStyle(2,0x000000);

rajzvaszon.graphics.drawCircle(get_point_x(adat_long_felso+(s1*pixelnagyitas*long_leptek) ),get_point_y(adat_lat_felso-((s2*pixelnagyitas)*lat_leptek)),3);

rajzvaszon.graphics.lineStyle(1, 0xFF0000);

szoveg = new Label();
szoveg.setStyle("fontSize", "12");
szoveg.setStyle("color", "0x000000");
szoveg.setStyle("textAlign", "center");
szoveg.setStyle("verticalAlign", "middle");
szoveg.text=nev;
 
szoveg.x=get_point_x(adat_long_felso+(s1*pixelnagyitas*long_leptek) )+5;

szoveg.y=get_point_y(adat_lat_felso-((s2*pixelnagyitas)*lat_leptek));

rajzvaszon.addElement(szoveg);
}
  
//A magassági adatok megjelenítése a megfelelő színnel
public function keppont_kirajzol(x:int,y:int,mag:int):void
{
//zöld színárnylatok
if(mag<=50) kep.setPixel(x,y,0xa9e89b);
elseif(mag<=100) kep.setPixel(x,y,0x86d575);
elseif(mag<=200) kep.setPixel(x,y,0x65c750);
elseif(mag<=300) kep.setPixel(x,y,0xdbb66b);
elseif(mag<=400) kep.setPixel(x,y,0xd2aa59);
elseif(mag<=500) kep.setPixel(x,y,0xcca048);

//barna színárnyalatok
elseif(mag<=600) kep.setPixel(x,y,0xca9c40);
elseif(mag<=700) kep.setPixel(x,y,0xc89835);
elseif(mag<=800) kep.setPixel(x,y,0xc8942b);
elseif(mag<=900) kep.setPixel(x,y,0xbd881e);
elseif(mag<=1000) kep.setPixel(x,y,0xb67f0f);
elseif(mag<=1100) kep.setPixel(x,y,0xb27906);
elseif(mag<=1200) kep.setPixel(x,y,0xa97306);
elseif(mag<=1300) kep.setPixel(x,y,0xa06c04);
elseif(mag<=1400) kep.setPixel(x,y,0x916100);
elseif(mag<=1500) kep.setPixel(x,y,0x7d5300);
elseif(mag<=2000) kep.setPixel(x,y,0x5f4000);
}

//A GPS koordináták leképezését végző algoritmusok
public function display_init(lat1:Number,long1:Number,lat2:Number,long2:Number,szeles:int,magas:int):void
{
rajzvaszon.graphics.clear();

rajzvaszon.removeAllElements();

kep_arany=kep_arany_2=szeles/magas;

nyers_szeles=Math.abs(long1-long2)*100000;

rajz_nagyitas=1/(int)(nyers_szeles/kepernyo_w)/4;

if(stage.stageWidth > stage.stageHeight)
 kep_arany=1;

if(stage. stageWidth < stage. stageHeight)
 kep_arany_2=1;
}
  
//GPS koordináta átalakítása X képernyőkoordinátává
public function get_point_x(long:Number):int
{    
return (int)((0)- (((21-long)*100000) * rajz_nagyitas) * kep_arany );
}
  
//GPS koordináta átalakítása Y képernyőkoordinátává
public function get_point_y(lat:Number):int
{
return (int)((((47.5-lat)*100000) )*rajz_nagyitas)*kep_arany_2;
}

//Kilépés az alkalmazásból
public function Kilepes():void
{
NativeApplication.nativeApplication.exit();
}

]]>
</fx:Script>
<s:SpriteVisualElement width="100%" height="100%" id="bitkep_rajzvaszon" click="Kilepes()"/>

<s:Group id="rajzvaszon" width="100%" height="100%"/>

</s:Application>




13.4. Egyszerűbb és fotorealisztikus megjelenítés

A koordináta- és térképmegjelenítő motorunk univerzális megoldást ad ugyan, ám nagymennyiségű térképadat megjelenítésekor (itt több millió magasságadatra gondoljunk) meglehetősen sokáig (másodpercek) is eltarthat a térképek kirajzolása, nagyítása stb.

Sebességnövekedést elérni kétféleképpen lehet:
- folyamatok párhuzamosításával
- műveletek csökkentésével.

Ez utóbbit fogjuk megcélozni a következő két példaprogramban.
Az első egyszerűsítést tartalmazó példaprogramban (Pelda_13) a kép nagyítását és a képarány beállítását fogjuk az Actionscript Bitmap objektumainak beépített metódusaival elvégezni. A kirajzolandó képpontok száma így mindig viszonylag alacsony marad, míg az eredeti megoldásban ez akár meg is többszöröződhet.
Javítjuk továbbá a domborzat színezését is, azt dinamikussá téve. A színek megállapításához a megjelenítendő magasságadatok legkisebb és legnagyobb értékeit vesszük határértékeknek és ezek közt egyenletes léptékeket véve határozzuk meg a végleges színt. Így mindig a lehető legszélesebb színspektrumot használhatjuk fel a megjelenítéshez.
Ehhez szükségünk lesz a 8.4. fejezetben megismert RGBtoINT(r:int,g:int,b:int):intfüggvényre:

public function RGBtoINT(r:int,g:int,b:int):int
{
 return r << 16 | g <<8 | b;
}

A színátmenetekhez szükséges színeket a 8.12. fejezetben bemutatott színátmenet-generáló algoritmussal végeztetjük el.

Emlékeztetőül:
r = 0 * (1.0- szazalek) + 255 * szazalek;
g = 0 * (1.0-szazalek) + 255 * szazalek;
b = 0 * (1.0-szazalek) + 255 * szazalek;

Példánkban roppant egyszerűen járunk el, az ábrázolást hozzáigazítva kicsit térképünk domborzati viszonyaihoz:

- A 0-200 méterig terjedő magasságokat egy sötétzöld-világoszöld színátmenettel jelöljük, melyek RGB értékei (16,142,2) és (113,236,119).

- A 200-2655 méterig (A Kárpátok legmagasabb pontja a Magas-Tátrában található: 2655 méter) terjedő magasságokat egy világosbarna-sötétbarna színátmenettel jelöljük, melyek RGB értékei (255,241,174) és (120,99,1).

A keppont_kirajzol függvényünk módosított változata ennek megfelelően:


public function keppont_kirajzol(x:int,y:int,mag:int):void
{
var r:int;
var g:int;
var b:int;
var szin:int;
var szazalek:Number;
   
if(mag<=200)
{
 szazalek=mag/200;

 r= 16 * (1.0-szazalek) + 113 * szazalek;

 g= 142 * (1.0-szazalek) + 236 * szazalek;

 b= 2 * (1.0-szazalek) + 119 * szazalek;
}
else if(mag>200 && mag <2655)
{
 szazalek=(mag-200)/2455;

 r= 255 * (1.0-szazalek) + 120 * szazalek;

 g= 241 * (1.0-szazalek) + 99 * szazalek;

 b= 174 * (1.0-szazalek) + 1 * szazalek;
}

szin=RGBtoINT(r,g,b);

kep.setPixel(x,y,szin);
}


A színátmenetes megjelenítés nagyon szép, már-már foto¬realisztikus képek megjelenítését teszi lehetővé.

A következő változókra nem lesz szükségünk:

public var nyers_szeles:int;

public var rajz_nagyitas:Number;

Bevezetünk viszont egy újat:

public var TERKEP_nagyitas:Number;

A display_init függvényüket lényegesen leegyszerűsítjük, és csak a képarány kiszámítására használjuk:


public function display_init(lat1:Number,long1:Number,lat2:Number,long2:Number,szeles:int,magas:int):void
{

rajzvaszon.graphics.clear();

rajzvaszon.removeAllElements();

kep_arany=kep_arany_2=szeles/magas;


if(stage.width > stage.height)
kep_arany=1;

if(stage.width < stage.height)
kep_arany_2=1;
}


A képarány ismeretében már beállíthatjuk a bitképünk képarányát, melyet nagyítással is kombinálunk:

TERKEP_nagyitas=5.0;

bmp.scaleY=TERKEP_nagyitas*kep_arany_2;

bmp.scaleX=TERKEP_nagyitas*kep_arany;

A térkép kirajzolásakor így már nem kell törődnünk sem a képarányokkal, sem a pixelek nagyításával, azokat a Flash végzi el helyettünk:

for(i=0;i<matrix_sorszam;++i)
{
for(j=0;j<matrix_oszlopszam;++j)
{
 keppont_kirajzol(j,i,domborzat.matrix[i][j]);
}
}

A  varosnev_kirajzol függvényből is eltávolíthatjuk a pixelnagyitas változóra történő hivatkozást.

További egyszerűsítést eszközölhetünk el a képernyő koordináták kiszámolását végző függvényekben:

public function get_point_x(long:Number):int
{
return (int)( kep_arany * (long-15.0)/( long_leptek/TERKEP_nagyitas) );
}   

  
public function get_point_y(lat:Number):int
{
return (int)(kep_arany_2 * ((50-lat)/( lat_leptek/TERKEP_nagyitas))  );
}

Az alkalmazásból a megjelenített kép megérintésével tudunk kilépni.
Végeredményben nem lett kevesebb a forráskód, ám jóval egyszerűbb lett és a térkép kirajzolásának sebessége is nagyságrendekkel növekedett.


Az alkalmazás teljes forráskódja:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
applicationComplete="init(event)">
<fx:Script>
<![CDATA[

import flash.events.Event;   
import mx.events.FlexEvent;   
import spark.components.Label;   
import terkepadat.*;
  
public var domborzat:Terkepadat = new Terkepadat();

public var matrix_oszlopszam:int;

public var matrix_sorszam:int;
  
//A térképmegjelenítéshez használt terület előkészítése
public var kep:BitmapData=new BitmapData(800,800,false,0xdddddd);
public var bmp:Bitmap = new Bitmap(kep);   
  
//Általános segédváltozók a rajzoláshoz
public var kepernyo_w:int;
public var kepernyo_h:int;
public var TERKEP_nagyitas:Number;
  
//Segédváltozók a térképadatok arányos megjelenítéséhez
public var lat_leptek:Number;
public var long_leptek:Number;
public var adat_long_felso:Number;
public var adat_lat_felso:Number;
public var adat_long_also:Number;
public var adat_lat_also:Number;
public var kep_arany_2:Number;
public var kep_arany:Number;
  
//Az alkalmazás inicializálása   
public function init(event:FlexEvent):void
{
//Segédváltozók létrehozása ciklusok kezeléséhez
var i:int;
var j:int;
       
//A rendelkezésre álló képernyőméretek lekérdezése
kepernyo_w=stage.stageWidth;
kepernyo_h=stage.stageHeight;
//A térképadatok megadása
adat_long_felso=15;
adat_lat_felso=50;
adat_long_also=27;
adat_lat_also=45;
lat_leptek=0.08333;
long_leptek=0.08333;
//Segédváltozók beállítása
matrix_oszlopszam=domborzat.matrix[0].length;
matrix_sorszam=domborzat.matrix.length;
   
//A rajzolás előkészítése
display_init(adat_lat_felso,adat_long_felso,adat_lat_also,adat_long_also,kepernyo_w,kepernyo_h);

//Térképadatok megadása
TERKEP_nagyitas=5.0;
bmp.scaleY=TERKEP_nagyitas*kep_arany_2;
bmp.scaleX=TERKEP_nagyitas*kep_arany;
   
//A rajzvászon előkészítése
bmp.x=0;
bmp.y=0;
bitkep_rajzvaszon.addChild(bmp);
 
//A magasságadatok kirajzolása
for(i=0;i<matrix_sorszam;++i)
{
 for(j=0;j<matrix_oszlopszam;++j)
 {
  keppont_kirajzol(j,i,domborzat.matrix[i][j]);
 }
}
   
//A konkrét városok megejelnítése
varosnev_kirajzol(46.253,20.1414,"Szeged");
varosnev_kirajzol(47.4979,19.0402,"Budapest");
}
  
//A városnevek kirajzolása
public function varosnev_kirajzol(lat:Number,long:Number,nev:String):void
{
//Segédváltozók
var s1:Number;
var s2:Number;
var szoveg:Label;

s1=(long-adat_long_felso)/long_leptek;

s2=(adat_lat_felso-lat)/lat_leptek;

rajzvaszon.graphics.lineStyle(2,0x000000);

rajzvaszon.graphics.drawCircle(get_point_x(adat_long_felso+(s1*long_leptek) ),get_point_y(adat_lat_felso-(s2*lat_leptek)),3);

rajzvaszon.graphics.lineStyle(1, 0xFF0000);

szoveg = new Label();
szoveg.setStyle("fontSize", "12");
szoveg.setStyle("color", "0x000000");
szoveg.setStyle("textAlign", "center");
szoveg.setStyle("verticalAlign", "middle");
szoveg.text=nev;
szoveg.x=get_point_x(adat_long_felso+(s1*long_leptek) )+5;
szoveg.y=get_point_y(adat_lat_felso-(s2*lat_leptek));

rajzvaszon.addElement(szoveg);
}
  
//Színek előállítása RGB színkódok alapján
public function RGBtoINT(r:int,g:int,b:int):int
{
return r << 16 | g <<8 | b;
}
  
//A magassági adatok megjelenítése a megfelelő színnel
public function keppont_kirajzol(x:int,y:int,mag:int):void
{
var r:int;
var g:int;
var b:int;
var szin:int;
var szazalek:Number;

if(mag<=200)
{
szazalek=mag/200;

r= 16 * (1.0-szazalek) + 113 * szazalek;

g= 142 * (1.0-szazalek) + 236 * szazalek;

b= 2 * (1.0-szazalek) + 119 * szazalek;
}
else if(mag>200 && mag < 2655)
{
szazalek=(mag-200)/2455;

r= 255 * (1.0-szazalek) + 120 * szazalek;

g= 241 * (1.0-szazalek) + 99 * szazalek;

b= 174 * (1.0-szazalek) + 1 * szazalek;
}

szin=RGBtoINT(r,g,b);

kep.setPixel(x,y,szin);
}
  
//A GPS koordináták leképezését végző algoritmusok
public function display_init(lat1:Number,long1:Number,lat2:Number,long2:Number,szeles:int,magas:int):void
{
rajzvaszon.graphics.clear();

rajzvaszon.removeAllElements();

kep_arany=kep_arany_2=szeles/magas;

if(stage.width > stage.height)
kep_arany=1;

if(stage.width < stage.height)
kep_arany_2=1;
}
  
//GPS koordináta átalakítása X képernyőkoordinátává
public function get_point_x(long:Number):int
{
return (int)( kep_arany * (long-15.0)/(0.08333/TERKEP_nagyitas) );
}
  
//GPS koordináta átalakítása Y képernyőkoordinátává
public function get_point_y(lat:Number):int
{
return (int)(kep_arany_2 * ((50-lat)/(0.08333/TERKEP_nagyitas))  );
}

//Kilépés az alkalmazásból
public function Kilepes():void
{
NativeApplication.nativeApplication.exit();
}

]>
</fx:Script>
<s:SpriteVisualElement width="100%" height="100%" id="bitkep_rajzvaszon" click="Kilepes()"/>

<s:Group id="rajzvaszon" width="100%" height="100%"/>

</s:Application>


13.5. Még gyorsabb térképmegjelenítés

Lehet-e még tovább növelni a domborzat kirajzolásának sebességét? Igen, mégpedig úgy, hogy ha egyetlen képpontot sem rajzolunk ki! Harmadik domborzatmegjelenítő példaprogramunkban (Pelda_14) extrém mértékben leegyszerűsítjük programunk forráskódját, közel felére csökkentve azt!
Az egyszerűsítést úgy fogjuk elérni, hogy a domborzatot előre elkészítjük és egy statikus képet fogunk megjeleníteni. Sőt, a képet be is ágyazzuk alkalmazásunkba, egy Image típusú Flex komponens segítségével.
A kép formátuma PNG (Portable Network Graphics). Ez a formátum különösen ajánlható rétegmegjelenítéshez, mivel képes tárolni átlátszó területeket is a képen.
A végeredmény ugyanaz lesz, ám a domborzat kirajzolásához szükséges időt gyakorlatilag nullára csökkentjük.
A képfájl neve Karpat_medence_2.png' és a projektmappán belül, a terkep mappában helyezzük el. Mérete 144x60.
A térképpprojekcióhoz szükséges léptékeket a következők szerint számolhatjuk ki ebben az esetben:

TÉRKÉP_SZÉLESSÉGE FOKOKBAN / KÉP VÍZSZINTES FELBONTÁSA

TÉRKÉP_MAGASSÁGA FOKOKBAN / KÉP FÜGGŐLEGES FELBONTÁSA

Konkrét számadatokkal:

lat_leptek = (adat_lat_felso - adat_lat_also) / 333;

long_leptek = (adat_long_felso - adat_long_also) /800;

Konkrét értékek helyett természetesen a beolvasott kép width és height értékeit is behelyettesíthetjük, így még általánosabb megoldást kapunk.
A városok helyének kirajzolására továbbra is szükség lesz, de csak azokat a metódusokat fogjuk megtartani, melyekre mindenképpen szükség van.
Számos segédváltozó mellett törölhetjük a display_init és a színelőállító függvényeinket is.
A képarányt úgy állítjuk be, hogy a kép magassága 1.5-ször nagyobb lesz, mint a szélessége, valamint a get_point_y metódus is 1.5-tel szorozza meg a kiszámolt értékeket.
Az alkalmazás ilyen mértékű egyszerűsítésének ára, hogy maga után vonja a későbbi bővíthetőség, testreszabhatóság esetleges korlátait.

Az extrém módon leegyszerűsített alkalmazás teljes forráskódja:


<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
applicationComplete="init(event)"
backgroundColor="#000000">
<fx:Script>
<![CDATA[

import flash.events.Event;   
import mx.events.FlexEvent;   
import spark.components.Label;   
     
//Általános segédváltozók a rajzoláshoz
public var TERKEP_nagyitas:Number;
  
//Segédváltozók a térképadatok arányos megjelenítéséhez
public var lat_leptek:Number;
public var long_leptek:Number;
public var adat_long_felso:Number;
public var adat_lat_felso:Number;
public var adat_long_also:Number;
public var adat_lat_also:Number;
  
//Az alkalmazás inicializálása   
public function init(event:FlexEvent):void
{
//Segédváltozók létrehozása ciklusok kezeléséhez
  
//A térképadatok megadása
adat_long_felso=15;
adat_lat_felso=50;
adat_long_also=27;
adat_lat_also=45;
lat_leptek=0.08333;
long_leptek=0.08333;
   
//Térképadatok megadása
TERKEP_nagyitas=3.0;
terkep.scaleY=TERKEP_nagyitas*1.5;
terkep.scaleX=TERKEP_nagyitas;
     
//A konkrét városok megejelnítése
varosnev_kirajzol(46.253,20.1414,"Szeged");
varosnev_kirajzol(47.4979,19.0402,"Budapest");
}
  
//A városnevek kirajzolása
public function varosnev_kirajzol(lat:Number,long:Number,nev:String):void
{
//Segédváltozók
var s1:Number;
var s2:Number;
var szoveg:Label;

s1=(long-adat_long_felso)/long_leptek;

s2=(adat_lat_felso-lat)/lat_leptek;

rajzvaszon.graphics.lineStyle(2,0x000000);

rajzvaszon.graphics.drawCircle(get_point_x(adat_long_felso+(s1*long_leptek) ),get_point_y(adat_lat_felso-(s2*lat_leptek)),3);

rajzvaszon.graphics.lineStyle(1, 0xFF0000);

szoveg = new Label();
szoveg.setStyle("fontSize", "12");
szoveg.setStyle("color", "0x000000");
szoveg.setStyle("textAlign", "center");
szoveg.setStyle("verticalAlign", "middle");
szoveg.text=nev;
szoveg.x=get_point_x(adat_long_felso+(s1*long_leptek) )+5;
szoveg.y=get_point_y(adat_lat_felso-(s2*lat_leptek));

rajzvaszon.addElement(szoveg);
}
  
//GPS koordináta átalakítása X képernyőkoordinátává
public function get_point_x(long:Number):int
{
return (int)( (long-15.0)/( long_leptek/TERKEP_nagyitas) );
}
  
//GPS koordináta átalakítása Y képernyőkoordinátává
public function get_point_y(lat:Number):int
{
return (int)(1.5 * ((50-lat)/( lat_leptek/TERKEP_nagyitas))  );
}

//Kilépés az alkalmazásból
public function Kilepes():void
{
NativeApplication.nativeApplication.exit();
}
 
]]>
</fx:Script>
<s:Image id="terkep" source="@Embed('..\\terkep\\Karpat_medence.png')" click="Kilepes()"/>

<s:Group id="rajzvaszon" width="100%" height="100%"/>

</s:Application>


13.6. A magyarországi autópályák kirajzolása

Az előző fejezetben megismert alkalmazást továbbfejlesztjük, mégpedig kirajzoljuk a magyarországi autópályák nyomvonalát (Pelda_15).
Az útvonalakra mint vonalakra fogunk tekinteni és vektoros módon kezeljük őket. Minden útvonal meghatározott számú pont összekapcsolásával rajzolható ki.
Ezúttal egy nagyobb, 800x333-as felbontású képet fogunk használni (Karpat_medence_2.png'), melyet ezúttal is a projektmappán belül, a terkep mappában helyezünk el.



A projekcióhoz használt legfontosabb változók értékei változatlanok, egyedül a lépték változik a felhasznált térkép mérete miatt:

adat_long_felso=15;
adat_lat_felso=50;
adat_long_also=27;
adat_lat_also=45;

lat_leptek=0.015;
long_leptek=0.015;

A képernyő tartalmának dinamikus átméretezésével nem foglalkozunk.
Ezt a térképet praktikus fekvő képernyőtájolással nézni, ezért az alkalmazás –app.xml leírófájljában állítsuk be az alábbi kapcsolókat:

<aspectRatio>landscape</aspectRatio>

<autoOrients>false</autoOrients>

Ezzel beállítjuk a fekvő képernyőtájolást és letiltjuk a képernyő automatikus forgatását.
Az egyes útvonalakat, ill. nagyobb szakaszokat egyedi azonosítóval fogjuk jelölni. Ez azért fontos, hogy az útrajzoló algoritmusunk minden útvonalat külön-külön, helyesen ki tudjon rajzolni. Összesen 26 különböző szakaszt fogunk kirajzolni.
Az útadatok tárolására használhatunk kétdimenziós tömböt, mely az azonosítókat és a GPS koordinátákat (mint a kirajzolandó vonalak pontjait) tartalmazza. Ez a legegyszerűbb megoldás.
Az alábbi kódrészlet erre mutat példát, egy utvonal_lista elnevezésű tömb használata esetén:

Tegyük fel, hogy a tömb neve autopalyak. Ekkor az 1. sorszámú út első két koordinátája a következőképpen tárolható:

autopalyak[0]=[1, 47.612397095, 19.0643692017]
autopalyak[1]=[1, 47.6120634222, 19.0666651726]
...

Tehát az utak azonosítói az autopalyak[SORSZÁM][0], a latitude koordinátáai az autopalyak[SORSZÁM][1] és a longitude koordinátái az  autopalyak[SORSZÁM][2] tömbelemek lesznek.
Számos professzionális digitális térképrendszer ezt a tárolási elvet alkalmazza az ehhez hasonló adatok tárolására, például az ún. shapefájlok (lásd 14. fejezetet) is.
Az egyes útvonalak adatait szépen sorban, egymás után, ugyanebben a tömbben tároljuk. Szükséges viszont, hogy az ugyanahhoz az úthoz tartozó adatok logikai, lineáris sorrendben, egy csoportban legyenek tárolva. Erre az adatgyűjtés, ill. adatrendszerezés során ügyelnünk kell.
Az ugyanahhoz az úthoz tartozó pontokat folyamatosan összekötjük vonalakkal. Mindössze arra kell figyelnünk, hogy a soron következő pont ugyanahhoz az úthoz tartozik-e.

Ezt a következő ellenőrzéssel végezzük el:

if(autopalyak.matrix[i][0] == autopalyak.matrix[i+1][0])

Amennyiben a feltétel nem teljesül, nem kell csinálnunk semmit.
Az útvonalak kirajzolására egy külön függvényt írunk, melynek neve uthalozat_kirajzol :


public function uthalozat_kirajzol():void
{

var i:int;

rajzvaszon.graphics.clear();

rajzvaszon.removeAllElements();

rajzvaszon.graphics.lineStyle(3, 0xFFFFFF);


for (i = 0; i<autopalyak.matrix.length-1; ++i)
{     

if(autopalyak.matrix[i][0] == autopalyak.matrix[i+1][0])
{

rajzvaszon.graphics.moveTo(get_point_x(autopalyak.matrix[i][2]),get_point_y(autopalyak.matrix[i][1]));
  rajzvaszon.graphics.lineTo(get_point_x(autopalyak.matrix[i][2]),get_point_y(autopalyak.matrix[i][1]));

}

}

}


Az utakat 3 képpont szélességű vonalakkal rajzoltatjuk ki, hogy jól láthatóak legyenek a képernyőn.

Az úthálózat kirajzolása után megjelenítünk pár várost is:

varosnev_kirajzol(46.253,20.1414,"Szeged");
varosnev_kirajzol(47.4979,19.0402,"Budapest");
  
varosnev_kirajzol(46.8417,16.8416,"Zalaegerszeg");
varosnev_kirajzol(47.9495,21.7244,"Nyíregyháza");
varosnev_kirajzol(47.6874,17.6504,"Győr");

Az autópályák adatainak a tárolására külön osztályt hozunk létre.

Ennek neve uthalozat lesz:

package uthalozat
{
public class Uthalozat
{
 public var matrix:Array = new Array();
 
 public function Uthalozat()
 {
   matrix[0]=[1,47.612397095,19.0643692017];
matrix[1]=[1,47.610123064,19.08659935];
matrix[2]=[1,47.5944992076,19.1383337975];
matrix[3]=[1,47.5519387066,19.1817855835];
matrix[4]=[1,47.5347821537,19.2373895654];
matrix[5]=[1,47.5109243921,19.2679595947];
matrix[6]=[1,47.5013017073,19.3081998755];
matrix[7]=[1,47.4640911863,19.3318033149];
matrix[8]=[1,47.4018892199,19.3132638933];
matrix[9]=[1,47.3901140518,19.2980289389];
matrix[10]=[1,47.3538714909,19.2025852134];
matrix[11]=[1,47.367183676,19.1630744934];
matrix[12]=[1,47.3673406309,19.1113042814];
matrix[13]=[1,47.3785203046,19.0669727308];
matrix[14]=[1,47.3807346515,19.019651413];
matrix[15]=[1,47.3995615114,19.0093517303];
matrix[16]=[1,47.4019182672,18.9727306368];
matrix[17]=[1,47.42040847,18.9461660368];
matrix[18]=[1,47.4206698033,18.9138078672];
matrix[19]=[1,47.4631598642,18.8786888127];
matrix[20]=[2,47.5961179466,19.1362094879];
matrix[21]=[2,47.6008233935,19.1519165039];
matrix[22]=[2,47.6176044303,19.1683959961];
matrix[23]=[2,47.6545037596,19.1598129272];
matrix[24]=[2,47.6830568152,19.1744041443];
matrix[25]=[2,47.7250564914,19.1570663452];
matrix[26]=[2,47.747619214,19.1768074036];
matrix[27]=[2,47.7670288235,19.1815066338];
matrix[28]=[2,47.7958520602,19.1520023346];
matrix[29]=[2,47.8184635775,19.0912342072];
matrix[30]=[3,47.463392939,19.0107357502];
matrix[31]=[3,47.4589382268,18.9885592461];
matrix[32]=[3,47.4526123901,18.9626598358];
matrix[33]=[3,47.4522622231,18.9557933807];
matrix[34]=[3,47.4519884704,18.9289498329];
matrix[35]=[3,47.4524237644,18.9133501053];
matrix[36]=[3,47.4665378979,18.86906147];
matrix[37]=[3,47.4918551102,18.8255882263];
matrix[38]=[3,47.508674938,18.7534475327];
matrix[39]=[3,47.510111776,18.7144589424];
matrix[40]=[4,47.463392939,19.0107357502];
matrix[41]=[4,47.4589382268,18.9885592461];
matrix[42]=[4,47.4526123901,18.9626598358];
matrix[43]=[4,47.4522622231,18.9557933807];
matrix[44]=[4,47.4519884704,18.9289498329];
matrix[45]=[4,47.4503778535,18.9140152931];
matrix[46]=[4,47.3848608641,18.8388061523];
matrix[47]=[4,47.3657128892,18.8234424591];
matrix[48]=[4,47.3560287276,18.8090229034];
matrix[49]=[5,47.5572099715,19.1321539879];
matrix[50]=[5,47.5790405225,19.1663789749];
matrix[51]=[5,47.5934843352,19.1986083984];
matrix[52]=[5,47.5926122243,19.2356872559];
matrix[53]=[5,47.5938605737,19.2412233353];
matrix[54]=[5,47.6011822131,19.2539691925];
matrix[55]=[5,47.6122350815,19.3083000183];
matrix[56]=[5,47.6166864328,19.3661499023];
matrix[57]=[5,47.6127519831,19.3993663788];
matrix[58]=[5,47.6240916525,19.4168758392];
matrix[59]=[5,47.6346769809,19.4735240936];
matrix[60]=[5,47.6426001131,19.5269107819];
matrix[61]=[5,47.6633892458,19.6072912216];
matrix[62]=[6,47.4358220126,19.1444063187];
matrix[63]=[6,47.4314820566,19.1489124298];
matrix[64]=[6,47.3975774457,19.1661858559];
matrix[65]=[6,47.3505395267,19.2048740387];
matrix[66]=[6,47.3056567694,19.2801904678];
matrix[67]=[6,47.2209924309,19.4051599503];
matrix[68]=[6,47.1491510592,19.4619369507];
matrix[69]=[6,47.1141148803,19.503235803];
matrix[70]=[7,47.400640171,18.9754915237];
matrix[71]=[7,47.387479764,18.9683675766];
matrix[72]=[7,47.3699855907,18.9342069626];
matrix[73]=[7,47.36103479,18.9104747772];
matrix[74]=[7,47.3573408395,18.904466629];
matrix[75]=[7,47.3329894704,18.8879442215];
matrix[76]=[7,47.3177882728,18.8715076447];
matrix[77]=[7,47.3055675349,18.8671302795];
matrix[78]=[8,47.9703643101,22.2091054899];
matrix[79]=[8,47.8895207682,21.7809963226];
matrix[80]=[8,47.8833083106,21.7091560364];
matrix[81]=[8,47.9022810228,21.6588449618];
matrix[82]=[8,47.9002095182,21.5551614901];
matrix[83]=[9,47.9002095182,21.5551614901];
matrix[84]=[9,47.8996532612,21.4808034897];
matrix[85]=[9,47.8809770081,21.4060878754];
matrix[86]=[9,47.7932900215,21.256570816];
matrix[87]=[9,47.8081934297,21.1651182175];
matrix[88]=[9,47.8469165233,21.1450767517];
matrix[89]=[9,47.8473485377,21.0769701004];
matrix[90]=[9,47.8543946629,21.0563993463];
matrix[91]=[10,46.175988193,19.9791240683];
matrix[92]=[10,46.2087567613,20.0180339813];
matrix[93]=[10,46.303058552,20.0425815582];
matrix[94]=[10,46.3420058503,20.0147724152];
matrix[95]=[10,46.4042753042,19.9714279175];
matrix[96]=[10,46.5723402969,19.8747396469];
matrix[97]=[11,46.5723402969,19.8747396469];
matrix[98]=[11,46.6775834527,19.8233842867];
matrix[99]=[11,46.8022832865,19.7307300533];
matrix[100]=[11,46.9050608355,19.6164035867];
matrix[101]=[11,46.9919807281,19.609065055];
matrix[102]=[11,47.0527838159,19.5525741577];
matrix[103]=[11,47.1141148803,19.503235803];
matrix[104]=[12,47.8543946629,21.0563993463];
matrix[105]=[12,47.8847761139,20.8575868607];
matrix[106]=[12,47.8036104383,20.6771707535];
matrix[107]=[12,47.7714950999,20.5418300594];
matrix[108]=[13,47.771402803,20.5419445038];
matrix[109]=[13,47.7219135587,20.2843666077];
matrix[110]=[13,47.7062634401,20.0910329819];
matrix[111]=[13,47.7323347384,19.944691658];
matrix[112]=[13,47.6884131047,19.6887874603];
matrix[113]=[13,47.6633892458,19.6072912216];
matrix[114]=[14,47.5546593598,18.4887456894];
matrix[115]=[14,47.5750453377,18.4671592712];
matrix[116]=[14,47.5786024191,18.4136867523];
matrix[117]=[14,47.5953944409,18.3947181702];
matrix[118]=[14,47.6236616376,18.3202600479];
matrix[119]=[14,47.6748007673,18.0917358398];
matrix[120]=[14,47.6832995042,17.8898191452];
matrix[121]=[15,47.3560287276,18.8090229034];
matrix[122]=[15,47.2313677328,18.5838031769];
matrix[123]=[15,47.1286329864,18.4064340591];
matrix[124]=[15,47.0374875301,18.2845973969];
matrix[125]=[15,47.0244375828,18.2590198517];
matrix[126]=[16,47.3055675349,18.8671302795];
matrix[127]=[16,47.2599766795,18.8485050201];
matrix[128]=[16,47.1778374556,18.8064050674];
matrix[129]=[16,47.0417282712,18.9003896713];
matrix[130]=[16,46.8543044,18.8748966];
matrix[131]=[18,47.5110229455,18.7267541885];
matrix[132]=[18,47.5065587416,18.6056900024];
matrix[133]=[18,47.4941497416,18.5693836212];
matrix[134]=[18,47.5546593598,18.4887456894];
matrix[135]=[19,46.8970020665,18.9848899841];
matrix[136]=[19,46.9224165341,18.8951969147];
matrix[137]=[20,46.8543044,18.8748966];
matrix[138]=[20,46.7002385785,18.8637113562];
matrix[139]=[20,46.6698562102,18.8140153902];
matrix[140]=[20,46.4937328135,18.7594699877];
matrix[141]=[20,46.4603078558,18.7081861461];
matrix[142]=[20,46.3808745967,18.7538909877];
matrix[143]=[20,46.1571080196,18.6522102356];
matrix[144]=[21,46.3893707561,18.7130355833];
matrix[145]=[21,46.3541637892,18.9652919839];
matrix[146]=[22,46.1571080196,18.6522102356];
matrix[147]=[22,46.110934921,18.5898542404];
matrix[148]=[22,45.9918593619,18.5805845261];
matrix[149]=[23,46.9587826287,18.1568813324];
matrix[150]=[23,46.8789350903,17.9720449448];
matrix[151]=[23,46.6678708405,17.3419189453];
matrix[152]=[23,46.5545439535,17.2202968597];
matrix[153]=[24,46.5545439535,17.2202968597];
matrix[154]=[24,46.510646806,17.1896982193];
matrix[155]=[24,46.4119393375,16.7007637024];
matrix[156]=[25,47.0244375828,18.2590198517];
matrix[157]=[25,46.9587826287,18.1568813324];
matrix[158]=[26,47.6832995042,17.8898191452];
matrix[159]=[26,47.6331481206,17.6718521118];
matrix[160]=[26,47.9244485976,17.1108198166];

 }
}
}


Az osztálypéldányt ezzel a sorral hozzuk létre:

public var autopalyak:Uthalozat = new Uthalozat();

Az alkalmazásból a térkép megérintésével tudunk kilépni.

Az MXML alkalmazás forráskódja:


<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
     xmlns:s="library://ns.adobe.com/flex/spark"
     xmlns:mx="library://ns.adobe.com/flex/mx"
     applicationComplete="init(event)"
     backgroundColor="#000000">
<fx:Script>
<![CDATA[

import flash.events.Event;   
import mx.events.FlexEvent;   
import spark.components.Label;  
import uthalozat.*;
  
//Az úthálózatokat tartalmazó osztály példányosítása
public var autopalyak:Uthalozat = new Uthalozat();
  
//Általános segédváltozók a rajzoláshoz
public var TERKEP_nagyitas:Number;
  
//Segédváltozók a térképadatok arányos megjelenítéséhez
public var lat_leptek:Number;
public var long_leptek:Number;
public var adat_long_felso:Number;
public var adat_lat_felso:Number;
public var adat_long_also:Number;
public var adat_lat_also:Number;
//Az alkalmazás inicializálása   
public function init(event:FlexEvent):void
{//Segédváltozók létrehozása ciklusok kezeléséhez
   
//A térképadatok megadása
adat_long_felso=15;
adat_lat_felso=50;
adat_long_also=27;
adat_lat_also=45;
lat_leptek=0.015;
long_leptek=0.015;
   
//Térképadatok megadása
TERKEP_nagyitas=1.0;
terkep.scaleY=TERKEP_nagyitas*1.5;
terkep.scaleX=TERKEP_nagyitas;
//A rajzvászon előkészítése
rajzvaszon.graphics.clear();
rajzvaszon.removeAllElements();
    
//Az autópályák kirajzolása
uthalozat_kirajzol();
 
//A konkrét városok megejelnítése
varosnev_kirajzol(46.253,20.1414,"Szeged");
varosnev_kirajzol(47.4979,19.0402,"Budapest");
varosnev_kirajzol(46.8417,16.8416,"Zalaegerszeg");
varosnev_kirajzol(47.9495,21.7244,"Nyíregyháza");
varosnev_kirajzol(47.6874,17.6504,"Győr");
}
  
//A városnevek kirajzolása
public function varosnev_kirajzol(lat:Number,long:Number,nev:String):void
{//Segédváltozók
var s1:Number;
var s2:Number;
var szoveg:Label;
   
s1=(long-adat_long_felso)/long_leptek;

s2=(adat_lat_felso-lat)/lat_leptek;

rajzvaszon.graphics.lineStyle(2,0x000000);

rajzvaszon.graphics.drawCircle(get_point_x(adat_long_felso+(s1*long_leptek) ),get_point_y(adat_lat_felso-(s2*lat_leptek)),3);
rajzvaszon.graphics.lineStyle(1, 0xFF0000);

szoveg = new Label();
szoveg.setStyle("fontSize", "12");
szoveg.setStyle("backgroundColor", "0xffffff");
szoveg.setStyle("color", "0x000000");
szoveg.setStyle("textAlign", "center");
szoveg.setStyle("verticalAlign", "middle");    szoveg.text=nev;
szoveg.x=get_point_x(adat_long_felso+(s1*long_leptek) )+5;
szoveg.y=get_point_y(adat_lat_felso-(s2*lat_leptek));

rajzvaszon.addElement(szoveg);
}
  
//Az autópályák kirajzolását végző algoritmus
public function uthalozat_kirajzol():void
{var i:int;
   
//A rajz színének beállítása
rajzvaszon.graphics.lineStyle(3, 0xFFFFFF);
   
for (i = 0; i < autopalyak.matrix.length-1; ++i)
{     
if(autopalyak.matrix[i][0] == autopalyak.matrix[i+1][0])
{
rajzvaszon.graphics.moveTo(get_point_x(autopalyak.matrix[i][2]),get_point_y(autopalyak.matrix[i][1]));
        
rajzvaszon.graphics.lineTo(get_point_x(autopalyak.matrix[i+1][2]),get_point_y(autopalyak.matrix[i+1][1]));
}
}

}
  
//GPS koordináta átalakítása X képernyőkoordinátává
public function get_point_x(long:Number):int
{
return (int)( (long-15.0)/(long_leptek/TERKEP_nagyitas) );
}
  
//GPS koordináta átalakítása Y képernyőkoordinátává
public function get_point_y(lat:Number):int
{
return (int)(1.5 * ((50-lat)/(lat_leptek/TERKEP_nagyitas))  );
}
  
//Kilépés az alkalmazásból
public function Kilepes():void
{
NativeApplication.nativeApplication.exit();
}

]]>
</fx:Script>
<s:Image id="terkep" source="@Embed('..\\terkep\\Karpat_medence_2.png')" click="Kilepes()"/>

<s:Group id="rajzvaszon" width="100%" height="100%"/>
</s:Application>


Az alkalmazás képernyője futás közben:



13.7. A Balaton kirajzolása és a pozíció valós idejű megjelenítése

Utolsó példaalkalmazásunkban (Pelda_16) meg fogjuk jeleníteni a Balatont és alkalmazásunkat felkészítjük arra is, hogy megjelenítse pillanatnyi helyzetünket. Ez utóbbi funkciót a 10. példaalkalmazásból (11. fejezet) vesszük kölcsön.
Ezzel egy valós idejű helymegjelenítő térképalkalmazást készítünk el.
A pozíció megjelenítésére külön rajzréteget fogunk használni:

<s:Group id="GPS_rajzvaszon" width="100%" height="100%"/>

Célünk továbbá a Balaton részletes képének kirajzolása a térképünkön:



Tavak, zárt területek kirajzolása ugyanúgy történhet, mint az útvonalaké, azzal a különbséggel, hogy itt ki is kell tölteni a körülhatárolt területet. Ehhez a beginFill.. .. endFill rajzmetódusokat fogjuk használni.
A rajzvásznunkat enyhén átlátszóvá tesszük az alpha tulajdonság módosításával. Így tetszetősebb megjelenítést fogunk kapni.

<s:Group id="rajzvaszon" width="100%" height="100%" alpha=”0.8”/>

A GPS vevő inicializálása a már ismert módon történik:

if(Geolocation.isSupported==true)
{
geoLocation = new Geolocation();
    geoLocation.setRequestedUpdateInterval(10000);
    geoLocation.addEventListener(GeolocationEvent.UPDATE,handleLocationRequest);
}

A handleLocationRequest függvény kizárólag a helyadatok lekérdezését és a pillanatnyi hely megjelenítését végzi el:


public function handleLocationRequest(event:GeolocationEvent):void
{
adat_lat=event.latitude;

adat_long=event.longitude;

GPS_rajzvaszon.graphics.clear();

GPS_rajzvaszon.removeAllElements();

GPS_rajzvaszon.graphics.lineStyle(3,0xFF0000);   

GPS_rajzvaszon.graphics.moveTo(get_point_x(adat_long),get_point_y(adat_lat));    

GPS_rajzvaszon.graphics.lineTo(get_point_x(adat_long)+10,get_point_y(adat_lat)-40);
 
GPS_rajzvaszon.graphics.lineTo(get_point_x(adat_long)-10,get_point_y(adat_lat)-40);
GPS_rajzvaszon.graphics.lineTo(get_point_x(adat_long),get_point_y(adat_lat));
}


Amennyiben az alkalmazást futtató mobileszköz nem rendelkezik semmilyen helymeghatározó képességgel, a térkép akkor is meg fog jelenni.
A Balaton adatait tároló tömb még az autópályák adatait tartalmazó tömbnél is egyszerűbb felépítésű. Mivel csak egy alakzatot akarunk kirajzolni, felesleges tárolnunk az alakzat azonosítóját. Tehát elég mindössze a szélességi és hosszúsági koordinátákat tárolni.

A tó adatait tároló osztály forráskódja:

package tavak
{
public class Tavak
{
 public var matrix:Array = new Array();
 
 public function Tavak()
 {
  matrix[0]=[46.7143400648,17.2487068176];
  matrix[1]=[46.7069246286,17.2646713257];
  matrix[2]=[46.707984039,17.2756576538];
  matrix[3]=[46.7112798495,17.2883605957];
  matrix[4]=[46.7105736214,17.2933387756];
  matrix[5]=[46.7109267366,17.2979736328];
  matrix[6]=[46.7130453796,17.2995185852];
  matrix[7]=[46.7158701074,17.3127365112];
  matrix[8]=[46.7149285479,17.3173713684];
  matrix[9]=[46.716223188,17.3256111145];
  matrix[10]=[46.7155170245,17.3407173157];
  matrix[11]=[46.7113975533,17.3606300354];
  matrix[12]=[46.7090434286,17.3712730408];
  matrix[13]=[46.7070423419,17.3831176758];
  matrix[14]=[46.7064537729,17.4047470093];
  matrix[15]=[46.7073954802,17.4227714539];
  matrix[16]=[46.7128099789,17.4627685547];
  matrix[17]=[46.7148108518,17.4768447876];
  matrix[18]=[46.726226178,17.5158119202];
  matrix[19]=[46.7294032102,17.5194168091];
  matrix[20]=[46.7344625419,17.5305747986];
  matrix[21]=[46.7421094679,17.5386428833];
  matrix[22]=[46.7511667309,17.5547790527];
  matrix[23]=[46.7588112913,17.5853347778];
  matrix[24]=[46.7775064829,17.6440429688];
  matrix[25]=[46.7803278349,17.6479911804];
  matrix[26]=[46.7910241186,17.6900482178];
  matrix[27]=[46.7988980459,17.7291870117];
  matrix[28]=[46.8140550495,17.7736473083];
  matrix[29]=[46.844591071,17.8591346741];
  matrix[30]=[46.8595004366,17.8893470764];
  matrix[31]=[46.8664254268,17.9006767273];
  matrix[32]=[46.879451377,17.9001617432];
  matrix[33]=[46.8841446382,17.9214477539];
  matrix[34]=[46.89540679,17.9909706116];
  matrix[35]=[46.912999168,18.0556869507];
  matrix[36]=[46.9233173429,18.0871009827];
  matrix[37]=[46.932226895,18.1089019775];
  matrix[38]=[46.9400801394,18.1279563904];
  matrix[39]=[46.9501587354,18.1427192688];
  matrix[40]=[46.9657416649,18.1566238403];
  matrix[41]=[46.9774551644,18.1628036499];
  matrix[42]=[46.9957231011,18.1674385071];
  matrix[43]=[47.0108245639,18.1604003906];
  matrix[44]=[47.0214749659,18.1375694275];
  matrix[45]=[47.0224111634,18.1293296814];
  matrix[46]=[47.0301341664,18.1169700623];
  matrix[47]=[47.0316552323,18.1138801575];
  matrix[48]=[47.0303681794,18.1071853638];
  matrix[49]=[47.0321232438,18.0929374695];
  matrix[50]=[47.0369201254,18.0819511414];
  matrix[51]=[47.0389021237,18.0731964111];
  matrix[52]=[47.0423531436,18.0628967285];
  matrix[53]=[47.0494299607,18.0541419983];
  matrix[54]=[47.0545176705,18.0480480194];
  matrix[55]=[47.0585524056,18.0390357971];
  matrix[56]=[47.0579676803,18.0365467072];
  matrix[57]=[47.050658073,18.0295944214];
  matrix[58]=[47.0458624265,18.02942276];
  matrix[59]=[47.0418267313,18.0278778076];
  matrix[60]=[47.0347489055,18.0313110352];
  matrix[61]=[47.0303613521,18.0284786224];
  matrix[62]=[47.0283137037,18.0241012573];
  matrix[63]=[47.0270265703,18.0173206329];
  matrix[64]=[47.0255638809,18.0127716064];
  matrix[65]=[47.0193031176,18.0099391937];
  matrix[66]=[47.0110518317,18.0077075958];
  matrix[67]=[47.0095301788,18.0053901672];
  matrix[68]=[47.0044381787,18.0036735535];
  matrix[69]=[47.0011603127,18.0013561249];
  matrix[70]=[46.9968870782,17.9958629608];
  matrix[71]=[46.9923793236,17.9905414581];
  matrix[72]=[46.9861146695,17.9868507385];
  matrix[73]=[46.9821330125,17.9713153839];
  matrix[74]=[46.9783267395,17.9669380188];
  matrix[75]=[46.9753986529,17.9657363892];
  matrix[76]=[46.9739931143,17.9617023468];
  matrix[77]=[46.9758671575,17.9486560822];
  matrix[78]=[46.9765699068,17.9460811615];
  matrix[79]=[46.9756914688,17.9433345795];
  matrix[80]=[46.9720604385,17.9419612885];
  matrix[81]=[46.9701862619,17.939043045];
  matrix[82]=[46.9658519771,17.9284858704];
  matrix[83]=[46.9664377118,17.9229068756];
  matrix[84]=[46.9677263055,17.9191303253];
  matrix[85]=[46.9651490869,17.9165554047];
  matrix[86]=[46.9608729732,17.9173278809];
  matrix[87]=[46.9549561426,17.9090881348];
  matrix[88]=[46.9556005816,17.9030799866];
  matrix[89]=[46.9537844153,17.8946685791];
  matrix[90]=[46.9479839877,17.8832530975];
  matrix[91]=[46.9448198531,17.8817081451];
  matrix[92]=[46.9418899322,17.8767299652];
  matrix[93]=[46.9398974943,17.8701210022];
  matrix[94]=[46.9367328817,17.8658294678];
  matrix[95]=[46.9320442231,17.859992981];
  matrix[96]=[46.9295238994,17.859992981];
  matrix[97]=[46.9255966471,17.8654003143];
  matrix[98]=[46.9241311807,17.869348526];
  matrix[99]=[46.9246587532,17.875957489];
  matrix[100]=[46.923662223,17.883682251];
  matrix[101]=[46.9220208386,17.886428833];
  matrix[102]=[46.9187965444,17.8896045685];
  matrix[103]=[46.9178585314,17.8922653198];
  matrix[104]=[46.9112919811,17.8942394257];
  matrix[105]=[46.907949767,17.8929519653];
  matrix[106]=[46.9025548766,17.8983592987];
  matrix[107]=[46.8909422908,17.895526886];
  matrix[108]=[46.8888306417,17.8926944733];
  matrix[109]=[46.8898278197,17.8772449493];
  matrix[110]=[46.9035517994,17.8502082825];
  matrix[111]=[46.9092984048,17.8492641449];
  matrix[112]=[46.913578639,17.8464317322];
  matrix[113]=[46.9168618747,17.837677002];
  matrix[114]=[46.9165101091,17.8350162506];
  matrix[115]=[46.9138131625,17.8322696686];
  matrix[116]=[46.9121128439,17.8317546844];
  matrix[117]=[46.9093570404,17.8238582611];
  matrix[118]=[46.9066597339,17.8148460388];
  matrix[119]=[46.903141304,17.8102111816];
  matrix[120]=[46.9034345154,17.8082370758];
  matrix[121]=[46.9025548766,17.8070354462];
  matrix[122]=[46.8969248469,17.805147171];
  matrix[123]=[46.8952826435,17.7966499329];
  matrix[124]=[46.8915288452,17.7869510651];
  matrix[125]=[46.8898864766,17.78008461];
  matrix[126]=[46.8860149802,17.7759647369];
  matrix[127]=[46.8835511552,17.774848938];
  matrix[128]=[46.882729855,17.7725315094];
  matrix[129]=[46.8822018697,17.7613735199];
  matrix[130]=[46.879503197,17.7501296997];
  matrix[131]=[46.879503197,17.736825943];
  matrix[132]=[46.8787991731,17.7295303345];
  matrix[133]=[46.8767457172,17.7214622498];
  matrix[134]=[46.8768043885,17.7180290222];
  matrix[135]=[46.876217673,17.7157974243];
  matrix[136]=[46.8734600244,17.7151966095];
  matrix[137]=[46.872579894,17.707901001];
  matrix[138]=[46.8748095297,17.7018070221];
  matrix[139]=[46.8746921828,17.6958847046];
  matrix[140]=[46.8728732724,17.691078186];
  matrix[141]=[46.8707022341,17.6873874664];
  matrix[142]=[46.8668880353,17.6846408844];
  matrix[143]=[46.8655970143,17.6771736145];
  matrix[144]=[46.8614890137,17.6761436462];
  matrix[145]=[46.859317515,17.6717662811];
  matrix[146]=[46.85233291,17.666015625];
  matrix[147]=[46.8450538677,17.6554584503];
  matrix[148]=[46.8420597835,17.6533126831];
  matrix[149]=[46.8358949635,17.6467037201];
  matrix[150]=[46.8326067702,17.6470470428];
  matrix[151]=[46.8299643263,17.6467037201];
  matrix[152]=[46.8278468511,17.6427984238];
  matrix[153]=[46.8280523872,17.6374769211];
  matrix[154]=[46.8277587639,17.6349449158];
  matrix[155]=[46.8262318966,17.6339578629];
  matrix[156]=[46.8266170442,17.6303958893];
  matrix[157]=[46.8223851734,17.6204395294];
  matrix[158]=[46.8232954916,17.6145172119];
  matrix[159]=[46.8240589723,17.610912323];
  matrix[160]=[46.824264523,17.608423233];
  matrix[161]=[46.8236478686,17.6040458679];
  matrix[162]=[46.822532,17.6001834869];
  matrix[163]=[46.8204470254,17.5973939896];
  matrix[164]=[46.8200065275,17.595076561];
  matrix[165]=[46.8203295596,17.5928878784];
  matrix[166]=[46.8221502501,17.5883817673];
  matrix[167]=[46.8222677119,17.5866222382];
  matrix[168]=[46.8205057582,17.5791978836];
  matrix[169]=[46.8148083793,17.5745201111];
  matrix[170]=[46.8116657522,17.5736188889];
  matrix[171]=[46.8096684732,17.5707435608];
  matrix[172]=[46.8085816873,17.5667524338];
  matrix[173]=[46.8083467037,17.5613451004];
  matrix[174]=[46.8072011439,17.559800148];
  matrix[175]=[46.8042343241,17.5543069839];
  matrix[176]=[46.8039699461,17.5525045395];
  matrix[177]=[46.8041755736,17.5479984283];
  matrix[178]=[46.8035129113,17.5453591347];
  matrix[179]=[46.8039976075,17.5449728966];
  matrix[180]=[46.8039388567,17.5437712669];
  matrix[181]=[46.8032355559,17.5416040421];
  matrix[182]=[46.8030886767,17.5370121002];
  matrix[183]=[46.8034705619,17.5334072113];
  matrix[184]=[46.8031768043,17.5321626663];
  matrix[185]=[46.8039405707,17.5269269943];
  matrix[186]=[46.8043647986,17.5265622139];
  matrix[187]=[46.8045704246,17.5256609917];
  matrix[188]=[46.8043941738,17.5236654282];
  matrix[189]=[46.8038801058,17.5225067139];
  matrix[190]=[46.8016769015,17.5202322006];
  matrix[191]=[46.800707463,17.517914772];
  matrix[192]=[46.7998702066,17.5171637535];
  matrix[193]=[46.79715442,17.516412735];
  matrix[194]=[46.795127222,17.5142669678];
  matrix[195]=[46.7942751884,17.5145244598];
  matrix[196]=[46.7897209872,17.5093746185];
  matrix[197]=[46.7878404304,17.5076150894];
  matrix[198]=[46.7858422668,17.5067138672];
  matrix[199]=[46.7822571403,17.5005340576];
  matrix[200]=[46.7832269112,17.4904060364];
  matrix[201]=[46.7847843854,17.4833250046];
  matrix[202]=[46.7864887396,17.4780464172];
  matrix[203]=[46.7890745532,17.473154068];
  matrix[204]=[46.7879873512,17.4716949463];
  matrix[205]=[46.7884868792,17.4696779251];
  matrix[206]=[46.790426179,17.465429306];
  matrix[207]=[46.7908081541,17.4625968933];
  matrix[208]=[46.7906024756,17.4595069885];
  matrix[209]=[46.7892508541,17.4556446075];
  matrix[210]=[46.7871645892,17.4521684647];
  matrix[211]=[46.7860479635,17.4519968033];
  matrix[212]=[46.7842848231,17.4446582794];
  matrix[213]=[46.7846962277,17.4389076233];
  matrix[214]=[46.7855190275,17.4328136444];
  matrix[215]=[46.7863711997,17.4295520782];
  matrix[216]=[46.7883987275,17.4268913269];
  matrix[217]=[46.7900735843,17.4262475967];
  matrix[218]=[46.7933056166,17.4197673798];
  matrix[219]=[46.7943633304,17.4178791046];
  matrix[220]=[46.7953328832,17.4113130569];
  matrix[221]=[46.793863857,17.4086093903];
  matrix[222]=[46.7951894111,17.4001979828];
  matrix[223]=[46.795365692,17.3955631256];
  matrix[224]=[46.7943079978,17.3926448822];
  matrix[225]=[46.7888429134,17.3882675171];
  matrix[226]=[46.7884868792,17.3867225647];
  matrix[227]=[46.7866062793,17.3873233795];
  matrix[228]=[46.7853720999,17.3852205276];
  matrix[229]=[46.7829070856,17.3830318451];
  matrix[230]=[46.7801446456,17.3789978027];
  matrix[231]=[46.7768530426,17.37616539];
  matrix[232]=[46.7750308187,17.373418808];
  matrix[233]=[46.7726206859,17.3711872101];
  matrix[234]=[46.7677413065,17.3665523529];
  matrix[235]=[46.7649780883,17.3636341095];
  matrix[236]=[46.7615679642,17.3589134216];
  matrix[237]=[46.7573344063,17.3579692841];
  matrix[238]=[46.7533945468,17.3575401306];
  matrix[239]=[46.7471607478,17.3477554321];
  matrix[240]=[46.74974845,17.3372840881];
  matrix[241]=[46.7495720197,17.3332500458];
  matrix[242]=[46.751453913,17.3278427124];
  matrix[243]=[46.752982903,17.316942215];
  matrix[244]=[46.7532769346,17.3086166382];
  matrix[245]=[46.7543942399,17.3048400879];
  matrix[246]=[46.7585692284,17.2952270508];
  matrix[247]=[46.7612739779,17.2846698761];
  matrix[248]=[46.7629790762,17.2754859924];
  matrix[249]=[46.7642725631,17.2689628601];
  matrix[250]=[46.7642725631,17.2653579712];
  matrix[251]=[46.7629790762,17.2587490082];
  matrix[252]=[46.7613915726,17.2558307648];
  matrix[253]=[46.7586280288,17.2538566589];
  matrix[254]=[46.7567463859,17.2506809235];
  matrix[255]=[46.7516891451,17.2475910187];
  matrix[256]=[46.7505129746,17.2460460663];
  matrix[257]=[46.7466314297,17.2469043732];
  matrix[258]=[46.7415732397,17.2468185425];
  matrix[259]=[46.7330438215,17.245016098];
  matrix[260]=[46.7281608577,17.2478485107];
  matrix[261]=[46.7247484037,17.246131897];
  matrix[262]=[46.7143400648,17.2487068176];

 }
}
}

Az osztálypéldányt ezzel a sorral hozzuk létre:

public var Balaton:Tavak = new Tavak();

Az egyszerűbb adatszerkezet a kirajzoló algoritmus egyszerűsítését is lehetővé teszi:


public function Balaton_kirajzol():void
{var i:int;
   
rajzvaszon.graphics.lineStyle(1,0xa0c3fd);

rajzvaszon.graphics.beginFill(0xa0c3fd,1);
   
rajzvaszon.graphics.moveTo(get_point_x(Balaton.matrix[0][1]),get_point_y(Balaton.matrix[0][0]));

for (i = 1; i<Balaton.matrix.length-1; ++i)
{

rajzvaszon.graphics.lineTo(get_point_x(Balaton.matrix[i+1][1]),get_point_y(Balaton.matrix[i+1][0]));
}
   
rajzvaszon.graphics.endFill();

}


Az alkalmazásból a térkép megérintésével tudunk kilépni.

Az MXML alkalmazás forráskódja:


<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
     xmlns:s="library://ns.adobe.com/flex/spark"
     xmlns:mx="library://ns.adobe.com/flex/mx"
     applicationComplete="init(event)"
     backgroundColor="#000000">
<fx:Script>
<![CDATA[

import flash.sensors.Geolocation;
import flash.events.Event;   
import mx.events.FlexEvent;   
import spark.components.Label;   
import uthalozat.*;
import tavak.*;
  
//Az úthálózatokat tartalmazó osztály példányosítása
public var autopalyak:Uthalozat = new Uthalozat();
  
//A Balaton adatait tartalmazó osztály példányosítása
public var Balaton:Tavak = new Tavak();
  
//Általános segédváltozók a rajzoláshoz
public var TERKEP_nagyitas:Number;
  
//Segédváltozók a térképadatok arányos megjelenítéséhez
public var lat_leptek:Number;
public var long_leptek:Number;
public var adat_long_felso:Number;
public var adat_lat_felso:Number;
public var adat_long_also:Number;
public var adat_lat_also:Number;
  
//A GPS koordináták kezelésére
public var geoLocation:Geolocation;   
public var adat_long:Number;   
public var adat_lat:Number;
  
//Az alkalmazás inicializálása   
public function init(event:FlexEvent):void
{//Segédváltozók létrehozása ciklusok kezeléséhez
   
//A térképadatok megadása
adat_long_felso=15;
adat_lat_felso=50;
adat_long_also=27;
adat_lat_also=45;
lat_leptek=0.015;
long_leptek=0.015;
   
//Térképadatok megadása
TERKEP_nagyitas=1;
terkep.scaleY=TERKEP_nagyitas*1.5;
terkep.scaleX=TERKEP_nagyitas;
   
//A rajzvászon előkészítése
rajzvaszon.graphics.clear();
rajzvaszon.removeAllElements();
   
//Az autópályák kirajzolása
uthalozat_kirajzol();
//A Balaton kirajzolása
Balaton_kirajzol();
   
//A konkrét városok megejelnítése
varosnev_kirajzol(46.253,20.1414,"Szeged");
   varosnev_kirajzol(47.4979,19.0402,"Budapest");
   varosnev_kirajzol(46.8417,16.8416,"Zalaegerszeg");
   varosnev_kirajzol(47.9495,21.7244,"Nyíregyháza");

varosnev_kirajzol(47.6874,17.6504,"Győr");
   
//A GPS vevő előkészítése
if(Geolocation.isSupported==true)
{
geoLocation = new Geolocation();
geoLocation.setRequestedUpdateInterval(10000);
geoLocation.addEventListener(GeolocationEvent.UPDATE, handleLocationRequest);
}
}
  
//A GPS adatok beolvasásakor frissítjük a megjelenítést
public function handleLocationRequest(event:GeolocationEvent):void
{
adat_lat=event.latitude;

adat_long=event.longitude;

GPS_rajzvaszon.graphics.clear();

GPS_rajzvaszon.removeAllElements();
   GPS_rajzvaszon.graphics.lineStyle(3,0xFF0000);    
GPS_rajzvaszon.graphics.moveTo(get_point_x(adat_long),get_point_y(adat_lat));    

GPS_rajzvaszon.graphics.lineTo(get_point_x(adat_long)+10,get_point_y(adat_lat)-40);

GPS_rajzvaszon.graphics.lineTo(get_point_x(adat_long)-10,get_point_y(adat_lat)-40);

GPS_rajzvaszon.graphics.lineTo(get_point_x(adat_long),get_point_y(adat_lat));
}
  
//A városnevek kirajzolása
public function varosnev_kirajzol(lat:Number,long:Number,nev:String):void
{//Segédváltozók
var s1:Number;
var s2:Number;
var szoveg:Label;
   
s1=(long-adat_long_felso)/long_leptek;

s2=(adat_lat_felso-lat)/lat_leptek;

rajzvaszon.graphics.lineStyle(2,0x000000);
rajzvaszon.graphics.drawCircle(get_point_x(adat_long_felso+(s1*long_leptek) ),get_point_y(adat_lat_felso-(s2*lat_leptek)),3);

rajzvaszon.graphics.lineStyle(1, 0xFF0000);

szoveg = new Label();
szoveg.setStyle("fontSize", "12");
szoveg.setStyle("backgroundColor", "0xffffff");
szoveg.setStyle("color", "0x000000");
szoveg.setStyle("textAlign", "center");
szoveg.setStyle("verticalAlign", "middle");    
szoveg.text=nev;
szoveg.x=get_point_x(adat_long_felso+(s1*long_leptek) )+5;
szoveg.y=get_point_y(adat_lat_felso-(s2*lat_leptek));

rajzvaszon.addElement(szoveg);
}
  
//Az autópályák kirajzolását végző algoritmus
public function uthalozat_kirajzol():void
{var i:int;
   
//A rajz színének beállítása
rajzvaszon.graphics.lineStyle(2, 0xFFFFFF);
   
for (i = 0; i < autopalyak.matrix.length-1; ++i)
{     
if(autopalyak.matrix[i][0] == autopalyak.matrix[i+1][0])
    {
rajzvaszon.graphics.moveTo(get_point_x(autopalyak.matrix[i][2]),get_point_y(autopalyak.matrix[i][1]));
rajzvaszon.graphics.lineTo(get_point_x(autopalyak.matrix[i+1][2]),get_point_y(autopalyak.matrix[i+1][1]));

}

}

}
  
//A Balaton kirajzolása
public function Balaton_kirajzol():void
{var i:int;
   
rajzvaszon.graphics.lineStyle(1,0xa0c3fd);

rajzvaszon.graphics.beginFill(0xa0c3fd,1);
 
rajzvaszon.graphics.moveTo(get_point_x(Balaton.matrix[0][1]),get_point_y(Balaton.matrix[0][0]));

for (i = 1; i<Balaton.matrix.length-1; ++i)
{
rajzvaszon.graphics.lineTo(get_point_x(Balaton.matrix[i+1][1]),get_point_y(Balaton.matrix[i+1][0]));

}
   
rajzvaszon.graphics.endFill();

}  
  
//GPS koordináta átalakítása X képernyőkoordinátává
public function get_point_x(long:Number):int
{
return (int)( (long-15.0)/(long_leptek/TERKEP_nagyitas) );
}
  
//GPS koordináta átalakítása Y képernyőkoordinátává
public function get_point_y(lat:Number):int
{
return (int)(1.5 * ((50-lat)/(lat_leptek/TERKEP_nagyitas))  );
}
  
//Kilépés az alkalmazásból
public function Kilepes():void
{
NativeApplication.nativeApplication.exit();
}

]]>
</fx:Script>
<s:Image id="terkep" source="@Embed('..\\terkep\\Karpat_medence_2.png')" click="Kilepes()"/>

<s:Group id="rajzvaszon" width="100%" height="100%" alpha=”0.8”/>

<s:Group id="GPS_rajzvaszon" width="100%" height="100%"/>

</s:Application>




13.8. További ötletek, javaslatok

A leírásban ismertetett további funkciókkal egészíthetjük ki és tovább optimalizálhatjuk.
Ehhez adunk néhány gondolatébresztő ötletet:
- Alakítsuk át az algoritmusokat úgy, hogy akkor is helyesen megjelenítsék a domborzatot, ha a domborzati tömb dimenziói meghaladják a kijelző felbontását.
- Folyamatosan keressük az algoritmusok egyszerűsítésének lehetőségeit! Elsősorban a matematikai műveletekre koncentráljunk! Az ismétlődő műveleteket érdemes csak egyszer kiszámoltatni, a kiszámolt értéket egy változónak átadni és utána ezt a változót használni a művelet folyamatosan ismételt végrehajtása helyett.
- A nagyon bonyolult alakzatok (különösen, ha sok van belőlük) kirajzolása helyett érdemes megfontolni előre elkészített képek megjelenítését.
- Az útvonaltömbökben további adatokat is tárolhatunk az utakról, például azok neveit is.



14. A NASA domborzati adatainak használata

14.1. Az SRTM adatok bemutatása

Az SRTM werboldala és a domborzati adatfájlok már nem elérhetőek az eredeti formájukban, ez a leírás a régi változaton alapul.

A Shuttle Radar Topography Mission (SRTM) az amerikai National Geospatial-Intelligence Agency (NGA) és a National Aeronautics and Space Administration (NASA) egyik projektje, melynek célja a Föld felszínének digitális letapogatása.
A küldetést 2000. február 11-én kezdték meg, a végrehajtást pedig az Endeavour űrsikló segítségével valósították meg. A küldetés 10 napja során a Föld felszínének közel 80 százalékáról gyűjtöttek topográfiai adatokat, mely a világ első ilyen jellegű munkája volt.


A Shuttle Radar Topography Mission honlapja

A méréseket radartechnikával végezték, két radar felhasználásával. Az egyik az űrsikló Föld felőli oldalára (a hasára) volt erősítve, a másikat pedig az űrhajótól 60 méteres távolságban rögzítették.

A projekt hivatalos weboldala: http://www2.jpl.nasa.gov/srtm/

A küldetés során 9 terabájt nyers adat keletkezett, melyet azután feldolgoztak, tovább javítottak és finomítottak. A jelenleg elérhető legfrissebb verziója a 2.1-es.
Az adatok teljesen publikusak és részletes leírást is mellékelnek a felhasználásukhoz. Az adatokat az Egyesült Államok Geológiai Intézetének EROS adatközpontjából tölthetjük le: http://dds.cr.usgs.gov/srtm/version2_1/

Alapvetően négyféle adathalmazt tudunk letölteni:
- SRTM1: az Egyesült Államok területének szuperfinom felszíni magassági rasztere, kb. 1 fokmásodperces lépésközökkel.
- SRTM3: A Föld 60. és -60. szélességi fokai közt elhelyezkedő területek rasztere, kb. 3 fokmásodperces léptékekkel, ami kb. 80 méternek felel meg.
- SRTM30: Globális raszter, kb. 30 fokmásodperces léptékekkel.
- SWBD: Folyók, szárazföldek belsejében található vizek adatai speciális ESRI Shapefile formátumban. Ezek a fájlok jóval bonyolultabb felépítésűek és a vektoros megjelenítést támogatják.

A leírásban az SRTM3 adatok felhasználását mutatjuk be. Ezen adatok összmérete viszonylag nagy, közel 40 gigabájt, ezért a teljes rasztert nem egyben, hanem hosszúsági és szélességi fokok szerint felosztott egységekben lehet letölteni, ZIP fájlokba csomagolva.
A kicsomagolt fájlok .HGT kiterjesztésű nyers adatok, melyeket az SRTM interferometrikus radar processzora állított elő. Az adatok egy 1201x1201-es mátrixot alkotnak. A mátrix minden eleme egy két bájton tárolt előjeles egész szám (-32768 - 32767 értéktartományban).
Az adatok sorfolytonosak, ám az első sor az adott raszterben valójában a legalsó szélességi fok, az északi, ill. a déli sarkhoz képest.



A dolgokat tovább bonyolítja, hogy a kétbájtos egész számok a Motorola processzorokon használt ábrázolási formában vannak tárolva, ami azt jelenti, hogy a szám alsó és felső bájtja sorrendben fel van cserélve. Minden egyes szám bájtját tehát előbb fel kell cserélni, hogy az Intel processzorok ábrázolási módjának megfeleljen és dolgozni tudjunk velük.
A tengerek és óceánok magassága egységesen 0 méterre van beállítva.
A -32768 valamilyen légköri zavar stb. miatt nem kiértékelhető adatot jelöl az adathalmazban.
Minden más adat a tengerszinthez viszonyított magasságot jelöli, méterben.

14.2. Az adatok feldolgozása

Ebben a fejezetben egy olyan, C nyelven megírt segédprogramot mutatunk be (HGTtoAS.c), mely képes egy HGT fájlból egy ActionScript osztályleíró fájlt készíteni, mely tartalmazza a magassági adatokat pontosan olyan formában, hogy azt a 13. fejezetben ismertetett megjelenítési módszerek segítségével is könnyedén felhasználhassuk. A tömb méretét változtathatjuk is, ha céljainknak elegendő kisebb felbontású térkép is.
A segédprogram teljesen szabványos könyvtári függvényeket használ, bármely C, vagy C++ fordítóval lefordítható.
Az adatok alsó és felső bájtjainak cseréjét egy unió adatstruktúra segítségével oldjuk meg: először a felső bájtot olvassuk be, majd az alsót. az adatstruktúra egesz nevű adattagja automatikusan a helyes formában fogja tartalmazni a számot.

union minta {
     short int egesz;
     char resz[2];
     } adat;

Az adatokat egyetlen, viszonylag nagy méretű tömbbe olvassuk be.
Említettük, hogy az elkészített ActionScript fájl egy olyan tömbleírást fog tartalmazni, melyet tetszőlegesen átméretezhetünk (kisebbre). A léptéket a leptek nevű változó értékének beállításával szabhatjuk meg. Az 1 azt jelenti, hogy 1:1 arányú tömb jön létre, azaz az összes adat felhasználásával készül el az Actionscript tömb (összesen 1201x1201 tömbelem). Az 5-ös érték például csak minden 5. sort és oszlopot vesz figyelembe, a leképezés így 5:1 arányú. A leptek változó értéke lebegőpontos szám is lehet, de egész értékek használata javasolt.
Mivel tömbelemre csak egész számú indexekkel hivatkozhatunk, ezért a tömbökön történő végighaladáshoz használt lebegőpontos segédváltozók (i és j) értékét egész értékű változóknak (ii és jj) adjuk át, mielőtt tömbelemre hivatkoznánk.

short int tomb[1201*1201];
int ii,jj,s1;
float leptek,i,j;

A .HGT fájl beolvasását a feldolgoz(char *fajlnev) függvény végzi el, mely egyúttal feltölti tömbünk elemeit is. A fájl feldolgozása után a tömbön végighaladva elkészítjük az Actionscript forrásfájlt.

A segédprogram teljes forráskódja:


#include <stdio.h>
#include <stdlib.h>

//adatfeldolgozáshoz használt adatstruktúra
union minta {
     short int egesz;
     char resz[2];
     } adat;

//a fájlfeldolgozást végző függvény prototípusa
void feldolgoz(char *fajlnev);

//fájlleíró
FILE *fajl;

//tömb és tömbfeldolgozáshoz használt segédváltozók
short int tomb[1201*1201];
int ii,jj,s1;
float leptek,i,j;

//Az Actionscript fájl generálásához használt segédváltozó tömbindexek generálására
int kimeneti_szamlalo;

int main(int argc, char *argv[])
{

// beolvassuk a .HGT fájlt
 feldolgoz("N47E018.hgt");

// kiíratjuk az ActionScript osztályfájl bevezető kódrészleteit a szabványos kimenetre
 printf("package terkepadat\n{\npublic class Terkepadat\n{public var matrix:Array = new Array();\n\npublic function Terkepadat()\n{\n\n");

 kimeneti_szamlalo=0;

//A klépték megadása
 leptek=1;

//A tömb kiíratása
for(i=0;i<1201;i+=leptek,++kimeneti_szamlalo)
 {ii=i;
  s1=(1201/leptek)-1-kimeneti_szamlalo;
  printf("\nmatrix[%i]=[",s1);
  for(j=0;j<1201;j+=leptek)
  {jj=j;
   if(jj < (1201-leptek)) printf("%i,",tomb[jj+((1200-ii)*1201)]);
   else printf("%i",tomb[jj+((1200-ii)*1201)]);
  }
   printf("];");
 }

//Az actionScript fájl záró sorainak kiírása
 printf("\n}\n}\n}");

// programműködés befejezése
 return 0;
}

void feldolgoz(char *fajlnev)
{int i,j; // segédváltozók tömbfeldolgozáshoz
short int elozo; //adathibák javításához használt segédváltozó

 //fájl megnyitása
   fajl=fopen(fajlnev,"rb");
   if(fajl==NULL) {printf("IO Hiba!");return;}

   for(i=0;i<1201;++i) //sorok
   {
   for(j=0;j<1201;++j) //oszlopok
   {
//adatok beolvasása bájtonként
       fread(&adat.resz[1],1,1,fajl);
       fread(&adat.resz[0],1,1,fajl);

      //hibás adat kicserélése a legutóbbi helyes adatra
       if(adat.egesz == -32768) adat.egesz=elozo;
       else elozo=adat.egesz;

//a beolvasott adat elhelyezése a program egydimenziós tömbjébe
       tomb[(i*1201)+j]=adat.egesz;
   }
   }

 //fájl lezárásaa
   fclose(fajl);
}


A program fordítás után a HGTtoAS > Terkepadat.as paranccsal futtatva 1-2 másodperc alatt elkészíti az ActionScript forrásfájlt, melyet AIR alkalmazásainkhoz felhasználhatunk.



15. Függelék

15.1. Dijkstra algoritmusa C nyelven

Az alábbiakban leközöljük a 10.3. fejezetben bemutatott Dijkstra algoritmus C nyelvű változatát.

Emlékeztetőül, a kapcsolati ábra:



Ebben a példában is a 4. és a 3. számú csomópont közötti legrövidebb útvonalat keressük.
A legfontosabb különbség az ActionScript kódhoz képest, hogy itt osztály helyett struktúratömböt használunk a csomópontok ábrázolásához.


#include <mem.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

//Ez a változó egy végtelen távolságot ad meg
//segédváltozóként használjuk a tömbök előkészítésekor.
#define VEGTELEN 9999
//A maximális elemszám
#define MAX_ELEMSZAM 1000
//A maximális kapcsolatok száma
#define MAX_SZOMSZED 20

int main()
{
//Segédváltozók ciklusokhoz
int i,j;
//a végeredmény kiírásához használt segédváltozók
char seged_szoveg[256];
char *eredmeny_szoveg;

//A csomópontok listájának mintaeleme
struct graf_elem
{
 int elem1,elem2,tavolsag;
};

//ezekből építjük fel a kapcsolati hálózatot.
//Az elemek tömböt alkotnak és tömbindexük alapján hivatkozhatunk rájuk.

 struct Minta_osztaly
 {
//Az adott szomszéd elem tömbindexe.
int end_elem[MAX_SZOMSZED];
//A távolság egy-egy csomóponttól
  int cost_elem[MAX_SZOMSZED];
//Ez a tulajdonság adja meg, hogy hány másik elemhez kapcsolódik a csomópont
//maximum 20 kapcsolódási pont lehetséges
  int alelemek_szama;
 } szomszedok[VEGTELEN+1];

//Segédtömb, a tömbök előkészítésekor használjuk.
 int csere_prio_sor[MAX_ELEMSZAM];

//Elsőbbségi sor
 int prio_sor[MAX_ELEMSZAM];

//Éleket leíró tömb
 int elek[MAX_ELEMSZAM];

//Segédtömb, a tömbök előkészítésekor használjuk.
 int csere_elek[MAX_ELEMSZAM];  
 //Segédváltozó
 int check_multi_elek;

 //A kiindulási és az elérni kívánt csomópont sorszáma
 int start,cel;
//A kapcsolódásokat leíró élmátrix feltöltése
struct graf_elem graf_tomb[] = {
        {0,1,1},
        {1,4,1},
        {0,1,1},
        {1,5,1},
        {5,6,1},
        {0,2,1},
        {2,3,1},
        {4,1,1},
        {1,0,1},
        {5,1,1},
        {6,5,1},
        {2,0,1},
        {3,2,1} };

 //Távolságokat tároló tömb
 int tavolsag[VEGTELEN+1];
//A megelőző elemeket tároló tömb
 int elozo[VEGTELEN+1];

//Segédváltozók
int szomszedok_elemszam;
int csere_prio_sor_elemszam;
int prio_sor_elemszam;
int elek_elemszam;
int csere_elek_elemszam;
int graf_elemszam;
int utvonal_elemszam;
//További segédváltozók
 int min;
 int u;
 int alt;
//A végleges útvonal tárolására használt tömb
 int utvonal[MAX_ELEMSZAM];  

// A start és a cél megadása
 start=4;
 cel=3;

//Segédváltozók alapértékei
 csere_elek_elemszam=0;
 utvonal_elemszam=0;
 prio_sor_elemszam=0;

 graf_elemszam=13; //itt meg kell adnunk manuálisan az élek számát


//A távolságokat és az útvonalakat nyilvántartó tömbök előkészítése
 for(i=0;i<VEGTELEN+1;++i)
 {
 //-1 -el jelöljük, ha érvénytelen egy elem
  elozo[i]=-1;
  tavolsag[i]=-1;
 }
 for(i=0;i<(VEGTELEN+1);++i)
 {// az isset helyett -1 -el jelöljük, ha érvénytelen egy elem
  for(j=0;j<MAX_SZOMSZED;++j)
  {
  szomszedok[i].end_elem[j]=-1;
  szomszedok[i].cost_elem[j]=-1;
  szomszedok[i].alelemek_szama=0;   
  }
 }

 elek_elemszam=0;
 szomszedok_elemszam=0;
 for(i=0;i<graf_elemszam;++i)
 {
  elek[elek_elemszam]=graf_tomb[i].elem1;
  ++elek_elemszam;
  elek[elek_elemszam]=graf_tomb[i].elem2;
  ++elek_elemszam;   
  szomszedok[graf_tomb[i].elem1].end_elem[szomszedok[graf_tomb[i].elem1].alelemek_szama]=graf_tomb[i].elem2;

szomszedok[graf_tomb[i].elem1].cost_elem[szomszedok[graf_tomb[i].elem1].alelemek_szama]=graf_tomb[i].tavolsag;
  ++szomszedok[graf_tomb[i].elem1].alelemek_szama;
  ++szomszedok_elemszam;
 }

 //A redundáns élek eltávolítása
 csere_elek_elemszam=0;
 for(i=0;i<elek_elemszam;++i)
 {
  check_multi_elek=0;   
  for(j=0;j<csere_elek_elemszam;++j)
  {
   if(csere_elek[j] == elek[i]) check_multi_elek=1;
  }

  if(check_multi_elek == 0)
  {csere_elek[csere_elek_elemszam]=elek[i];
   ++csere_elek_elemszam;}
 }

 for(i=0;i<elek_elemszam;++i)
 {
  elek[i]=csere_elek[i];
 }
 elek_elemszam=csere_elek_elemszam;

 for(i=0;i<elek_elemszam;++i)
 {
  tavolsag[elek[i]]=VEGTELEN;
 }  

 tavolsag[start]=0;

 for(i=0;i<elek_elemszam;++i)
 {
  prio_sor[i]=elek[i];
 }
 prio_sor_elemszam=elek_elemszam;


//Az adatok előkészítése befejeződött
  

//Amíg van feldolgozatlan elem a prioritásos sorban,
//addig folyamatosan feldolgozzuk a kapcsolati hálót
 while (prio_sor_elemszam > 0)
 {
       //A legkisebb távolságú él kiválasztása
       // a prioritásos sorból
       min=VEGTELEN;
       for(i=0;i<prio_sor_elemszam;++i)
       {
        if (tavolsag[prio_sor[i]] < min)
        {
         min=tavolsag[prio_sor[i]];
         u=prio_sor[i];
        }
       }

       //A prioritásos sorból eltávolítjuk a legkisebb távolságú élt
       csere_prio_sor_elemszam=0;
       for(i=0;i<prio_sor_elemszam;++i)
       {
        if(prio_sor[i] != u)
        {
         csere_prio_sor[csere_prio_sor_elemszam]=prio_sor[i];
         ++csere_prio_sor_elemszam;
        }
       }

       for(i=0;i<csere_prio_sor_elemszam;++i)
       {
        prio_sor[i]=csere_prio_sor[i];
       }
       prio_sor_elemszam=csere_prio_sor_elemszam;

       //Ha elfogytak az élek, vagy megtaláltuk a célállomást,
       //akkor befejeződik az algoritmus működése
       if((tavolsag[u] == VEGTELEN) || (u == cel)) break;

       //Az összes szomszédos csomópont feldolgozása
       //a távolságok és az útvonal (elozo tömb) frissítésével
       if(szomszedok[u].end_elem[0] != -1)
       {
        for(i=0;i<szomszedok[u].alelemek_szama;++i)
        {
         alt=tavolsag[u]+szomszedok[u].cost_elem[i];
         if(alt < tavolsag[szomszedok[u].end_elem[i]])
         {
                tavolsag[szomszedok[u].end_elem[i]]=alt;
                elozo[szomszedok[u].end_elem[i]]=u;
         }
        }

       }
 }

//Az utvonal előállítása
 u=cel;
 while(elozo[u] != -1)
 {
  utvonal[utvonal_elemszam]=u;
  ++utvonal_elemszam;
  u=elozo[u];
 }

 //Az útvonal kiíratása
 utvonal[utvonal_elemszam]=u;
  ++utvonal_elemszam;

 itoa(start,seged_szoveg,10);
 strcpy(eredmeny_szoveg,seged_szoveg);
 strcat(eredmeny_szoveg,"-");
 itoa(cel,seged_szoveg,10);
 strcat(eredmeny_szoveg,seged_szoveg);
 printf("%s: ",eredmeny_szoveg);

 for(i=0;i<utvonal_elemszam;++i)
 {
  printf("%d ",utvonal[utvonal_elemszam-i-1]);
 }
 printf("\n",eredmeny_szoveg);
 system("pause");
 return 0;
}


Az alkalmazás futtatásakor  az alábbi kimenet látható.



15.2. SRTM adatokból előállított képek

Domborzati képek kedvcsinálónak. A képek SRTM/HGT adatokból lettek előállítva, egyenként 12x6, azaz 72 darab HGT fájl tartalmának egymás mellé illesztésével.


1. ábra: Az Amazonas torkolatvidéke


2. ábra: A Kárpát-medence, domborzati árnyékolással


3.ábra: Észak-Németország partvidéke


4.ábra: Észak-Franciaország, Párizs és környéke
KAPCSOLAT

E-mail:
feher.konyvek@gmail.com
KAPCSOLAT

E-mail:
feher.konyvek@gmail.com
Vissza a tartalomhoz