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:
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.