OpenLayers esettanulmány

A feladat

A feladat egy olyan webtérkép készítése, amelynek alapja a http://www.sat24.com/image2.ashx?region=hu címen található, rendszeresen frissülő műholdkép. További elvárások:

A kezdetek: a térkép létrehozása

A térképnek készítünk egy div elemet tárolónak, létrehozzuk az OpenLayers.Map objektumot és rajta egy image layert. A műholdkép vetülete poláris sztereografikus vetület, 60°-os normálparallelkörrel és 10°-os kezdőmeridiánnal. Ebben a vetületben a pixelméret kb. 1503m, és a kép bal felső sarkának koordinátái: (26154, -4160639). Ezeket az értékeket használjuk a befoglaló téglalap és a képméret megadásakor:

<!DOCTYPE html>
<html>
 <head>
  <META name="Author" content="Gede Mátyás">
  <meta content="text/html; charset=utf-8" http-equiv=Content-Type>
  <LINK href="../../../../style.css" rel="stylesheet" type="text/css">
  <title>Gede Mátyás - OpenLayers esettanulmány - 1. lépés</title>
  <script src="http://openlayers.org/api/OpenLayers.js"></script>
  <script type="text/javascript">
   function init() // az oldal betöltésekor végrehajtandó függvény
   {
     // létrehozzuk a térképet a 'terkep_helye' azonosítójú div elemben
     var map = new OpenLayers.Map('terkep_helye', 
      {
        maxExtent: new OpenLayers.Bounds(26154, -4160639-615*1503, 26154+845*1503, -4160639)
      });
     // létrehozunk egy WMS réteget
     var bmp = new OpenLayers.Layer.Image( "Közép-Európa műholdkép",
         'http://www.sat24.com/image2.ashx?region=hu',
         new OpenLayers.Bounds(26154, -4160639-615*1503, 26154+845*1503, -4160639),
         new OpenLayers.Size(845*1503,615*1503),
          {
            numZoomLevels: 4,
            maxResolution: 1503 // pixelméret méterben
          });
     // hozzáadjuk a réteget a térképhez    
     map.addLayer(bmp);
     // kb. Magyarországra nagyítunk
     map.zoomToMaxExtent();
   }
  </script>  
 </head>
 <body onload="init()">
  <div style="width:845px; height:615px" id="terkep_helye"></div>
 </body>
</html>  
  

1. lépés: a weboldal a raszteres képpel.

Vetület definiálása

Mivel a továbbiakhoz szükséges a térkép vetületének ismerete, itt az ideje ezt is megadni. Mivel a poláris szereografikus vetület egyenletei egyszerűek, adjuk meg a transzformációkat. Ezek után már használhatjuk a Graticule controlt is a fokhálózat megjelenítésére.

...
   var rad=Math.PI/180; // fok/radián váltószám
   var R=6378137; // közepes földsugár
   var c=1+Math.sqrt(3)/2; // c=1+sin(60°)
   function sign(x) { return x>=0?1:-1; } // signum függvény
   
   // vetületi transzformáció definiálása a metsző sztereografikus vetülethez (la0=10)
   OpenLayers.Projection.addTransform("EPSG:4326","meteo_stereo",
      function (point) 
      {
        var fi=point.y;
        var la=point.x-10;
        var ro,x,y;
        with(Math)
        {
          ro=c*tan(PI/4-fi*rad/2);
          point.x=R*ro*sin(la*rad);
          point.y=-R*ro*cos(la*rad);
        }
        return point;
      }); 
   // inverz vetületi transzformáció definiálása a metsző sztereografikus vetülethez (la0=10)
   OpenLayers.Projection.addTransform("meteo_stereo","EPSG:4326",
      function (point) 
      {
        var x=point.x,y=point.y;
        with(Math)
        {
          ro=sqrt(x*x+y*y);
          if (ro==0)
            la=0;
          else
            la=acos(-y/ro)*sign(x)/rad;
          la+=10;
          if (la>180)
            la-=360;
          point.x=la;
          point.y=(PI/4-atan(ro/R/c))/rad*2;
        }
        return point;
      });
...
     // létrehozzuk a térképet a 'terkep_helye' azonosítójú div elemben
     var map = new OpenLayers.Map('terkep_helye', 
      {
        projection: new OpenLayers.Projection('meteo_stereo'),
        maxExtent: new OpenLayers.Bounds(26154, -4160639-615*1503, 26154+845*1503, -4160639)
      });
...
     // legyen fokhálózat is
     map.addControl(new OpenLayers.Control.Graticule( 
                        { 
                          layerName: 'Fokhálózat',
                          lineSymbolizer: { strokeColor: "#ffff00", strokeWidth: 1 },
                          labelSymbolizer: { fontColor: "#ffff00" },
                        } ));
     // és egy layerswitcher
     map.addControl(new OpenLayers.Control.LayerSwitcher());
...
  

2. lépés: definiáltuk a vetületet és rajzoltatunk fokhálózatot is.

Vízrajz hozzáadása

A vízrajzhoz legcélszerűbb valami kész alapanyagot felhasználni. A k_eu_viz.kml fájlban megtalálható Közép-Európa erősen generalizált vízrajza, ez most tökéletesen megfelel. A KML fájlt Vector layerként adjuk a térképhez:

...
     // Közép-Európa vízrajz KML layer
     var kml=new OpenLayers.Layer.Vector(
          "Vízrajz", 
          {
            projection : new OpenLayers.Projection("EPSG:4326"),
            strategies: [ new OpenLayers.Strategy.Fixed() ],
            protocol: new OpenLayers.Protocol.HTTP( 
              {
                url: "k_eu_viz.kml",
                format: new OpenLayers.Format.KML()
              }),
            style : { 'fillColor': '#8080ff', 'fillOpacity' : 1, 'strokeColor' : '#0000ff', 'strokeWidth' : 1 }
		        });
     map.addLayer(kml);     
