Hi, Does arcgis runtime for wpf support to export layer to ESRI shape file?

5819
12
02-28-2015 04:11 AM
Labels (1)
PeterChen1
New Contributor

Hi, Does arcgis runtime for wpf support to export layer to ESRI shape file?  If so, could anyone provide some code snippets to help me out? thanks in adavance.

0 Kudos
12 Replies
FreddieGibson
Occasional Contributor III

The short answer is no. The Runtime for WPF products doesn't support the creation of local data. This functionality may be coming in a future release of the Runtime for .NET API. If you want to do this currently in the Runtime for WPF API you'd need to leverage a GP Service (whether local or online) to convert the resulting GraphicsLayers to Shapefiles that can be downloaded or copied to a specific location on the client machine.

0 Kudos
PeterChen1
New Contributor

Thanks Freddie very much for your reply.

What's a pity to know that the Runtime for WPF products doesn't support the creation of local data. This functionality is what i indeed need at this moment.

The way to use GP Service to convert GraphicsLayer to shape file as you mentioned is a bit difficult for me a beginner, could you please give me some clue to make it? any code snippet would be much helpful for me.

I found a similar way to do this using runtime for java here http://blogs.esri.com/esri/arcgis/2012/08/24/arcgis-runtime-sdk-for-java-writing-graphics-to-shapefiles/, but didn't find a good example for wpf...

0 Kudos
FreddieGibson
Occasional Contributor III

Hi Peter,

I looked at the link you posted above and it appears to be doing what I was suggest. I currently have a .NET sample that does the exact same thing, but I feel that this workflow currently is a bit of a hack. I have a second way that I want to create this sample that may be closer to a best practice (i.e. zipping up the shapefile and providing a url that I can use to download it instead of hacking out the path to the local servers output directory). I will try to write it up by this weekend and upload it so that you can see both options.

0 Kudos
PeterChen1
New Contributor

Thanks Freddie very much for your help. I am looking forward to seeing your good samples...

By the way, do you mind sending your handy .net sample to my email(it's a bit difficult for me to access the GeoNet website due to the network restriction) so that i can learn how you implement it right now? and then when you finish your second better way, i can learn it later.

0 Kudos
PeterChen1
New Contributor
0 Kudos
PeterChen1
New Contributor
0 Kudos
FreddieGibson
Occasional Contributor III

I'll just paste the code below for you to use in your project. You'll just need to create the gpk and reference it within the code, and update the parameters provided to it. I believe the last tool I used was the Copy Features tool. You should be able to use any tool that creates an output shapefile (i.e. Copy Features, Feature Class to Feature Class, Select, etc.).

*** XAML ***

<Window x:Class="CopyFeatures.MainWindow"

        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"

  Title="Copy Features GP" Height="700" Width="800">

    <Grid x:Name="LayoutRoot">

        <Grid.Resources>

            <esri:SimpleLineSymbol x:Key="ResultLineSymbol" Color="Red" Style="Solid" Width="3" />

            <esri:SimpleFillSymbol x:Key="ResultFillSymbol" Color="LimeGreen" Outline="{StaticResource ResultLineSymbol}" />

            <esri:SimpleRenderer x:Key="ResultRenderer" Symbol="{StaticResource ResultFillSymbol}" />

        </Grid.Resources>

       

        <esri:MapView x:Name="MyMapView">

            <esri:Map />

            <esri:MapView.GraphicsOverlays>

                <esri:GraphicsOverlay x:Name="ResultsOverlay" Renderer="{StaticResource ResultRenderer}"/>

            </esri:MapView.GraphicsOverlays>

        </esri:MapView>

        <Border HorizontalAlignment="Right" VerticalAlignment="Top" Margin="30" Padding="20" Width="300"

                Background="White" BorderBrush="Black" BorderThickness="1">

            <Border.Effect>

                <DropShadowEffect />

            </Border.Effect>

            <StackPanel>

                <TextBlock Text="Click on the Copy Features button to execute the gp tool. This will create a new shapefile and add the results to the display." TextWrapping="Wrap" />

                <Button x:Name="CopyFeaturesButton" Content="Copy Features" FontWeight="Bold" Margin="0,12,0,0" Padding="5" Click="CopyFeaturesButton_OnClick" IsEnabled="False" />

                <TextBlock x:Name="TxtInfo" FontSize="12" Visibility="Collapsed" Margin="0,12,0,0">

  <Run Text="Shapefile:" FontSize="14" FontWeight="Bold" />

  <LineBreak />

  <Run Text="Name: " FontWeight="Bold" />

  <Run Text="{Binding ID, Mode=OneWay}" />

  <LineBreak />

  <Run Text="Path: " FontWeight="Bold" />

  <Run Text="{Binding DisplayName, Mode=OneWay}" />

  <LineBreak />

  <Run Text="Spatial Reference: " FontWeight="Bold" />

  <Run Text="{Binding FeatureTable.SpatialReference.Wkid, Mode=OneWay}" />

  <LineBreak />

  <Run Text="Geometry Type: " FontWeight="Bold" />

  <Run Text="{Binding FeatureTable.GeometryType, Mode=OneWay}" />

  <LineBreak />

  <Run Text="Features: " FontWeight="Bold" />

  <Run Text="{Binding FeatureTable.RowCount, Mode=OneWay}" />

                </TextBlock>

            </StackPanel>

        </Border>

    </Grid>

</Window>

*** CODE BEHIND ***

using Esri.ArcGISRuntime.Data;

using Esri.ArcGISRuntime.Layers;

using Esri.ArcGISRuntime.LocalServices;

using Esri.ArcGISRuntime.Symbology;

using Esri.ArcGISRuntime.Tasks.Geoprocessing;

using System;

using System.Collections.Generic;

using System.Diagnostics;

using System.IO;

using System.Linq;

using System.Text;

using System.Text.RegularExpressions;

using System.Threading.Tasks;

using System.Windows;

using System.Windows.Controls;

namespace CopyFeatures

{

    public partial class MainWindow : Window

    {

        private const string ShpPath = @".\data\in_shp\States.shp";

        private const string CopyFeaturesPath = @".\data\packages\CopyFeatures.gpk";

        private const string ResultPath = @".\data\out_shp";

        private const string ServiceName = "/Copy%20Features";

        private Geoprocessor _gpTask;

        private static readonly Regex _pathRegex = new Regex(@"(([a-z]:|\\\\[a-z0-9_.$]+\\[a-z0-9_.$]+)?(\\?(?:[^\\/:*?""<>|\r\n]+\\)+)[^\\/:*?""<>|\r\n]+).shp", RegexOptions.IgnoreCase);

        private static readonly Random _random = new Random();

       

        public MainWindow()

        {

            InitializeComponent();

           

            MyMapView.Loaded += async (sender, args) =>

            {

                await LoadShapefile(ShpPath);

                LocalGeoprocessingService copyFeaturesService = new LocalGeoprocessingService(CopyFeaturesPath, GeoprocessingServiceType.SubmitJob);

                await copyFeaturesService.StartAsync();

               

                _gpTask = new Geoprocessor(new Uri(copyFeaturesService.UrlGeoprocessingService + ServiceName));

                Debug.WriteLine(copyFeaturesService.UrlGeoprocessingService);

                CopyFeaturesButton.IsEnabled = true;

            };

        }

        private async Task LoadShapefile(string path, bool useRenderer=false)

        {

            try

            {

                // open shapefile table

                var shapefile = await ShapefileTable.OpenAsync(path);

                // create feature layer based on the shapefile

                var flayer = new FeatureLayer(shapefile)

                {

                    ID = shapefile.Name,

                    DisplayName = path,

                };

                if (useRenderer)

                    flayer.Renderer = LayoutRoot.Resources["ResultRenderer"] as SimpleRenderer;

                await flayer.InitializeAsync();

                // Add the feature layer to the map

                MyMapView.Map.Layers.Add(flayer);

                if (MyMapView.Map.Layers.Count == 1)

                    MyMapView.SetView(flayer.FeatureTable.Extent.Expand(1.1));

                else

                {

                    TxtInfo.DataContext = flayer;

                    TxtInfo.Visibility = Visibility.Visible;

                }

            }

            catch (Exception ex)

            {

                MessageBox.Show("Error creating feature layer: " + ex.Message, "Sample Error");

            }

        }

        // Submit GP Job and Poll the server for results every 2 seconds.

        private static async Task<GPJobInfo> SubmitAndPollStatusAsync(GPInputParameter parameter, Geoprocessor gpTask)

        {

            // Submit gp service job

            var result = await gpTask.SubmitJobAsync(parameter);

            // Poll for the results async

            while (result.JobStatus != GPJobStatus.Cancelled && result.JobStatus != GPJobStatus.Deleted

                && result.JobStatus != GPJobStatus.Succeeded && result.JobStatus != GPJobStatus.TimedOut)

            {

                result = await gpTask.CheckJobStatusAsync(result.JobID);

                await Task.Delay(2000);

            }

            return result;

        }

        private async void CopyFeaturesButton_OnClick(object sender, RoutedEventArgs e)

        {

            try

            {

                // Disable the button while gp executes

                ((Button) sender).IsEnabled = false;

                // Get the Feature Layer from the map

                var layer = MyMapView.Map.Layers[0] as FeatureLayer;

                var filter = new QueryFilter {WhereClause = "1=1"};

                // Get a list of the states in the loaded shapefile

                var features = await layer.FeatureTable.QueryAsync(filter);

                var states = features.Select(feature => feature.Attributes["STATE_NAME"].ToString()).ToArray();

                // Select 9 random states from the list of states

                var randomStates = new List<string>();

                while (randomStates.Count < 9)

                {

                    var state = states[_random.Next(0, states.Length - 1)];

                    if (randomStates.Contains(state))

                        continue;

                    randomStates.Add(state);

                }

                // Create a feature set with the selected states

                filter.WhereClause = string.Format("STATE_NAME IN ('{0}')", string.Join("', '", randomStates));

                var featSet = new FeatureSet(await layer.FeatureTable.QueryAsync(filter));

                // Use the geoprocessor to copy the shapefile with only the selected records

                var parameter = new GPInputParameter();

                parameter.GPParameters.Add(new GPFeatureRecordSetLayer("in_features", featSet));

                // Submit gp job and wait for status update

                var result = await SubmitAndPollStatusAsync(parameter, _gpTask);

                if (result.JobStatus != GPJobStatus.Succeeded) return;

                // Confirm that the output is a GPFeatureRecordSetLayer

                var resultData = await _gpTask.GetResultDataAsync(result.JobID, "out_feature_class");

                if (!(resultData is GPFeatureRecordSetLayer)) return;

                // Parse the messages returned from the geoprocessor

                StringBuilder builder = new StringBuilder();

                string copy = string.Empty;

                foreach (var message in result.Messages)

                {

                    builder.AppendLine(message.Description);

                    // Special Task: Search for shapefile path in output messages

                    var match = _pathRegex.Match(message.Description);

                    if (!match.Success) continue;

                    string filePath = match.Value;

                    if (!File.Exists(filePath)) continue;

                    var files = Directory.GetFiles(Path.GetDirectoryName(filePath),

                        Path.GetFileNameWithoutExtension(filePath) + ".*");

                    if (!(Directory.Exists(ResultPath)))

                        Directory.CreateDirectory(ResultPath);

                    // Special Task: If needed shapefile is found...copy all files related to it to a known location

                    foreach (var file in files)

                    {

                        File.Copy(file, Path.Combine(ResultPath, Path.GetFileName(file)), true);

                        if (file.EndsWith("Copy.shp"))

                            copy = Path.Combine(ResultPath, Path.GetFileName(file));

                    }

                }

                // Display the results in the map

                if (copy != string.Empty)

                    await LoadShapefile(copy, true);

                else

                {

                    GPFeatureRecordSetLayer gpLayer = resultData as GPFeatureRecordSetLayer;

                    ResultsOverlay.Graphics.Clear();

                    ResultsOverlay.Graphics.AddRange(gpLayer.FeatureSet.Features.OfType<Graphic>());

                }

                Debug.WriteLine("**** GP Messages ****");

                Debug.WriteLine(builder.ToString());

            }

            catch (Exception ex)

            {

                MessageBox.Show(ex.Message, "EXCEPTION", MessageBoxButton.OK, MessageBoxImage.Error);

            }

            finally

            {

                // Enable the button now that geoprocessing has completed

                ((Button)sender).IsEnabled = true;

            }

        }

    }

}

0 Kudos
PeterChen1
New Contributor

Thanks Freddie for your hepful sample. However, i got some problem following your code:

1, After it executs SubmitJobAsync method, at first, the state of "result" becomes "submitted", and then it's state is "Failed" without any more error information.

2, I did see a message saying that "Copy Features Tool requires ArcGIS Runtime Standard with Geoprocessing deployment" when i creates the gpk. I a bit doubt that this is the reason why it fails on step 1 as I am currently using a develpment license, am i right?

0 Kudos
FreddieGibson
Occasional Contributor III

What are the geoprocessing messages you're getting from the local server?

0 Kudos