A Google Maps JavaScript API használata

Hogyan is működik a Google Maps?

A Google Maps (és az összes hasonló térképszolgáltatás, pl. a Yahoo Maps vagy a Bing Maps) hátterében előre elkészített, darabokra vágott raszteres térképek illetve űrfelvételek állnak. A térképek Mercator-féle szögtartó hengervetületben készültek. Mivel ebben a vetületben a pólusok a végtelenben vannak, a térkép északon és délen csak kb. a 85°-os szélességekig ábrázolja a Földet.

A térképek felbontását a nagyítási (zoom) fokozat (z) határozza meg. A legkisebb, z = 0 nagyításnál az egész világ egy 256*256 pixeles négyzetre képeződik le:

x=0 y=0 z=0

Magasabb nagyítási fokozatokban 2z * 2z darab mozaikdarabból (ún. tile-okból) áll össze a térkép. z = 1 esetén pl.:

x=0 y=0 z=1 x=1 y=0 z=1
x=0 y=1 z=1 x=1 y=1 z=1
Ezen a térképen a szemléletesség kedvéért minden tile-on látható annak sorszáma (x y zoom), és határa.

A Google Maps felhasználói felület lényege tehát, hogy ezekből a tile-okból mindig a megfelelő darabokat jeleníti meg.

Ezen felül természetesen számos további szolgáltatása van: a térképhez saját vektoros vagy raszteres tartalmat adhatunk, eseménykezelőket definiálhatunk stb.

Az API használata

A Google Maps API segítségével a saját weboldalunkba ágyazhatunk egy Google Maps térképet, és azzal különféle dolgokat művelhetünk.

Az API folyamatos fejlesztés alatt van. Ez az anyag a 3. verzió használatát mutatja be.

Az API használatához részletes útmutató és referencia található a Google Code oldalain.


A térképet mindig egy <div> tartalmazza. A legegyszerűbb esetben néhány sornyi kódra van szükségünk:

<script type="text/javascript"
    src="http://maps.google.com/maps/api/js?sensor=false">
</script>

<script type="text/javascript">

function initialize() 
{
  var latlng = new google.maps.LatLng(47.475, 19.062);
  var myOptions = 
      {
	zoom: 13,
	center: latlng,
	mapTypeId: google.maps.MapTypeId.HYBRID
      };
  var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
}

</script>

...

<body onload='initialize();'>
<div id="map_canvas" style="width:700px; height:500px"></div>
</body>

Ennek eredménye: 1. példa - térkép beágyazás

Lássuk mi is történik:

A térképet kezelő JavaScript kódokat letöltjük a Google szerveréről az első <script> elemmel. Ezután készítünk egy egyszerű függvényt, ami az oldal betöltése után (<body> elem onload eseménye) hívódik majd meg. Ez a függvény fogja a "map_canvas" azonosítójú div-be betölteni a megfelelően beállított térképet. Ehhez először létrehozunk "map" néven egy google.maps.Map objektum példányt, melynek konstruktorát a térképet tartalmazó div objektumával hívjuk meg.

A beállítások változtatásával egyszerűen alakíthatunk a térkép kinézetén, kezelőszervein. Ha a műholdképre vetített úthálózat helyett mást szeretnénk látni, akkor használhatjuk az alábbi előre definiált konstansokat a mapTypeId értékeként:

A különböző kezelőszerveket is ki-be kapcsolhatjuk, változtathatunk azok tulajdonságain. A követkető példában a Streetview és a "Pan" kezelőszerv nem látható, van viszont mértékléc (scale) és csak kétféle térképtípus közül választhatunk.

...
  var myOptions = 
      {
	zoom: 13,
	center: latlng,
	panControl: false,
	streetViewControl: false,
	mapTypeControl: true,
	mapTypeControlOptions:
	 { mapTypeIds: [google.maps.MapTypeId.ROADMAP,google.maps.MapTypeId.TERRAIN] },
	zoomControl: true,
	scaleControl: true,
	mapTypeId: google.maps.MapTypeId.ROADMAP
      };
...

1/a. példa - egyszerű térkép, módosított beállításokkal

Eseménykezelő definiálása

A google.maps.event.addListener() függvénnyel egyszerűen definiálhatunk a térkép egyes objektumaihoz eseménykezelőt:

...
  google.maps.event.addListener(map, 'click', function(event) {
    with(event.latLng)
      alert("fi="+lat()+"° lambda="+lng()+"°");
  });
...

2. példa - eseménykezelés

Pont (marker) hozzáadása

A térképünkhöz legegyszerűbb módon ún. markerek lerakásával tudunk további tartalmat hozzáadni. A marker egy földrajzi koordinátákkal meghatározott pont, melyet a térképen egy ikon jelöl. Ilyen pontot egy google.maps.Marker objektum egy példányának létrehozásával tudunk definiálni. A következő példában a térképre kattintva egy új pont jön létre az adott helyen. Ehhez az előbbi példa eseménykezelőjét kell átírnunk:

...
    var marker = new google.maps.Marker({
        position: event.latLng, 
        map: map,
        title: (++markerCnt)+". pont"
    });
...

3. példa - eseménykezelés; pont (marker) hozzáadása

Áthelyezhető marker

Ha egy markernél beállítjuk a draggable: true tulajdonságot, akkor az az egérrel áthelyezhető lesz. Az előző példa ezzel kiegészítve:

3.b példa - áthelyezhető markerek hozzáadása

Információs buborék

A google.maps.InfoWindow objektum segítségével "információs buborékot" hozhatunk létre. A konstruktornak paraméterként adott objektum legfontosabb eleme a "content" mező, melyben a buborék tartalmát határozhatjuk meg. A buborékot az objektum open() metódusával nyithatjuk meg. Ha itt nem csak a térképet adjuk paraméternek, hanem pl. egy markert, úgy ahhoz lesz kötve a buborék.

Az előző példát továbbfejlesztve a markerekve kattintva jelenjen meg egy hozzájuk kapcsolt InfoWindow:

...
    var marker = new google.maps.Marker({
        position: event.latLng, 
        map: map,
        title: (++markerCnt)+". pont"
    });
    var infowindow = new google.maps.InfoWindow({
        content: "<b>"+markerCnt+". pont</b><br>"+
                 "&phi;="+event.latLng.lat()+"<br>&lambda;="+event.latLng.lng()
    });

    google.maps.event.addListener(marker, 'click', function() { infowindow.open(map,marker); });
...

4. példa - eseménykezelés; pont hozzáadása információs buborékkal

Töröttvonal (polyline) és sokszög (polygon)

A térképre felvehetünk töröttvonalat is. Ehhez létre kell hoznunk egy google.maps.Polyline objektumot, megadni a pontjait, és az objektum setMap() metódusával hozzárendelni a térképet, amin meg akarjuk jeleníteni. A töréspontokat egy tömb (illetve egy ahhoz hasonlító objektumtípus, az MVCArray) tartalmazza, amelyre a getPath() ad referenciát.

Az alábbi példában töröttvonalat rajzolhatunk a térképre: minden kattintás egy újabb szakaszt ad a vonalhoz. A kódban kihasználjuk, hogy az MVCArray push() metódusa automatikusan a tömb végére tesz be egy új elemet.

...
  var polyOptions = {
    strokeColor: '#ff0000',
    strokeOpacity: 0.6,
    strokeWeight: 5
  }
  poly = new google.maps.Polyline(polyOptions);
  poly.setMap(map);

  google.maps.event.addListener(map, 'click', function(event) {
    var path=poly.getPath();
    path.push(event.latLng);
  });
...

5. példa - eseménykezelés; töröttvonal rajzolása

Teljesen hasonlóan lehet sokszöget is definiálni a google.maps.Polygon objektummal. Az előző példát kicsit módosítva így zárt poligont is rajzolhatunk.

6. példa - eseménykezelés; poligon rajzolása

Téglalap és kör hozzáadása

