Navigation überspringen
All Places > GeoDev Germany > Blog
1 2 3 Zurück Weiter

GeoDev Germany

236 Einträge

As I've presented a way to create your own widget for the Web Appbuilder in my last post I would like to explain the options for multi language support in your widget.

NLS

The strings for our last widgets are more or less hard coded in the Setting.html and Widget.html file at the moment. If you want to replace these string with the language set inside the browser or portal you need to label the strings and create a library of strings. This is done for the Setting.html and for the Widget.html at different places inside the widget structure.

Setting.html

The setting folder contains a folder called nls. Inside you create a folder for each language you would like to support and an initial strings.js file with the strings that should be used in the settings of the widget by default. The strings.js file consists of the actual default strings and an indicator, whether or not a single language (we only support "de" in our example) is supported:

define({
  root: ({
    ZoomSetCheckboxText: "Show Zoom Level",
    ScaleSetCheckboxText: "Show Scale",
       configText: "Set Configuration:"
  }),
  "de": 1,
  "es": 0
});

You might have noticed that we use a variable called "ZoomSetCheckboxText" and "ScaleSetCheckboxText" and define the content of the variable with some basic strings. These strings should be used, wherever the variables are used. We place the variables inside the Setting.html file. This looks now a little bit different as the nomenclature looks a bit weird:

  <div>
    <div class="title">${nls.configText}</div>
    <div class="row">
      <input type="checkbox" id="ZoomSetCheckbox" data-dojo-attach-point="ZoomSetCheckbox"></input>
      <p style="display:inline">${nls.ZoomSetCheckboxText}</p>
    </div>
    <div class="row">
      <input type="checkbox" id="ScaleSetCheckbox" data-dojo-attach-point="ScaleSetCheckbox"></input>
      <p style="display:inline">${nls.ScaleSetCheckboxText}</p>
    </div>
  </div>

All strings are defined with a "${nls.XXX}" schema. This allows  of the strings and the automatic translation according your definition in the strings.js file.
Off course we also need to define a translation in German in identificationour case. This is done inside the "de" subfolder in the nls folder. We place a second strings.js there which holds the German strings:

define({
  "ZoomSetCheckboxText": "Zeige Zoomlevel",
  "ScaleSetCheckboxText": "Zeige Maßstab",
  "configText": "Einstellung",
});

Please not the different usage of string indicators compared to the default strings.js file in the parent folder.

The Widget.html

The widget itself works similar. Inside the Widget folder you create a nls folder with subfolders regarding the desired languages you would like to support. A list of languages which are supported, can be seen here:

Native Language Support

Once again we substitute the strings used in the original file with the "${nls.XXX}" schema:

<div class="ZoomLeveLInfo">
     <div data-dojo-attach-point="zoomInfoDiv">${nls.label1} <p style="display: inline;" data-dojo-attach-point="MapZoomInfo"></p></div>
     <div data-dojo-attach-point="scaleInfoDiv">${nls.label2} <p style="display: inline;" data-dojo-attach-point="scaleInfo"></p></div>
</div>

In our strings.js file we set the default strings:

define({
  root: ({
    _widgetLabel: "Zoom Level Info"
    label1: "Zoom Level:",
       label2: "Approx. Scale:",
  }),
  "de": 1
});

In the "de" subfolder I use a similar pattern as describe in the chapter above:

define({
  "_widgetLabel": "Zoomstufeninfo",
  "label1": "Zoom Stufe:",
  "label2": "ungef. Maßstab:"
});

The Result

The widget is now able to start in English as well as in German depending on the settings in the Portal:

English language supportGerman language support

Working with a GIS results sometimes in quite advanced workflows, models and so on. The next step might be: How to get these tools shared so other parties and people can work with them? There are multiple approaches to do so:

  • publish Geoprocessing as a Web Tool.
  • ArcGIS JavaScript API
  • Widgets for the Web AppBuilder

In this blog post I would like to describe the last approach and create a customizable Widget for the Web AppBuilder.

Web AppBuilder Dev Edition

You might ask yourself, why do I need the Web AppBuilder Dev Edition (short: WAB)? As we will create a widget is it always good to test the solution prior publishing it as a custom application on AGOL. With the Web AppBuilder Dev Edition we can use template widgets and test the widgets local on a server (or a resource that has a pretty domain...)

WAB runs on Node.js but is only available for Windows at the moment.

Once it is unzipped and configured with your ArcGIS account (connect with Portal for ArcGIS, create an App on your Portal, register the App with your Portal) you're ready to go:

connect WAB with the portal of your choice

the registration settings of your widget in the portal of your choice

Follow the steps defined at the Get started section.

The Widgets Purpose

For this introduction I would like to create a widget that shows the scale as well as the current zoom level of a map. Of course it is somewhat an enhancement of the standard Scalebar Widget. Therefore I will first show the standalone way and later on describe the enhancement of the default Scalebar widget to do the same: Show not only the Scalebar but also show the zoom level as well as the scale of the underlying map.

A Widget's Structure

A Widget itself consists of a several files that are described below:

  • The JavaScript file that defines the widget function (Widget.js)
  • The template file that defines the widget’s user interface(Widget.html)
  • The widget’s configuration file (config.json)
  • The widget's manifest file which is needed for publishing(manifest.json)
  • The widget’s i18n strings file for internationalization of strings if needed (nls/strings.js)
  • The widget’s style file if you're a fancy guy and like border-rounding, background-images, sliders and circles (css/style.css)

In the end the structure of your widget should look something like this:

As you may have noticed the structure also contains a folder called setting. As we want to customize the behavior of our widget we need to define some settings and interact with them.

The Code

Let's get through the files. We will let the user define, whether or not he/she would like to see the scale, the zoom level or both. This is done in the setting.html

The Setting.html / Setting.js

Therefore we will show two Checkboxes. As I like it plain Vanilla (and didn't got the dijit checkboxes to work  ;-) ) we use HTML input checkboxes:

<div>
  <div class="title">Zoom Level Info</div>
  <div class="row">
    <input type="checkbox" id="ZoomSetCheckbox"></input>
    <p style="display:inline">Show Zoom Info</p>
  </div>
  <div class="row">
    <input type="checkbox" id="ScaleSetCheckbox"></input>
    <p style="display:inline">Show Approx. Scale</p>
  </div>
</div>

But as this is a widget and we would like to store the default settings in a configuration file called config.json we need to connect the setting.html with the configuration file using the settings.js file.

Therefore we will not only use ids of the buttons (each widget could have items with identical ids...) but also data connectors:

<div>
  <div class="title">Zoom Level Info</div>
  <div class="row">
    <input type="checkbox" id="ZoomSetCheckbox" data-dojo-attach-point="ZoomSetCheckbox"></input>
    <p style="display:inline">Show Zoom Info</p>
  </div>
  <div class="row">
    <input type="checkbox" id="ScaleSetCheckbox" data-dojo-attach-point="ScaleSetCheckbox"></input>
    <p style="display:inline">Show Approx. Scale</p>
  </div>
</div>

Now we can communicate with the sates of the buttons.

The setting.js file has two main functions: reading and writing the settings to the config.json file so the widget uses always the correct settings in a new app.

The settings in the config.json file are to set as follow:

{
     "settings": {
          "ZoomSetCheckbox": true,
          "ScaleSetCheckbox": true
     }
}

As visible, the default state of the widget isto enable both checkboxes. The states of the config can be set and read using two functions: getConfig and setConfig (download the zip at the end of the post to get the whole script):

define(['dojo/_base/declare','jimu/BaseWidgetSetting'],
function(declare, BaseWidgetSetting) {
     return declare([BaseWidgetSetting], {
          startup: function(){
               this.inherited(arguments);
               console.log(this.config);
               this.setConfig(this.config);
          },
          setConfig: function(config){
               this.config = config;
               this.ZoomSetCheckbox.checked = config.settings.ZoomSetCheckbox;
               this.ScaleSetCheckbox.checked = config.settings.ScaleSetCheckbox;
               console.log("settings read from file: ");
               console.log(config);
          },
          getConfig: function(){
               settings = this.config.settings;
               if (this.ZoomSetCheckbox) {
                    settings.ZoomSetCheckbox = this.ZoomSetCheckbox.checked;
               }
               if (this.ScaleSetCheckbox) {
                    settings.ScaleSetCheckbox = this.ScaleSetCheckbox.checked;
               }
               this.config.settings = settings;
               console.log("settings write to file: " + settings);
               console.log(this.config);
               return this.config;
          }
     });
});

This reads the states of the button (this.XXXSetCheckbox) and writes it in the config for each app it is used in and reads the configuration according the used settings.

The Widget.html / Widget.js

As we covered the settings in the paragraph above let's concentrate on the main code in the Widget.html and Widget.js.

We are only showing two lines in the widget: One line for the Zoom Level, one for the Scale. Therefore the Widget.html file is very short and consists of 2 lines:

<div class="ZoomLeveLInfo">
     <div data-dojo-attach-point="zoomInfoDiv">Zoom Level: <p style="display: inline;" data-dojo-attach-point="MapZoomInfo"></p></div>
     <div data-dojo-attach-point="scaleInfoDiv">Approx. Scale: <p style="display: inline;" data-dojo-attach-point="scaleInfo"></p></div>
</div>

You can see a similar approach as in the Setting.html: We use data-dojo-attach-points to access the div element and alter the content of this div. We could do this inline but we stick to the structure and define the JavaScript part in the Widget.js.

define([
    'dojo/_base/declare',
    'dojo/_base/lang',
    'jimu/BaseWidget',
    'dojo/on', //as we alter the div when the map is panned "on.extent-change"
    'esri/map' //as we interact with the map
],
  function(declare, lang, BaseWidget, on, Map) {
     return declare([BaseWidget], {
          baseClass: 'jimu-widget-zoomlevelinfo',
          postCreate: function() {
               this.inherited(arguments);
               this.own(on(this.map, 'extent-change', lang.hitch(this, this._zoomInfo)));
               this._zoomInfo();
          },
          startup: function() {
               this.inherited(arguments);
          },
          _zoomInfo: function(){
               scale = esri.geometry.getScale(this.map);
               var json = this.config; //read the config
               if (json.settings.ZoomSetCheckbox){ //show Zoom Info set to true
                    this.MapZoomInfo.innerHTML = this.map.getZoom(); //alter the inner  HTML with the Zoom Info
               } else {
                    dojo.destroy(this.zoomInfoDiv); //get rid of the div so it looks clean if not wanted
               }
               if (json.settings.ScaleSetCheckbox){
                    this.scaleInfo.innerHTML = "1:" + Math.round(this.map.getScale()/1000)*1000 ;
               } else {
                    dojo.destroy(this.scaleInfoDiv);
               }
          }
     });
});

The Manifest.json

To implement the new custom app in a web app we need to create a file called manifest.json. This file holds the basic information about the name and main settings:

{
  "name": "ZoomLevelInfo",
  "platform": "HTML",
  "version": "2.8",
  "wabVersion": "2.8",
  "author": "Esri Deutschland GmbH // Riccardo Klinger",
  "description": "This is a widget to show scale and zoom level of the underlying map.",
  "copyright": "",
  "license": "http://www.apache.org/licenses/LICENSE-2.0",
  "properties": {
    "inPanel": false, //as it is off-panel
    "hasUIFile": true, // we do have a settings ui file
    "supportMultiInstance": false´// we just allow one instance of the widget.
  }
}

Now we can test the widget.

Testing the Widget

As we created the main widget we should test it in our WAB Developer installation now. Therefore place the whole folder (in our case ZoomLevelInfo) in the client widget folder of the WAB (in my case "C:\WebAppBuilderForArcGIS\client\stemapp\widgets").

For the test, restart the Web AppBuilder and create a new application:

new App in the WAB dev edition

The widget is now part of the Web AppBuilder and can be selected as a widget:

Hosting the Widget

If your tests were successful you can host the widget on a server and implement it in ArcGIS Online or your ArcGIS Enterprise (>10.5) environment as a custom widget. If you own the portal you might also want to add the folder inside the WAB of the portal (in my case "C:\Program Files\ArcGIS\Portal\apps\webappbuilder\stemapp\widgets").

Download the Example

If you want to use the example, use github here. Or you download it directly below.

Erstellt man mit dem Web AppBuilder eine Web-App, so wird die dabei konfigurierte Web Map fest mit der App verdratet. In manchen Anwendungsfällen möchte man aber eventuell eine Web-App auch mit unterschiedlichen Web Maps verwenden können.
Dies ist mit dem Web AppBuilder relativ einfach realisierbar, indem man die ID der gewünschten Web Map als URL-Parameter beim Aufruf der App mit angibt.
Ein leider schlecht dokumentierter URL-Parameter des Web AppBuilders ist der Parameter itemid. Mit diesem gibt man die ID der Web Map an, die in der Webanwendung verwendet werden soll. Diese als URL-Parameter angegebene Web Map überschreibt die in der Konfiguration des Web AppBuilders angegebene Web Map und wird so in der Web-App angezeigt.
Dieses Prinzip funktioniert sowohl beim Web AppBuilder Developer Edition als auch beim im Portal integrierten Web AppBuilder.

 

Ein URL-Aufruf einer mit der Developer-Edition erstellten App könnte dann z.B. so aussehen:

http://<host>/MyApp/index.html?itemid=851bfb6399de45faa7b62b811b7f8979

 

Für eine App im Portal sieht ein URL-Aufruf der App z.B. wie folgt aus:

https://<host>/portal/apps/webappbuilder/index.html?id=b3381f7da4d945a98e25ea9655f33d77&itemid=851bfb6399de45faa7b62b811b7f8979

 

Dabei ist in beiden Fällen 851bfb6399de45faa7b62b811b7f8979 die ID der Web Map, die in der App angezeigt werden soll.

Web AppBuilder for ArcGIS ist eine Anwendung zum Erstellen fokussierter 2D und 3D Web-Apps mithilfe von intuitiven Widgets und hübschen Designs. Mit der Developer Edition können Entwickler die bestehenden Widgets und Designs anpassen und erweitern oder auch vollständig eigene erstellen.

 

Messen in 3D

 

Die neue Version 2.8 der Web AppBuilder for ArcGIS Developer Edition hat nun den gleichen funktionalen Stand wie der eingebettete Web AppBuilder in ArcGIS Online vom April 2018 Release. So sind viele bestehende Widgets aktualisiert wurden und auch einige neue Widgets hinzugekommen, z.B. Messen in 3D. Weiterhin wurde an der Usability der Konfigurationsoberfläche des Builder selbst einiges verbessert und die Doku angepasst.

 

Vollständige Infos dazu stehen im What's New auf dem ArcGIS Developer Portal.

Mit AppStudio for ArcGIS können mithilfe von Templates native Apps für alle gängigen Plattformen erstellt werden. Die Power von ArcGIS Runtime wird dafür voll genutzt. Nun ist eine neue Version mit einigen Neuerungen verfügbar:

 

Highlights sind:

 

  • Unterstützung hochgenauer GPS/GNSS Empfänger
  • Änderungen/Verbesserungen der Esri Templates (z.B. kann die Map Tour komplett offline genommen werden)
  • ArcGIS Runtime wurde auf Version 100.2.1 hochgezogen
  • AppStudio Player wurde verbessert
  • mehr..

 

 

Ausführliche Informationen zu den Neuerungen von AppStudio for ArcGIS Version 3 findet Ihr in diesem News Blog von Esri Inc. und in der AppStudio GeoNet Gruppe.

Es geht Schlag auf Schlag. Erst Anfang März wurde die ArcGIS API for Python 1.4 released, nun gibt es schon wieder ein neues Update. Und das ist nicht klein. Neben Bugfixing wurden viele neue Funktionen hinzugefügt und bestehende verbessert.

 

Python Notebook

Python Notebook

 

Hier einige Highlights:

 

Workflows

 

Datenmanagement

  • Verbesserung des Attachment Managers (Filtern von Attachments, kann mit großen Dateien umgehen)
  • clone_items() Funktion wurde überarbeitet und ist jetzt performanter
  • GroupManager wurde um neue Funktionen erweitert

 

Analsyen

 

Verwaltung von AGOL und ArcGIS Enterprise

  • Funktionen zur Überwachung von Anmeldungen/Anmeldeinformationen

 

Samples

  • Python Samples wurden aktualisiert und es wurden neue hinzugefügt

 

 

Ausführliche Informationen zu allen Neuerungen der ArcGIS API for Python 1.4.1 findet Ihr in diesem News Blog von Esri Inc. und in den Release Notes auf dem ArcGIS Developer Portal.

Ab sofort stehen allen Entwicklern die neuen Versionen der ArcGIS API for JavaScript zur Verfügung. In den Updates der Version 4.7 sowie 3.24 sind neue Features wie z.B. die Unterstützung von 3D Inhalten auf mobilen Geräten, das Rendering von Kanten in 3D, eine neue clientseitige Projection Engine und viele Updates bei vorhandenen Widgets

enthalten. Außerdem wurde in der Version 3.24 das Drucken noch einmal verbessert.

 

Wichtig: bei der Unterstützung von ES Promises gab es einige Breaking Changes. Mehr dazu in der Dokumentation, siehe unten.

 

 

Alle Neuerungen werden in diesem Blog-Post detailliert vorgestellt:

ArcGIS API for JavaScript versions 4.7 and 3.24 released 

 

Mehr dazu zeigen auch die neusten Beispielcodes:

3.24 Latest Samples

4.7 Latest Samples

 

und die Release Notes:

3.24 Release Notes

4.7 Release Notes

 

Zu den Unterschieden zwischen den beiden Versionen 3.24 und 4.7 und dazu, welche Version in welchem Anwendungsfall zu bevorzugen ist, gibt die aktualisierte FAQ einige gute Hinweise: 
ArcGIS API for JavaScript FAQ

 

Happy coding!

ArcGIS ist eine leistungsfähige Plattform für das Entwickeln von Geolösungen. Doch oft fällt der Anfang nicht leicht. Wir haben Euch zugehört und konsequent das ArcGIS Developer Portal unter https://developers.arcgis.com für Euch verbessert. Im Folgenden möchte ich auf einige der Verbesserungen eingehen:

 

Verbesserte Startseite

Zunächst einmal haben wir die Startseite selbst übersichtlicher und vor allem selbsterklärender gemacht. Einsteiger finden hier zunächst eine Übersicht wesentlicher Funktionalitäten wie bspw. Geocoding, Data Visualization oder Spatial Analysis.

 

 

Ein Klick auf eines der Kernfunktionalitäten bringt Euch auf eine Detailseite, auf der ihr Euch weiter einlesen könnt. So werden bspw. die verschiedenen Spielarten von Geocoding erläutert und anhand von Live Samples (JavaScript, eh klar) verdeutlicht.

 

Erfahrene Entwickler profitieren ebenfalls von der verbesserten Startseite, indem sie gleich oben auf per Shortcut zur Programmiersprache ihrer Wahl gelangen:

 

GeoDev Labs

Habt Ihr Euch einen Überblick verschafft und wollt mit der konkreten Arbeit loslegen? Dann schaut unbedingt bei unseren GeoDev Labs vorbei. Hierbei handelt es sich um kurze Übungen, die Euch durch die verschiedenen Aspekte des Entwickelns mit ArcGIS führen. So gibt es bspw. Labs zum Importieren von Daten, Komponieren von Webkarten, Geosuche uvm. Die Labs können auf der linken Seite einfach nach Thema und Zielplattform gefiltert werden.

 

Jedes Lab dauert zwischen 5 und 15 Minuten und führt Euch durch die jeweilige Aufgabenstellung. Nach einem Überblick erhaltet Ihr Schritt-für-Schritt Anleitungen. Auf GitHub findet Ihr den zugehörigen Source Code sowie Testdaten. Außerdem könnt Ihr Live Demos des Ergebnisses anschauen.

 

 

Esri baut die Labs kontinuierlich aus, schon heute gibt es bereits über 100 Labs.

 

Example Apps

Wollt Ihr Euch lieber fertige Apps anschauen, um das ein oder andere abzukupfern? Auch kein Problem! Hier stellen wir euch eine Reihe von Beispielanwendungen bereit. Wie bei den GeoDev Labs erhaltet Ihr umfangreiche Infos und könnt Euch den Source Code herunterladen.

 

Success Stories

Schließlich könnt Ihr Euch auch einfach von "echten" Anwendungen aus der Praxis inspirieren lassen. Schaut hierzu bei den Success Stories vorbei. Diese lassen sich nach Branche filtern. Hier mit Fast@Home mal ein schönes deutsches Beispiel aus der Branche Telekommunkation:

 

 

 

 

 

Wir arbeiten natürlich weiterhin daran, Euch beim Entwickeln mit ArcGIS zu unterstützen. Freut Euch auf weitere Verbesserungen im Laufe des Jahres. Und solltet Ihr einen Verbesserungsvorschlag für uns haben, dann hören wir es natürlich gerne!

Mit ArcGIS Runtime können native Apps für alle gängigen Plattformen erstellt werden. Native Apps sind besonders geeignet für mobile Szenarien für den Außendienst. Da im Feld eine stabile Internetverbindung oftmals nicht garantiert ist, sollten die Maps inklusive aller zugehörigen Daten lokal auf dem Gerät gespeichert sein. Es gibt bisher schon mehrere Möglichkeiten, dies mit ArcGIS Runtime zu tun:

 


Diese Möglichkeiten werden auch in ArcGIS Apps wie Collector, Navigator oder Explorer genutzt. Seit der ArcGIS Runtime Version 100.2 gibt es nun zusätzlich noch den Preplanned Workflow für die durchgängige und automatisierbare Planung von mobilen offline Szenarien.

 

Der Preplanned Workflow


In einem üblichen Außendienstszenario haben mehrere Außendienstmitarbeiter den gleichen Auftrag, aber in verschiedenen Arbeitsgebieten. Ein Beispiel ist die Inspektion von Straßen in einer Stadt, in der verschiedene Mitarbeiter in unterschiedlichen Stadtgebieten unterwegs sind. Ein Mitarbeiter benötigt in der Regel nur die mobilen Daten von seinem Gebiet und nicht die Daten der kompletten Map. Oftmals darf er auch nur die Daten seines eigenen Gebietes sehen.

 

 

Der Preplanned Workflow ist genau für solche Szenarien konzipiert. Er besteht im Wesentlichen aus zwei Teilen – die Planung von Arbeitsgebieten im Büro und die Nutzung der „Teil-Maps“ in ArcGIS Runtime Apps im Außendienst.

 

 

Planung von Arbeitsgebieten im Büro

Voraussetzung ist eine offlinefähige WebMap. Von dieser WebMap wird die Portal Item ID benötigt (die Zeichenfolge in der URL).

 


Dann werden die relevanten Arbeitsgebiete (MapAreas) in der WebMap definiert. Der einfachste Weg ist Bookmarks zu erstellen. Es können aber auch Extents der Gebiete ermittelt und im JSON Format genutzt werden, z.B. so:

 

{
  "xmin": -13184700,
  "ymin": 3988556,
  "xmax": -13004945,
  "ymax": 4061479,
  "spatialReference": {
       "wkid": 102100
}}

 

Mithilfe des neuen Offline Packaging Geoprocessing Service für den Preplanned Workflow können dann folgende Tasks erledigt werden:

 


Für eine WebMap können bis zu 16 MapAreas erstellt werden. Die mobilen Daten der jeweiligen MapAreas werden direkt im offline nutzbaren Format im Portal gespeichert:

 

  • Features als SQLite Datenbank
  • Vector Tile Layer als Vector Tile Packages (.vtpk)
  • Rasterdaten als Tile Packages (.tpk)

 

Die mobilen Daten haben eine Referenz zu der zugehörigen MapArea. Und alle MapAreas haben eine Referenz zu der zugehörigen WebMap. Das bedeutet: wird eine MapArea gelöscht, werden auch die zugehörigen mobilen Daten im Portal gelöscht. Und wird die WebMap gelöscht, sind alle MapAreas plus mobile Daten auch weg.

MapAreas und zugehörige mobile Daten werden nicht in der Standard-Oberfläche des Portals angezeigt. Im Catalog in ArcGIS Pro können diese Items angezeigt oder gelöscht werden. Eine weitere Möglichkeit ist mit dem AGOL Assistant.

 

Wie wird es in der Praxis umgesetzt?

Geoprocessing Services können in Web Apps mit der ArcGIS API for JavaScript und in nativen Apps mit ArcGIS Runtime genutzt werden. Mit beiden wäre es z.B. möglich, Konfigurator-Apps für Planer im Büro zu erstellen. Eine weitere Option ist die Nutzung des Offline Packaging Geoprocessing Service direkt in ArcGIS Desktop. Mein persönlicher Favorit ist allerdings die Implementierung mit der ArcGIS API for Python und dem OfflineMapAreaManager. Mit diesen paar Zeilen Python Code werden MapAreas von allen Bookmarks einer WebMap erzeugt und auch gleich die zugehörigen mobilen Daten generiert:

 

# Import modules
from arcgis.gis import GIS
from arcgis.mapping import WebMap
from getpass import getpass

# Setup the organization and user information
password=getpass()
gis = GIS('https://www.arcgis.com', 'my named user', password)

# get the webmap from the portal item id
offline_map_item = gis.content.get("my webmap id")
offline_webmap = WebMap(offline_map_item)

# Loop through the webmap's bookmarks and create a map area for each one bookmark
for bookmark in offline_webmap.definition.bookmarks:
    bookmark_name = bookmark.name
    item_prop = {'title': offline_map_item.title + '_' + bookmark_name + '_MapArea',
            'snippet': 'my snippet',
            'description': 'my description',
            'tags': ['python api', 'MapArea', 'my tag']}
    offline_area = offline_webmap.offline_areas.create(area=bookmark_name, folder='my portal content folder', item_properties=item_prop)  

 

Für einen schnellen Test kann das Python Notebook im Anhang dieses Blogs auf notebooks.esri.com genutzt werden (Upload).

 

Nutzen von MapAreas in ArcGIS Runtime Apps im Außendienst

Die MapAreas einer WebMap und die zugehörigen mobilen Daten sind nun im Portal erstellt worden. Jetzt müssen sie nur noch mit ArcGIS Runtime heruntergeladen und genutzt werden. Die Preplanned Workflow Funktionen in den SDKs sind erst ab ArcGIS Runtime Version 100.2 verfügbar. Hier sind die groben Schritte für die Implementierung am Beispiel des .NET SDKs:

 

1. Laden der WebMap

var portal = await ArcGISPortal.CreateAsync();
var webmapItem = await PortalItem.CreateAsync(portal, "WebMap Portal ID");
var myMap = new Map(webmapItem);

 

2. Abfragen der MapAreas einer WebMap

var offlineMapTask = await OfflineMapTask.CreateAsync(myMap);
var preplannedMapAreas = await offlineMapTask.GetPreplannedMapAreasAsync();

 

3. Download der Infos und Daten einer MapArea als exploded Mobile Map Package

var myArea = preplannedMapAreas.FirstOrDefault();

var downloadJob = offlineMapTask.DownloadPreplannedOfflineMap(myArea, "Speicherpfad");
var results = await downloadJob.GetResultAsync();

 

4. Laden und Anzeigen der lokal gespeicherten MapArea in der Map

var offlineMapArea = await MobileMapPackage.OpenAsync("Speicherpfad");
myMap = offlineMapArea.Maps[0];

 

Synchronisierung von Edits mit dem Feature Service

Für die Synchronisierung von Feature Edits in beide Richtungen wird die lokale SQLite Geodatabase automatisch durch den OfflineMapTask beim Feature Service  registriert. Darum muss sich der Entwickler nicht kümmern. So wird synchronisiert:

 

var editLayer = (FeatureLayer)myMap.OperationalLayers[0];
var featureTable = (GeodatabaseFeatureTable)editLayer.FeatureTable;

var syncTask = await GeodatabaseSyncTask.CreateAsync(featureTable.Geodatabase.Source);

// sync geodatabase
var taskParameters = await syncTask.CreateDefaultSyncGeodatabaseParametersAsync(featureTable.Geodatabase);

var syncJob = syncTask.SyncGeodatabase(taskParameters, featureTable.Geodatabase);
var result = await syncJob.GetResultAsync();

 

Bevor eine Geodatabase auf dem Gerät gelöscht werden soll, muss sie vorher beim Feature Service auch wieder deregistriert werden (sonst wird er irgendwann mit Unmengen Replicas zugemüllt). Weiterführende Informationen dazu stehen in der Dokumentation. So kann es umgesetzt werden:

var editLayer = (FeatureLayer)myMap.OperationalLayers[0];
var featureTable = (GeodatabaseFeatureTable)editLayer.FeatureTable;

var syncTask = await GeodatabaseSyncTask.CreateAsync(featureTable.Geodatabase.Source);

// unregister geodatabase
await syncTask.UnregisterGeodatabaseAsync(featureTable.Geodatabase);

// delete geodatabase

 

Abschließende Worte

Mit dem Preplanned Workflow wurde das Thema mobile offline Szenarien in ArcGIS zu Ende gedacht. Das Ganze ist jetzt eine runde Sache, hat aber noch an der einen oder anderen Stelle Optimierungspotential, z.B. wären Shapes als MapAreas nützlicher statt nur rechteckiger Flächen (welches Arbeitsgebiet ist schon exakt rechteckig). Wir werden noch Verbesserungen sehen und insgesamt ist er schon sehr hilfreich. Den Preplanned Workflow werden wir demnächst auch in einigen ArcGIS Apps (Collector, Explorer) wiederfinden. Momentan ist er nur in ArcGIS Online verfügbar, in ArcGIS Enterprise wird er in einer späteren Version implementiert.

 

 

 

P.s.: Die Abschnitte zum Erzeugen von MapAreas mit der Python API (Im Anhang ist ein neues Python Notebook) und Synchronisieren von Feature Edits wurden leicht überarbeitet. Vielen Dank an Antti Kajanus vom ArcGIS Runtime Team für den Tipp mit der smarten Python API  Implementierung und seine Korrektur von Register/Unregister von Geodatenbanken. 

Was ist ein Stream Service?

 

Neben den bekannten und schon lange existierenden Map- & Feature Services gibt es seit ein paar Jahren (erster Release mit 10.3.) noch einen neuen Servicetyp in der ArcGIS Welt, welcher primär zur effizienten Visualisierung von Echtzeitdaten entwickelt wurde - der Stream Service.

 

Der Stream Service ist an den ArcGIS GeoEvent Server gekoppelt, Esri's Softwarelösung um Echtzeitdatenströme zu integrieren, in Echtzeit eventbasierte Analysen auf den einkommenden Daten durchzuführen und diese anschließend zu verteilen, sprich innerhalb und außerhalb der ArcGIS Plattform nutzbar zu machen. Dabei spielt natürlich die Anforderung die Daten in Echtzeit nicht nur zu prozessieren sondern auch zu visualisieren eine wichtige Rolle. Aufgrund dieser Anforderung hat sich das Entwicklungsteam rund um den GeoEvent Server Gedanken gemacht, wie man die Latenzzeit bei der Visualisierung von dynamischen Daten verringern kann um nicht nur zu zeigen WO etwas geschieht sondern auch direkt WENN es geschieht!

 

Ein Beispiel für einen Stream Service, welcher die aktuelle Position der International Space Station von einer API abfragt und visualisiert kann hier angeschaut werden.

 

 

 

Ein neues Konzept zur Visualisierung von dynamischen Daten!

 

Klassischerweise sind die Daten welche einem dynamischen Feature Services zugrunde liegen in einer Enterprise Geodatabase (EGDB) oder anderen Form von Datenspeicher wie z.B. dem Spatiotemporal Big Data Store (SBDS*) gespeichert. Dies bedeutet, dass bei der Visualisierung der Klient, also z.B. unsere Web Anwendung, in regelmäßigen Abständen den aktuellen Status aus dem Datenspeicher abfragen muss, um die Visualisierung der Features auf der Karte zu aktualisieren. In diesem Zusammenhang spricht man von Polling was einem Aktualisierungsintervall gleich kommt, welches somit eine Verzögerung in der Prozessierung von Echtzeitdatenströmen verursacht nachdem der GeoEvent Server diese verarbeitet und in die Datenbank geschrieben hat.

 

 

 

Um diese Latenzzeit zu minimieren, baut der Stream Server auf ein Konzept auf, bei welchem die Daten nicht erst in eine Datenbank geschrieben werden sondern direkt an den Klienten gepusht werden. Hierzu werden WebSocket Verbindungen direkt zwischen dem Stream Server und dem Stream Layer im Klienten aufgebaut, über welche die Features ohne jegliche Verzögerung übertragen und visualisiert werden!

 

 

Persistieren der Daten

 

Vielleicht kommt nun die Frage auf, was passiert wenn sich ein Klient initial mit dem Stream Server verbindet und die existierenden Daten nicht gespeichert wurden? Dies würde bedeuten, dass der Klient existierende Features erst visualisieren kann wenn ein neues Event also Statusupdate für dieses Feature vom GeoEvent Server prozessiert und an den Stream Server weiter geleitet wurde. Dies ist natürlich kein ideales Szenario, besonders wenn nicht alle Features in kurzen regelmäßigen Intervallen aktualisiert werden und somit unter Umständen für einen längeren Zeitraum gar nicht in der Anwendung auftauchen würden.

 

Deshalb, gibt es für die Stream Server das Konzept eines "Store Latest" Feature Service, welcher begleitend zu dem Stream Service veröffentlicht werden kann und wo, wie der Name es bereits vermuten lässt, parallel die aktuellsten Zustände der jeweiligen Features gespeichert werden. Wählt man diese Option, werden die Informationen über den "Store Latest" Feature Service in der Beschreibung des Stream Services hinterlegt. So können Klienten auf Basis dieser Information beim initialen Verbinden mit dem Stream Server parallel eine Query für die aktuellen Zustände der Features aus dem Feature Service durchführen und auf der Karte visualisieren. Anschließend übernimmt wieder der Stream Layer und visualisiert neue Events umgehend in Echtzeit!

 

Join der Geometrien und statischen Attribute im Klienten

 

Natürlich sollen im Idealfall nur dynamische Informationen eines Features ständig aktualisiert werden, in dem Fall von z.B. statischen Sensoren in einem Sensornetzwerk sind dies in der Regel sich immer aktualisierende Messwerte, Informationen wie z.B. die Geometrie oder weitere Sensoreigenschaften sind jedoch überwiegend statisch. Daher wurde auch für diese Anwendungsfälle eine Konzept in dem Stream Server integriert, welches es erlaubt einen Feature Service mit "Related Features" zu referenzieren. Mit diesen Informationen, welche dem Klienten ebenfalls über die Stream Server Beschreibung zur Verfügung gestellt werden, können statische Informationen direkt im Klienten an das eingehende Event über eine eindeutige ID (Track ID) hinzugefügt werden.

 

Möglichkeiten für Entwickler

 

Stream Server bzw. der Stream Layer in den Anwendungen bieten ebenfalls für Entwickler interessante Möglichkeiten diese Technologie weitergehend zu nutzen. Seit dem ursprünglichen Release der Technologie in 10.3. ist der Stream Layer in JavaScript basierenden Web Anwendungen über die ArcGIS API für JavaScript nutzbar und wird kontinuierlich in die weiteren ArcGIS Komponenten und API's integriert. Eine Interessante Möglichkeit ist hier sich dem "onMessage" (JS API) / "on_features" (Python API) Events des Stream Layer zu verschreiben, welches es ermöglicht jedes Event, welches im Esri JSON Format gesendet wird, zu analysieren bevor es auf der Karte visualisiert wird.

 

In der bereits erwähnten ISS Demo, verwende ich zum Beispiel das "onMessage" Event eines StreamService, welcher bei dem überfliegen der ISS über ein Land bzw. des Ozeans dieses als GeoTag mitsendet, um die Textanzeige beim "Landeswechsel" zu aktualisieren.

 

Viel Spaß beim Erkunden und Verwenden dieses neuen Layer-Typs in euren Echtzeit Anwendungen! 

 

Tom

 

*PS: Mehr Details zu dem Spatiotemporal Big Data Store wird es in einem der nächsten Blog Einträge geben!

Zum 10. April ist das nächste ArcGIS Online Update geplant. Diese Version beinhaltet neue Möglichkeiten zur Organisation und Erforschung von Inhalten, sowie zusätzliche Funktionen beim Anhängen von Daten, Definieren von Interessengebieten und Arbeiten in 3D. Alles weitere lest Ihr hier im Blog von Jörg MoosmeierArcGIS Online – April Update 

 

Möchtet Ihr schon vorab die neue Version ausprobieren? Dann nehmt an unserem Early Adopter Program for ArcGIS Online teil. Zur Anmeldung geht's hier.

 

 

Esri Deutschland-Schweiz Partner Community

Die ArcGIS API for Python ermöglicht die Erstellung von Skripts und die Automatisierung von Tasks in ArcGIS unter Verwendung einer Pythonic-Bibliothek. Dank der Python-API können Big-Data-Analysen, die Content-Verwaltung und die Web-GIS-Verwaltung optimiert werden. Die API lässt sich außerdem mit Jupyter Notebook und dem SciPy-Stack integrieren, so dass Akademiker, Data Scientists und GIS-Analysten Programme gemeinsam nutzen und reproduzierbare Erkenntnisse teilen können.

 

   

 

 

Mit der Version 1.4 kommen neben diversen Bufixes viele coole Neuerungen. Hier ein Auszug:

 

  • ArcGIS API for Python kann nun auch optional durch PyPI als pip Package installiert werden
  • Spatial Data Frame wurde stark erweitert , z.B. können nun viele 3rd Party Geodatenformate direkt genutzt werden
  • Es wurde eine breitere Palette an Symbologie-Optionen für das Map-Widget im Jupyter Notebook hinzugefügt
  • u.v.m.

 

Ausführliche Informationen zu allen Neuerungen der ArcGIS API for Python 1.4 findet Ihr in diesem News Blog von Esri Inc. und auf ArcGIS for Developers.

Dezember 2017 wurde das Major Release ArcGIS Runtime 100.2 veröffentlicht. Die nun veröffentlichte Version 100.2.1 ist ein darauf aufbauendes fokussiertes Release, in dem einige kritische Bugs gefixt und kleinere Verbesserungen an bestehenden Funktionen vorgenommen wurden. So zum Beispiel:

 

  • Verbesserungen mit WMS Services
  • Mobile Map Packages, erzeugt mit ArcGIS Pro 2.1, können nun auch Raster-Basemaps (.tpk) beinhalten und in ArcGIS Runtime 100.2.1 genutzt werden
  • 3D Scenes unterstützen nun Raster Layer & Basemaps in WGS84

 

Ausführliche Informationen zu allen Neuerungen in ArcGIS Runtime 100.2.1 findet Ihr in diesem News Blog von Esri Inc. und auf ArcGIS for Developers.

 

 

ArcGIS unterstützt multidimensionale Daten schon seit längerer Zeit. ArcGIS Pro visualisiert diese Daten in wenigen Schritten.

 

NetCDF (Network Common Data Form) ist ein Dateiformat zum Speichern von mehrdimensionalen wissenschaftlichen Daten (Variablen) wie Temperatur, Feuchtigkeit, Druck, Windgeschwindigkeit und Windrichtung. Alle Variablen können in ArcGIS über eine Dimension (z. B. Zeit) dargestellt werden, indem aus der netCDF-Datei ein Layer oder eine Tabellensicht erstellt wird.

 

Hier werden die Windstärken im Karibikraum Anfang September 2017 visualisiert. Der rote Ball ist ein Hurrikan und wurde als „Irma“ bezeichnet. Zunächst trifft er die kleinen und die großen Antillen danach Florida. „Jose“ folgte „Irma“ unmittelbar, drehte allerdings glücklicherweise ab bevor er die Inseln erreicht hätte.

 

Animation von Hurrikan Irma

 

1. Daten hinzufügen

Add data > Multidimensional Raster Layer > Input NetCDF-File - Select Variables - Choose Output Config

 

Unter Add data findet ihr den Menüeintrag zur Erstellung eines Multidimensional Raster Layer.

Multidimensionaler Raster Layer erzeugen


In dem sich öffnenden Menü fügt ihr die netCDF-Datei hinzu.

 

Wählt die Variable aus welche visualisiert werden soll.


(Hier in diesem Beispiel ist nur die Windgeschwindigkeit als Variable im Datensatz vorhanden)

 

Als Output Configuration wählt ihr Classic NetCDF Raster und bestätigt alles mit OK.

 

Weitere Informationen findet ihr hier.

netCDF-Datei hinzufügen und Variablen definieren

2. Zeit aktivieren

NetCDF-Layer > Properties > Time > Layer Time - Layer has time as dimension

Der neu hinzugefügte Layer wird dem Karteninhalt (Contents) hinzugefügt.
Um die Windgeschwindigkeit anhand der Zeit animieren zu können, ist zuerst in den Layereigenschaften die sog. Layer Time zuzuweisen.
Hierfür klickt ihr mit der rechten Maustaste auf den Layer, geht in die Eigenschaften, aktiviert den Reiter Time und wählt unter Layer Time, Layer has time as dimension aus und bestätigt alles mit OK.

Zeit in den Layereigenschaften aktivieren

 

3. Animation abspielen

Time Slider > Play

Nachdem in den Layereigenschaft die Zeit aktiviert wird, erscheint automatisch eine Zeitleiste.
Zusätzlich wird die Menüleiste Time angezeigt in denen weitere Einstellungen für die Animationen vorgenommen werden können. Übernehmt die Voreinstellungen und aktiviert Play auf der Zeitleiste.

 

Das wars. In nur drei einfachen Arbeitsschritten habt ihr mit ArcGIS Pro einen multidimensionalen Raster Layer erzeugt und ihn animiert. Den gesamten Workflow seht ihr in dem folgenden Video.

 

Maps bestehen in der Regel aus Basemap Layers zur Orientierung und Operational Layers für das Visualisieren, Abfragen, Bearbeiten und Analysieren von Geo-Informationen. In ArcGIS werden zwei Typen von Basemaps unterschieden: Raster Tile Services (gecachte Map Services und Image Services) und die noch recht jungen Vector Tile Services.

 

      Link zur Live Map

 

Für die Offline-Nutzung von Basemaps in ArcGIS Runtime können schon seit langer Zeit Tile Packages (.tpk) direkt aus Raster Tile Services exportiert werden. Brandneu ist nun die Möglichkeit, Bereiche von Vector Tile Services zu exportieren und lokal als Vector Tile Packages (.vtpk) nutzen zu können:

 

Vector Tile Services für den Export


Der große Vorteil von Vector Tile Services ist das clientseitige dynamische Rendering von Tiles. Somit ist die Basemap in jedem Maßstab der Map und bei jeder Auflösung des Displays immer gestochen scharf. Weiterhin werden die Daten und der Style der Basemap getrennt behandelt. Das ermöglicht dynamisches Anzeigen von Labels und das Erstellen von Basemaps mit unterschiedlichen Themen basierend auf immer denselben Datensatz. Und letztendlich werden Vektoren übertragen, also keine Bilder wie bei Raster Tile Services. Das sorgt für hohe Performanz und deutlich kleinere Datenmengen.

In ArcGIS Online werden schon eine Reihe von exportierbaren Vector Tile Basemaps bereitgestellt. Die hier verlinkten Services basieren alle auf denselben World Basemap Daten, aber immer jeweils mit anderem Style. Falls mehrere dieser Basemaps offline genutzt werden sollen, muss nur von einem der Services das vtpk (Daten + „Root Style“) und von allen Services jeweils nur der Style exportiert werden. Dazu später mehr.

Es können auch eigene Vector Tile Services auf ArcGIS Online erstellt und für den Export freigeschaltet werden. Dazu wird in den allgemeinen Einstellungen des Vector Tile Service Portal Items in der Registerkarte Einstellungen der Offline-Modus aktiviert:

 

 

Wie eigene Vector Tile Services erzeugt und dafür verschiedene Styles erstellt werden können, wird in diesen zwei Esri UK - Blogs sehr gut beschrieben:

 

 

Export von Vector Tile Packages und Styles in ArcGIS Runtime

Für den Export von Vector Tile Services und Styles werden die PortalItem IDs der Services, eine Identität (Named User oder ArcGIS Online Developer Account) und mindestens die ArcGIS Runtime Version 100.2 benötigt. Hier ist die exemplarische Vorgehensweise anhand des ArcGIS Runtime SDK for .NET:

 

      Online Map mit dem zu exportierenden Gebiet (AreaOfInterest im ExportVectorTilesParameters)

 

1. Authentifizieren

Der anonyme Export ist nicht erlaubt, es ist eine Authentifizierung erforderlich. Dafür gibt es mehrere Möglichkeiten, z.B in diesem Sample.

 

2. Vector Tile Service PortalItem erzeugen

Wir nutzen in unserem Code-Beispiel die World Topographic Map (for Export). Hiervon wird die Portal ID benötigt.

 

var portal = await ArcGISPortal.CreateAsync();
var portalItem = await PortalItem.CreateAsync(portal,"df541726b3df4c0caf99255bb1be4c86");

 

TIP:

 

Es ist leider nicht möglich, von einem Vector Tile Service equivalente Services mit anderen Styles abzufragen. Um das trotzdem zu automatisieren kann in ArcGIS Runtime nach PortalItems-Typen und Tags gesucht werden.

 

Z.B ist in den hier beschriebenen ArcGIS Online Basemaps der spezifischer Tag "World_Basemap_Export_v2".

 

Bei eigenen Services muss dementsprechend ein eigener spezifischer Tag gewählt werden.

 

So wird gesucht:

var queryExpression = string.Format("tags:\"{0}\"type: \"Vector Tile Service\"", "World_Basemap_Export_v2");

PortalQueryParameters queryParams = new PortalQueryParameters(queryExpression, 100);

// Search the portal using the query parameters and await the results
PortalQueryResultSet<PortalItem> findResult = await portal.FindItemsAsync(queryParams);

// Get the items from the query results
var tileServicePortalItems = findResult.Results;

 

3. Task, Parameter und Job erzeugen und ausführen

Im folgenden Code werden nur die Daten (.vtpk) und nicht der Style exportiert.

// Create the task
var exportTask = await ExportVectorTilesTask.CreateAsync(portalItem);

// Create a new parameters instance
var parameters = new ExportVectorTilesParameters()
{
    AreaOfInterest = exportArea.Geometry,    //area to export
    MaxLevel = exportTask.VectorTileSourceInfo.LevelsOfDetail.Count - 1  //levels to export
};

// Create the export job
var job = exportTask.ExportVectorTiles(parameters, Path.Combine(_vectorTileCachePath, "name.vtpk"));

// Subscribe to notifications for status updates
job.JobChanged += (sender, e) =>
{

   if (job.Status == Esri.ArcGISRuntime.Tasks.JobStatus.Succeeded)
   {
      //do something
   }
   else if (job.Status == Esri.ArcGISRuntime.Tasks.JobStatus.Failed)
   {
      // Notify the user
      ShowStatusMessage(job.Error.Message);
   }
};

// Start the export job
job.Start();

 

Es kann auch das vtpk und der Style (falls vorhanden) in einem Rutsch exportiert werden . Dazu muss dem ExportVectorTilesJob nur zusätzlich der Speicherpfad für einen Ordner für den Style mitgegeben werden.

ExportVectorTilesJob job;
if (exportTask.HasStyleResources)
{
   job = exportTask.ExportVectorTiles(parameters, Path.Combine(_vectorTileCachePath, "name.vtpk"), _styleResourcePath);
}
else
{
   job = exportTask.ExportVectorTiles(parameters, Path.Combine(_vectorTileCachePath, "name.vtpk"));
}

 

Und wenn nur der Style exportiert werden soll sieht es so aus:

var exportTask = await ExportVectorTilesTask.CreateAsync(portalItem);
if (exportTask.HasStyleResources)
{
   var exportStyleJob = exportTask.ExportStyleResourceCache(_styleResourcePath);
   var result = await exportStyleJob.GetResultAsync();
   //do something
}

 

Wenn zum Beispiel alle die weiter oben beschriebenen exportierbaren ArcGIS Online Vector Tile Services lokal als Basemaps verfügbar sein sollen, muss von einem Service das vtpk und von allen Services nur die Styles exportiert werden. In einem StyleResource Ordner befinden sich nur JSON-Dateien mit der Beschreibung des Styles, diese sind sehr klein.

 

Nutzen von lokalen Vector Tile Packages und Styles in ArcGIS Runtime

Das lokale vtpk und ein vorhandener Style können dann so in die Map eingebunden werden:

 

//Load a style
var itemResourceCache = new ItemResourceCache(_styleResourcePath);

//Load the vtpk
var offlineVectorTileCache = new VectorTileCache(Path.Combine(_vectorTileCachePath, "name.vtpk"));

// Create a tile layer with the cache and the style
var layer = new ArcGISVectorTiledLayer(offlineVectorTileCache, itemResourceCache);

// Create a tile layer only with the cache
//var layer = new ArcGISVectorTiledLayer(offlineVectorTileCache);

// Create a new map with the layer as basemap
var map = new Map(new Basemap(layer));

// Apply the map to the mapview
MyMapView.Map = map;

 

Wenn der Entwickler alles beschriebene schön kombiniert, können so in der ArcGIS Runtime App lokal mehrer Basemaps auf Basis von nur einem vtpk genutzt werden. Um den Speicherplatz der Daten auf dem Gerät muss sich meistens auch keine Sorgen mehr gemacht werden. In einem Test mit dem Gebiet grob Bayern in der gesamten Detailtiefe ist die Größe des vtpks + Styles nur ca.230 MB groß und der Export hat knapp 7 min gedauert. Wichtig ist noch die Information, dass es eine maximale Begrenzung ("maxExportTilesCount":10000) für einen Export gibt.

 

     Offline Vector Tile Basemap ohne extra Style (nur das .vtpk geladen)

 

       Offline Vector Tile Basemap mit Style

 

      Offline Vector Tile Basemap mit Style

 

      Offline Vector Tile Basemap mit Style

 

      Offline Vector Tile Basemap mit Style

Blog filtern

Nach Datum: Nach Tag: