Creating and using 'dynamic data layers'

2480
0
05-18-2021 09:39 AM
Labels (1)
DonKemlage
Esri Contributor
0 0 2,480

Introduction

In a previous blog article titled: Creating and using 'dynamic map layers', I showed how to use ArcGIS Pro to author and publish a map service on ArcGIS Server to create a dynamic map layer. A dynamic map layer is one of three types of ArcGIS Server REST `layerSource` objects. An interesting property of a dynamic map layer on ArcGIS Server is that, these map services are public, enabling anyone to explore and tinker with the layer rendering in their apps.

Organizations that have sensitive or propriety map layers may not want to have their data explored and used by other applications. In this case, ArcGIS Server has an option to transmit dynamic mapping information to only those apps that have the correct access information. The ArcGIS Server REST `layerSource` object type of a dynamic data layer gives apps dynamic mapping capabilities but in a private scenario.

This blog post will show how to use ArcGIS Pro to author a shapefile containing private information. Then using the ArcGIS Server Manager, we will add this shapefile to an existing ArcGIS Server published map service, but with the caveat that this new dynamic layer can only be accessed when certain key pieces of information are known to the developer or end user of the app. Finally, using the WPF framework in the C# programming language, we'll create an ArcGIS Runtime application to consume the dynamic data layer. This app will be able to modify the rendering of the dynamic data layer on the fly, and perform identify operations to look at underlying attribute information.

 

ArcGIS Pro

Using ArcGIS Pro, I created a shapefile (named States.shp) based on the USA state boundaries. The attribute data contained the following fields for each USA county:

FIELD NAMETYPENOTES
FIDObject IDObject ID for each record
ST_NAMEStringState name
REGIONStringGeographical region of the USA that contains multiple states

 

For display purposes in ArcGIS Pro, I set the fill for the county boundaries to be transparent and the outline to be 0.5 pixels wide. However, it really does not matter how the shapefile is rendered in ArcGIS Pro, because the rendering is not preserved when added to an existing map service with the ArcGIS Server Manager, as we will see later.

image.png

 

ArcGIS Server and ArcGIS Server Manager

First, we want to copy the shapefile (States.shp and all of the other files with the same name but different file extensions) that was created in ArcGIS Pro to location that ArcGIS Server has read/write access to. In our case, we will copy the shapefile to the physical hard drive of the server, as depicted here:

image.png

In my prior blog post: Creating and using 'dynamic map layers', we had a published map service (called COVID19_Counties (Map Server)) that had the Supports Dynamic Layers: true with the following configuration:

image.png

Now will use our Administrative username and password to access the ArcGIS Server Manager feature of ArcGIS Server.  We will add a dynamic data layer to this existing map service.

Note: The URL for ArcGIS Server Manager follows the URL pattern is https://[YourArcGISServer].[YourDomain].com:6443/arcgis/manager/. Alternatively, you can open Manager from the operating system shortcut that is installed with ArcGIS Server.

After logging into ArcGIS Server Manager, navigate to the existing published map service. In our case this is COVID19_Counties.

Next, click on the Capabilities tab. Then scroll towards bottom and click the Add button to register a database, file geodatabase, shapefile or raster workspace.

When the Add Dynamic Workspace dialog opens, specify the following options and click the Add button:

Workspace TypeShapefile Folder
Workspace IDHiddenState
Location[YourHardDriveFolderLocationOnArcGISServer]

 

It is important that the Location string for the shapefile is the same place where you copied the shapefile from ArcGIS Pro onto the ArcGIS Server Machine.

image.png

NOTE: If you desire to use one of the other Workspace Types of: Enterprise Database, File Geodatabase, or Raster Folder, you can click the Help link in the Add Dynamic Workspace dialog to learn more about those options.

After the new dynamic workspace has been added, review the information to ensure it is correct, and then click the Save and Restart button for the new dynamic data layer to become in-effect for the map service.

image.png

We just registered our dynamic data layer with an existing ArcGIS Server map service. This act of registration enables direct access to the GIS data layer. There was no symbology rendering defined as when we authored and published the dynamic map layer with ArcGIS Pro (described in the previously blog article). This means that any rendering (like a SimpleRenderer, UniqueValueRenderer, etc.) must be defined by the developer when accessing this data in their app.

