Getting exception: "The shape must not have a M value."

7155
10
Jump to solution
09-29-2015 10:03 AM
MikeWinsor
New Contributor III

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

0 Kudos
1 Solution

Accepted Solutions
ThadTilton
Esri Contributor

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

View solution in original post

0 Kudos
10 Replies
MichaelBranscomb
Esri Frequent Contributor

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

0 Kudos
MikeWinsor
New Contributor III

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;
0 Kudos
MikeWinsor
New Contributor III

Has there been any progress on this since I submitted my code?

0 Kudos
ThadTilton
Esri Contributor

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

0 Kudos
ThadTilton
Esri Contributor

... 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.

0 Kudos
MikeWinsor
New Contributor III

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:

  • Query the service feature table for the centerline, and retrive all the IDs and geometry within an Envelope.
  • Retrieve the event data from the SQL server linked by the IDs returned from the aforementioned query
    • Convert this data into a feature set for passing to the geoprocessor.
  • Execute my geoprocessor, passing in the centerline data, and the event data as parameters (Centerline is a Feature Set parameter, and the event data is a record set parameter).
  • Get a feature set returned from the geo processor
  • Add the features as graphics (not a fan of that, I'm going to try and change this to a feature service table).

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?

0 Kudos
ThadTilton
Esri Contributor

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;

}

0 Kudos
MikeWinsor
New Contributor III

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.

0 Kudos
MikeWinsor
New Contributor III

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.

0 Kudos