Skip To Content ArcGIS for Developers Sign In Dashboard

ArcGIS Runtime SDK for .NET

Symbol Dictionary Search

Download Samples Repository

Description

Sample shows how to use search the Mil2525C symbol dictionary.

"Desktop" "Store" "Phone" Available for Desktop, Store, Phone

Sample Code

<UserControl x:Class="ArcGISRuntime.Samples.Desktop.Symbology.Specialized.SymbolDictionarySearchSample"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
			 xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
			 xmlns:special="clr-namespace:Esri.ArcGISRuntime.Symbology.Specialized;assembly=Esri.ArcGISRuntime"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="800" d:DesignWidth="600">
    <UserControl.Resources>
        <Style x:Key="overlayBorderStyle" TargetType="Border">
            <Setter Property="BorderBrush" Value="Black" />
            <Setter Property="Background" Value="White" />
            <Setter Property="BorderThickness" Value="2" />
            <Setter Property="Margin" Value="30" />
            <Setter Property="VerticalAlignment" Value="Top" />
        </Style>
        <Style x:Key="headerTextBlockStyle" TargetType="TextBlock">
            <Setter Property="Margin" Value="5,5,10,5" />
            <Setter Property="FontWeight" Value="Bold" />
        </Style>
    </UserControl.Resources>
    <Grid>
        <esri:MapView x:Name="MyMapView">
            <esri:Map>
                <esri:ArcGISTiledMapServiceLayer ID="Basemap"
					ServiceUri="http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"/>
                <special:MessageLayer ID="Messages" SymbolDictionaryType="Mil2525C" />
            </esri:Map>
        </esri:MapView>

        <!-- Sample info -->
        <Border Style="{StaticResource overlayBorderStyle}"
                HorizontalAlignment="Left" >
            <StackPanel Margin="30,20">
                <TextBlock Text="Search the symbol dictionary by choosing a combination of one or more keywords or filters. To view symbols on the map select a symbol from the results list then click on the map."
                           FontSize="14"  Width="400" TextAlignment="Left" TextWrapping="Wrap"/>
            </StackPanel>
        </Border>

        <!-- Search -->
        <Border Style="{StaticResource overlayBorderStyle}"
                HorizontalAlignment="Right" VerticalAlignment="Top">
            <Grid Width="400" >
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="auto"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="auto"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="auto"/>
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>

                <!-- Keywords -->
                <TextBlock Text="Keywords:" 
						   Style="{StaticResource headerTextBlockStyle}"
						   Grid.ColumnSpan="2" Grid.Row="0"/>
                <TextBlock Text="Keyword:" Margin="10,5,10,5" Grid.Row="1"/>
                <ComboBox Name="cmbKeyword" SelectionChanged="KeywordSelectionChanged" 
						  ItemsSource="{Binding Keywords}" 
                           Margin="5,5,5,0" 
						  Grid.Column="1" Grid.Row="1" />
                <ListBox Name="lstKeywords" 
						 ItemsSource="{Binding SelectedKeywords}"
                         Margin="10,5,10,5"
						 Grid.Row="2" Grid.ColumnSpan="2"
						 BorderThickness="0">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding}" Margin="0,0,0,5" MinWidth="350"/>
                                <Button Height="16" Width="16" Click="RemoveKeyword" HorizontalAlignment="Right" >
                                    <Image Source="/ArcGISRuntime;component/Assets/x-24x24.png"/>
                                </Button>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>

                <!-- Filters -->
                <TextBlock Text="Filters:"
						   Style="{StaticResource headerTextBlockStyle}"
						   Grid.Row="3" Grid.ColumnSpan="2"/>

                <!-- Category -->
                <TextBlock Text="Category:" Margin="10,5,5,5" Grid.Row="4"/>
                <ComboBox Name="cmbCategory" 
						  ItemsSource="{Binding Categories}" SelectionChanged="SelectionChanged"  
						  Grid.Row="4" Grid.Column="1" 
                           Margin="5,5,5,0"/>
                <Button Name="btnClearCategory" Grid.Row="5" Grid.Column="1" Height="20" HorizontalAlignment="Right" 
                        Margin="5,5,5,0" Width="110" Content="Clear Category" Click="btnClearCategory_Click"  />
                <TextBlock Text="Symbol ID Search:" 
						   Style="{StaticResource headerTextBlockStyle}"
						   Grid.Row="6"  Grid.ColumnSpan="2"/>

                <TextBlock Text="Symbol ID:" Margin="10,5,5,5" Grid.Row="7"/>
                <TextBox Name="txtSymbolName"  
                                  Margin="5,5,5,0" Grid.Row="7" Grid.Column="1"/>

                <Button Name="btnSearch" Margin="5,5,5,0" Click="btnSearch_Click" Grid.Row="8" Grid.Column="1" IsEnabled="False">Search</Button>
                <!-- Results -->
                <TextBlock Text="Symbol Results:" 
						   Style="{StaticResource headerTextBlockStyle}"
						   Grid.Row="9" Grid.ColumnSpan="2" />
                <TextBlock Text="Symbols found:" Margin="10,5,10,5" Grid.Row="10" />
                <TextBlock Text="{Binding Symbols.Count}" 
						   Margin="15,5,10,5" Grid.Row="10" Grid.Column="1" />

                <ListBox x:Name="SymbolListBox" 
						 ItemsSource="{Binding Symbols}" SelectionChanged="SymbolListBox_SelectionChanged"
						 SelectionMode="Single"
						 Grid.ColumnSpan="2" Margin="10,0,10,15" Grid.Row="11">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <!-- Symbol -->
                                <Image Source="{Binding Thumbnail}"
									   Width="{Binding ImageSize}" Height="{Binding ImageSize}" 
									   Grid.Row="1" Grid.Column="0" 
									   VerticalAlignment="Center" Stretch="Uniform" />
                                <!-- Info -->
                                <StackPanel>
                                    <TextBlock Text="{Binding Name}" FontWeight="Bold" Grid.Column="1" />
                                    <Grid>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="auto"></RowDefinition>
                                            <RowDefinition Height="auto"></RowDefinition>
                                            <RowDefinition Height="auto"></RowDefinition>
                                        </Grid.RowDefinitions>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="auto"/>
                                            <ColumnDefinition Width="auto"/>
                                        </Grid.ColumnDefinitions>
                                        <TextBlock Text="Category:" Grid.Row="0" Grid.Column="0"/>
                                        <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Category}"/>
                                        <TextBlock Text="Symbol ID:" Grid.Row="1" Grid.Column="0"/>
                                        <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding SymbolID}"/>
                                    </Grid>
                                </StackPanel>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
                <Button x:Name="ClearSymbols" Content="Clear Map"
                        Grid.Row="12" Grid.ColumnSpan="2"
                        HorizontalAlignment="Center" Height="20" Width="150"
                        Margin="5" Click="ClearMap_Click"></Button>
            </Grid>

        </Border>
    </Grid>