At this point, you will want to record two pieces of information required to access the shapefile based dynamic data layer from your client app. Without these two pieces of information, users cannot discover or access the dynamic data layer.

The first is the WorkspaceID to your shapefile. In our case this is the string "HiddenState". This will be used as the workspaceId parameter of the .NET Runtime Esri.ArcGISRutime.Mapping.TableSublayerSource object constructor. You can use any string you desire for the WorkspaceID; it just provides a unique name that only the author of the data and the user of the data know about.

The second is the DataSourceName for your shapefile. In our case, this is the string "States.shp" (which is the name of the shapefile with the .shp extension). This will be used as the dataSoureceName parameter of the .NET Runtime Esri.ArcGISRutime.Mapping.TableSublayerSource object constructor. TIP: When trying to figure out the DataSourceName for an Enterprise Database (or SDE), it usually follows the pattern: "[DatabaseName].[SchemaOwner].[FeatureClass]". Example "ss6.gdb.States".

 

ArcGIS Runtime

Using Visual Studio 2019, I created a new C# application based on the WPF Framework. Using the NugetPackage Manager dialog, I added the most recent Esri.ArcGISRuntime.WPF package from nuget.org. Three files in the Visual Studio project require modification to create the app. This section names those files and describes the code changes needed.

App.xaml.cs

Modify this file to enable the API Key so that you can view the basemap hosted on an ArcGIS Online service. Provide your own API Key by going to the ArcGIS developer dashboard at: https://developers.arcgis.com/dashboard/

 

using System.Windows;

namespace DynamicLayerCovid19
{
  public partial class App : Application
  {
   protected override void OnStartup(StartupEventArgs e)
   {
    base.OnStartup(e);

    // Note: it is not best practice to store API keys in source code.
    // The API key is referenced here for the convenience of this tutorial.
    //
    // Get your open API key from: https://developers.arcgis.com/dashboard/
    Esri.ArcGISRuntime.ArcGISRuntimeEnvironment.ApiKey = "PROVIDE YOUR API KEY";
   }
  }
}

 

 

MainWindow.xaml

Modify this file is to define the GUI for the app.

 

<Window x:Class="DynamicDataLayer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DynamicDataLayer"
        xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
        mc:Ignorable="d"
        Title="Dynamic data layer" Height="800" Width="1200">
    <Grid>
        <esri:MapView x:Name="MyMapView"/>
        <Border
            Background="White" BorderBrush="Black" BorderThickness="1"
            HorizontalAlignment="Left" VerticalAlignment="Bottom"
            Margin="10,0,0,30" Padding="10" Width="400">
            <StackPanel Orientation="Vertical">
                <TextBlock x:Name="TextBlockInstructions" TextWrapping="Wrap" Width="350" Height="130" Margin="0,0,0,10"/>
                <Button x:Name="ButtonDynamicMapLayer" Click="Button_DynamicMapLayer_Click" Content="1 - Show Dynamic Map Layer" Width="350"/>
                <Button x:Name="ButtonDynamicDataLayer" Click="Button_DynamicDataLayer_Click" Content="2 - Show Dynamic Data Layer" Width="350"/>
                <Button x:Name="ButtonJoinDynamicLayers" Click="Button_JoinDynamicLayers_Click" Content="3 - Join Dynamic Layers and Symbolize" Width="350"/>
            </StackPanel>
        </Border>
    </Grid>
</Window>

 

 

MainWindows.xamls.cs

This file contains the main logic for the running of the app. There are 7 main sections worth discussing to provide insight on how the app works:

(1) The using statements at the top of the file shorten the syntax for writing the code.

(2) The IntializeMap() function sets up the initial look of the GUI. It adds the world light gray base map, zoomed to the area of the continental USA to provide orientation for the operational layers. A default GraphicsOverlay is created to aid in the identify process. Finally, the GeoViewTapped event is wired up to handle the user map taps, to obtain the identify results.

(3) The ResetLayers() function will clear out any graphics in the GraphicsOverlay from a prior identify operation, and clear out any prior operational layers that were created by adding dynamic layers.

(4) The Button_DynamicMapLayer_Click() function adds a dynamic map layer to the application showing USA county COVID-19 cases. The default rendering of the layer comes from the published ArcGIS Server map service that was authored in a prior blog post. The dynamic map layer is created via the MapSubLayerSource object to create an ArcGISMapImageSubLayer object. The dynamic map layer is attached as an ArcGISSubLayer of the ArcGISMapImageLayer and added to the map.

(5) The Button_DynamicDataLayer_Click() function adds a dynamic data layer to the application showing the USA state boundaries. This is the layer that was created using the ArcGIS Server Manager and is only accessible when the WorkspaceID and DataSourceName strings are known to the developer or user of the app. The dynamic data layer rendering must be specified by the developer in the app; in our case we use a SimpleRenderer based on a SimpleFillSymbol. The dynamic data layer is created via the TableSubLayerSource object in order to create an ArcGISMapImageLayer.  The dynamic data layer is attached as an ArcGISSubLayer  of the ArcGISMapImageLayer  and added to the map.

(6) The Button_JoinDynamicLayers_Click() function creates a dynamic layer based on the TableJoinSublayerSource object. The TableJoinSublayerSource takes any two abstract SubLayerSource objects and joins them together based a common field in the attributes. In our case, we will join together the USA counties layer using a MapSubLayerSource, and the USA states layer using a TableSubLayerSource as the abstract SubLayerSource types. The common field to join the two dynamic layers is the state name (ST_NAME). To make things visually appealing, a UniqueValueRender was used to show which counties belong to a particular region of the USA. I used the term dynamic layer in the generic sense for this function; think of it as a hybrid of information coming from a dynamic map layer and a dynamic data layer.

(7) The MyMapView_GeoViewTapped() function provides attribute information on the dynamic layers in the map where the user taps. The existing GraphicsOverlay is obtained and cleared from previous user interaction. Using the IdentifyLayersAsync method, the IdentifyLayerResult object is returned and SubLayerResults are obtained. While iterating over the results, the GeoElement is obtained and used to construct a yellow solid fill. It is then added to the GraphicsOverlay. Additionally while iterating over the results, the attribute information is obtained. By iterating over the Attributes the Key/Value pairs for each identified county, the results are present back to the user in a MessageBox.

 

using System;
using System.Collections.Generic;
using System.Windows;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Symbology;
using Esri.ArcGISRuntime.Data;
using Esri.ArcGISRuntime.UI;
using Esri.ArcGISRuntime.UI.Controls;

namespace DynamicDataLayer
{
 public partial class MainWindow : Window
 {
  public MainWindow()
  {
   InitializeComponent();

   // Function to start up the mapping app
   InitializeMap();
  }

  private async void InitializeMap()
  {
   // Provide instructions on how to use the app
   TextBlockInstructions.Text = "After clicking each button (in order), tap on the map over a feature to see" +
     " the attribute information. Take note how the field names change to be prefaced with DataSourceName" + 
     " when the TableJoinSublayerSource is used.";

   // Create new map
   Map myMap = new Map();

   // Get the base map from the map
   Basemap myBasemap = myMap.Basemap;

   // Get the layer collection for the base map 
   LayerCollection myLayerCollection_BaseLayers = myBasemap.BaseLayers;

   // Create uri for the reference base layer
   Uri myUri_BaseLayer = new Uri("http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer");

   // Create new reference map image layer from the uri
   ArcGISMapImageLayer myArcGISMapImageLayer_BaseLayer = new ArcGISMapImageLayer(myUri_BaseLayer);

   // Provide a base layer name
   myArcGISMapImageLayer_BaseLayer.Name = "WorldLightGrayBase";

   // Add the reference base layer to the layer collection
   myLayerCollection_BaseLayers.Add(myArcGISMapImageLayer_BaseLayer);

   // Assign the map to the MapView
   MyMapView.Map = myMap;

   // Create an envelope that focuses on the continental United States
   Envelope myEnvelope = new Envelope(-14131181.98, 2324882.74, -7251750.27, 6636148.22, SpatialReferences.WebMercator);

   // Set the view point of the map view to that of the envelope
   await MyMapView.SetViewpointGeometryAsync(myEnvelope, 20);

   // Create a graphics overlay used to highlight identified counties
   MyMapView.GraphicsOverlays.Add(new GraphicsOverlay());

   // Wire up the GeoView tapped event for the identify operation
   MyMapView.GeoViewTapped += MyMapView_GeoViewTapped;
  }

