Select to view content in your preferred language

Add some flair to your map with PictureFillSymbols

1446
0
06-07-2021 12:49 PM
Labels (1)
DonKemlage
Esri Contributor
0 0 1,446

Introduction

I am sure you are familiar with the standard solid color fill symbol used to denote the grouping of some phenomena visually in a map layer. An example of this might include countries with over 1 billion people colored red, countries with less than a billion people but more than 500 million colored yellow, and countries with less than 500 million people colored green. But what if you wanted to add a little more flair to your map beyond just using a solid fill to convey information. Using a PictureFillSymbol might be for you.

A PictureFillSymbol is a type of FillSymbol for a polygon layer that is based on an image. The supported image formats for a PictureFillSymbol in ArcGIS Runtime SDK for .NET are: BMP, GIF, ICO, JPEG, and PNG. Note: animated GIF is not supported.

In this article, I will share C# code for a WPF app that shows displaying the image of the state flag as the backdrop for each US state polygon boundary using a UniqueValueRenderer.  

 

ArcGIS Runtime

Using Visual Studio 2019, I created a new C# application based on the WPF framework. Using the NuGet Package 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 ArcGIS Online. Provide your own API Key by signing in to your ArcGIS developer dashboard at: https://developers.arcgis.com/dashboard/

 

 

using System.Windows;

namespace PictureFillFymbol
{
    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 UI for the app.

 

 

<Window x:Class="PictureFillFymbol.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:PictureFillFymbol"
        xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
        mc:Ignorable="d"
        Title="Picture Fill Symbol" 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="40" Margin="0,0,0,10"/>
                <Button x:Name="ButtonPictureFillSymbolLayer" Click="Button_PictureFillSymbol_Click" Content="PictureFillSymbol" Width="350"/>
            </StackPanel>
        </Border>
    </Grid>
</Window>

 

 

MainWindows.Xaml.cs

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

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

(2) The IntializeMap() function sets up the initial look of the UI. It adds the world light gray base map, zoomed to the area of the continental USA to provide orientation for the operational layers.

(3) The Button_PictureFillSymbol_Click() function creates a UniqueValueRenderer for each state polygon in the US using the "state_name" field of a FeatureLayer as the attribute value for the symbolization. Construct a Uri to the US Census bureau state flag images for the PictureFillSymbol used by the UniqueValueRenderer.

 

 

using System;
using System.Windows;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Symbology;
using Esri.ArcGISRuntime.Data;
using System.Threading.Tasks;

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

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

  private async Task InitializeMap()
  {
   // Provide instructions on how to use the app
   TextBlockInstructions.Text = "Click the button to draw a FeatureLayer using a PictureFillSymbol that shows the USA state flags.";

   try
   {
    // Ensure you have set an API key in App.xaml.cs
    Map myMap = new Map(BasemapStyle.ArcGISLightGrayBase);

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

    // Create an envelope that focuses on the continental United States
    Envelope continentalUSEnvelope = 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(continentalUSEnvelope, 20);
   }
   catch (Exception ex)
   {
    MessageBox.Show($"Error: {ex.Message}");
   }
  }

  private async void Button_PictureFillSymbol_Click(object sender, RoutedEventArgs e)
  {
   // Provide the Uri to the ArcGIS Server map service feature layer that has the USA state polygons
   Uri statesLayerUri = new Uri("http://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/2");

   // Create a feature layer from the Uri
   FeatureLayer statesFeatureLayer = new FeatureLayer(statesLayerUri);

   // Get all features
   FeatureQueryResult statesFeatureQueryResult = await statesFeatureLayer.FeatureTable.QueryFeaturesAsync(new QueryParameters()
   {
    WhereClause = "1=1",
    ReturnGeometry = false
   });

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

   // Provide the name of the attribute field that the unique value rendering will be based
   statesUniqueValueRenderer.FieldNames.Add("state_name");

   // Create unique values for the unique value renderer using state flag images
   // available on the USA Census web site. The images will used as a PictureFillSymbol.

   string stateFlagUriFragment = "https://www.census.gov/schools/facts/flags/";

   foreach (Feature stateFeature in statesFeatureQueryResult)
   {
    // Get the raw string attribute value
    string originalStateName = stateFeature.Attributes["state_name"].ToString();

    // Format the raw attribute value to form the Uri
    string formattedStateName = originalStateName.ToLower().Replace(" ", "_");

    // Define the string for the Uri of the state flag image
    Uri imageUri = new Uri($"{stateFlagUriFragment}{formattedStateName}.gif");

    // Create the outline of the polygon boundary
    SimpleLineSymbol outlineSimpleLineSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, System.Drawing.Color.Black, 1);

    // Create a picture fill symbol from the Uri 
    PictureFillSymbol stateFlagPictureFillSymbol = new PictureFillSymbol(imageUri)
    {
     Outline = outlineSimpleLineSymbol
    };

    // Define the label, description, symbol, and attribute value for each unique value
    UniqueValue stateUniqueValue = new UniqueValue(originalStateName, originalStateName, stateFlagPictureFillSymbol, originalStateName);

    // Add the new unique value to the unique values collection of the renderer
    statesUniqueValueRenderer.UniqueValues.Add(stateUniqueValue);
   }

   // Set the renderer of the feature layer to a unique value renderer
   statesFeatureLayer.Renderer = statesUniqueValueRenderer;

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

   // Add the feature layer to the layer collection
   myMap.OperationalLayers.Add(statesFeatureLayer);
  }
 }
}

 

 

The following picture shows the UI of the app after clicking the PictureFillSymbol button:

MichaelBranscomb_1-1623358330980.png

 

Note: Obtaining images over the internet via a Uri for PictureFillSymbol may have degraded performance if the images are large in size and/or you have a slow internet connection. Another option to improve performance could be to have the images locally in the device and use the alternate constructor for the PictureFillSymbol. In this case, the RuntimeImage class could be used to load the image locally.

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.