How to open shape file in ArcGIS Runtime WPF SDK as Graphics/Feature layer

9772
24
01-23-2013 06:23 AM
KevinChan
New Contributor
I googled and followed this tutorial http://www.arcgis.com/home/item.html?id=953a530ada30477d95cc69e788268bc9 and figured out how to import Shape files and Rasters into Runtime.

However, it seems most of the functionality uses a FeatureLayer/GraphicLayer instead an ArcGISLocalDynamicMapServiceLayer that the sample uses.

My question is if there is a way I can import the shape file as a graphics layer or if i can somehow cast my ArcGISLocalDynamicMapServiceLayer as a Graphics layer because shape file is essentially the same as a shape file.

An example is i'm trying to use the FeatureDataGrid() to show the attribute table of my shapep file, but I can't since it requires a graphics layer, not an ArcGISLocalDynamicMapServiceLayer.

Thanks and Regards, Kev
0 Kudos
24 Replies
jp
by
New Contributor II
Hi Mike,

I wanted this same kind of functionality - to access the attributes of a shapefile added on the fly. For which I contacted Esri Tech support and the GIS Analyst after research concluded that its not possible to access attributes for shapefile.

Thanks for the code.

Hi,

I had a revelation that actually I'd probably confused the issue by keeping the ArcGISDynamicMapServiceLayer in my example whilst introducing a FeatureLayer as well. So I've simplified it to just use a FeatureLayer (below). So it now assumes that you actually want to add the FeatureLayer to the map and display that instead of the ArcGISDynamicMapServiceLayer. I'd recommend you definitely use the accelerated display mode if the feature layer may contain many thousands of graphics, or graphics with complex polygons.

Code:

    /// <summary>
    /// Interaction logic for DynamicLayersFeatureDataGrid.xaml
    /// </summary>
    public partial class DynamicLayersFeatureDataGrid : UserControl
    {
        // Get the path of the "empty" MPK from the application folder
        string _emptyMpkPath = @"..\Data\DynamicLayers\EmptyMPK_WGS84.mpk";

        public DynamicLayersFeatureDataGrid()
        {
            InitializeComponent();

            MyDataGrid.SelectionChanged += (s3, e3) =>
            {
                foreach (Graphic graphic in e3.RemovedItems)
                {
                    graphic.SetZIndex(0);
                }
                foreach (Graphic graphic in e3.AddedItems)
                {
                    graphic.SetZIndex(1);
                }
            };
        }

        /// <summary>
        /// Handles the Click event of the AddShapefileButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void AddShapefileButton_Click(object sender, RoutedEventArgs e)
        {
            // Setup the OpenFiledialog.
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "Shapefiles (*.shp)|*.shp";
            openFileDialog.RestoreDirectory = true;
            openFileDialog.Multiselect = false; // This sample assumes a single file is selected

            if (openFileDialog.ShowDialog() == true)
            {
                try
                {
                    // Remove any existing FeatureLayers in the Map
                    List<FeatureLayer> featureLayers = MyMap.Layers.OfType<FeatureLayer>().ToList();
                    foreach (var fl in featureLayers)
                    { MyMap.Layers.Remove(fl); }

                    // Call the add dataset method with workspace type, parent directory path, file names (without extensions) and delegate.
                    AddFileDatasetToDynamicMapServiceLayer(WorkspaceFactoryType.Shapefile,
                        Path.GetDirectoryName(openFileDialog.FileName),
                        Path.GetFileNameWithoutExtension(openFileDialog.SafeFileName));
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
            }
        }

        /// <summary>
        /// Adds a file dataset (Shapefile) to a new feature layer.
        /// </summary>
        /// <param name="workspaceType">The workspace type (FileGDB, Raster, SDE, Shapefile) <see cref="http://resources.arcgis.com/en/help/runtime-wpf/apiref/index.html?ESRI.ArcGIS.Client.Local~ESRI.ArcGIS.Client.Local.WorkspaceFactoryType.html"/>.</param>
        /// <param name="directoryPath">A <see cref="System.String"/> representing the directory path.</param>
        /// <param name="fileNames">A <see cref="System.Collections.Generic.List{System.String}"/> representing the name of the file.</param>
        public void AddFileDatasetToDynamicMapServiceLayer(WorkspaceFactoryType workspaceType, string directoryPath, string fileName) 
        {
            try
            {
                // Generate a unique workspace ID (any unique string).
                string uniqueId = Guid.NewGuid().ToString();

                // Create a new WorkspaceInfo object with a unique ID.
                WorkspaceInfo workspaceInfo = new WorkspaceInfo(uniqueId, workspaceType, "DATABASE=" + directoryPath);

                // Create a new LocalMapService instance.
                LocalMapService localMapService = new LocalMapService
                {
                    Path = _emptyMpkPath, // Set the path property. 
                    EnableDynamicLayers = true, // Enable the dynamic layers capability.
                    MaxRecords = 1000000, // Set the maximum number of records
                };

                // Register the workspace to be used with this service.
                localMapService.DynamicWorkspaces.Add(workspaceInfo);

                // Asynchronously start the local map service.
                localMapService.StartAsync(x =>
                {
                    // Create a new ArcGISLocalDynamicMapServiceLayer passing in the newly started local service.
                    FeatureLayer featureLayer = new FeatureLayer()
                    {
                        Url = localMapService.UrlMapService + "/dynamicLayer", // Construct the URL to include the /dynamicLayer resource.
                        ID = fileName, // Assign ID 
                        OutFields = new ESRI.ArcGIS.Client.Tasks.OutFields() { "*" }, // Display all fields
                        SelectionColor = new SolidColorBrush(Colors.Yellow), // Yellow is generally a nice selection color
                    };

                    // The workspace is a feature class so create a new TableDataSource
                    DataSource dataSource = new TableDataSource
                    {
                        DataSourceName = fileName, // Match the DataSourceName to the physical filename on disk (excluding extension).
                        WorkspaceID = workspaceInfo.Id // Provide the WorkspaceID (the unique workspace identifier created earlier).
                    };

                    // Set the Source property of the DynamicLayerInfo object.
                    LayerDataSource layerDataSource = new LayerDataSource { DataSource = dataSource };

                    // Assign the LayerDataSource
                    featureLayer.Source = layerDataSource;

                    featureLayer.Initialized += (s, e) =>
                    {
                        // Set the FeatureDataGrid's Map property
                        MyDataGrid.Map = MyMap;

                        // Set the new FeatureLayer as the FeatureDataGrid's GraphicsLayer property
                        MyDataGrid.GraphicsLayer = featureLayer as GraphicsLayer;

                        SimpleRenderer renderer = null;

                        switch (featureLayer.LayerInfo.GeometryType)
                        {
                            case ESRI.ArcGIS.Client.Tasks.GeometryType.MultiPoint:
                                renderer = new SimpleRenderer() { Symbol = new SimpleMarkerSymbol() { Color = new SolidColorBrush(GetRandomColor()), Size = 8 } };
                                break;
                            case ESRI.ArcGIS.Client.Tasks.GeometryType.Point:
                                renderer = new SimpleRenderer() { Symbol = new SimpleMarkerSymbol() { Color = new SolidColorBrush(GetRandomColor()), Size = 8 } };
                                break;
                            case ESRI.ArcGIS.Client.Tasks.GeometryType.Polygon:
                                renderer = new SimpleRenderer() { Symbol = new SimpleFillSymbol() { Fill = new SolidColorBrush(GetRandomColor()), BorderBrush = new SolidColorBrush(GetRandomColor()) } };
                                break;
                            case ESRI.ArcGIS.Client.Tasks.GeometryType.Polyline:
                                renderer = new SimpleRenderer() { Symbol = new SimpleLineSymbol() { Color = new SolidColorBrush(GetRandomColor()) } };
                                break;
                            default:
                                break;
                        }

                        featureLayer.Renderer = renderer;
                    };
                    MyMap.Layers.Add(featureLayer);
                });
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        // Utility function: Generate a random System.Windows.Media.Color
        Random _random = new Random();
        private Color GetRandomColor()
        {
            var colorBytes = new byte[3];
            _random.NextBytes(colorBytes);
            Color randomColor = Color.FromRgb(colorBytes[0], colorBytes[1], colorBytes[2]);
            return randomColor;
        }

        private void Legend_Refreshed(object sender, Legend.RefreshedEventArgs e)
        {
            // Clear the sub items from the basemap layer.
            if (e.LayerItem.Layer == _worldTopographicBasemap)
                e.LayerItem.LayerItems.Clear();
        }
    }



Sorry - I haven't had time to look at the code in your previous yet... it's the end of the week here...

Cheers

Mike
0 Kudos
GeorgeFaraj
Occasional Contributor III
jhansi131,

That's strange, because I can access the attributes of shapefile features. I used the approach mentioned in this post. The only thing I haven't been able to do with shapefiles is use the Editor toolkit control to select features on it. If anyone has any suggestions, please let me know.
0 Kudos
MichaelBranscomb
Esri Frequent Contributor

The only thing I haven't been able to do with shapefiles is use the Editor toolkit control to select features on it. If anyone has any suggestions, please let me know.


Hi,

To get selections to work you'll need to make sure the Editor control has it's Map property set, and do this in code because the binding does not work in WPF when done in XAML. You'll also need to add the ID of your new layer to the LayerIDs property of the Editor control. Then selections should just work...

To test this I combined the code from earlier in this thread which involves a changing the datasource of a FeatureLayer and binding that to the FeatureDataGrid, with the code from one of the Edit tools samples (EditToolsAutoSave) which demonstrates a custom toolbar of selection tools and it worked fine.

The key things I did were:-

In the Map.Loaded event I set the Editor.Map property:

ESRI.ArcGIS.Client.Editor myEditor = LayoutRoot.Resources["MyEditor"] as ESRI.ArcGIS.Client.Editor;
myEditor.Map = MyMap;


Then in the FeatureLayer.Initialized event handler I set the LayerIDs property:

Editor editor = LayoutRoot.Resources["MyEditor"] as Editor;
editor.LayerIDs = new List<String>() { featureLayer.ID };


Let me know if you have any issues.

Cheers

Mike
0 Kudos
GeorgeFaraj
Occasional Contributor III
Yes, I am doing all of this.

Upon closer inspection, it does seem to be selecting graphics (the SelectedGraphics collection of the layer does get updated correctly), but I don't see the features highlighted on the map. I have set a SelectionColor of yellow. Any ideas why this would happen?

Thanks!
0 Kudos
DominiqueBroux
Esri Frequent Contributor
Some symbols don't support the 'SelectionColor'.

Did you create your symbols by yourself or did you get them from the service?
0 Kudos
GeorgeFaraj
Occasional Contributor III
What do you mean by 'symbols'?
0 Kudos
DominiqueBroux
Esri Frequent Contributor
What do you mean by 'symbols'?

I meant, did you instantiate by code one symbol of the ESRI.ArcGIS.Client.Symbols namespace in order to set the Symbol property of a renderer?

From your question I guess you didn't, but It was worth checking because these symbols don't support the SelectionColor
0 Kudos
GeorgeFaraj
Occasional Contributor III
Well, I might be doing that, I just wasn't sure why the Renderer would affect the selection color. Here's the code:

private void AddFeatureLayerFromShapeFile(UserMapLayer userMapLayer, ArcGISLocalDynamicMapServiceLayer dynamicLayer)
{
 var featureLayer = new FeatureLayer();
 featureLayer.ID = Guid.NewGuid().ToString();
 featureLayer.DisplayName = dynamicLayer.DisplayName;
 featureLayer.OutFields.Add("*");
 featureLayer.Mode = FeatureLayer.QueryMode.OnDemand;
 featureLayer.OnDemandCacheSize = 500;
 featureLayer.MaximumResolution = 15;
 featureLayer.Url = dynamicLayer.Service.UrlMapService + "/dynamicLayer";
 featureLayer.Source = dynamicLayer.DynamicLayerInfos[0].Source;
 //featureLayer.SelectionColor = System.Windows.Media.Brushes.Yellow;
 //featureLayer.Renderer = dynamicLayer.LayerDrawingOptions[0].Renderer;

 featureLayer.Initialized += (s1, e1) =>
 {
  featureLayer.SelectionColor = System.Windows.Media.Brushes.Yellow;
  featureLayer.Renderer = dynamicLayer.LayerDrawingOptions[0].Renderer;

  if (featureLayer.DisplayName == "parcels") // TEMP
  {
   context.Selection.OnParcelLayerLoaded();
  }
 };
 AddLayer(featureLayer, true, userMapLayer);
}


The dynamicLayer.LayerDrawingOptions[0].Renderer is this:


Renderer = GetRandomSimpleRenderer()


And the support methods:


public static SimpleRenderer GetRandomSimpleRenderer()
{
 return new SimpleRenderer() { Symbol = GetRandomSimpleFillSymbol() };
}

public static SimpleFillSymbol GetRandomSimpleFillSymbol()
{
 return new SimpleFillSymbol() { Fill = GetRandomSolidBrush() };
}

public static SolidColorBrush GetRandomSolidBrush()
{
 return new SolidColorBrush(GetRandomColor());
}
  
public static Color GetRandomColor()
{
 var colorBytes = new byte[3];
 random.NextBytes(colorBytes);
 return Color.FromRgb(colorBytes[0], colorBytes[1], colorBytes[2]);
}


Do you see anything wrong with this?

Thanks,
George
0 Kudos
DominiqueBroux
Esri Frequent Contributor
public static SimpleFillSymbol GetRandomSimpleFillSymbol()
{
return new SimpleFillSymbol() { Fill = GetRandomSolidBrush() };
}


Try by replacing 'new SimpleFillSymbol()' by  'new ESRI.ArcGIS.Client.FeatureService.Symbols.SimpleFillSymbol()'

The latter symbol supports the selection but is supposed to be for internal use only. So normally you should create your own symbol that supports the selection state.
0 Kudos
GeorgeFaraj
Occasional Contributor III
Wow, that worked. Thanks a lot!
0 Kudos