  private void ResetLayers()
  {
   // Get the graphics overlay collection
   GraphicsOverlayCollection myGraphicsOverlayerCollection = MyMapView.GraphicsOverlays;

   // Get the first graphics overlay in the collection
   GraphicsOverlay myGraphicsOverlay = myGraphicsOverlayerCollection[0];

   // Get the graphic collection 
   GraphicCollection myGraphicCollection = myGraphicsOverlay.Graphics;

   // Clear out all of the graphics in the graphic collection
   myGraphicCollection.Clear();

   // Clear out any previous operational layers (specifically, any dynamic layers)
   MyMapView.Map.OperationalLayers.Clear();
  }

  private void Button_DynamicMapLayer_Click(object sender, RoutedEventArgs e)
  {
   // In this function we will create an ArcMapImageSubLayer based on a MapSublayerSource.
   // The MapSubLayerSource will be created from an ArcGIS Server REST 'layerSource' object
   // known as a 'dynamic map layer'. The 'dynamic map layer' was authored in ArcGIS Pro and
   // Published as a Map Service on ArcGIS Server.

   // Reset the graphics and any operational layers from previous runs
   ResetLayers();

   // Create a new map sub layer source object; this will be the basis for the new 'dynamic map layer'
   // generated by ArcGIS Server. NOTE: The input parameter is the Id (or index number)
   // of the sub layer that is listed in the ArcGIS REST Services Directory documentation for the
   // ArcGIS Server; in this particular case 0 = the 'COVID19_Counties' sub layer.
   MapSublayerSource myMapSubLayerSource = new MapSublayerSource(0);

   // Create a new map image sub layer (aka 'dynamic map layer') based on the map sub layer source;
   // provide an id value as well
   ArcGISMapImageSublayer myArcGISMapImageSubLayer = new ArcGISMapImageSublayer(99, myMapSubLayerSource);

   // Create uri for the existing ArcGIS map image layer
   var myUri = new Uri("https://[Your.Server].com/server/rest/services/NA/COVID19_Counties/MapServer");

   // Create new ArcGIS map image layer from the url
   ArcGISMapImageLayer myArcGISMapImageLayer = new ArcGISMapImageLayer(myUri);

   // Get the list of sub layers from the map image layer
   IList<ArcGISSublayer> myListOfSublayers = myArcGISMapImageLayer.Sublayers;

   // Add the map image sub layer (aka 'dynamic map layer') to the ArcGIS map image layer
   myListOfSublayers.Add(myArcGISMapImageSubLayer);

   // Get the map from the map view
   Map myMap = MyMapView.Map;

   // Get the layer collection from the map
   LayerCollection myLayerCollection = myMap.OperationalLayers;

   // Add the ArcGIS map image layer to the layer collection. The default rendering from the ArcGIS
   // Server dynamic map service for the counties layer will be used.
   myLayerCollection.Add(myArcGISMapImageLayer);
  }

  private void Button_DynamicDataLayer_Click(object sender, RoutedEventArgs e)
  {
   // In this function we will create an ArcMapImageSubLayer based on a TableSublayerSource.
   // The TableSubLayerSource will be created from an ArcGIS Server REST 'layerSource' object
   // known as a 'dynamic data layer'. The 'dynamic data layer' was authored in ArcGIS Server
   // Manager using an existing Published a Map Service on ArcGIS Server.

   // Reset the graphics and any operational layers from previous runs
   ResetLayers();

   // Supply a Workspace ID string. NOTE: You will need to obtain this string from your ArcGIS
   // Server Administrator or author of the 'dynamic data layer' that was created with ArcGIS
   // Server Manager.
   string myWorkspaceID = "HiddenState";

   // Supply the Data Source Name string (sometimes called the acronym 'DSN') that will retrieve
   // records for the 'dynamic data layer'. NOTE: You will need to obtain this string from your
   // ArcGIS Server Administrator or author of the 'dynamic data layer' that was created with
   // ArcGIS Server Manager. In the case of a shapefile, the pattern of the Data Source Name
   // is "[ShapeFileName].shp" (example: "States.shp"). In the case of a Relational Database (aka
   // RDBMS) based geodatabase or SDE, the pattern of the Data Source Name is
   // "[DatabaseName].[SchemaOwner].[FeatureClass]". Example "ss6.gdb.States".
   string myDataSourceName = "States.shp"; 

   // Create a new table sub layer source object using the required parameters
   TableSublayerSource myTableSubLayerSource = new TableSublayerSource(myWorkspaceID, myDataSourceName);

   // Create a new ArcGIS map image sub layer based upon the table sub layer source with give it an id value
   ArcGISMapImageSublayer myArcGISMapImageSubLayer = new ArcGISMapImageSublayer(69, myTableSubLayerSource);

   // Define a new simple renderer for the states sub layer. NOTE: Unlike a 'dynamic map layer'
   // which will have a default renderer and symbology applied from the ArcGIS Server map service
   // that was authored via ArcGIS Pro with the Publish a Map Service, a 'dynamic data layer'
   // is authored by the ArcGIS Server Manager and setting up a default renderer and symbology
   // is not an option. As a result, the 'dynamic data layer' will have a random simple render
   // provided if we do not specify and explicit one. We want to control the initial look of the
   // 'dynamic data layer' in our app with a black outline hollow fill (like the
   // COVID19_Counties_Dynamic sub layer).
   SimpleRenderer mySimpleRenderer = new SimpleRenderer();

   // Define a simple fill symbol (black outline with a hollow or transparent fill)
   SimpleFillSymbol mySimpleFillSymbol = new SimpleFillSymbol();
   mySimpleFillSymbol.Style = SimpleFillSymbolStyle.Null;

   // Set the symbol of the states sub layer to the newly defined symbol
   mySimpleRenderer.Symbol = mySimpleFillSymbol;

   // Set the renderer for the map image sub layer
   myArcGISMapImageSubLayer.Renderer = mySimpleRenderer;

   // Create new image layer from the url. NOTE we want to use the same map service
   // as the 'dynamic map layer' because that is where the 'dynamic data layer' was defined.
   var myUri = new Uri("https://[Your.Server].com/server/rest/services/NA/COVID19_Counties/MapServer");

   // Create new image layer from the url
   ArcGISMapImageLayer myArcGISMapImageLayer = new ArcGISMapImageLayer(myUri);

   // Get the list of sub layers from the newly created map image layer
   IList<ArcGISSublayer> myListOfSublayers = myArcGISMapImageLayer.Sublayers;

   // Add the map image sub layer to the map image layer
   myListOfSublayers.Add(myArcGISMapImageSubLayer);

   // Get the map from the map view
   Map myMap = MyMapView.Map;

   // Get the layer collection from the map
   LayerCollection myLayerCollection = myMap.OperationalLayers;

   // Add the ArcGIS map image layer to the layer collection.
   myLayerCollection.Add(myArcGISMapImageLayer);
  }

