Running an addin in ArcGIS Pro v3.3. When `GeometryEngine.Instance.Union(...)` is given ellipses (or apparently anything even created from ellipses), it produces empty geometries. Has anyone seen this before & come up with a workaround? Thanks!
This code . . .
List<Polygon> polygonParts = [];
foreach (Layer layer in layers)
{
// Stuff omitted for brevity
polygonFeatures = polygonFeatures.Where(p => p is not null && !p.IsEmpty)
.Select(polygon =>
{
// I tried adding this because I figured the elliptical curves were the issue, but clearly not. Note that circles DON'T need this treatment for Union to handle them appropriately.
if (polygon.HasCurves)
{
Log.Information("Pre-densified polygon has {0} vertices and is empty: {1}", polygon.PointCount, polygon.IsEmpty);
polygon = (Polygon)GeometryEngine.Instance.DensifyByAngle(polygon, -1);
Log.Information("Densified polygon has {0} vertices, is empty({1}), has area({2}), and has curves({3})", polygon.PointCount, polygon.IsEmpty, polygon.Area, polygon.HasCurves);
}
return polygon;
})
.ToArray();
polygonParts.AddRange(polygonFeatures);
}
Log.Information("Creating a union from {0} selected features in these layers: {1}", polygonParts.Count, layers.Select(layer => layer.Name));
var unionGeometry = await QueuedTask.Run(() => GeometryEngine.Instance.Union(polygonParts));
Log.Information("Unioned geometry has {0} vertices and is empty({1})", unionGeometry.PointCount, unionGeometry.IsEmpty);
var unionPolygon = (Polygon)unionGeometry;
Log.Information("Unioned polygon has area({0}), and has curves({1})", unionPolygon.Area, unionPolygon.HasCurves);
. . . produces this log when given ellipses (note the zero area, emptiness, and zero vertices) . . .
[11:48:03 INFO v1.0] Pre-densified polygon has 2 vertices and is empty: false
[11:48:03 INFO v1.0] Densified polygon has 37 vertices, is empty(false), has area(-11803.5397706948), and has curves(false)
[11:48:03 INFO v1.0] Pre-densified polygon has 2 vertices and is empty: false
[11:48:03 INFO v1.0] Densified polygon has 37 vertices, is empty(false), has area(-11803.5397706948), and has curves(false)
[11:48:03 INFO v1.0] Creating a union from 2 selected features in these layers: ["Graphics Layer"]
[11:48:03 INFO v1.0] Unioned geometry has 0 vertices and is empty(true)
[11:48:03 INFO v1.0] Unioned polygon has area(0), and has curves(false)
. . . and this log when given circles (note the nonzero area, non-emptiness, and nonzero vertices) . . .
[11:57:40 INFO v1.0] Pre-densified polygon has 2 vertices and is empty: false
[11:57:40 INFO v1.0] Densified polygon has 37 vertices, is empty(false), has area(7722.7340715872815), and has curves(false)
[11:57:40 INFO v1.0] Pre-densified polygon has 2 vertices and is empty: false
[11:57:40 INFO v1.0] Densified polygon has 37 vertices, is empty(false), has area(7722.7340715872815), and has curves(false)
[11:57:40 INFO v1.0] Creating a union from 2 selected features in these layers: ["Graphics Layer"]
[11:57:40 INFO v1.0] Unioned geometry has 74 vertices and is empty(false)
[11:57:40 INFO v1.0] Unioned polygon has area(15445.469156841702), and has curves(false)
Solved! Go to Solution.
The `DensifyByAngle(...)` method doesn't seem to work, but `Buffer(...)` does. I made this extension method that buffers by a tiny distance then unbuffers by the same distance . . .
using ArcGIS.Core.Geometry;
using Serilog;
using EsriPolygon = ArcGIS.Core.Geometry.Polygon;
namespace IDWR.WREdit.Contracts.Extensions;
/// <summary>
/// Offers extension methods for Esri geometries
/// </summary>
public static class EsriGeometryExtensions
{
/// <summary>
/// The <see cref="GeometryEngine"/> class has shown some intolerance for ellipses. This handles the problem by replacing any curves with a splined approximation
/// </summary>
/// <param name="polygon">Geometry to be splined</param>
/// <remarks>See work item 78279</remarks>
public static EsriPolygon SplineEllipse(this EsriPolygon polygon)
{
if (!polygon.HasCurves) return polygon;
Log.Debug("Pre-densified polygon has {0} vertices and is empty: {1}", polygon.PointCount, polygon.IsEmpty);
polygon = (Polygon)GeometryEngine.Instance.Buffer(
GeometryEngine.Instance.Buffer(
polygon,
0.1),
-0.1);
Log.Debug("Densified polygon has {0} vertices, is empty({1}), has area({2}), and has curves({3})", polygon.PointCount, polygon.IsEmpty, polygon.Area, polygon.HasCurves);
return polygon;
}
}
. . . and now the code looks like below, and the polygon that comes out the end is valid even when the inputs include ellipses. Not a fix, but a workaround until the problems in `GeometryEngine` are resolved. Maybe when we finally upgrade past v3.3?
List<Polygon> polygonParts = [];
foreach (Layer layer in layers)
{
// Stuff omitted for brevity
polygonFeatures = polygonFeatures.Where(p => p is not null && !p.IsEmpty)
.Select(polygon => polygon.SplineEllipse())
.ToArray();
polygonParts.AddRange(polygonFeatures);
}
Log.Information("Creating a union from {0} selected features in these layers: {1}", polygonParts.Count, layers.Select(layer => layer.Name));
var unionGeometry = await QueuedTask.Run(() => geometryEngine.Union(polygonParts));
Log.Debug("Unioned geometry has {0} vertices and is empty({1})", unionGeometry.PointCount, unionGeometry.IsEmpty);
var unionPolygon = (Polygon)unionGeometry;
Log.Debug("Unioned polygon has area({0}), and has curves({1})", unionPolygon.Area, unionPolygon.HasCurves);
The `DensifyByAngle(...)` method doesn't seem to work, but `Buffer(...)` does. I made this extension method that buffers by a tiny distance then unbuffers by the same distance . . .
using ArcGIS.Core.Geometry;
using Serilog;
using EsriPolygon = ArcGIS.Core.Geometry.Polygon;
namespace IDWR.WREdit.Contracts.Extensions;
/// <summary>
/// Offers extension methods for Esri geometries
/// </summary>
public static class EsriGeometryExtensions
{
/// <summary>
/// The <see cref="GeometryEngine"/> class has shown some intolerance for ellipses. This handles the problem by replacing any curves with a splined approximation
/// </summary>
/// <param name="polygon">Geometry to be splined</param>
/// <remarks>See work item 78279</remarks>
public static EsriPolygon SplineEllipse(this EsriPolygon polygon)
{
if (!polygon.HasCurves) return polygon;
Log.Debug("Pre-densified polygon has {0} vertices and is empty: {1}", polygon.PointCount, polygon.IsEmpty);
polygon = (Polygon)GeometryEngine.Instance.Buffer(
GeometryEngine.Instance.Buffer(
polygon,
0.1),
-0.1);
Log.Debug("Densified polygon has {0} vertices, is empty({1}), has area({2}), and has curves({3})", polygon.PointCount, polygon.IsEmpty, polygon.Area, polygon.HasCurves);
return polygon;
}
}
. . . and now the code looks like below, and the polygon that comes out the end is valid even when the inputs include ellipses. Not a fix, but a workaround until the problems in `GeometryEngine` are resolved. Maybe when we finally upgrade past v3.3?
List<Polygon> polygonParts = [];
foreach (Layer layer in layers)
{
// Stuff omitted for brevity
polygonFeatures = polygonFeatures.Where(p => p is not null && !p.IsEmpty)
.Select(polygon => polygon.SplineEllipse())
.ToArray();
polygonParts.AddRange(polygonFeatures);
}
Log.Information("Creating a union from {0} selected features in these layers: {1}", polygonParts.Count, layers.Select(layer => layer.Name));
var unionGeometry = await QueuedTask.Run(() => geometryEngine.Union(polygonParts));
Log.Debug("Unioned geometry has {0} vertices and is empty({1})", unionGeometry.PointCount, unionGeometry.IsEmpty);
var unionPolygon = (Polygon)unionGeometry;
Log.Debug("Unioned polygon has area({0}), and has curves({1})", unionPolygon.Area, unionPolygon.HasCurves);