</UserControl>
using Esri.ArcGISRuntime.Controls;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Symbology.Specialized;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ArcGISRuntime.Samples.Desktop.Symbology.Specialized
{
    /// <summary>
    /// Sample shows how to use search the Mil2525C symbol dictionary.
    /// </summary>
    /// <title>Symbol Dictionary Search</title>
    /// <category>Symbology</category>
    /// <subcategory>Specialized</subcategory>
	/// <requiresSymbols>true</requiresSymbols>
    public partial class SymbolDictionarySearchSample : UserControl, INotifyPropertyChanged
    {
        private SymbolViewModel _selectedSymbol;
        private SymbolDictionary _symbolDictionary;
        private List<string> _keywords;
        private int _imageSize;
        private MessageLayer _messageLayer;

        public SymbolDictionarySearchSample()
        {
            InitializeComponent();

            Init();
        }

        private async void Init()
        {
            // Wait until all layers are loaded
            await MyMapView.LayersLoadedAsync();

            bool isSymbolDictionaryInitialized = false;
            try
            {
                // Create a new SymbolDictionary instance 
                _symbolDictionary = new SymbolDictionary(SymbolDictionaryType.Mil2525c);
                isSymbolDictionaryInitialized = true;
            }
            catch { }

            if (!isSymbolDictionaryInitialized)
            {
                MessageBox.Show("Failed to create symbol dictionary.", "Symbol Dictionary Search Sample");
                return;
            }

            // Collection of strings to hold the selected symbol dictionary keywords
            SelectedKeywords = new ObservableCollection<string>();

            // Remove any empty strings space from keywords
            _keywords = _symbolDictionary.Keywords.OrderBy(k => k).ToList();
            _keywords = _keywords.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList();

            // Remove any empty strings space from categories
            _categories = new[] { "" }.Concat(_symbolDictionary.Filters["CATEGORY"]);
            Categories = _categories.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct();

            // Collection of view models for the displayed list of symbols
            Symbols = new ObservableCollection<SymbolViewModel>();

            // Set the image size
            _imageSize = 64;

            // Get reference to MessageLayer to use with messages
            _messageLayer = MyMapView.Map.Layers.OfType<MessageLayer>().First();

            // Fire initial search to populate the results with all symbols
            Search();

            // Enable the UI
            btnSearch.IsEnabled = true;

            // Set the DataContext for binding
            DataContext = this;
        }

        // Search results 
        public ObservableCollection<SymbolViewModel> Symbols { get; private set; }

        // Currently selected keywords
        public ObservableCollection<string> SelectedKeywords { get; private set; }

        // All keywords in alphabetical order
        public IEnumerable<string> Keywords
        {
            get { return _keywords; }
            set
            {
                _keywords = value.ToList();
                RaisePropertyChanged("Keywords");
            }
        }

        // All categories used in symbol dictionary
        private IEnumerable<string> _categories;
        public IEnumerable<string> Categories
        {
            get { return _categories; }
            set
            {
                _categories = value;
                RaisePropertyChanged("Categories");
            }
        }

        // SelectedChanged event handler for the ListBoxes
        private void SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Search();
        }

        // Remove a keyword from the selected keywords collection and rerun the search
        private void RemoveKeyword(object sender, RoutedEventArgs e)
        {
            Button b = (Button)sender;
            string kw = (string)b.DataContext;
            SelectedKeywords.Remove(kw);
            Search();
        }

        // SelectionChanged event handler for the keywords ListBox
        private void KeywordSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (string.IsNullOrEmpty((string)cmbKeyword.SelectedValue))
                return;

            var kw = cmbKeyword.SelectedValue.ToString();

            // Add keyword to selected list if it is not already there
            if (SelectedKeywords.FirstOrDefault(s => s == kw) == null)
                SelectedKeywords.Add(kw);
            Search();
        }

        // Request geometry and new message to the layer
        private async void AddSymbolAsync(DrawShape requestedShape)
        {
            try
            {
                // Keep adding messages until next symbol is selected
                while (true)
                {
                    Esri.ArcGISRuntime.Geometry.Geometry geometry = null;

                    try
                    {
                        geometry = await MyMapView.Editor.RequestShapeAsync(requestedShape, null, null);
                    }
                    catch { }

                    if (geometry == null)
                        return;

                    // Create a new message
                    Message msg = new Message();

                    // Set the ID and other parts of the message
                    msg.Id = Guid.NewGuid().ToString();
                    msg.Add("_type", "position_report");
                    msg.Add("_action", "update");
                    msg.Add("_wkid", MyMapView.SpatialReference.Wkid.ToString());
                    msg.Add("sic", _selectedSymbol.SymbolID);
                    msg.Add("uniquedesignation", "1");

                    // Construct the Control Points based on the geometry type of the drawn geometry.
                    switch (requestedShape)
                    {
                        case DrawShape.Point:
                            MapPoint point = geometry as MapPoint;
                            msg.Add("_control_points", point.X.ToString(CultureInfo.InvariantCulture) + "," + point.Y.ToString(CultureInfo.InvariantCulture));
                            break;
                        case DrawShape.Polygon:
                            Polygon polygon = geometry as Polygon;
                            string cpts = string.Empty;
                            foreach (var pt in polygon.Parts[0].GetPoints())
                                cpts += ";" + pt.X.ToString(CultureInfo.InvariantCulture) + "," + pt.Y.ToString(CultureInfo.InvariantCulture);
                            msg.Add("_control_points", cpts);
                            break;
                        case DrawShape.Polyline:
                            Polyline polyline = geometry as Polyline;
                            cpts = string.Empty;
                            foreach (var pt in polyline.Parts[0].GetPoints())
                                cpts += ";" + pt.X.ToString(CultureInfo.InvariantCulture) + "," + pt.Y.ToString(CultureInfo.InvariantCulture);
                            msg.Add("_control_points", cpts);
                            break;
                    }

                    // Process the message
                    if (!_messageLayer.ProcessMessage(msg))
                        MessageBox.Show("Failed to process message.");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Symbol Dictionary Search Sample");
            }
        }

        // Sets the currently selected symbol
        private void SymbolListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count != 1)
                return;

            _selectedSymbol = e.AddedItems[0] as SymbolViewModel;

            Dictionary<string, string> values = (Dictionary<string, string>)_selectedSymbol.Model.Values;
            string geometryControlType = values["GeometryType"];
            DrawShape requestedShape = DrawShape.Point;

            switch (geometryControlType)
            {
                case "Point":
                    requestedShape = DrawShape.Point;
                    break;
                case "Line":
                    requestedShape = DrawShape.Polyline;
                    break;
                case "Polygon":
                    requestedShape = DrawShape.Polygon;
                    break;
                case "Circle":
                    requestedShape = DrawShape.Circle;
                    break;
                case "Rectangular":
                    requestedShape = DrawShape.Rectangle;
                    break;
                default:
                    MessageBox.Show("Selected symbol is not supported in this sample");
                    return;
            }

            // Enable adding symbols to the map
            AddSymbolAsync(requestedShape);
        }

        // Function to search the symbol dictionary based on the selected value in the style file, category and/or geometry type ListBoxes
        private void Search()
        {
            Dictionary<string, string> filters = new Dictionary<string, string>();

            // Set Category filter
            if (!string.IsNullOrEmpty((string)cmbCategory.SelectedValue))
                filters["CATEGORY"] = cmbCategory.SelectedValue.ToString();

            // Clear the current Symbols collection
            Symbols.Clear();

            // Perform the search applying any selected keywords and filters 
            IEnumerable<SymbolProperties> symbols = _symbolDictionary.FindSymbols(SelectedKeywords, filters);
            var allSymbols = symbols.ToList();

            // Update the list of applicable keywords (excluding any keywords that are not on the current result set)
            if (SelectedKeywords == null || SelectedKeywords.Count == 0)
            {
                _keywords = _symbolDictionary.Keywords.ToList().Where(k => !IsSymbolId(k)).ToList();
            }
            else
            {
                IEnumerable<string> allSymbolKeywords = allSymbols.SelectMany(s => s.Keywords);
                _keywords = allSymbolKeywords.Distinct()
                    .Except(SelectedKeywords)
                    .Where(k => !IsSymbolId(k))
                    .ToList();
            }
            RaisePropertyChanged("Keywords");

            // Add symbols to UI collection
            foreach (var s in from symbol in allSymbols select new SymbolViewModel(symbol, _imageSize))
                Symbols.Add(s);
        }

        // Do not add keywords which represent a single symbol to the Keywords list.
        private bool IsSymbolId(string keyword)
        {
            if (keyword.Length == 15)
            {
                keyword = keyword.ToUpperInvariant();

                if (!"SGWIOE".Contains(keyword[0]))
                    return false;

                if (!"PUAFNSHGWMDLJKO-".Contains(keyword[1]))
                    return false;

                if (!"PAGSUFXTMOEVLIRNZ-".Contains(keyword[2]))
                    return false;

                if (!"APCDXF-".Contains(keyword[3]))
                    return false;

                if (Enumerable.Range(4, 6).Any(i => !"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-".Contains(keyword[i])))
                    return false;

                if (Enumerable.Range(10, 2).Any(i => !"ABCDEFGHIJKLMNOPQRSTUVWXYZ-*".Contains(keyword[i])))
                    return false;

                if (Enumerable.Range(12, 2).Any(i => !"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-*".Contains(keyword[i])))
                    return false;

                if (!"AECGNSX-".Contains(keyword[14]))
                    return false;

                return true;
            }
            return false;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string name)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(name));
        }

        private void btnSearch_Click(object sender, RoutedEventArgs e)
        {
            IEnumerable<SymbolProperties> symbols = _symbolDictionary.FindSymbols(new List<string>() { txtSymbolName.Text });
            if (symbols == null)
                return;
            Symbols.Clear();
            foreach (var s in from symbol in symbols.ToList() select new SymbolViewModel(symbol, _imageSize))
                Symbols.Add(s);
        }

        private void btnClearCategory_Click(object sender, RoutedEventArgs e)
        {
            // Clear selected items from the Category combo box
            cmbCategory.SelectedIndex = -1;
        }

        private void ClearMap_Click(object sender, RoutedEventArgs e)
        {
            if (_messageLayer != null)
            {
                _messageLayer.ChildLayers.Clear();
            }

        }
    }


    // Presents single symbol
    public class SymbolViewModel : INotifyPropertyChanged
    {
        private int _imageSize;
        private SymbolProperties _model;
        private ImageSource _image;

        public SymbolViewModel(SymbolProperties model, int imageSize)
        {
            _model = model;
            _imageSize = imageSize;
        }

        public string Name { get { return _model.Name; } }

        public string Keywords { get { return string.Join(", ", _model.Keywords); } }

        public string Category
        {
            get { return _model.Values["Category"].ToString(); }
        }

        public string SymbolID
        {
            get { return _model.Values["SymbolID"].ToString(); }
        }

        public int ImageSize
        {
            get { return _imageSize; }
        }

        public ImageSource Thumbnail
        {
            get
            {
                if (_image == null)
                {
                    try
                    {
                        _image = _model.GetImage(_imageSize, _imageSize);
                    }
                    catch (Exception)
                    {
                        return null;
                    }
                }
                return _image;
            }
        }

        public SymbolProperties Model
        {
            get { return _model; }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string name)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(name));
        }
    }
}
Feedback on this topic?