  private void Button_JoinDynamicLayers_Click(object sender, RoutedEventArgs e)
  {
   // In this function we will create an ArcMapImageSubLayer based on a TableJoinSublayerSource.
   // The TableJoinSubLayerSource takes 2 input generic SubLayerSource objects. We will use
   // the MapSublayerSource and TableSublayerSource objects just like in the
   // Button_DynamicMapLayer_Click() and Button_DynamicDataLayer_Click() functions above.
   // Additionally, we will specify the fields in each of the SubLayerSource's attributes that
   // will be used to join the spatial datasets together to create the TableJoinSublayerSource.

   // Reset the graphics and any operational layers from previous runs
   ResetLayers();

   // Supply the name of the field in the left table for joining. You could fully qualify the
   // field name with it's DataSourceName (Ex: "COVID_Counties.ST_NAME").
   string myLeftFieldName = "ST_NAME";

   // Create a new map sub layer source object. NOTE: The input parameter is the Id (or index
   // number) of the sub layer that is listed in the ArcGIS REST Services Directory documentation
   // for the ArcGIS Server; in this particular case 0 = the 'COVID19_Counties' sub layer.
   MapSublayerSource myLeftSublayerSource = new MapSublayerSource(0);

   // Supply the name of the field in the right table for joining. You could fully qualify the
   // field name with it's DataSourceName (Ex: "States.ST_NAME").
   string myRightFieldName = "ST_NAME";

   // Supply a Workspace ID string. NOTE: You will need to obtain this string from your ArcGIS
   // Server Administrator or author of the 'dynamic data layer' that was created with ArcGIS
   // Server Manager.
   string myWorkspaceID = "HiddenState";

   // Supply the Data Source Name string (sometimes called the acronym 'DSN') that will retrieve
   // records for the 'dynamic data layer'. NOTE: You will need to obtain this string from your
   // ArcGIS Server Administrator or author of the 'dynamic data layer' that was created with
   // ArcGIS Server Manager. In the case of a shapefile, the pattern of the Data Source Name
   // is "[ShapeFileName].shp" (example: "States.shp"). In the case of a Relational Database (aka
   // RDBMS) based geodatabase or SDE, the pattern of the Data Source Name is
   // "[DatabaseName].[SchemaOwner].[FeatureClass]". Example "ss6.gdb.States".
   string myDataSourceName = "States.shp"; 

   // Create a new table sub layer source object using the required parameters
   TableSublayerSource myTableSubLayerSource = new TableSublayerSource(myWorkspaceID, myDataSourceName);

   // Create a new table join sub layer source object using the required parameters. NOTE: The
   // LeftSubLayerSource is has the Geometry that will displayed in the map.
   TableJoinSublayerSource myTableJoinSublayerSource = new TableJoinSublayerSource(myLeftFieldName, myLeftSublayerSource, myRightFieldName, myTableSubLayerSource, JoinType.InnerJoin);

   // Create a new map image sub layer based upon the table sub layer source with give it an id value
   ArcGISMapImageSublayer myArcGISMapImageSubLayer = new ArcGISMapImageSublayer(7, myTableJoinSublayerSource);

   // Create a new unique value renderer
   UniqueValueRenderer myUniqueValueRenderer = new UniqueValueRenderer();

   // Add the "REGION" field name to the renderer. NOTE: You can find out the names of the fields in
   // a dynamic sub layer from your ArcGIS Server Administrator or author of the dynamic layer that
   // was created with ArcGIS Desktop/ArcGIS Pro.
   // IMPORTANT: When supplying a field name for a renderer (UniqueValue and ClassBreaks) that is
   // based on a table join, you must fully qualify the field name by including the DataSourceName
   // (ex: "State.REGION" for a shapefile, or "ss6.gdb.States.REGION" for RDBMS or SDE).
   myUniqueValueRenderer.FieldNames.Add("States.REGION");

   // Define a line symbol to use for the sub_region fill symbols
   SimpleLineSymbol stateOutlineSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, System.Drawing.Color.Black, 0.7);

