c.brunneresri-de-esridist

Vektordaten exportieren und synchronisieren

Blog Post created by c.brunneresri-de-esridist Employee on Mar 31, 2015

Die Produktfamilie der ArcGIS Runtime SDKs ist seit der Version 10.2 offline-fähig. Dabei können Hintergrundkarten und Vektordaten von Diensten exportiert, und im Falle der Vektordaten auch synchronisiert werden. Um diese Funktionen zu veranschaulichen hat Esri Deutschland eine Demoanwendung entwickelt, welche die Offline Funktionalität von Collector for ArcGIS mit dem ArcGIS Runtime SDK for .NET und Java nachbildet. Diese Demo trägt den Namen Collector Light und ist auf GitHub veröffentlicht. Außerdem ist bereits ein Blog dazu erschienen.

In diesem Artikel wird jetzt, am Beispiel der Java Demo, der Ablauf zum Exportieren und Synchronisieren von Vektordaten genauer betrachtet.

Ein paar Vorbereitungen müssen sein

Die Demo verwendet als Datenquelle eine WebMap. Diese wäre aber nicht zwingend nötig. Um Vektordaten exportieren zu können wird lediglich ein FeatureService benötigt. Dieser kann sowohl bei ArcGIS Online, in Portal for ArcGIS oder auf einem eigenen ArcGIS for Server gehostet sein. Dabei ist zu beachten dass im Dienst der Vorgang „Synchronisieren“ aktiviert ist. Am ArcGIS for Server kann dies im ArcGIS Server Manager im jeweiligen Dienst unter Funktionen -> Feature Access gemacht werden.

SyncEnabled_ArcGISServer.png

In ArcGIS Online und Portal for ArcGIS in den Properties des Dienstes.

SyncEnabled_ArcGISOnline.png

Wird diese Funktion aktiviert bietet der Dienst zusätzliche REST-Schnittstellen an. Mit Hilfe der Operation „Create Replica“ können die Daten offline geholte werden. „Synchronize Replica“ ist für die Synchronisation der Daten und mit „Unregister Replica“ können die Daten gelöscht werden. Dazu aber später mehr.

ServiceDirectory_MitSync.png

Jetzt startet endlich die Programmierung

Wir müssen diese REST-Schnittstellen aber nicht direkt implementieren. Diese werden bereits vom ArcGIS Runtime SDK for Java gekapselt, so dass wir hierfür Java-Klassen und Methoden zur Verfügung haben. Die Verwendung zum Exportieren von Vektordaten findet man in der Demo in der Klasse action.CreateOfflineProject. Genauer gesagt in der Methode createGeodatabaseFromService.

Nach einer kleinen Aufbereitung der Daten, was der Verwendung einer WebMap geschuldet ist, beginnt der spannende Teil in Zeile 330 mit dem Erzeugen der GenerateGeodatabaseParameters.

 

//set up the parameters
  GenerateGeodatabaseParameters params = new GenerateGeodatabaseParameters(
       ArrayUtils.toPrimitive(layerIds.toArray(new Integer[layerIds.size()])),
       extent,
       map.getSpatialReference(),
       false,  
       SyncModel.GEODATABASE,
       map.getSpatialReference());

 

Hier zeigt sich, dass man die zu-exportierenden Daten einschränken kann. Zum einen besteht die Möglichkeit einzelne Layer des Diensts zu selektieren. Zum anderen kann ein Extent mitgegeben werden, so dass der Bereich eingeschränkt wird. Auch Anhänge können, müssen aber nicht geladen werden. Zudem ist es wichtig ein gewünschtes Koordinatensystem als Parameter mitzugeben, damit die Daten hinterher auch richtig auf unserer Karte dargestellt werden können. Anschließend werden zwei Callback Klassen benötigt. Eine wird verwendet um Statusinformationen während der Laufzeit entgegen zu nehmen. Die andere wird aufgerufen wenn der Task beendet ist und nimmt schlussendlich das Ergebnis entgegen. Jetzt kommt auch schon die Klasse GeodatabaseSyncTask ins Spiel, die den eigentlichen Aufruf generateGeodatabase macht.

 

// ------------------------------------------------------------------------ 
// Generate the geodatabase from the service and download 
// ------------------------------------------------------------------------ 
GeodatabaseSyncTask gdbTask = new GeodatabaseSyncTask(featureServiceUrl, PortalConnector.getUser(true));
gdbTask.generateGeodatabase(params, pathToGdb, false, statusCallback, gdbResponseCallback);

 

Zum Erzeugen dieser Klasse benötigt man nur die URL zum FeatureService. Falls es sich um einen abgesicherten Dienst handelt, muss man, wie in diesem Beispiel, zudem Credentials mit übergeben. Details zum Thema Autorisierung und Authentifizierung gibt es einem eigenen Blog.

 

Wo sind denn jetzt meine Daten?

