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

GeoDev Germany

242 Einträge

Introduction

Publishing one feature layer in ArcGIS Online or ArcGIS Enterprise in one feature service is a simple procedure, there is a native function for uploading data inside a Spatial Dataframe to one hosted feature layer inside a feature service available in ArcGIS API for Python.

However, publishing multiple layers with different geometries inside one feature service requires more effort. To make this process less complex and more convenient for all users we created a 10 step workflow. We attached a Jupyter Notebook to the end of this blog, which can be simply uploaded to Esri Jupyter Hub to test the workflow.

 

To simplify the written language of this article, we describe the workflow mentioning ArcGIS Online only. All steps performed within this article are also applicable to ArcGIS Enterprise.

 

The Data

Data is available from multiple sources on the web in various geospatial formats. The ArcGIS Spatial Dataframe is a pythonic data structure, that allows to import and include several other geospatial formats on the fly. The table like structure is merely based on a Pandas DataFrame, but incorporates several extensions for geodata elements.

We use the ArcGIS Spatial Dataframe object to store tabular and geometric data. Pandas DataFrames are also used, to store tabular data.

 

Step 1: Import of the needed modules and login to ArcGIS Online with your subscription

We will have to load a few modules to setup the publishing process, the getpass module allows us to interactively enter our password. Using this module we have a secure way to do the login process, as we do not need to type our password as plaintext. The second argument to the GIS function should be changed from "username" to your username for ArcGIS Online. We suggest the installation of the needed python modules including the python environment and the Jupyter Notebooks by using Anaconda Distribution. It features python environments, which are separated from the system wide python installations and therefore reduces complexity and enhances compatibility.

from arcgis import GIS
from arcgis import geometry
from arcgis import features as fs
from getpass import getpass as pwd
import pandas as pd
pwd = pwd()
gis = GIS("https://www.arcgis.com/home/index.html", "username", pwd)

Step 2: Create the features and setup two geometry types

To demonstrate this workflow, we will now create some hard coded sample features with basic point and polyline geometries. To define the two features, we will also create some field values to visualize how field creation and setting attributes to features works in ArcGIS Online.

2.1 Creation of sample field values for features

We create two dictionaries for each geometry and two lists to contain the dictionaries. The list of dictionaries of each geometric element is then added to a Pandas DataFrame. If we had to create attributes for multiple features of one geometry, we would have to add as many dictionaries to the list of dictionaries as geometric features are available.

dictPointAttr = {}
dictPointAttrList = []
dictPointAttr["someint1"] = 123456789101112
dictPointAttr["sometext1"] = "Text"
dictPointAttr["somefloat1"] = 1.25
dictPointAttrList.append(dictPointAttr)
dfpnt = pd.DataFrame.from_dict(dictPointAttrList)

dictPolyLineAttr = {}
dictPolyLineAttrList = []
dictPolyLineAttr["someint2"] = 121110987654321
dictPolyLineAttr["sometext2"] = "Txet"
dictPolyLineAttr["somefloat2"] = 5.21
dictPolyLineAttrList.append(dictPolyLineAttr)
dfpll = pd.DataFrame.from_dict(dictPolyLineAttrList)

2.2 Creation of a simple point geometry

To setup a simple point geometry we will need to create a dictionary containing the two point coordinates “x” and “y” as keys, as well as a sub dictionary with “spatialReference” as key, containing the spatial reference defined by EPSG codes. The dictionary object is then converted to an ArcGIS point geometry and appended to a list of geometries. To store the feature data, an ArcGIS Spatial Dataframe is used. It is capable of storing lists of geometries as well as Pandas DataFrames containing the attribute data for every feature.

listGeometryPoint = []
pnt = {"x": 11.0419520737521,"y": 48.4391156813199, "spatialReference" : {"wkid" : 4326,"latestWkid" : 4326}}
point = geometry.Point(pnt)
listGeometryPoint.append(point)
spatialDataFramePoint = fs.SpatialDataFrame(data=dfpnt, geometry=listGeometryPoint)