Téglalapot a google.maps.Rectangle, kört pedig a google.maps.Circle objektumokkal tudunk létrehozni. A téglalapnál a délnyugati és az északkeleti sarok koordinátáit kell megadnunk google.maps.LatLng objektumokkal, míg a körnél a középpontot, valamint a méterben kifejezett sugarat. Amennyiben az objektumoknál beállítjuk az editable: true tulajdonságot, akkor az alakzatok szerkeszthetők is lesznek. A következő példában a megfelelő gombok megnyomásakor létrejön egy kör vagy téglalap az aktuális nézet közepén, úgy, hogy mkérete fele lesz a térkép méretének.

... példa - Méretezhető/áthelyezhető téglalap vagy kör rajzolása

Geokódolás

A google.maps.Geocoder objektum segítségével geokódolási kéréseket küldhetünk, melyek segítségével teljes vagy részleges címekhez, településnevekhez rendelhetünk földrajzi koordinátákat, vagy fordítva: egy koordinátákkal adott ponthoz a címet. A geokódolás aszinkron folyamat: mikor elküldjük a kérést, a script futása folytatódik. Ha megérkezett a kérésre a válasz, az eredményt paraméterként megkapja egy általunk definiált függvény. Ezzel a függvénnyel lehet aztán feldolgozni az eredményt.

A kérést a Geocoder objektum geocode() metódusával küldhetjük el. A szükséges adatokat az első paraméterként adott objektumban adjuk át. Ha címből indulunk ki, akkor azt az address mezőben tároljuk; ha a földrajzi koordinátákból, akkor a latLng értékét állítjuk be.

A következő példában a geocode() függvény a paraméterként kapott címet elhelyezi a térképen. A függvényt egy szövegdoboz onclick eseménye aktiválja.

...
var map,geocoder;

function initialize() 
{
  var latlng = new google.maps.LatLng(47.475, 19.062);
  var myOptions = 
      {
	zoom: 13,
	center: latlng,
	mapTypeId: google.maps.MapTypeId.HYBRID
      };
  map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
  geocoder = new google.maps.Geocoder();
}

function geocode(addr)
{
    geocoder.geocode( { address: addr }, function(results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
        map.setCenter(results[0].geometry.location);
        var marker = new google.maps.Marker({
            map: map, 
            position: results[0].geometry.location,
	    title: addr
        });
      } else {
        alert("A geokódolás nem sikerült. A hiba oka: " + status);
      }
    });
}
...
Írj be egy címet: <input type=text onchange='geocode(this.value)' />
...

7. példa - geokódolás

Ehhez teljesen hasonlóan valósítható meg az inverz geokódolás is (koordináták alapján cím meghatározása).

...
function inverseGeocode(loc)
{
    geocoder.geocode( { latLng: loc }, function(results, status) {
      if (status == google.maps.GeocoderStatus.OK) 
      {
	       alert(results[0].formatted_address);
      } else {
        alert("A geokódolás nem sikerült. A hiba oka: " + status);
      }
    });
}
...

8. példa - inverz geokódolás

Útvonaltervezés

A google.maps.DirectionsService objektummal útvonalat is terveztethetünk. A geokódoláshoz hasonlóan az útvonaltervezés is aszinkron folyamat. A szervernek küldött kérés tartalmazza a kezdőpontot, a célt, az utazás módját (DRIVING=autózás, WALKING=gyaloglás és BICYCLING=kerékpározás). Bonyolultabb esetben köztes útpontokat is megadhatunk. A pontokat megadhatjuk koordinátáikkal, vagy címükkel.

...
var directionsService = new google.maps.DirectionsService();
...
 var request = 
 {
  origin: 'kiindulópont', 
  destination: 'célpont',
  travelMode: google.maps.DirectionsTravelMode.DRIVING
 };
 directionsService.route(request, function(result, status) 
 {
  if (status == google.maps.DirectionsStatus.OK) 
  {
    // ide jöhet az eredményt feldolgozó kód
  }
  else
    alert('Nem sikerült az útvonaltervezés.\n'+status);
 });  
... 

Ha sikerült az útvonaltervezés, akkor a válaszként kapott result objektum routes[] eleme tartalmazza a lehetséges útvonalakat. Minden útvonal útszakaszokból (leg) áll. Ha csak a kezdő és a végpontot adtuk meg, akkor cak egy ilyen leg van, ha köztes pontokat is, akkor minden két útpont közti útvonalrész egy-egy külön leget alkot. Ezek az útvonalakon belül a legs[] tömbben vannak. Például az első lehetséges útvonal első szakasza: result.routes[0].legs[0]. Az útszakaszok az előzőekhez hasonlóan további rövidebb szakaszokra oszlanak (steps[]), amelyekben a path[] tömb már ténylegesen az adott útszakaszt reprezentáló töröttvonal pontjait tartalmazza.

Látható, hogy az útvonalterv egy meglehetősen összetett objektum. Szerencsére van egy olyan eszköz, melynek segítségével minimális kódolással megjeleníthetjük az eredményt, ez pedig a google.maps.DirectionsRenderer objektum. Az objektum egy példányához egyszerűen hozzárendeljük a megjelenítéshez használt térkép objektumot és az útvonalat, ezen felül semmi dolgunk nincs.

...
  var directionsDisplay = new google.maps.DirectionsRenderer();
  directionsDisplay.setMap(map);
... 
   if (status == google.maps.DirectionsStatus.OK) 
   {
     directionsDisplay.setDirections(result);
     alert(result.routes[0].legs[0].distance.text+' - '+result.routes[0].legs[0].duration.text);
   }
...

9. példa - útvonaltervező

Magassági adatok lekérdezése

Lehetőségünk van arra is, hogy lekérdezzük a földfelszín egy vagy több pontjának (vagy akár egy töröttvonal mentén pontok sorozatának) tengerszint feletti magasságát. Erre a google.maps.ElevationService objektumot kell használnunk. A megoldás a geokódoláshoz meglehetősen hasonló.

Fontos megjegyezni, hogy a kapott értékek nem a valós magassági adatok, hanem egy digitális domborzatmodell alapjány számított magasságok. Emiatt az értékek többszörösen is hibákkal terheltek:

Egy egyszerű, egy pontra vonatkozó magasságiadat-kérés a következőképp néz ki:

...
  var elev = new google.maps.ElevationService();
... 
  google.maps.event.addListener(map, 'click', function(event) 
  {
    elev.getElevationForLocations({ locations: [event.latLng] }, function(results, status) 
    {
      if (status == google.maps.ElevationStatus.OK)
        with(event.latLng)
          alert("fi="+lat()+"°\n lambda="+lng()+"°\n h="+results[0].elevation+"m");
    });
  });
...

10. példa - magasság lekérdezése

Saját raszteres térkép

Ha akarunk, saját raszteres térképet is megjeleníthetünk a Google Maps API segítségével. A legegyszerűbb megoldás az, ha ún. lefedő képként adjuk a saját raszteres képet a térképhez:

...
    // a foktrapéz, ahova a GroundOverlay kerül
    var hely=new google.maps.LatLngBounds(new google.maps.LatLng(47.3456, 18.6086),new google.maps.LatLng(47.8550, 19.2015));
    // GroundOverlay definiálása
    ovl = new google.maps.GroundOverlay("bp_kornyek.jpg", hely);
    ovl.setMap(map);
...

11. példa - lefedő kép (GroundOverlay)

A lefedő képet egy, a délnyugati és az északkeleti sarkának koordinátáival definiált foktrapézra illesztjük. A foktrapéz egy google.maps.LatLngBounds objektum, a lefedő kép pedig egy google.maps.GroundOverlay. A létrehozott GroundOverlay objektum átlátszósága is állítható annak setOpacity() metódusával; a paraméter értéke 0 (teljesen átlátszó) és 1 (teljesen átlátszatlan) között változhat.

Ez az oldal is GroundOverlay-t használ a Curiosity rover által megtett út szemléltetésére.

Lefedő képet csak kisebb méretű raszteres térképből érdemes készíteni, mivel a túl nagy méretű kép megjelenítése esetleg akadozóvá teszi a weboldalt. A nagyobb képeket célszerűbb feldarabolni. Ilyenkor le kell gyártanunk azokat a mozaikdarabokat, amelyekből a különböző nagyítási fokozatokon felépül a kép. Ha azt szeretnénk, hogy térképünk a Google saját térképeivel kombinálva is látható legyen, nagyon fontos, hogy ugyanazt a vetületet és szelvényezést alkalmazzuk.

A Google Maps-féle vetület a következő:

EPSG szám
3857 (vagy 900913)
Alapfelület
Google Maps (gömb, r=6378137m; a WGS84-es koordináták transzformáció nélkül használhatók)
Vetület
Mercator

Szerencsére vannak olyan programok, amik a mozaikdarabolást és az egyéb kulimunkát elvégzik helyettünk. Talán a legkényelmesebb a Global Mapper alkalmazása. Ebben a programban még a vetülettel sem kell foglalkoznunk; bármilyen korrekt georeferenciával rendelkező anyagból gombnyomásra legyártja a Google Maps mozaikkészletet.

A mozaikdarabokat úgy kell elnevezni, hogy a zoom és a mozaiksorszámok ismeretében egyszerűen meghatározható legyen az adott kép fájlneve.

Ha megvannak a mozaikdarabok, akkor definiálnunk kell egy saját térképtípust. Itt a legfontosabb annak a függvénynek az elkészítése, ami adott x, y, zoom paraméterekre megadja a megfelelő tile URL-jét:

...
var globuszOpts = 
	{
	  getTileUrl: function(coord, zoom) 
	  {
	    return "nemet_gomb/n_"+zoom+"_"+coord.y+"_"+coord.x+".png";
	  },
	  tileSize: new google.maps.Size(256, 256),
	  isPng: true
	  minZoom: 0,
	  maxZoom: 4,
	  name: "Földgömb"
	};

var globuszMapType = new google.maps.ImageMapType(globuszOpts);
...

Az így elkészített térképtípussal két dolgot tehetünk. Egyszerűbb esetben fedvényként a Google térkép fölé helyezhetjük. Ez különösen előnyös akkor, ha térképünk háttere átlátszó, mivel így az alatta lévő tartalom is látható.

...
  map.overlayMapTypes.insertAt(0, globuszMapType);
...

11a. példa - saját térképmozaik, mint fedvény

A másik esetben a térképtípust felvesszük a kiválasztható önálló térképtípusok közé. Ezután ugyanúgy kiválasztható lesz, mint a beépített típusok:

...
  var myOptions = 
      {
        zoom: 2,
        center: latlng,
        mapTypeControl: true,
        mapTypeControlOptions: { mapTypeIds: ['gömb',google.maps.MapTypeId.SATELLITE]},
      };
  var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
  map.mapTypes.set('gömb', globuszMapType);
  map.setMapTypeId(google.maps.MapTypeId.SATELLITE);
...

11b. példa - saját térképmozaik, mint térképtípus

11c. példa - térképmozaik, mint térképtípus - Google Sky és Moon

KML/KMZ fájl megjelenítése

KML vagy KMZ formátumú fájlokat is megjeleníthetünk a térképünkön. Ehhez mindössze be kell töltenünk a megfelelő fájlt a google.maps.KmlLayer függvény segítségével, majd az így létrehozott objektumhoz hozzá kell rendelnünk a térképet, amin megjelenítjük:

...
  var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
  var kml = new google.maps.KmlLayer('http://mercator.elte.hu/~saman/hu/okt/gmaps3/hochschwab.kmz');
  kml.setMap(map);
...

12. példa - KML/KMZ fájl megjelenítése

Fontos megjegyezni, hogy ebben az esetben nem használhatunk lokális fájlneveket, sem relatív URL-t. Minden esetben meg kell adnunk az abszolút URL-t. Ennek az az oka, hogy a KML/KMZ fájl raszteres képpé alakítása nem a böngészőben történik, hanem a Google egyik szerverén.

Összetettebb példák


innentől szerkesztés alatt :)

13. példa - "Snap to road" - töröttvonal rajzolás úgy, hogy a jelölt pontok közt az úthálózaton fut a vonal