...
  

3. lépés: vízrajz réteg.

Geokódolás

A következő lépés a geokódolás megvalósítása, azaz mutassa meg egy földrajzi név helyét, és fordítva. A geokódoláshoz az OSM Nominatim-ot fogjuk hasznáni. A helynév alapján való kereséshez a HTML kódban elhelyezünk egy input dobozkát, amibe írhatunk, és egy hozzá tartozó gombot:

...
 <body onload="init()">
  Település:<input type="text" id="helynev" onkeypress="if (event.keyCode==13) geokod(this.value);" />
  <input type="button" value="Mutat" onclick="geokod(document.getElementById('helynev').value)" />
  <div style="width:845px; height:615px" id="terkep_helye"></div>
 </body>
...
  

A geokódolást script node-ok létrehozásával valósítjuk meg. Az eredményt egy külön erre a célra létrehozott "pts" nevű rétegen pontokként jelenítjük meg:

...
   // Geokódolás az OSM Nominatim használatával
   function geokod(hely)
   {
     if (hely=='')
       return;
     var s=document.createElement('script');
     s.src='http://nominatim.openstreetmap.org/search?q='+hely+'&format=json&json_callback=helyetMutat&addressdetails=1';
     document.body.appendChild(s);
   }
   
   // Geokódolás callback függvénye
   function helyetMutat(valasz)
   {
    if (valasz[0])
     {
       var name=valasz[0].display_name;
       var w=new OpenLayers.Projection("EPSG:4326");
       var p=new OpenLayers.Projection("meteo_stereo");
       var pt={x:valasz[0].lon,y:valasz[0].lat};
       OpenLayers.Projection.transform(pt,w,p);
       var marker=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(pt.x, pt.y),
                                              {},
                                              {
                                                label: name,
                                                labelAlign: 'lb',
                                                labelXOffset: 10,
                                                labelYOffset: 0,
                                                fontColor: '#ff50ff',
                                                fontWeight: 'bold',
                                                externalGraphic: valasz[0].icon, 
                                                graphicHeight: 20, 
                                                graphicWidth: 20, 
                                                graphicYOffset:-15,
                                                graphicXOffset:-10
                                              });
       pts.addFeatures(marker);     }
     else // ha nincs eredmény
       alert('Nincs találat.');
   }
...
     // pts layer a geokódolt pontoknak     
     pts=new OpenLayers.Layer.Vector(
          "Pontok",
          {
            isBaseLayer: false,
            projection: new OpenLayers.Projection('meteo_stereo')
          });
     map.addLayer(pts);
...
  

Hasonlóan megvalósíthatjuk az inverz geokódolást is: itt a térképre kattintva megjelenik az adott hely neve.

...
   // Inverz geokódolás az OSM Nominatim használatával
   function inverzGeokod(loc)
   {
     var s=document.createElement('script');
     s.src='http://nominatim.openstreetmap.org/reverse?lat='+loc.lat+'&lon='+loc.lon+'&zoom=10&format=json&json_callback=inverzHelyetMutat&addressdetails=1';
     document.body.appendChild(s);
   }
   
   // Inverz geokódolás callback függvénye
   function inverzHelyetMutat(valasz)
   {
     if (valasz.display_name)
       alert(valasz.display_name);
     else
       alert('Nem tartozik helynév ehhez a ponthoz.')
   }
...
     // egérkattintás eseménykezelő ( geokódolás )
     map.events.register("click", map, function(e) 
      {
        // a kattintás helye a térképet tartalmazó div-ben
        var pxPos=map.events.getMousePosition(e); 
        // átszámítva az aktuális vetületi rendszerbe
        var projPos=map.getLonLatFromPixel(pxPos);
        // átszámítva földrajzi koordinátákra
        var geoPos=projPos.clone().transform(new OpenLayers.Projection("meteo_stereo"),new OpenLayers.Projection("EPSG:4326"));
        inverzGeokod(geoPos);
      });
...
  

4. lépés: geokódolás.

Területmérés

Adjunk egy újabb gombot a kezelőfelülethez (területmérés), és definiáljunk egy helyet, ahova a mérési eredmény kerülhet:

...
  <input type="button" id="teruletmero" value="Területmérés" onclick="terulet(this)" />
  <span id="meret"></span>
...
  

A területméréshez a OpenLayers.Control.Measure controlt használjuk, poligon handlerrel.

...
   var mero, meres=false; // globális változók a térképi mérésekhet
...
     // területmérő control
     mero=new OpenLayers.Control.Measure(
                    OpenLayers.Handler.Polygon, {
                        persist: true, 
                        immediate: true
                    }
                )
     map.addControl(mero);
     // területmérő eseménykezelői
     mero.events.on({"measurepartial": function (event)
         {           
           document.getElementById("meret").innerHTML='Terület: '+(event.geometry.getArea()/1000000).toFixed(0)+" km²";
         }});     
     mero.events.on({"deactivate": function (event)
         {           
           document.getElementById("meret").innerHTML='';
         }});     
...
  

A gomb megnyomásával ki-be kapcsolhatjuk a mérést, amit a gomb felirata is jelez:

...
   // területmérés indítása/befejezése
   function terulet(button)
   {
     meres=(button.value=='Területmérés');
     button.value=meres?'Mérés befejezése':'Területmérés'; // a gomb felirata az állapottól függ
     if (meres)
       mero.activate();
     else
       mero.deactivate();
   }
...
  

5. lépés: területmérés.