2.3 Creation of a simple polyline geometry

The workflow of creating a simple polyline geometry is more or less the same as the creation of a simple point geometry, it differs in requirement of another syntax to define the vertices of the line.

listGeometryPolyLine = []
plyln = { "paths": [[[11.0419520737521,48.4391156813199],[11.0416097302404,48.4390815262157], [11.0414886646127,48.4392674452629]]], "spatialReference" : {"wkid" : 4326, "latestWkid" : 4326}}
polyline = geometry.Polyline(plyln)
listGeometryPolyLine.append(polyline)
spatialDataFramePolyLine = fs.SpatialDataFrame(data=dfpll, geometry=listGeometryPolyLine)

Step 3: Creation of the feature service and a feature layer collection

In this step we will setup a feature service and create a feature layer collection from a new (empty) service. The feature layer collection is used to store the definition of the feature service as well as the layer objects, which will be created in the following steps. To create the service we call the function “gis.content.create_service” from ArcGIS API for Python.

Feature services must have a unique name. If you like to repeat this workflow, please ensure to rename your feature service.

 

Provided function parameters are:

 

  • A name for the resulting feature service via name=
  • The spatial reference via wkid=
  • The spatial extent via item_properties=

 

The extent setting is necessary to ensure, that layers inside the feature service are displayed correctly in maps on ArcGIS Online. Without the extent setting, the wrong initial area might be displayed and users need to zoom to the appropriate part of the map by using “zoom to layer”. In this example we use the extent of the whole world. The items inside the dictUpdate dictionary can be set to user defined values. We recommend setting the "objectIdField" key to a custom value. Therefore, we ensure:

 

  • Every new feature object is provided with a new feature ID
  • No conflicts with the default setting of "objectIdField" occur (default setting is "objectId")

 

The key "capabilities" is needed to define what users with access to the feature service item can do with the service. Information on capabilities is provided in the ArcGIS REST API documentation.

newService = gis.content.create_service(name="featureservice", wkid=4326, item_properties={"extent":[-180.0,-90.0,180.0,90.0]})

newFtLC = fs.FeatureLayerCollection.fromitem(newService)

dictUpdate = {"copyrightText": "copyrightText",
              "objectIdField" : "FID",
              "globalIdField" : "GlobalID",
              "maxRecordCount": 25000,
              "serviceDescription": "description",
              "capabilities": 'Editing,Query,Update,Uploads,Delete,Sync,Extract',
              "spatialReference": {"wkid": 4326,"latestWkid": 4326},
              "initialExtent":{
                   "xmax": 180.0,"ymin": -90.0,"xmin": -180.0,"ymax": 90.0,
                   "spatialReference": {"wkid" : 4326, "latestWkid" : 4326}
                  },
              "fullExtent":{
                   "xmax": 180.0,"ymin": -90.0,"xmin": -180.0,"ymax": 90.0,
                  "spatialReference": {"wkid" : 4326, "latestWkid" : 4326}
                  }
    }
newFtLC.manager.update_definition(dictUpdate)

Step 4: Setup of required and optional fields inside the feature layers

The fields required to describe a valid feature layer are the "objectIdField" and the "globalIdField", which we already defined by their name in the previous step. All fields can be provided with an alias if the field name is too short to provide a meaningful description of its content. The length attribute should be set to appropriate values to add the needed count of features to the layers. The field attributes ["nullable"] and ["editable"] should be set to False for both fields "objectIdField" and "globalIdField".

# General Fields
dictFieldOBJECTID = {}
dictFieldOBJECTID["alias"] = "FID"
dictFieldOBJECTID["name"] = "FID"
dictFieldOBJECTID["type"] = "esriFieldTypeOID"
dictFieldOBJECTID["sqlType"] = "sqlTypeOther"
dictFieldOBJECTID["length"] = 10
dictFieldOBJECTID["nullable"] = False
dictFieldOBJECTID["editable"] = False
dictFieldOBJECTID["domain"] = None
dictFieldOBJECTID["defaultValue"] = None

dictFieldGlobalID = {}
dictFieldGlobalID["alias"] = "GlobalID"
dictFieldGlobalID["name"] = "GlobalID"
dictFieldGlobalID["type"] = "esriFieldTypeGlobalID"
dictFieldGlobalID["sqlType"] = "sqlTypeOther"
dictFieldGlobalID["length"] = 38
dictFieldGlobalID["nullable"] = False
dictFieldGlobalID["editable"] = False
dictFieldGlobalID["domain"] = None
dictFieldGlobalID["defaultValue"] = None

The optional fields are defined to ensure all values we defined in step 2.1 are transferred to the appropriate fields in the layer.

For integer fields we need to pay attention to the required field lengths. If we have 64 bit numbers, we need to define a data type for the key ["sqlType"] named "sqlTypeBigInt" and set the key ["type"] to "esriFieldTypeInteger". By ignoring this fact we may cause data corruption in our layers in ArcGIS Online, the values which do not fit into a standard integer field are sometimes written on into the next field, overwriting values in the next field.

 

FYI: If you use the Spatial Dataframe workflow as mentioned in the introduction, you are not able to use 64 bit numbers in integer fields, they are automatically converted into string fields / strings or stored as 32 bit integers causing data corruption in the hosted feature layer.

Fields containing float values can be defined by setting the key ["type"] to "esriFieldTypeDouble" and the key ["sqlType"] to "sqlTypeOther".

Fields containing strings can be defined by setting the key ["sqlType"] to "sqlTypeOther", the key ["type"] to "esriFieldTypeString" and defining a field length by setting the key ["length"] to a numeric integer value like the number 10.

# Fields for point layer
dictFieldInt1 = {}
dictFieldInt1["alias"] = "someint1"
dictFieldInt1["name"] = "someint1"
dictFieldInt1["sqlType"] = "sqlTypeBigInt"
dictFieldInt1["type"] = "esriFieldTypeInteger"
dictFieldInt1["nullable"] = "yes"
dictFieldInt1["editable"] = "yes"
dictFieldInt1["visible"] = "yes"

dictFieldString1 = {}
dictFieldString1["alias"] = "sometext1"
dictFieldString1["name"] = "sometext1"
dictFieldString1["sqlType"] = "sqlTypeOther"
dictFieldString1["type"] = "esriFieldTypeString"
dictFieldString1["length"] = 10

dictFieldFloat1 = {}
dictFieldFloat1["alias"] = "somefloat1"
dictFieldFloat1["name"] = "somefloat1"
dictFieldFloat1["sqlType"] = "sqlTypeOther"
dictFieldFloat1["type"] = "esriFieldTypeDouble"

 

# Fields for polyline layer
dictFieldInt2 = {}
dictFieldInt2["alias"] = "someint2"
dictFieldInt2["name"] = "someint2"
dictFieldInt2["sqlType"] = "sqlTypeBigInt"
dictFieldInt2["type"] = "esriFieldTypeInteger"
dictFieldInt2["nullable"] = "yes"
dictFieldInt2["editable"] = "yes"
dictFieldInt2["visible"] = "yes"

dictFieldString2 = {}
dictFieldString2["alias"] = "sometext2"
dictFieldString2["name"] = "sometext2"
dictFieldString2["sqlType"] = "sqlTypeOther"
dictFieldString2["type"] = "esriFieldTypeString"
dictFieldString2["length"] = 10

dictFieldFloat2 = {}
dictFieldFloat2["alias"] = "somefloat2"
dictFieldFloat2["name"] = "somefloat2"
dictFieldFloat2["sqlType"] = "sqlTypeOther"
dictFieldFloat2["type"] = "esriFieldTypeDouble"

 

Step 5: Create list of fields for each layer element and append field definitions

To store our field definitions in the following layer definitions, we create two lists, which store the dictionaries for every field. We need two lists to store the fields because we are not allowed to put the two different geometric objects, point and polyline, into the same layer.

The python list.append operation is applied for every field dictionary.

 

# Lists containing the fields
listFieldsPoint = []
listFieldsPolyLine = []

# Append fields to list of fields for points
listFieldsPoint.append(dictFieldOBJECTID)
listFieldsPoint.append(dictFieldGlobalID)
listFieldsPoint.append(dictFieldInt1)
listFieldsPoint.append(dictFieldString1)
listFieldsPoint.append(dictFieldFloat1)

# Append fields to list of fields for polylines
listFieldsPolyLine.append(dictFieldOBJECTID)
listFieldsPolyLine.append(dictFieldGlobalID)
listFieldsPolyLine.append(dictFieldInt2)
listFieldsPolyLine.append(dictFieldString2)
listFieldsPolyLine.append(dictFieldFloat2)

Step 6: Create layer definitions for two layers

We need to define the layer definitions to provide information on what type of features we would like to add to the feature service and how the layers are named inside the service. As we defined fields via dictionary elements, we need to define the layer definition using dictionaries as well. The key ["geometryType"] defines the geometries, one layer contains. The layer definition also contains information, we already set in step 3, the definition of the ["objectIdField"] and the ["globalIdField"]. For every layer a separate extent, which matches the extent of all features in the layer and a separate name can be defined. Fields are defined by setting the key ["fields"] to the corresponding list of fields for the layer.

dictPolyLineLayer = {}
dictPolyLineLayer["geometryType"] = 'esriGeometryPolyline'
dictPolyLineLayer["globalIdField"] = 'GlobalID'
dictPolyLineLayer["objectIdField"] = 'FID'
dictPolyLineLayer["extent"] = {
         "xmax": 180.0,"ymin": -90.0,"xmin": -180.0,"ymax": 90.0,
        "spatialReference": {"wkid": 4326, "latestWkid": 4326}
        }
dictPolyLineLayer["name"] = 'PolyLineLayer'
dictPolyLineLayer["fields"] = listFieldsPolyLine
dictPointLayer = {}
dictPointLayer["geometryType"] = 'esriGeometryPoint'
dictPointLayer["globalIdField"] = 'GlobalID'
dictPointLayer["objectIdField"] = 'FID'
dictPointLayer["extent"] = {
         "xmax": 180.0,"ymin": -90.0,"xmin": -180.0,"ymax": 90.0,
        "spatialReference": {"wkid": 4326, "latestWkid": 4326}
        }
dictPointLayer["name"] = 'PointLayer'
dictPointLayer["fields"] = listFieldsPoint

Step 7: Create a layer list, append layer definitions and update feature layer collection

This step will update the feature layer collection, by adding the layer definitions. They are added to a list and this list is then added to a dictionary as value for the key "layers". The feature layer collection is then updated by the dictionary.

layersList = []
layersList.append(dictPointLayer)
layersList.append(dictPolyLineLayer)
dictUpdate = {"layers": layersList }
newFtLC.manager.add_to_definition(dictUpdate)

Step 8: Create FeatureSets from Spatial Dataframes and append FeatureSets to list

The Spatial Dataframes are converted to FeatureSet elements and then added to a list. The list is used to store the values for the next step, the creation of features using a feature layer collection.

dflist = []
featureSetPoint = spatialDataFramePoint.to_featureset()
featureSetPolyLine = spatialDataFramePolyLine.to_featureset()
dflist.append(featureSetPoint)
dflist.append(featureSetPolyLine)

Step 9: Create features and add them to feature layer collection

The actual feature creation is performed in this step. We need to loop through our feature layer collection using a counter to add new features.

To improve readability and structure we create the function add_features, which performs the additions of features for each layer. The input values for the function are the current layer object and the current set of features. We define a variable result, which we set to the output of the edit function layer.edit_features of the given layer. As argument for the function layer.edit_features, we set (adds = featlist). The variable featlist matches our current set of features. We then return the result of our editing. The function add_features is called inside the for loop

for i in range(len(newFtLC.layers)):


were we loop through the feature layer collection by counter. The output of the function call is set to the variable status, which is then printed to the console.

The console output shows us, if our layer operation was successful. If this is the case, we will see two times an output containing: 'success': True.

def add_features(layer, featlist):
     result = layer.edit_features(adds = featlist)
     return result

for i in range(len(newFtLC.layers)):
     status = add_features(newFtLC.layers[i],
     dflist[i])
     print (status)

Step 10: Inspect results on ArcGIS Online

We created a sample feature service containing two different layers with two different layer types. To check if everything was created and all of the items work, we open another web browser window or tab and go to our portal. There should be an item in the list of items named “featureservice” if we click on the “content”  tab. We can now click on the item and inspect it, there should be values in the attribute table and one point in the point layer as well as one polyline in the polyline layer when regarding the features inside the layers. When we add the feature service to a map we should see a result like this:

The results

Conclusion

Setting up a feature service with multiple layers can be performed fast and efficiently using ArcGIS API for Python. There is no need to publish services from ArcGIS for Desktop, datasets can be easily published to ArcGIS Enterprise or ArcGIS Online using a few lines of python code. Though it is cool to publish a feature service with multiple layers in ten steps, it would be great if there was a function available, which makes the effort to publish multiple layers in one single feature service smaller and allows setting up a service and the creating layers and features within only a few steps. If you have any questions or comments please feel free to contact me or leave a comment in the comment section below.

 

Happy coding, Lukas

I like to explore the city via public transport and as I am a big map fan, I fell in love with the map of our Berlin based public transport service provider BVG. The map is a great product and has so many levels of information. It has a well know style established in Berlin so I wanted to use it in my own web mapping application.

As I scanned the website of the BVG I noticed the leaflet map on their page:

So I asked the question: "Where does the tiles came from". A short look in the network developer pane in firefox revealed the address https://fahrinfo.bvg.de/tiles/base/15/17605/22019.png  (example)

The Tiles can be easily added inside a ArcGIS Javascript API based webmap:

var tiledLayer = new WebTileLayer({
     urlTemplate: "https://fahrinfo.bvg.de/tiles/base/{level}/{col}/{row}.png",
     title: "BVG Basemap",
     copyright:            
          "Map Data by <a href='https://Bvg.de'>BVG</a>, " +
          "Imagery by <a href='https://Bvg.de/'>BVG</a>"
});

But as soon you embed this, you will notice: There are only 404s send back from the BVG server. So I examined the URL-schema and found out, that the Y id is somewhat different compared to mapbox or OSM Y ids for the same tile:

y-tile: BVGy-tile: MapboxzoomLevel
220201074715
220151075215
220131075415
11007537614
440252151016
440262150916

So the tile indices are somehow shifted compared to the normal schema... So let us reverse engineer the conversion. First approach: plot the IDs on a chart and try some regression:

But as you can see: the line looks straight but the equation is just crap. Let's zoom in a bit:

This is of course a bit different: the regression line flipped and we do have no residuals. The summand looks very common as it is near a power of 2. to be exact: 2^15-1

So there is a direct equation to transform the original tile row into a BVG tile row:

var rownew = -1*row+Math.pow(2,level)-1;

So now we need to import this logic into the method ArcGIS uses to get the tiles. And BAMM: here are the tiles:

var tiledLayer = new WebTileLayer({
     urlTemplate: "https://fahrinfo.bvg.de/tiles/base/{level}/{col}/{row}.png",
     title: "BVG Basemap",
     getTileUrl: function (level, row, col) {
          var rownew = -1*row+Math.pow(2,level)-1;
          return this.urlTemplate.replace("{level}", level).replace("{col}", col).replace("{row}", rownew);
     },
     copyright:            
          "Map Data by <a href='https://Bvg.de'>BVG</a>, " +
          "Imagery by <a href='https://Bvg.de/'>BVG</a>"
});

Explore the webmap on CodePen, or download it below.

And last but not least: You can even rotate it:

In the end: If you know the math, you can change the world ;-)

Introduction

Esri Deutschland developed a python tool based on ArcGIS API for Python to manage licenses and Named User in ArcGIS  and ArcGIS Enterprise. The tool is named ArcGIS Python package for user administration (ArcGIS admin toolbox). Information on this tool can be found in a blog on GIS IQ (in german language). While optimizing, several pitfalls and common reasons for major errors in user administration tasks and license assignment could be identified. Problems occur, when programmers do not notice, that some items do not exist at a certain point or for certain users or some items are assigned to users, which then prevents functions from completion. The most common identified pitfalls in administration tasks and license assignment are presented below.

 

1. Deleting User

 

Deleting Named User fails if a user has one or more licenses assigned, to ensure deleting works without any problem, programmers are reminded to always revoke licenses before deleting users. Exceptions should always be handled correctly, exceptions invisible to the user may also prevent scripts from working correctly. Sometimes missing attributes like biography-description-texts can lead to malfunctioning of scripts, it is always suggested to use if-else-queries to prevent accessing attributes, which are missing.

Missing biography-description-text causes delete to fail

 

Deleting Named User from ArcGIS  can be safely performed if the user:

 

  • Has no licenses assigned
  • Missing properties (e.g. biography-description-text) for users are properly handled
  • Unimportant exceptions are properly handled

 

2. Assigning Licenses

 

Assigning licenses to Named User fails if users are not entitled to use the requested license. If licenses are not available in the user’s organization, but are hard-coded and therefore required for every user, a exception may be raised if licenses are not available in some organizations. Invalid licenses as well as a missing count of licenses also prevent programs from running correctly. If-else-queries help to avoid running into problems and notify the user of invalid arguments. Exceptions should always be handled correctly, exceptions invisible to the user may also prevent scripts from working correctly.

If-queries to inform users on invalid arguments or properly handle list and string objects

 

Assigning Licenses to users in ArcGIS  works flawlessly if:

 

  • The user has the rights and is entitled for the requested license
  • The requested license is available for the user’s organization
  • The requested license is a valid license
  • Enough licenses are available
  • Unimportant exceptions are properly handled

 

3. Revoking Licenses

 

Misspelling or non-existence of licenses or Named User is also a common cause for errors. Revoking licenses will fail if a requested user, whose license should be revoked is misspelled or does not exist. License being revoked from users must be previously assigned to the user. 

 

Revoking Licenses from users in ArcGIS  works flawlessly if:

 

  • The requested user exists
  • The requested license to revoke has been assigned to the user
  • The requested license to revoke is a valid license
  • Unimportant exceptions are properly handled

 

Conclusion

 

ArcGIS API for Python provides several functions for user administration, license assignment and revoking of licenses. There are several ways of accessing these functions and use them in scripts. It is up to the programmer to implement tasks and workflows properly. Beginners should note, that the API requires at least basic know-how on programming tasks and procedures and does not check if users that should be deleted, have licenses assigned. The programmer should be aware, that non-revoked licenses may cause deleting tasks to fail. Though there are a few things to notice, it is great to have many opportunities to do user administration tasks in python with the ArcGIS API for Python. If you have any questions or comments please feel free to contact me or leave a comment in the comment section below.

