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?