Im gdbResponseCallback in diesem Beispiel wird lediglich einem ProjectManager gesagt, dass neue Daten verfügbar sind. Deshalb stellt sich jetzt die Frage: Wo sind denn meine Daten? Die finden wir unter dem Pfad und Namen, der beim Aufruf als Parameter (pathToGdb) übergeben wurde. An den Namen wird noch die Dateiendung .geodatabse angehängt. Die Daten liegen in einer Datenbankstruktur vor, genauer gesagt einem SQLite Format, und können mit jedem gängigen SQLite-Tool betrachtet werden.

Wie kommen die Daten in meine Karte?

Aber wir wollen die Daten ja in unserer Karte betrachten und nicht in irgendeinem anderen Tool. Hierfür bietet das ArcGIS Runtime SDK for Java die Klasse FeatureLayer. Dieser kann mit Hilfe von verschiedenen Datenquellen als Basis erzeugt werden. Das ist das Schöne an dieser Klasse. Unabhängig von der Datenquelle ist das Handling immer das Gleiche. Eine von diesen Datenquellen ist die GeodatabaseFeatureTable. An diese Tables kommen wir, indem wir unsere SQLite Datenbank mit Hilfe der Klasse Geodatabse initialisieren. Für jeden einzelnen Layer wird dabei eine GeodatabseFeatureTable erzeugt.

 

Geodatabase localGeodatabase = new Geodatabase("PathToGeodatabase");
  for (GeodatabaseFeatureTable table : localGeodatabase.getGeodatabaseTables()) {
  FeatureLayer featureLayer = new FeatureLayer(table);
  featureLayer.initializeAsync();
}

 

Ich will wieder nach Hause telefonieren

So, jetzt sind die Daten offline. Wir können uns nun frei, fernab der digitalen Zivilisation, bewegen und Daten erfassen, ändern oder löschen. Irgendwann wird aber der Moment kommen, in dem wir unsere Änderungen mit unseren Kollegen teilen wollen. Außerdem interessiert mich ja vielleicht auch was diese in meiner Abwesenheit getrieben haben. Wir müssen also mit dem Mutterschiff (dem FeatureService) Kontakt aufnehmen. In der Demo passiert dies in der Klasse action.SyncProject. Die Methode syncGeodatabase in der bereits bekannten Klasse GeodatabaseSyncTask hilft uns dabei.

 

gdbTask.syncGeodatabase(syncParams, gdb, statusCallback, syncResponseCallback);

 

Wir sehen wieder die zwei Callbacks für Statusmeldungen und für das Ergebnis. Als weitere Parameter brauchen wir die Geodatabase (gdb) und spezielle SyncGeodatabaseParameters. Die Geodatabase haben wir ja bereits im vorherigen Schritt zur Visualisierung der Daten erzeugt. Und auch wegen der SyncParameter müssen wir uns keine Gedanken machen, denn die werden von der Geodatabase erzeugt.

 

final SyncGeodatabaseParameters syncParams = gdb.getSyncParameters();

 

Das hat den Vorteil dass wir die komplexe Datenbankstruktur nicht kennen müssen, um unsere Änderungen zu extrahieren. Denn übertragen wird immer nur das Delta, nicht die komplette Datenbank. Bei der Synchronisation werden außerdem alle Änderungen am Dienst in meine lokale Datenbank übertragen, so dass ich auf dem aktuellsten Stand weiterarbeiten kann. Hier wird ebenfalls nur das Delta übertragen. Dies ist auch auf der folgenden Grafik erkennbar, die den gesamten Workflow schematisch darstellt.

 

SyncProcess.png

Haben mehrere Kollegen Änderungen an einem Datensatz vorgenommen, so gewinnt immer die letzte Änderung. Wobei dabei der Zeitstempel der Synchronisation und nicht der Zeitstempel der Änderung zählt. Zukünftig soll es auch andere Optionen zum Lösen von Konflikten geben, die dann pro Dienst eingestellt werden können. Aktuell ist das jedoch noch nicht möglich.

Beenden wir das Einsiedlerleben

Irgendwann kehrt man hoffentlich wieder zurück ins Büro und benötigt die lokale Kopie der Daten nicht mehr. Dann ist es wichtig diese nicht einfach zu löschen, sondern vorher einen Unregister am Dienst zu machen. Natürlich steht uns hierfür wieder die Klasse GeodatabaseSyncTask zur Verfügung, diesmal mit der Methode unregisterGeodatabase.

 

gdbTask.unregisterGeodatabase(gdb, unregisterCallback);

 

In der Demo findet man diesen Aufruf in der Klasse action.DeleteProject. Aber warum ist dieser Aufruf so wichtig? Der FeatureService erzeugt für jede Kopie ein sogenanntes Replica. Darin sind Informationen zur Kopie abgelegt, wie z.B. der Zeitpunkt der letzten Synchronisation. Diese Daten werden benötigt um den Synchronisationsmechanismus bewerkstelligen zu können. Beim Unregister wird dieses Replica gelöscht, da der Dienst dann weiß dass die lokale Kopie nicht mehr benötigt wird. Die Replicas eines Dienstes kann man sich auch in dessen Service Directory ansehen.

 

Replicas.png

Outcomes