   // Define distinct fill symbols for a few regions (use the same outline symbol)
   SimpleFillSymbol pacificFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Violet, stateOutlineSymbol);
   SimpleFillSymbol wncFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Blue, stateOutlineSymbol);
   SimpleFillSymbol wscFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Red, stateOutlineSymbol);
   SimpleFillSymbol encFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.White, stateOutlineSymbol);
   SimpleFillSymbol mtnFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Brown, stateOutlineSymbol);
   SimpleFillSymbol nengFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Cyan, stateOutlineSymbol);
   SimpleFillSymbol escFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Green, stateOutlineSymbol);
   SimpleFillSymbol midatlFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Gray, stateOutlineSymbol);
   SimpleFillSymbol satlFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Orange, stateOutlineSymbol);

   // Add values to the renderer: define the label, description, symbol, and attribute value for each
   myUniqueValueRenderer.UniqueValues.Add(new UniqueValue("Pacific", "Pacific", pacificFillSymbol, "Pacific"));
   myUniqueValueRenderer.UniqueValues.Add(new UniqueValue("W N Cen", "W N Cen", wncFillSymbol, "W N Cen"));
   myUniqueValueRenderer.UniqueValues.Add(new UniqueValue("W S Cen", "W S Cen", wscFillSymbol, "W S Cen"));
   myUniqueValueRenderer.UniqueValues.Add(new UniqueValue("E N Cen", "E N Cen", encFillSymbol, "E N Cen"));
   myUniqueValueRenderer.UniqueValues.Add(new UniqueValue("Mtn", "Mtn", mtnFillSymbol, "Mtn"));
   myUniqueValueRenderer.UniqueValues.Add(new UniqueValue("N Eng", "N Eng", nengFillSymbol, "N Eng"));
   myUniqueValueRenderer.UniqueValues.Add(new UniqueValue("E S Cen", "E S Cen", escFillSymbol, "E S Cen"));
   myUniqueValueRenderer.UniqueValues.Add(new UniqueValue("Mid Atl", "Mid Atl", midatlFillSymbol, "Mid Atl"));
   myUniqueValueRenderer.UniqueValues.Add(new UniqueValue("S Atl", "S Atl", satlFillSymbol, "S Atl"));

   // Set the map image sub layer's renderer to the one just created
   myArcGISMapImageSubLayer.Renderer = myUniqueValueRenderer; 

   // Create new image layer from the url
   var myUri = new Uri("https://{your.Server].com/server/rest/services/NA/COVID19_Counties/MapServer");

   // Create new image layer from the url
   ArcGISMapImageLayer myArcGISMapImageLayer = new ArcGISMapImageLayer(myUri);

   // Get the list of sub layers from the newly created dynamic map image layer
   IList<ArcGISSublayer> myListOfSublayers = myArcGISMapImageLayer.Sublayers;

   // Add the map image sub layer to the map image layer
   myListOfSublayers.Add(myArcGISMapImageSubLayer);

   // Get the map from the map view
   Map myMap = MyMapView.Map;

   // Get the layer collection from the map
   LayerCollection myLayerCollection = myMap.OperationalLayers;

   // Add the dynamic map layer to the layer collection
   myLayerCollection.Add(myArcGISMapImageLayer);
  }

  private async void MyMapView_GeoViewTapped(object sender, GeoViewInputEventArgs e)
  {
   // Get the graphics overlay collection
   GraphicsOverlayCollection myGraphicsOverlayerCollection = MyMapView.GraphicsOverlays;

   // Get the first graphics overlay in the collection
   GraphicsOverlay myGraphicsOverlay = myGraphicsOverlayerCollection[0];

   // Get the graphic collection 
   GraphicCollection myGraphicCollection = myGraphicsOverlay.Graphics;

   // Clear out all of the graphics in the graphic collection
   myGraphicCollection.Clear();

   // Set the maximum number of features to be selected/returned from the identify operation
   long myMaxSelectedFeatures = 50;

   // Set the search tolerance in pixels for the identify to find features from where the user
   // touched on the map 
   double myTolerance = 3;

   try
   {
    // Get the location on the map where the user touched (tapped, mouse click, etc.)
    var myScreenPoint = e.Position;

    // Get the map from the map view
    Map myMap = MyMapView.Map;

    // Get the collection of all layers in the map
    IReadOnlyList<Layer> myReadOnlyListOfLayers = myMap.AllLayers;

    // Get the second item in the collection of layers. It should be the dynamic layer, either one
    // created as a 'dynamic map layer' or a 'dynamic data layer'.
    Layer myLayer = myReadOnlyListOfLayers[1];

    // Get the identify layer result from where the user touched on the map subject to the
    // parameters entered
    IdentifyLayerResult myIdentifyLayerResult = await MyMapView.IdentifyLayerAsync(myLayer, myScreenPoint, myTolerance, false, myMaxSelectedFeatures);

    // Get the collection of identify layer result values from the sub layers
    IReadOnlyList<IdentifyLayerResult> mySublayerResults = myIdentifyLayerResult.SublayerResults;

    // Proceed if we have at least one result
    if (mySublayerResults.Count > 0)
    {
     // Loop through each identify layer result
     foreach (var oneIdentifyLayerResult in mySublayerResults)
     {
      // Get the geo element (shape + attributes) for the identify layer result
      IReadOnlyList<GeoElement> myGeoElements = oneIdentifyLayerResult.GeoElements;

      // Loop through each geo element
      foreach (var oneGeoElement in myGeoElements)
      {
       // Get the geometry from the geo element
       Esri.ArcGISRuntime.Geometry.Geometry myGeometry = oneGeoElement.Geometry;

       // Create symbol for the polygon
       SimpleFillSymbol mySimpleFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Yellow, null);

       // Create new graphic using the geometry and symbol to display to the user
       Graphic myPolygonGraphic = new Graphic(myGeometry, mySimpleFillSymbol);

       // Create overlay to where graphics are shown
       MyMapView.GraphicsOverlays[0].Graphics.Add(myPolygonGraphic);

       string keyValues = "";

       // Construct the identify information string to be presented to the
       // user based upon what is found in the key/value pairs of the geo elements
       foreach (KeyValuePair<string, object> element in oneGeoElement.Attributes)
       {
        keyValues += element.Key + " = " + (element.Value ?? "Null").ToString() + Environment.NewLine;
       }

       // Display the identify information to the user
       MessageBox.Show(keyValues, "Identify Results:");
      }
     }
    }
   }
   catch (Exception ex)
   {
    // If there was a problem display it to the user
    MessageBox.Show(ex.Message);
   }
  }
 }
}

 

 