Mit Web AppBuilder for ArcGIS können fokussierte 2D und 3D Web-Apps mithilfe von vielen konfigurierbaren, ready-to-use Widgets und Designs erstellt werden. Mit der Developer Edition des Web AppBuilders ist es möglich, bestehenden Widgets und Designs zu erweitern oder auch vollständig eigene zu erstellen.

 

Swipe Widget

Swipe Widget

 

Die neue Version 2.9 hat nun den gleichen funktionalen Stand wie der eingebettete Web AppBuilder in ArcGIS  vom Juni 2018 Update. Es wurden gut 15 Widgets erweitert, z.B.:

 

  • Messen von Flächen in 3D-Scenes
  • Feinere Filteroptionen
  • Vergleichen unterschiedlicher Layer mit dem Swipe Widget
  • Editieren von Features mit n:m Beziehung
  • Cost Analysis Widget ist nicht mehr in Beta
  • Uvm.

 

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

Seit Anfang Juli 2018 steht das neue ArcGIS Runtime Major Release Version 100.3 zur Verfügung. ArcGIS Runtime ist eine Familie aus mehreren SDKs zum Entwickeln nativer Geo-Apps für alle relevanten Plattformen. Das Update ist beschrieben als Phase 1 der vollständigen, echten Unterstützung von 3D Web GIS durch alle APIs hinweg. Neben Bugfixes, Performanceverbesserungen und plattformspezifischen Änderungen (z.B. Nutzen des .NET Standard 2.0 im .NET SDK) gibt es neue und erweiterte Funktionalität. Hier eine Auswahl:

 

Unterstützung von 3D Web Scenes


In ArcGIS ist eine Web Map die Beschreibung einer vollständig konfigurierten 2D Karte. Web Scenes dagegen ist das Äquivalent für Karten in 3D. Diese können nun in ArcGIS Runtime werden. 3D Objekte in Scenes können identifiziert und selektiert, und deren Darstellung durch Ändern des Renderers der Scene angepasst werden.


Des Weiteren wird nun das interaktive Messen in 3d Scenes unterstützt und die Sichtbarkeitsanalyse (Viewshed) wurde verbessert (nun 360 Grad).

 

3D Web Scene

 

 

Weiter Highlights:

 

  • WMS Support wurde erweitert, so können z.B. nun Inhalte nach Zeit gefiltert und die Darstellung der Layer durch verfügbare Styles geändert werden.
  • Labeling wurde verbessert
  • Für militärische Symbole wurde die App-6(B) und App-6(D) Spezifikation hinzugefügt
  • Viele neue Samples für alle SDKs
  • uvm.

 

Ausführliche Informationen zu allen Neuerungen in ArcGIS Runtime 100.2.1 findet Ihr in diesem News Blog von Esri Inc. und in den Release Notes der verschiedenen SDKs auf dem ArcGIS for Developers Portal.

Ab sofort stehen allen Entwicklern die neuen Versionen der ArcGIS API for JavaScript zur Verfügung. In den Updates der Version 4.8 sowie 3.25 wurden wieder viele Verbesserungen vorgenommen und einige neue Features hinzugefügt. Unter anderem:

 

Heatmap Renderer

 

Underground navigation in global scenes

 

3.25 Relationship Renderer

 

Alle Neuerungen werden in diesem Blog-Post detailliert vorgestellt:

ArcGIS API for JavaScript versions 4.8 and 3.25 released 

 

Mehr dazu zeigen auch die neusten Beispielcodes:

3.25 Latest Samples

4.8 Latest Samples

 

und die Release Notes:

3.25 Release Notes

4.8 Release Notes

 

Zu den Unterschieden zwischen den beiden Versionen 3.25 und 4.8 und dazu, welche Version in welchem Anwendungsfall zu bevorzugen ist, gibt die aktualisierte FAQ einige gute Hinweise: 
Choose between version 3.25 and 4.8

 

Happy coding!

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. 

Blog filtern

Nach Datum: Nach Tag: