Select to view content in your preferred language

Union discards ellipses

76
1
Jump to solution
Tuesday
DanNarsavage_IDWR
Frequent Contributor

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)

 

0 Kudos
1 Solution

Accepted Solutions
DanNarsavage_IDWR
Frequent Contributor

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);

 

View solution in original post

0 Kudos
1 Reply
DanNarsavage_IDWR
Frequent Contributor

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);

 

0 Kudos