„Geomajas” változatai közötti eltérés

Innen: GIS Wiki
(Áttekintés)
a
 
(45 közbenső módosítás, amit 2 másik szerkesztő végzett, nincs mutatva)
1. sor: 1. sor:
 
==Áttekintés==
 
==Áttekintés==
 
A Geomajas (http://www.geomajas.org/) egy Java nyelven íródott Open Source Web Mapping Framework. Mind szerver, mind kliensoldali komponenseket kínál. Szerveroldalon Java, Javascript, míg kliensoldalon Java (GWT) és Javascript nyelven tesz megvalósításokat elérhetővé.
 
A Geomajas (http://www.geomajas.org/) egy Java nyelven íródott Open Source Web Mapping Framework. Mind szerver, mind kliensoldali komponenseket kínál. Szerveroldalon Java, Javascript, míg kliensoldalon Java (GWT) és Javascript nyelven tesz megvalósításokat elérhetővé.
 +
 +
A kliensoldal bár használható különálló módon, igazi hatékonyság és funkcióbőség a szerver és a kliens összehangolt működésével érhető el.
 +
 +
Főbb funkciók:
 +
 +
* Integrált szerver-kliens architektúra (Spring)
 +
* Attribútum és geometria szerkesztés
 +
* Egyedi attribútumdefiníció
 +
* Magas szintű query lehetőségek (CQL)
 +
* Bővíthető plugin mechanizmus
 +
* Átfogó böngésző támogatás
 +
* Biztonság (Token)
  
 
==Előkészületek==
 
==Előkészületek==
 
A mintaprogramok fordításához és futtatásához Maven-t használnak (https://maven.apache.org/). Ahhoz, hogy futtatni és fordítani tudjuk a github-os projekteket, a Maven telepítési könyvtárában a settings.xml-ben (HOME/.m2/settings.xml) fel kell venni néhány dependency-t, ez az xml a github-os gwt kliensnél megtalálható. (https://github.com/geomajas/geomajas-project-client-gwt2)
 
A mintaprogramok fordításához és futtatásához Maven-t használnak (https://maven.apache.org/). Ahhoz, hogy futtatni és fordítani tudjuk a github-os projekteket, a Maven telepítési könyvtárában a settings.xml-ben (HOME/.m2/settings.xml) fel kell venni néhány dependency-t, ez az xml a github-os gwt kliensnél megtalálható. (https://github.com/geomajas/geomajas-project-client-gwt2)
  
Kezdésként a GWT-s standalone applicationt indítsuk el. (https://github.com/geomajas/geomajas-gwt2-quickstart-application) Fordítsuk le a Mavennel a projektet, majd futtassuk.
+
Kezdésként a GWT-s standalone applicationt indítsuk el, ez a következő linken érhető el. (https://github.com/geomajas/geomajas-gwt2-quickstart-application) Fordítsuk le a Mavennel a projektet, majd futtassuk.
  
 
<code>
 
<code>
12. sor: 24. sor:
 
mvn jetty: run
 
mvn jetty: run
 
</code>
 
</code>
 +
 +
Vagy egyben
 +
<code>mvn install jetty: run</code>
  
 
Ezután látogassunk el a http://localhost:8080/-ra, ahol megtekinthető az eredmény.
 
Ezután látogassunk el a http://localhost:8080/-ra, ahol megtekinthető az eredmény.
 +
==Szerver oldal ==
  
==A projekt szerkezete==
+
A Geomajas szerveroldali rész felelős az alkalmazás intergálhatóságért, célja a business logika integrálása GIS eszközökbe. A szerveroldalon konfigurálhatóak a layerek, térképek és a szolgáltatások. Bár sok megjelenítéssel kapcsolatos technológát (vektorgrafika, renderelés) tartalmaz, konkrét megjelenítéssel nem foglalkozik. A szolgáltatások dependecy injection-nel vannak összekötve, továbbá az inversion of controlnak hála a szerver nagyon rugalmas.
 +
 
 +
A Geomajas szerver oldal a Spring framework-öt használja. A szerver az alábbiakat nyújtja:
 +
* Utasítások: Az utasítások elsődleges interakciós pontok a szerver és a kliens között. A szolgáltatások, térképek, layerek megkeresése mind utasításokkal történit.
 +
* Layerek: Ezek tárolják a térképek tulajdonságaihoz a hozzáférési pontokat. Egy Layer lehet raszteres, vagy vektor alapú és szerkeszthető. A vektor layer részeit képező objektumok a "feature object"-ként érhetőek el, amit a Geomajas átkovertál számára használható formátumba, ezáltal nem kell interfaceket implementálni, minden POJO-k segítségével megoldható. Ezek a "feature"-ök tartalmazzák a geometriát és az attribútumok halmazát. Az attribútumok lehetnek komplexek: <code>@OneToMany</code> és <code>@ManyToOne</code> is.
 +
* Pipeline-ok: Minden layerekkel foglalkozó Geomajas rész pipeline-okkal van megoldva. A pipeline-ok utasítások sorozatai, amelyek sorrendben hajtódnak végre. Habár minden layernek van default pipeline működése, ez felülírható.
 +
* Biztonság: A biztonsági szolgáltatások közé tartozik a tokenes authentikáció, illetve a tokenhez tartozó hozzáféréssel elérhető objektumok megkeresése és visszatérése. Képes továbbá saját security ppolicy olvasására is.
 +
 
 +
A vektor layer és az <code>InternalFeautre</code> objektumai közötti konverziót a <code>FeatureModel</code> mondja meg. Ez a következőképpen néz ki:
 +
 
 +
<syntaxhighlight lang="java">
 +
@Api(allMethods = true)
 +
@UserImplemented
 +
public interface FeatureModel {
 +
 
 +
void setLayerInfo(VectorLayerInfo vectorLayerInfo) throws LayerException;
 +
 
 +
Attribute getAttribute(Object feature, String name) throws LayerException;
 +
 
 +
Map<String, Attribute> getAttributes(Object feature) throws LayerException;
 +
 
 +
String getId(Object feature) throws LayerException;
 +
 
 +
Geometry getGeometry(Object feature) throws LayerException;
 +
 
 +
void setAttributes(Object feature, java.util.Map<String, Attribute> attributes) throws LayerException;
 +
 
 +
void setGeometry(Object feature, Geometry geometry) throws LayerException;
 +
 
 +
Object newInstance() throws LayerException;
 +
 
 +
Object newInstance(String id) throws LayerException;
 +
 
 +
int getSrid() throws LayerException;
 +
 
 +
String getGeometryAttributeName() throws LayerException;
 +
 
 +
boolean canHandle(Object feature);
 +
}
 +
</syntaxhighlight>
 +
 
 +
A Geomajas POJO alapú, így nem vár komplex attribútumokat paraméterként, minden sima Java Object. Ez jól látható a vektor layer megvalósításában, amely az alábbi módon néz ki:
 +
 
 +
<syntaxhighlight lang="java">
 +
@Api(allMethods = true)
 +
@UserImplemented
 +
public interface VectorLayer extends Layer<VectorLayerInfo> {
 +
 
 +
boolean isCreateCapable();
 +
 
 +
boolean isUpdateCapable();
 +
 
 +
boolean isDeleteCapable();
 +
 
 +
FeatureModel getFeatureModel();
 +
 
 +
Object create(Object feature) throws LayerException;
 +
 
 +
Object saveOrUpdate(Object feature) throws LayerException;
 +
 
 +
Object read(String featureId) throws LayerException;
 +
 
 +
void delete(String featureId) throws LayerException;
 +
 
 +
Iterator<?> getElements(Filter filter, int offset, int maxResultSize) throws LayerException;
 +
 
 +
Envelope getBounds(Filter filter) throws LayerException;
 +
 
 +
Envelope getBounds() throws LayerException;
 +
}
 +
</syntaxhighlight>
 +
 
 +
Sok esetben az információ eljuttatása a layer objektumokhoz szabályokat követ:
 +
* Ha egy primitív attribútum változik meg egy új értékre, akkor ez az új érték lecseréli a régit.
 +
* Bármely attribútum értéke ha <code>null</code>-ra állítódik, törlődik.
 +
* Ha egy <code>@ManyToOne</code> attribútum értéke módosul egy létező objektumra, akkor az érték lecserélődik és az állapota frissül.
 +
* Ha egy <code>@OneToMany</code> attribútum értéke <code>null</code>-ra, vagy üresre módosul, üres kollekció lesz belőle. Az így árván maradt objektumok törléséről, vagy megmaradásáról a <code>FeatureModel</code>-ben lehet nyilatkozni.
 +
* Ha egy <code>@OneToMany</code> attribútum értéke egy új attribútum csoportra változik, akkor a meglévő attribútumok frissülnek, új attribútumok jönnek létre, a hiányzó értékekről pedig a <code>FeatureModel</code>-ben implementált viselkedés alkalmazódik.
 +
* Az előző szabályok rekurzívan alkalmazhatóak.
 +
 
 +
Ezen szabályok betartására példa az <code>Entity</code> interface, amely az objektum-fák gráf összeillesztésénél játszik szerepet, ugyanis az összeillesztés megvalósítása nem a <code>FeatureModel</code>-ben található, hanem az <code>EntityAttributeService</code>-ben, amely feltételezi, hogy minden entitás megvalósítja a már említett interface-t.
 +
 
 +
<syntaxhighlight lang="java">
 +
@Api(allMethods = true)
 +
@UserImplemented
 +
public interface Entity {
 +
 
 +
Object getId(String name) throws LayerException;
 +
 
 +
Entity getChild(String name) throws LayerException;
 +
 
 +
void setChild(String name, Entity entity) throws LayerException;
 +
 
 +
EntityCollection getChildCollection(String name) throws LayerException;
 +
 
 +
void setAttribute(String name, Object value) throws LayerException;
 +
 
 +
Object getAttribute(String name) throws LayerException;
 +
 
 +
}
 +
</syntaxhighlight>
 +
 
 +
Egy gyökérentitástól kezdve a <code>@OneToMany</code> és <code>@ManyToOne</code> kapcsolatok bejárhatóak a <code>getChild()</code> metódus segítségével.
 +
 
 +
==A GWT quickstart application==
 
A GWT-s projekt általában egy gyökér könyvtárbeli szerver és kliens oldali csomagból áll. A kliens oldali belépési pontot az Application.java fájl tartalmazza, ami a következő helyen található: ..\geomajas-gwt2-quickstart-application-master\src\main\java\org\geomajas\quickstart\gwt2\client.  
 
A GWT-s projekt általában egy gyökér könyvtárbeli szerver és kliens oldali csomagból áll. A kliens oldali belépési pontot az Application.java fájl tartalmazza, ami a következő helyen található: ..\geomajas-gwt2-quickstart-application-master\src\main\java\org\geomajas\quickstart\gwt2\client.  
  
 
Ez megvalósítja a com.google.gwt.core.client.EntryPoint interfészt, így implementálni kell az onModuleLoad metódust. A kód egyszerű: létrejön egy ApplicationLayout, majd hozzáadódik a megjelenítéshez.
 
Ez megvalósítja a com.google.gwt.core.client.EntryPoint interfészt, így implementálni kell az onModuleLoad metódust. A kód egyszerű: létrejön egy ApplicationLayout, majd hozzáadódik a megjelenítéshez.
  
<source lang="java">
+
<syntaxhighlight lang="java">
  
 
import com.google.gwt.core.client.EntryPoint;
 
import com.google.gwt.core.client.EntryPoint;
33. sor: 153. sor:
 
}
 
}
 
}
 
}
</source>
+
</syntaxhighlight>
  
 
Az ApplicationLayout geomajas specifikus layout, a következő módon keletkezik:
 
Az ApplicationLayout geomajas specifikus layout, a következő módon keletkezik:
  
<source lang="java">
+
<syntaxhighlight lang="java">
 
public ApplicationLayout() {
 
public ApplicationLayout() {
 
initWidget(UIBINDER.createAndBindUi(this));
 
initWidget(UIBINDER.createAndBindUi(this));
58. sor: 178. sor:
  
 
}
 
}
</source>
+
</syntaxhighlight>
  
 
Miután betöltődik a kontextusthoz szükséges GWT-s modul, létrejön egy MapPresenter, majd hozzá egy default inicializáló. Ezután a szerverkódból inicializáljuk ezt. Ezt követi a Layout beállítása, hozzáadása a megjelenítéshez, valamint egy ApplicationService létrehozása, amely kiszolgálja a ezt a Layert. Ezután hozzáadásra kerül egy egyedi eseménykezelő.
 
Miután betöltődik a kontextusthoz szükséges GWT-s modul, létrejön egy MapPresenter, majd hozzá egy default inicializáló. Ezután a szerverkódból inicializáljuk ezt. Ezt követi a Layout beállítása, hozzáadása a megjelenítéshez, valamint egy ApplicationService létrehozása, amely kiszolgálja a ezt a Layert. Ezután hozzáadásra kerül egy egyedi eseménykezelő.
 +
 +
A quickstart application csak a gwt-s kliens egy részét tartalmazza, ezért használja a <code>GeomajasServerExtension</code>-t, a szerveroldal létező példánya helyett, viszont jó kiindulási pont a framework megismeréséhez.
 +
 +
== Javascript API ==
 +
A Client Javascript API a Client GWT-t használja. A Javascriptes projektnél a javadoc-os résznél elakad a maven, így csak az API és a gwt2 projekt fordítható le egyenként. A gwt2 projekt tartalma egy javascript api-t használó mintaalkalmazás, így elég csak ezt lefordítani. A dokumentációban említett gs.js fájlt azonban már nem generálja le, csak egy dinamikus azonosítóval ellátott js-t.
 +
 +
A Githubról a Javascript project (https://github.com/geomajas/geomajas-project-javascript) letöltése után, lefordíthatjuk a gwt2-es projektet, ami legenerálja a szükséges javascript fájlokat és létrehozza a megfelelő index.html-eket, mind szerver mind kliens oldalon. Az eredmény ideiglenesen a http://people.inf.elte.hu/bagtaai címen elérhető. Eredeti oldal, ami néha nem megy: (http://dev.geomajas.org/geomajas-project-javascript-gwt2-distribution-1.0.0-SNAPSHOT/).
 +
 +
A map betöltéséhez ez a modul egy <code>onGeomajasLoad</code> függvényt használ, ami lényegében az inicializációt hajtja végre. Az alábbi kódrészlet mutatja a legenerált index.html-ben szereplő példát.
 +
 +
<syntaxhighlight lang="javascript">
 +
<script type="text/javascript">
 +
var map;
 +
var WMS_BASE_URL = "http://apps.geomajas.org/geoserver/demo_world/ows";
 +
var wmsVersion = "1.3.0"; //default value
 +
 +
 +
function onGeomajasLoad() {
 +
var mapConfig = new gm.MapConfiguration();
 +
mapConfig.setCrs("EPSG:4326", "DEGREES");
 +
mapConfig.setMinimumResolution(2.1457672119140625E-5)
 +
mapConfig.setMaxBounds(new gm.Bbox(-180, -90, 360, 180));
 +
 +
// Map létrehozása és inicializálása
 +
 +
map = new gm.Map("js-map-element", mapConfig);
 +
 +
// Kiürítjük a map-et
 +
map.getLayersModel().clear();
 +
 +
var tileConfig = new gm.layer.TileConfiguration(256, 256, new gm.Coordinate(-180, -90),
 +
map.getViewPort());
 +
var layerConfig = new gm.WmsLayerConfiguration();
 +
 +
layerConfig.setBaseUrl(WMS_BASE_URL);
 +
layerConfig.setFormat("image/png");
 +
layerConfig.setVersion(wmsVersion);
 +
layerConfig.setLayers("demo_world:simplified_country_borders");
 +
layerConfig.setMinimumResolution(2.1457672119140625E-5);
 +
layerConfig.setMaximumResolution(Number.MAX_VALUE);
 +
 +
// Létrehozunk egy WMS Layert és hozzáadjuk a maphez:
 +
 +
var wmsLayer = new gm.WmsLayer("Blue Marble", map, layerConfig, tileConfig);
 +
wmsLayer.setMaxBounds(new gm.Bbox(-180, -90, 360, 360));
 +
map.getLayersModel().addLayer(wmsLayer);
 +
}
 +
</script>
 +
</syntaxhighlight>
 +
 +
A térkép objektumhoz hozzárendelhetők események: az inicializációhoz, a térkép megszűnéséhez, vagy a térkép betöltéséhez.
 +
 +
<syntaxhighlight lang="javascript">
 +
    //Maphez hozzárendelhető események
 +
    map.getEventBus().addLayerAddedHandler(function(event) {
 +
        // custom code; event contains the added layer
 +
        alert('layer added: ' + event.getAddedLayer().getTitle());
 +
    });
 +
    map.getEventBus().addLayerRemovedHandler(function(event) {
 +
        // custom code; event contains the removed layer
 +
alert('layer removed: ' + event.getRemovedLayer().getTitle());
 +
    });
 +
    map.getEventBus().addMapInitializationHandler(function(event) {
 +
        // custom code
 +
alert('map fully initialized');
 +
    });
 +
</syntaxhighlight>
 +
 +
A térkép objektumhoz hozzárendelhetők eseménykezelők, ezek közül az előre definiáltak:
 +
*<code>'navigation'</code>-el elérhető a NavigationController,
 +
*<code>'zoomToRectangle'</code>-el elérhető a ZoomToRectangleController,
 +
*<code>'featureSelectionDrag'</code>-al a FeatureSelectionController érhető el a <code>SelectionMethod.CLICK_AND_DRAG</code>-el együtt,
 +
*<code>'featureSelectionSingle'</code>-el elérhető a FeatureSelectionController a <code>SelectionMethod.SINGLE_SELECTION</code>-el.
 +
 +
Hozzáadni az alábbi módon tudunk:
 +
 +
<syntaxhighlight lang="javascript">
 +
map.setMapController(gm.MapControllerFactory.createMapController("default_kontroller_neve"));
 +
</syntaxhighlight> 
 +
 +
Van lehetőség saját <code>MapController</code> létrehozására is:
 +
 +
<syntaxhighlight lang="javascript">
 +
// Üres kontroller létrehozása
 +
var customMapController = gm.MapControllerFactory.createMapController();
 +
 +
// Egyedi egérmozgás kezelő:
 +
customMapController.setMouseMoveHandler(function(event) {
 +
 
 +
  var screenLocation = mapController.getLocation(event, "screen");
 +
  var worldLocation = mapController.getLocation(event, "world");
 +
  var screenLocationAsText = "Screen: " + screenLocation.getX() + ", " + screenLocation.getY();
 +
  var worldLocationAsText = "World: " + worldLocation.getX() + ", " + worldLocation.getY();
 +
 
 +
});
 +
// Egyéb események, amelyek:
 +
//setMouseOverHandler
 +
//setMouseOutHandler
 +
//setDownHandler
 +
//setUpHandler
 +
//setDragHandler
 +
//setDoubleClickHandler
 +
 +
// Kontrollerre köthető események (activation, deactivation):
 +
customMapController.setActivationHandler(function() {
 +
 
 +
  alert('Custom controller activated!')
 +
});
 +
//setDeactivationHandler
 +
 +
// MapController hozzáadása a térképhez:
 +
map.setMapController(customMapController);
 +
</syntaxhighlight>
 +
 +
A layerekhez különböző funkciók társíthatóak, amelyek a <code>FeatureSearchService</code> segítségével érhetőek el.
 +
<syntaxhighlight lang="javascript">
 +
var service = map.getFeatureSearchService();
 +
// A layer egy specifikus műveletének lekérése
 +
service.searchById(layer, [id], function(featureHolder){
 +
  var feature = featureHolder.getFeatures()[0];
 +
  alert("Feature found: " + feature.getLabel());
 +
});
 +
// összes művelet megkeresése
 +
service.searchInBounds(layer, bounds, function(features) {
 +
  alert("Features found: " + featureHolder.getFeatures().size());
 +
});
 +
</syntaxhighlight>
 +
 +
==A Geomajas és a Geosparc==
 +
 +
A geomajas mostanra a geosparc (http://www.geosparc.com/) alapja lett, amivel egyedi webes GIS alkalmazásokat készítenek.
  
 
==Hibák==
 
==Hibák==
A Quickstart application, mavenes fordítása és futtatása probléma nélkül megtörtént, azonban a client-gwt2, és a szerveroldali projekt fordítása korántsem volt zökkenőmentes.
+
A Quickstart application mavenes fordítása és futtatása probléma nélkül megtörtént, azonban a client-gwt2, és a szerveroldali projekt fordítása korántsem volt zökkenőmentes. A javascriptes projekt a javadoc generálásánál elszáll, egyenként az api és a gwt2 projekt rész lefordítható.
  
 
===Checkstyle===
 
===Checkstyle===
70. sor: 321. sor:
 
===FirefoxBinary===
 
===FirefoxBinary===
 
A kliensoldali kód azonban így sem fordult le, a tesztek futtatásakor a selenium (http://www.seleniumhq.org/) plugin nem tudta elérni a FirefoxBinary-t és nem tudta lefuttatni a hozzá tartozó részeket. Ezt orvosolta a <code> mvn install -DskipTests </code> utasítás, ami a teszteket kihagyva képes sikeres fordítást produkálni.
 
A kliensoldali kód azonban így sem fordult le, a tesztek futtatásakor a selenium (http://www.seleniumhq.org/) plugin nem tudta elérni a FirefoxBinary-t és nem tudta lefuttatni a hozzá tartozó részeket. Ezt orvosolta a <code> mvn install -DskipTests </code> utasítás, ami a teszteket kihagyva képes sikeres fordítást produkálni.
 +
 +
===Javadoc===
 +
Valamilyen oknál fogva a java, a régebbi javadocos kódrészleteket nem tudja értelmezni a maven a projekteken belül és fordításnál szintaktikai hibát eredményez. A projektek szükséges részei ettől függetlenül egyenként is lefordíthatóak kisebb-nagyobb szerencsével.
 +
 +
===JettyRunner és az eclipse===
 +
A geomajas fejlesztői oldalán leírtak alapján, a GWT kliens és szerver oldal, az eclipse segítségével is fordítható lett volna, azonban a GWT pluginnal felkonfigurált eclipse a projekt betöltését követően a JettyRunner jetty szerverkonfiguráció futtatásánál elszáll. Az eclipse marketplace-ről letölthető GWT-s pluginnal és az eredeti GWT-s toollal sem sikerült a JettyRunner hibamentes konfigurációja, amely futtatásánál a SpringFramewörk valamiyel nhiányzó layerre panaszkodik.
 +
 +
A fent említett paraméterekkel az eclipse neon.1, neon.2 és a neon.3, illetve a kepler-es verzión is kipróbálva ugyanez a hiba jelentkezett. A kísérlet, minek során a quickstart application fordítására tettem kísérletet, az eclipse a maven-es pom.xml-ben hibát talált, valószínűleg kompatibilitási okokból. 
 +
 +
===Maven-es mintaprojekt===
 +
A források között található régebbi dokumentációk egyikében hivatkozás van egy alap geomajas projekt létrehozására külső url-ről a maven segítségével, azonban a funkció már nem elérhető.
 +
 +
===Eltérések a dokumentációktól===
 +
Sok helyen a dokumentációban hivatkozott komponensek nem léteznek, vagy nem elérhetőek. Legjobb példa a Javascriptes kliens kód, amely a dokumentáció alapján letölthető lenne a http://dev.geomajas.org/geomajas-project-javascript-gwt2-distribution-1.0.0-SNAPSHOT/ URL-ről. Ez tartalmazna egy gs.js-t és egy index.html-t. A githubról letölthető Javascript Client (https://github.com/geomajas/geomajas-project-javascript) distribution része ugyanezt a kódot generálja, vagy csak egy részét, ami működik azonban a dokumentációban említett elméletben geomajas javascript fájl helyett egy dinamikusan generált javascript keletkezik. Ez elindításkor nehezményezi a gs.js hiányát, azonban a dokumentációban szereplő példa részletek futtathatóak.
 +
 +
==Forrás, Linkek==
 +
 +
A wiki oldal megírásához az alábbi oldalakat használtam:
 +
* http://www.geomajas.org/geomajas - Geomajas weboldal.
 +
* https://github.com/geomajas - Geomajas Github.
 +
* http://www.geomajas.org/client-javascript/snapshot - Javascript Snapshot és kis konfigurálással működőképessé varázsolható dokumentáció.
 +
* http://files.geomajas.org/maven/ - A régebbi Geomajas dokumentációk.
 +
* http://mapservercloud.com:8080/geomajas/applications/tutorial/html/ - Működő bemutató a komponensekről.
 +
* http://files.geomajas.org/documentation/geomajas-project-server/snapshot/geomajas-server-documenatation/html/master.html - Geomajas szerver oldal dokumentációja.
 +
 +
* http://www.gwtproject.org/ - GWT oldala a pluginekkel.
 +
* https://maven.apache.org/ - Maven oldala, az utasításokkal, pom.xml felépítésével...
 +
* http://www.geosparc.com/ - Geosparc hivatalos oldala
 +
* Előadás és JS minta - [[Fájl:geomajas_JMDTDY.zip]]

A lap jelenlegi, 2017. május 25., 10:33-kori változata

Áttekintés

A Geomajas (http://www.geomajas.org/) egy Java nyelven íródott Open Source Web Mapping Framework. Mind szerver, mind kliensoldali komponenseket kínál. Szerveroldalon Java, Javascript, míg kliensoldalon Java (GWT) és Javascript nyelven tesz megvalósításokat elérhetővé.

A kliensoldal bár használható különálló módon, igazi hatékonyság és funkcióbőség a szerver és a kliens összehangolt működésével érhető el.

Főbb funkciók:

  • Integrált szerver-kliens architektúra (Spring)
  • Attribútum és geometria szerkesztés
  • Egyedi attribútumdefiníció
  • Magas szintű query lehetőségek (CQL)
  • Bővíthető plugin mechanizmus
  • Átfogó böngésző támogatás
  • Biztonság (Token)

Előkészületek

A mintaprogramok fordításához és futtatásához Maven-t használnak (https://maven.apache.org/). Ahhoz, hogy futtatni és fordítani tudjuk a github-os projekteket, a Maven telepítési könyvtárában a settings.xml-ben (HOME/.m2/settings.xml) fel kell venni néhány dependency-t, ez az xml a github-os gwt kliensnél megtalálható. (https://github.com/geomajas/geomajas-project-client-gwt2)

Kezdésként a GWT-s standalone applicationt indítsuk el, ez a következő linken érhető el. (https://github.com/geomajas/geomajas-gwt2-quickstart-application) Fordítsuk le a Mavennel a projektet, majd futtassuk.

mvn install

mvn jetty: run

Vagy egyben mvn install jetty: run

Ezután látogassunk el a http://localhost:8080/-ra, ahol megtekinthető az eredmény.

Szerver oldal

A Geomajas szerveroldali rész felelős az alkalmazás intergálhatóságért, célja a business logika integrálása GIS eszközökbe. A szerveroldalon konfigurálhatóak a layerek, térképek és a szolgáltatások. Bár sok megjelenítéssel kapcsolatos technológát (vektorgrafika, renderelés) tartalmaz, konkrét megjelenítéssel nem foglalkozik. A szolgáltatások dependecy injection-nel vannak összekötve, továbbá az inversion of controlnak hála a szerver nagyon rugalmas.

A Geomajas szerver oldal a Spring framework-öt használja. A szerver az alábbiakat nyújtja:

  • Utasítások: Az utasítások elsődleges interakciós pontok a szerver és a kliens között. A szolgáltatások, térképek, layerek megkeresése mind utasításokkal történit.
  • Layerek: Ezek tárolják a térképek tulajdonságaihoz a hozzáférési pontokat. Egy Layer lehet raszteres, vagy vektor alapú és szerkeszthető. A vektor layer részeit képező objektumok a "feature object"-ként érhetőek el, amit a Geomajas átkovertál számára használható formátumba, ezáltal nem kell interfaceket implementálni, minden POJO-k segítségével megoldható. Ezek a "feature"-ök tartalmazzák a geometriát és az attribútumok halmazát. Az attribútumok lehetnek komplexek: @OneToMany és @ManyToOne is.
  • Pipeline-ok: Minden layerekkel foglalkozó Geomajas rész pipeline-okkal van megoldva. A pipeline-ok utasítások sorozatai, amelyek sorrendben hajtódnak végre. Habár minden layernek van default pipeline működése, ez felülírható.
  • Biztonság: A biztonsági szolgáltatások közé tartozik a tokenes authentikáció, illetve a tokenhez tartozó hozzáféréssel elérhető objektumok megkeresése és visszatérése. Képes továbbá saját security ppolicy olvasására is.

A vektor layer és az InternalFeautre objektumai közötti konverziót a FeatureModel mondja meg. Ez a következőképpen néz ki:

@Api(allMethods = true)
@UserImplemented
public interface FeatureModel {

 void setLayerInfo(VectorLayerInfo vectorLayerInfo) throws LayerException;

 Attribute getAttribute(Object feature, String name) throws LayerException;

 Map<String, Attribute> getAttributes(Object feature) throws LayerException;

 String getId(Object feature) throws LayerException;

 Geometry getGeometry(Object feature) throws LayerException;

 void setAttributes(Object feature, java.util.Map<String, Attribute> attributes) throws LayerException;

 void setGeometry(Object feature, Geometry geometry) throws LayerException;

 Object newInstance() throws LayerException;

 Object newInstance(String id) throws LayerException;

 int getSrid() throws LayerException;

 String getGeometryAttributeName() throws LayerException;

 boolean canHandle(Object feature);
}

A Geomajas POJO alapú, így nem vár komplex attribútumokat paraméterként, minden sima Java Object. Ez jól látható a vektor layer megvalósításában, amely az alábbi módon néz ki:

@Api(allMethods = true)
@UserImplemented
public interface VectorLayer extends Layer<VectorLayerInfo> {

 boolean isCreateCapable();

 boolean isUpdateCapable();

 boolean isDeleteCapable();

 FeatureModel getFeatureModel();

 Object create(Object feature) throws LayerException;

 Object saveOrUpdate(Object feature) throws LayerException;

 Object read(String featureId) throws LayerException;

 void delete(String featureId) throws LayerException;

 Iterator<?> getElements(Filter filter, int offset, int maxResultSize) throws LayerException;

 Envelope getBounds(Filter filter) throws LayerException;

 Envelope getBounds() throws LayerException;
}

Sok esetben az információ eljuttatása a layer objektumokhoz szabályokat követ:

  • Ha egy primitív attribútum változik meg egy új értékre, akkor ez az új érték lecseréli a régit.
  • Bármely attribútum értéke ha null-ra állítódik, törlődik.
  • Ha egy @ManyToOne attribútum értéke módosul egy létező objektumra, akkor az érték lecserélődik és az állapota frissül.
  • Ha egy @OneToMany attribútum értéke null-ra, vagy üresre módosul, üres kollekció lesz belőle. Az így árván maradt objektumok törléséről, vagy megmaradásáról a FeatureModel-ben lehet nyilatkozni.
  • Ha egy @OneToMany attribútum értéke egy új attribútum csoportra változik, akkor a meglévő attribútumok frissülnek, új attribútumok jönnek létre, a hiányzó értékekről pedig a FeatureModel-ben implementált viselkedés alkalmazódik.
  • Az előző szabályok rekurzívan alkalmazhatóak.

Ezen szabályok betartására példa az Entity interface, amely az objektum-fák gráf összeillesztésénél játszik szerepet, ugyanis az összeillesztés megvalósítása nem a FeatureModel-ben található, hanem az EntityAttributeService-ben, amely feltételezi, hogy minden entitás megvalósítja a már említett interface-t.

@Api(allMethods = true)
@UserImplemented
public interface Entity {

 Object getId(String name) throws LayerException;

 Entity getChild(String name) throws LayerException;

 void setChild(String name, Entity entity) throws LayerException;

 EntityCollection getChildCollection(String name) throws LayerException;

 void setAttribute(String name, Object value) throws LayerException;

 Object getAttribute(String name) throws LayerException;

}

Egy gyökérentitástól kezdve a @OneToMany és @ManyToOne kapcsolatok bejárhatóak a getChild() metódus segítségével.

A GWT quickstart application

A GWT-s projekt általában egy gyökér könyvtárbeli szerver és kliens oldali csomagból áll. A kliens oldali belépési pontot az Application.java fájl tartalmazza, ami a következő helyen található: ..\geomajas-gwt2-quickstart-application-master\src\main\java\org\geomajas\quickstart\gwt2\client.

Ez megvalósítja a com.google.gwt.core.client.EntryPoint interfészt, így implementálni kell az onModuleLoad metódust. A kód egyszerű: létrejön egy ApplicationLayout, majd hozzáadódik a megjelenítéshez.

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootLayoutPanel;

public class Application implements EntryPoint {

	@Override
	public void onModuleLoad() {
		ApplicationLayout layout = new ApplicationLayout();
		RootLayoutPanel.get().add(layout);
	}
}

Az ApplicationLayout geomajas specifikus layout, a következő módon keletkezik:

public ApplicationLayout() {
		initWidget(UIBINDER.createAndBindUi(this));
		ApplicationResource.INSTANCE.css().ensureInjected();

		mapPresenter = GeomajasImpl.getInstance().createMapPresenter();
		mapPresenter.getEventBus().addMapInitializationHandler(new MyMapInitializationHandler());

		GeomajasServerExtension.getInstance().initializeMap(mapPresenter, "app", "mapMain");

		mapLayoutPanel = new MapLayoutPanel();
		mapLayoutPanel.setPresenter(mapPresenter);
		mapPanel.add(mapLayoutPanel);

		appService = ApplicationService.getInstance();
		appService.setMapPresenter(mapPresenter);
		appService.setMapLayoutPanel(mapLayoutPanel);

		appService.getMapPresenter().getEventBus().addHandler(FeatureMouseOverHandler.TYPE, new MyFeatureMouseOverHandler());

	}

Miután betöltődik a kontextusthoz szükséges GWT-s modul, létrejön egy MapPresenter, majd hozzá egy default inicializáló. Ezután a szerverkódból inicializáljuk ezt. Ezt követi a Layout beállítása, hozzáadása a megjelenítéshez, valamint egy ApplicationService létrehozása, amely kiszolgálja a ezt a Layert. Ezután hozzáadásra kerül egy egyedi eseménykezelő.

A quickstart application csak a gwt-s kliens egy részét tartalmazza, ezért használja a GeomajasServerExtension-t, a szerveroldal létező példánya helyett, viszont jó kiindulási pont a framework megismeréséhez.

Javascript API

A Client Javascript API a Client GWT-t használja. A Javascriptes projektnél a javadoc-os résznél elakad a maven, így csak az API és a gwt2 projekt fordítható le egyenként. A gwt2 projekt tartalma egy javascript api-t használó mintaalkalmazás, így elég csak ezt lefordítani. A dokumentációban említett gs.js fájlt azonban már nem generálja le, csak egy dinamikus azonosítóval ellátott js-t.

A Githubról a Javascript project (https://github.com/geomajas/geomajas-project-javascript) letöltése után, lefordíthatjuk a gwt2-es projektet, ami legenerálja a szükséges javascript fájlokat és létrehozza a megfelelő index.html-eket, mind szerver mind kliens oldalon. Az eredmény ideiglenesen a http://people.inf.elte.hu/bagtaai címen elérhető. Eredeti oldal, ami néha nem megy: (http://dev.geomajas.org/geomajas-project-javascript-gwt2-distribution-1.0.0-SNAPSHOT/).

A map betöltéséhez ez a modul egy onGeomajasLoad függvényt használ, ami lényegében az inicializációt hajtja végre. Az alábbi kódrészlet mutatja a legenerált index.html-ben szereplő példát.

<script type="text/javascript">
	var map;
	var WMS_BASE_URL = "http://apps.geomajas.org/geoserver/demo_world/ows";
	var wmsVersion = "1.3.0"; //default value


	function onGeomajasLoad() {
		var mapConfig = new gm.MapConfiguration();
		mapConfig.setCrs("EPSG:4326", "DEGREES");
		mapConfig.setMinimumResolution(2.1457672119140625E-5)
		mapConfig.setMaxBounds(new gm.Bbox(-180, -90, 360, 180));

		// Map létrehozása és inicializálása
			
		map = new gm.Map("js-map-element", mapConfig);

		// Kiürítjük a map-et
		map.getLayersModel().clear();
		
		var tileConfig = new gm.layer.TileConfiguration(256, 256, new gm.Coordinate(-180, -90),
				map.getViewPort());
		var layerConfig = new gm.WmsLayerConfiguration();

		layerConfig.setBaseUrl(WMS_BASE_URL);
		layerConfig.setFormat("image/png");
		layerConfig.setVersion(wmsVersion);
		layerConfig.setLayers("demo_world:simplified_country_borders");
		layerConfig.setMinimumResolution(2.1457672119140625E-5);
		layerConfig.setMaximumResolution(Number.MAX_VALUE);

		// Létrehozunk egy WMS Layert és hozzáadjuk a maphez:

		var wmsLayer = new gm.WmsLayer("Blue Marble", map, layerConfig, tileConfig);
		wmsLayer.setMaxBounds(new gm.Bbox(-180, -90, 360, 360));
		map.getLayersModel().addLayer(wmsLayer);		
	}
</script>

A térkép objektumhoz hozzárendelhetők események: az inicializációhoz, a térkép megszűnéséhez, vagy a térkép betöltéséhez.

    //Maphez hozzárendelhető események
    map.getEventBus().addLayerAddedHandler(function(event) {
        // custom code; event contains the added layer
        alert('layer added: ' + event.getAddedLayer().getTitle());
    });
    map.getEventBus().addLayerRemovedHandler(function(event) {
        // custom code; event contains the removed layer
	alert('layer removed: ' + event.getRemovedLayer().getTitle());
    });
    map.getEventBus().addMapInitializationHandler(function(event) {
        // custom code
	alert('map fully initialized');
    });

A térkép objektumhoz hozzárendelhetők eseménykezelők, ezek közül az előre definiáltak:

  • 'navigation'-el elérhető a NavigationController,
  • 'zoomToRectangle'-el elérhető a ZoomToRectangleController,
  • 'featureSelectionDrag'-al a FeatureSelectionController érhető el a SelectionMethod.CLICK_AND_DRAG-el együtt,
  • 'featureSelectionSingle'-el elérhető a FeatureSelectionController a SelectionMethod.SINGLE_SELECTION-el.

Hozzáadni az alábbi módon tudunk:

map.setMapController(gm.MapControllerFactory.createMapController("default_kontroller_neve"));

Van lehetőség saját MapController létrehozására is:

// Üres kontroller létrehozása
var customMapController = gm.MapControllerFactory.createMapController();

// Egyedi egérmozgás kezelő:
customMapController.setMouseMoveHandler(function(event) {
  
   var screenLocation = mapController.getLocation(event, "screen");
   var worldLocation = mapController.getLocation(event, "world");
   var screenLocationAsText = "Screen: " + screenLocation.getX() + ", " + screenLocation.getY();
   var worldLocationAsText = "World: " + worldLocation.getX() + ", " + worldLocation.getY();
   
});
// Egyéb események, amelyek:
//setMouseOverHandler
//setMouseOutHandler
//setDownHandler
//setUpHandler
//setDragHandler
//setDoubleClickHandler

// Kontrollerre köthető események (activation, deactivation):
customMapController.setActivationHandler(function() {
   
   alert('Custom controller activated!')
});
//setDeactivationHandler

// MapController hozzáadása a térképhez:
map.setMapController(customMapController);

A layerekhez különböző funkciók társíthatóak, amelyek a FeatureSearchService segítségével érhetőek el.

var service = map.getFeatureSearchService();
// A layer egy specifikus műveletének lekérése
service.searchById(layer, [id], function(featureHolder){
   var feature = featureHolder.getFeatures()[0];
   alert("Feature found: " + feature.getLabel());
});
// összes művelet megkeresése
service.searchInBounds(layer, bounds, function(features) {
   alert("Features found: " + featureHolder.getFeatures().size());
});

A Geomajas és a Geosparc

A geomajas mostanra a geosparc (http://www.geosparc.com/) alapja lett, amivel egyedi webes GIS alkalmazásokat készítenek.

Hibák

A Quickstart application mavenes fordítása és futtatása probléma nélkül megtörtént, azonban a client-gwt2, és a szerveroldali projekt fordítása korántsem volt zökkenőmentes. A javascriptes projekt a javadoc generálásánál elszáll, egyenként az api és a gwt2 projekt rész lefordítható.

Checkstyle

A checkstyle egy kódformázó plugin, amit a régebbi eclipsekben használtak, azonban az újakban már eleve benne van. Kezdetben a plugin (https://maven.apache.org/plugins/maven-checkstyle-plugin/) verzióját nem találta meg a maven, ezt orvosolta a fejlesztők levelezéséből kiderített client-gwt2 projektben található settings.xml megfelelő helyre történő átmozgatása.

FirefoxBinary

A kliensoldali kód azonban így sem fordult le, a tesztek futtatásakor a selenium (http://www.seleniumhq.org/) plugin nem tudta elérni a FirefoxBinary-t és nem tudta lefuttatni a hozzá tartozó részeket. Ezt orvosolta a mvn install -DskipTests utasítás, ami a teszteket kihagyva képes sikeres fordítást produkálni.

Javadoc

Valamilyen oknál fogva a java, a régebbi javadocos kódrészleteket nem tudja értelmezni a maven a projekteken belül és fordításnál szintaktikai hibát eredményez. A projektek szükséges részei ettől függetlenül egyenként is lefordíthatóak kisebb-nagyobb szerencsével.

JettyRunner és az eclipse

A geomajas fejlesztői oldalán leírtak alapján, a GWT kliens és szerver oldal, az eclipse segítségével is fordítható lett volna, azonban a GWT pluginnal felkonfigurált eclipse a projekt betöltését követően a JettyRunner jetty szerverkonfiguráció futtatásánál elszáll. Az eclipse marketplace-ről letölthető GWT-s pluginnal és az eredeti GWT-s toollal sem sikerült a JettyRunner hibamentes konfigurációja, amely futtatásánál a SpringFramewörk valamiyel nhiányzó layerre panaszkodik.

A fent említett paraméterekkel az eclipse neon.1, neon.2 és a neon.3, illetve a kepler-es verzión is kipróbálva ugyanez a hiba jelentkezett. A kísérlet, minek során a quickstart application fordítására tettem kísérletet, az eclipse a maven-es pom.xml-ben hibát talált, valószínűleg kompatibilitási okokból.

Maven-es mintaprojekt

A források között található régebbi dokumentációk egyikében hivatkozás van egy alap geomajas projekt létrehozására külső url-ről a maven segítségével, azonban a funkció már nem elérhető.

Eltérések a dokumentációktól

Sok helyen a dokumentációban hivatkozott komponensek nem léteznek, vagy nem elérhetőek. Legjobb példa a Javascriptes kliens kód, amely a dokumentáció alapján letölthető lenne a http://dev.geomajas.org/geomajas-project-javascript-gwt2-distribution-1.0.0-SNAPSHOT/ URL-ről. Ez tartalmazna egy gs.js-t és egy index.html-t. A githubról letölthető Javascript Client (https://github.com/geomajas/geomajas-project-javascript) distribution része ugyanezt a kódot generálja, vagy csak egy részét, ami működik azonban a dokumentációban említett elméletben geomajas javascript fájl helyett egy dinamikusan generált javascript keletkezik. Ez elindításkor nehezményezi a gs.js hiányát, azonban a dokumentációban szereplő példa részletek futtathatóak.

Forrás, Linkek

A wiki oldal megírásához az alábbi oldalakat használtam: