Using the WPF runtime SDK v 10.2.6
I have a ServiceFeatureTable, and I'm adding new poly line features to it. When I was adding poly lines with only the X and Y assigned to the MapPoint values, everything worked great.
Now I need to set the M value on the MapPoint in the poly line. Unfortunately, every time I call AddAsync, it gives me an exception "The shape must not have a M value. : ".
I've looked in my map, and it says that the layer I'm referencing supports M values.
Does anyone know how to solve this particular problem?
Thanks
Solved! Go to Solution.
Hi Mike -
I gave this a try on my end, and experienced the same issue. I took a closer look and I think I've found the problem.
Background:
When you add a feature layer from a feature service, a local table is created for working with the features on the client. The user can make edits to this table and you can push those edits back up when you want to commit edits to the parent feature service. Attempting to add a new feature with M-values fails when calling FeatureTable.AddAsync. This is an edit to the table on the client. Without successfully making those edits, you can't push the edits to the service (FeatureTable.ApplyEditsAsync, ie).
Cause:
I cracked open the local (SqlLite) table and found that the shape field was not configured to use M values for feature coordinates. When looking at the traffic in Fiddler when the service is accessed (and used to create the local table), I can see the request for features does not include "returnM=true", so the features come back without those values. For display efficiency, it's better to exclude extra coordinate information for features (Z or M values, ie) if it's not needed. Unfortunately, there is no way to request that information be included when you need it.
I've created an issue (bug) for this, and we can start to address it for a future release. The fix will likely include the ability to specify M or Z values be returned when you need them. By default, these values will probably continue to be excluded for performance reasons.
Workaround:
One way you can work around the problem is to make the edits directly to the feature service by sending a REST request straight to the service. The service expects features defined as JSON and fortunately, the Graphic class has "FromJson" and "ToJson" methods that greatly simplify that. After a feature is successfully added to the feature service, you can refresh your (local) ServiceFeatureTable to see the update on the client. If you need to manage a set of edits on the client and push them to the feature service later, you might use a GraphicsLayer locally to manage those edits.
I've enclosed a project that uses a helper class ("RestRequest") to apply edits to the feature service and then refreshes the local table to see the edit. Give it a look when you have a chance, hopefully this will be a viable solution for you.
Thad Tilton, Product Engineer, ArcGIS Runtime SDK for .NET
Hi,
Please can you post the entire section of code where you create a new feature, set the geometry and add to the table?
Thanks
Mike
Yep, I sure can.
The graphic being generated is a poly line. And when I disable adding the M value, and I open the data in ArcMap (ArcMap is on a separate computer, so there's no chance it's interfering) the shape field says "Polyline M" for every item in the attributes table. I took this to mean that it should support having custom measure values.
Regardless, the exception happens (according to my stack trace) in some internal Esri function called InsertRow, which I assume hits the native code because the stack ends there until it re-enters with a method called HandleException (or something like that).
This is pretty important to our application, we desperately need to be able to set custom measure values for the poly lines, or else our data ends up being wrong in some places. As you can imagine, this would greatly annoy our clients to no end.
One other note, you may notice in the code that adds the features back to the table that I'm calling .Wait() for each task. This is necessary for our tasks that build the table data. I don't expect that it would cause this issue (considering it works just fine before adding an M value).
This is the code to create the feature:
private GeodatabaseFeature CreateFeature(DynamicMapFeature feature, ServiceFeatureTable table) { GeodatabaseFeature result = table.CreateNew(); foreach (KeyValuePair<string, object> attrib in feature.Attributes) { result.Attributes[attrib.Key] = attrib.Value; } var points = new List<MapPoint>(); var latLongBuffer = new StringBuilder(32); MapPointBuilder builder = new MapPointBuilder(_spatialReference); foreach (DynamicMapPoint point in feature.MapPoints) { if (point.IsLatLong) { latLongBuffer.Length = 0; latLongBuffer.Append(point.X); latLongBuffer.Append(", "); latLongBuffer.Append(point.Y); MapPoint mapPoint = ConvertCoordinate.FromDecimalDegrees(latLongBuffer.ToString(), new SpatialReference(26910)); builder.X = mapPoint.X; builder.Y = mapPoint.Y; } else { builder = new MapPointBuilder(point.X, point.Y, builder.SpatialReference); } // This isn't working. Why? /*if (point.HasZ) { builder.Z = point.Z; }*/ // This isn't either, but we REALLY need this or else our facility information will be incorrect. if (point.HasMeasure) { builder.M = point.Measure; } points.Add(builder.ToGeometry()); } switch (feature.GeometryType) { case FeatureGeometryType.Polygon: result.Geometry = new Polygon(points, builder.SpatialReference); break; case FeatureGeometryType.Polyline: result.Geometry = new Polyline(points, builder.SpatialReference); break; case FeatureGeometryType.Point: result.Geometry = new Multipoint(points, builder.SpatialReference); break; } return result;
This is the code to add a feature back to the table:
ServiceFeatureTable table = _featureTables[bufferIndex]; // At an earlier preparation stage the feature creation code gets put into the _featureBuffers array // like so: _featureBuffers[index] = CreateFeature(...); GeodatabaseFeature[] features = _featureBuffers[bufferIndex]; Task addTask = table.AddAsync(features); // This is where it fails addTask.Wait(); Task<FeatureEditResult> syncTask = table.ApplyEditsAsync(false); syncTask.Wait(); _itemsProcessed = Interlocked.Add(ref _itemsProcessed, syncTask.Result.AddResults.Count(item => item.Success)); var state = new DynamicJobState(string.Format(Resources.DYNMAP_PROG_FEATURE_PROGRESS, _layerName, _itemsProcessed, TotalFeatureCount, (decimal)ItemsProcessed / TotalFeatureCount), ItemsProcessed, TotalFeatureCount); _progressModel.SetJobState(ref state); return syncTask.Result;
Has there been any progress on this since I submitted my code?
Hi Mike -
I gave this a try on my end, and experienced the same issue. I took a closer look and I think I've found the problem.
Background:
When you add a feature layer from a feature service, a local table is created for working with the features on the client. The user can make edits to this table and you can push those edits back up when you want to commit edits to the parent feature service. Attempting to add a new feature with M-values fails when calling FeatureTable.AddAsync. This is an edit to the table on the client. Without successfully making those edits, you can't push the edits to the service (FeatureTable.ApplyEditsAsync, ie).
Cause:
I cracked open the local (SqlLite) table and found that the shape field was not configured to use M values for feature coordinates. When looking at the traffic in Fiddler when the service is accessed (and used to create the local table), I can see the request for features does not include "returnM=true", so the features come back without those values. For display efficiency, it's better to exclude extra coordinate information for features (Z or M values, ie) if it's not needed. Unfortunately, there is no way to request that information be included when you need it.
I've created an issue (bug) for this, and we can start to address it for a future release. The fix will likely include the ability to specify M or Z values be returned when you need them. By default, these values will probably continue to be excluded for performance reasons.
Workaround:
One way you can work around the problem is to make the edits directly to the feature service by sending a REST request straight to the service. The service expects features defined as JSON and fortunately, the Graphic class has "FromJson" and "ToJson" methods that greatly simplify that. After a feature is successfully added to the feature service, you can refresh your (local) ServiceFeatureTable to see the update on the client. If you need to manage a set of edits on the client and push them to the feature service later, you might use a GraphicsLayer locally to manage those edits.
I've enclosed a project that uses a helper class ("RestRequest") to apply edits to the feature service and then refreshes the local table to see the edit. Give it a look when you have a chance, hopefully this will be a viable solution for you.
Thad Tilton, Product Engineer, ArcGIS Runtime SDK for .NET
... just to clarify ...
The workaround described above won't result in the M values being available on the client. The features (with M) will be added to the feature service and those features will appear in the client table (when refreshed), but without the M coordinates.
Reading the features into a GraphicsLayer would give you the ability to use the M values on the client.
And just like that, what was working is now giving me more issues...
So as I mentioned before, I'm using the MakeRouteEventLayer to add non-spatial data that's routed along the centerline. This works great in ArcMap. However, from my code... not so much.
So here's what I had prior to correcting the M value issue:
And prior to adding the M values, this all worked swimmingly (albeit, horrendously slow) except that the measures were off.
Now that I have my M values, I can't really use the first step any more because the M values are not returned to the client (as you mentioned). Ideally, what I'd really like to do is reference the layer directly in the geoprocessing package; that'd circumvent the issue entirely. Unfortunately, I have no idea how to do that, or even if it's possible? Am I able to reference a layer (that exists in my map package) in the geo processor package without that package needing to contain a snapshot of the layer data? This is because we are working with live data here, and snap shotting just won't cut it unfortunately. Or am I wrong in thinking that the packages take snap shots of data?
Hey Mike,
How about if you queried the feature service for the features you need (after the Ms are updated)? If you set "ReturnM = true", you'll get the M coordinates included in the result. For example:
public async Task<FeatureSet> GetFeaturesFromService(Geometry filterGeometry, string where, bool includeM, bool includeZ)
{
var queryTask = new QueryTask(new Uri(_serviceUrl));
var filter = new Query(where);
filter.ReturnGeometry = true;
filter.ReturnM = includeM;
filter.ReturnZ = includeZ;
filter.Geometry = filterGeometry;
var result = await queryTask.ExecuteAsync(filter);
return result.FeatureSet;
}
Hey Thad,
Thanks for that, I already figured out that the QueryTask will send back the M values.
Unfortunately, after I was able to do all that, I ran into an issue with the geoprocessing model/package that I had built to route my non spatial event data. Just can't win sometimes eh?
Anyway, I posted about the issue here Getting exception: " Error code '400' : 'Unable to complete operation.'\Error executing tool.: ERROR...
Thanks for all your help again, it's much appreciated.
Thanks Thad.
I'm going to try and integrate this workaround immediately. I'm not too worried about being able to retrieve the M values, just sending them to the local feature service is all I need.
What I'm doing is generating our centerline data, and then running the MakeRouteEventLayer tool on it via a geo processing package so I can integrate our non-spatial measurement data into the centerline on the map. So as long as the MakeRouteEventLayer can see my custom M values when generating my layers, I should be good to go. Currently I've got it set up in a model to run CreateRoutes on the centerline before MakeRouteEventLayer, but this results in our data being offset by a certain amount. Our clients give us custom measures along their lines so that we can put this data in the correct spot, and hence why I need to be able to set the M value.
I'll report back after I've tested it out.