After the code complied without any errors, it was time to test out the app. When the app opens, it shows the basemap layer of the world light gray base, zoomed into the continental USA.

First, let's click the 1 - Show Dynamic Map Layer button. This will add a USA counties dynamic map layer based on the ArcGIS Server published map service. Then, tap on any county to see the attribute information display for that record.

image.png

Second, click the 2 - Show Dynamic Data Layer button. This adds a USA states dynamic data layer based private registered dataset using ArcGIS Server Manager for the ArcGIS Server published map service. Then, tap on any state to see the attribute information display for that record.

image.png

Third, click the 3 - Join Dynamic Layers and Symbolize button. This adds a dynamic layer to the map that is a hybrid of a dynamic map layer and a dynamic data layer. The map shows the USA counties layer that has the attribute information from the USA states layer joined to it. Tap on any county to see the attribute information and you will find the attributes are display from both data sets. The layer will use a unique value renderer to show the counties by the region of the USA they belong to. Notice the identify results MessageBox to see that the name of the fields are prepended with which data layer the attribute belongs to. This shows the power of a spatial join, and accessing information that you want to keep private. 

image.png

Resources to learn more:

ArcGIS Server
[Dynamic layers] https://enterprise.arcgis.com/en/server/latest/publish-services/windows/about-dynamic-layers.htm
[Supported functionality in map services] https://enterprise.arcgis.com/en/server/latest/publish-services/windows/supported-functionality-in-m...
[Log in to Manager] https://enterprise.arcgis.com/en/server/latest/administer/linux/log-in-to-manager.htm

ArcGIS Runtime .NET Samples that demonstrate ArcGISMapImageLayer.Sublayers capabilities
[Change sublayer renderer] https://developers.arcgis.com/net/wpf/sample-code/change-sublayer-renderer/
[Map image layer sublayer visibility] https://developers.arcgis.com/net/wpf/sample-code/map-image-layer-sublayer-visibility/
[Identify layers] https://developers.arcgis.com/net/wpf/sample-code/identify-layers/
[Query map image sublayer] https://developers.arcgis.com/net/wpf/sample-code/query-map-image-sublayer/

Source for COVID-19 county level data
[University of Virginia] https://nssac.bii.virginia.edu/covid-19/dashboard/

About the Author
I have been with Esri for over 21 years and I had the privilege of working on/with various software products including: ArcGIS Maps SDK for Qt, ArcGIS Maps SDK for .NET, ArcGIS Desktop, Arc Pro, ArcGIS Server, Map Objects, ArcView GIS, and even PC Arc/Info. Currently, I author content on the https://developers.arcgis.com web site helping developers be successful with Esri software.