3

Using most recent .net core SDK of ESRI ArcGis on a local application (not web).

I try to create a wrapper class based on ArcGis:IFeatureSet which will take a collection of objects which implement IGeoCoordinate (Provide a GeoCoordinate) and will generate automatically all features and their properties.

Everything seems to be possible and almost working but I cannot find a way to create a Feature giving its polyline and a set of attributes.

Anybody know how to create a Feature programatically?

using Esri.ArcGISRuntime.Data;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Symbology;
using Esri.ArcGISRuntime.UI;
using EsriUtil;
using GeoCoordinatePortable;
using GeoGraphicalCore;
using HQ.Util.General.Reflection;
using System;
using System.CodeDom;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;

namespace ElectricalNetworkMapArcgis.Util
{
    public class FeatureSetWrapper<T> : IFeatureSet where T : IGeoCoordinateContainer // EO: It should already exists... 
    {
        public IEnumerable<T> Objects { get; private set; }
        public List<Feature> Features { get; private set; }
        public List<Field> _fields { get; private set; }

        public GeometryType GeometryType { get; private set; }
        public SpatialReference SpatialReference { get; private set; }

        public FeatureSetWrapper(IEnumerable<T> objects, GeometryType geometryType, SpatialReference spatialReference)
        {
            Objects = objects;
            GeometryType = geometryType;
            SpatialReference = spatialReference;

            FillFieldsThroughReflection();
            FillFeatures();
        }

        public void FillFieldsThroughReflection()
        {
            foreach(var prop in typeof(T).GetProperties())
            {
                if (prop.CanRead)
                {
                    Field field = new Field(prop.PropertyType.GetEsriAssociatedFieldType(), prop.Name, prop.DisplayName(), GetEvaluatedFieldLenght(prop), null, prop.CanWrite, false);
                    _fields.Add(field);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pi"></param>
        /// <returns>The proper maxLenght if the count of items if less than "maxIteration" otherwise return a very rough estimate</returns>
        int GetEvaluatedFieldLenght(PropertyInfo pi)
        {
            int stringLenghtMax = 0;

            if (pi.PropertyType == typeof(string))
            {
                const int maxIteration = 60; // EO: ~random choice. IF there is millions of record, it would take too much time to verify all, considering also that is from reflection.
                int iterCount = 0;
                foreach(T obj in Objects)
                {
                    stringLenghtMax = Math.Max(stringLenghtMax, pi.GetValue(obj).ToString().Length);

                    iterCount++;
                    if (iterCount == maxIteration) // Prevent potential super long delay
                    {
                        return stringLenghtMax * 2; // We reach maxIteration and estimate (hope we are right) to keep the double in order to give room for not evaluated values. 
                    }
                }
            }

            return stringLenghtMax;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns>Return the count of feature</returns>
        int FillFeatures()
        {
            int countOfObject = 0;

            var polyLineBuilder = new PolylineBuilder(SpatialReferences.Wgs84);

            foreach (T obj in Objects)
            {
                IEnumerable<GeoCoordinate> geoCoordinates = obj.GetGeoCoordinates();

                foreach (var geoCoordinate in geoCoordinates)
                {
                    // EO: check is coordinates are into Quebec limits
                    Debug.Assert(geoCoordinate.Longitude > -80 && geoCoordinate.Longitude < -57);
                    Debug.Assert(geoCoordinate.Latitude > 44 && geoCoordinate.Latitude < 62);

                    MapPoint pointIter = new MapPoint(geoCoordinate.Longitude, geoCoordinate.Latitude, SpatialReferences.Wgs84);
                    polyLineBuilder.AddPoint(pointIter);
                }

                var polyline = polyLineBuilder.ToGeometry();

                // For GraphicOverlay
                // var graphic = new Graphic(polyline, _simpleLineSymbol);


                // BUG HERE


                var feature = new Feature(polyline); // DOES NOT EXISTS

                // Add Attributes here

                Features.Add(feature);

                countOfObject++;
                polyLineBuilder.Parts.Clear();
            }

            return countOfObject;
        }

        private static readonly SimpleLineSymbol _simpleLineSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, System.Drawing.Color.FromArgb(128, 255, 0, 0), 2);

        public IReadOnlyList<Field> Fields => _fields;

        public IEnumerator<Feature> GetEnumerator()
        {
            return Features.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return Features.GetEnumerator();
        }
    }
}
1

Features are created from FeatureTables using the "CreateFeature" method: https://developers.arcgis.com/net/latest/wpf/api-reference/html/M_Esri_ArcGISRuntime_Data_FeatureTable_CreateFeature.htm

Features can't be standalone features without a table that they belong to. Instead you could return a FeatureCollectionTable with the features populated in it.

| improve this answer | |
  • Thanks for the information. I will have to read and try. But what I understand is IFeatureSet is for internal of ArcGIS and not for customer to implement? – Eric Ouellet Sep 14 at 20:45
  • Also, I did the equivalent job for GraphicOverlay which seems to be easier to implement as local data (not persisted in a database). But the problem is all fields "name" are replicated for each object which is not the case for features (same type of objects). The other problem with the SDK, there is missing a special GraphicOverlay Object which would not keep attributes themself but instead should query the underlying object for its props name and value. Another thing that is sad, ESRI architecture prevent us from suggesting things like that to developers (they should know our needs). – Eric Ouellet Sep 14 at 20:54
  • Thanks. I finished my code. But I can't make it works. I use: ServiceFeatureTable stf = new ServiceFeatureTable(); BUT Feature feature = stf.CreateFeature(); give me an exception: System.InvalidOperationException: 'Cannot call this method in this context: Can only call this method on a loaded table.' ... I want to create geo object by code because they only reside locally. How to create a FeatureLayer (and its FeatureTable) locally. What uri should I use for that? – Eric Ouellet Sep 15 at 17:47
  • Just to let you know. I added "Locally" to my question in order to be more accurate on my need. – Eric Ouellet Sep 15 at 18:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.