Friday, 6 May 2016

arcobjects - Is IPolygon2.QueryExteriorRingsEx Method not working in ArcGIS version 10.1 SP1?


I am updating my addins from 10.0 to 10.1 and my code is crashing on the IPolygon2.QueryExteriorRingsEx Method.


I understand there is something strange going on with this method because Developer Help says it does not work in C# .Net (See remarks: "Does not work in .NET. Use Polygon4.get_InteriorRingBag instead.") Under remarks it also states that this method only accepts an array of type IRing for exteriorRings. Using an array of IRing2 won't work. I am using iRing.


All worked great in 10.0 but the method now crashes if I attempt to query more that a single exterior ring. (Querying a polygon with one exterior ring passes)


Can someone confirm this behaviour before I attempt to re-write this procedure using the Polygon4.get_InteriorRingBag method instead?


Procedure below - the problem line is inside the do while loop


Public Sub PolygonsToPolylines(ByVal sourceFeatureLayer As ESRI.ArcGIS.Carto.IFeatureLayer, ByVal destFeatureLayer As ESRI.ArcGIS.Carto.IFeatureLayer)

On Error GoTo trap

Dim MxApplication As ESRI.ArcGIS.ArcMapUI.IMxApplication = TryCast(My.ArcMap.Application, ESRI.ArcGIS.ArcMapUI.IMxApplication)

Dim pEditor As ESRI.ArcGIS.Editor.IEditor2
Dim pEditLayers As ESRI.ArcGIS.Editor.IEditLayers
Dim pActiveView As ESRI.ArcGIS.Carto.IActiveView
Dim pEnumFeature As ESRI.ArcGIS.Geodatabase.IEnumFeature
Dim pFeature As ESRI.ArcGIS.Geodatabase.IFeature
Dim pPolygon As ESRI.ArcGIS.Geometry.IPolygon2

Dim pTempPolyline As ESRI.ArcGIS.Geometry.ITopologicalOperator
Dim pNewFeature As ESRI.ArcGIS.Geodatabase.IFeature
Dim pPolygon2 As ESRI.ArcGIS.Geometry.IPolygon2
Dim pPolyline As ESRI.ArcGIS.Geometry.IPolyline
Dim pPolylinePointColl As ESRI.ArcGIS.Geometry.IPointCollection
Dim pRings() As ESRI.ArcGIS.Geometry.IRing
Dim pRing As ESRI.ArcGIS.Geometry.IRing
Dim pRings2() As ESRI.ArcGIS.Geometry.IRing
Dim pRing2 As ESRI.ArcGIS.Geometry.IRing


'new
Dim pFeatureSelection As ESRI.ArcGIS.Carto.IFeatureSelection
Dim pSelectionSet As ESRI.ArcGIS.Geodatabase.ISelectionSet
Dim pFeatureCursor As ESRI.ArcGIS.Geodatabase.IFeatureCursor = Nothing

Dim r As Long, i2 As Long, r2 As Long, c As Long, c2 As Long
Dim d As Long 'progress dialog counter

Dim source3D As Boolean, dest3D As Boolean


Has_3D_value(sourceFeatureLayer.FeatureClass, source3D) 'determine whether source features class is 3D
Has_3D_value(destFeatureLayer.FeatureClass, dest3D) 'determine whether destination features class is 3D


'Get a handle to the Editor extension
pEditor = GetEditorFromArcMap(MxApplication)

If pEditor Is Nothing Then Exit Sub

pEditLayers = pEditor 'QI

pActiveView = pEditor.Map

If pEditor.EditState = ESRI.ArcGIS.Editor.esriEditState.esriStateNotEditing Then
MsgBox("You must be editing to use this feature!", MsgBoxStyle.Information)
Exit Sub
End If

'QUERY Layer selection to get cursor'''''''''''''''''''''''''''''''''''''''''''''''
pFeatureSelection = sourceFeatureLayer


pSelectionSet = pFeatureSelection.SelectionSet
d = pSelectionSet.Count
If d < 1 Then
MsgBox("You must select at least 1 feature!") : Exit Sub
End If

'retreive selection into a cursor
pSelectionSet.Search(Nothing, False, pFeatureCursor)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''


'Set up Progress Bar''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

'Create a CancelTracker
'Dim trackCancel As ESRI.ArcGIS.esriSystem.ITrackCancel = New ESRI.ArcGIS.Display.CancelTrackerClass '10.0
Dim trackCancel As ESRI.ArcGIS.esriSystem.ITrackCancel = New ESRI.ArcGIS.Display.CancelTracker '10.1 update

Dim progressDialogFactory As ESRI.ArcGIS.Framework.IProgressDialogFactory = New ESRI.ArcGIS.Framework.ProgressDialogFactoryClass

'Set the properties of the Step Progressor
Dim int32_hWnd As System.Int32 = My.ArcMap.Application.hWnd

Dim stepProgressor As ESRI.ArcGIS.esriSystem.IStepProgressor = progressDialogFactory.Create(trackCancel, int32_hWnd)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

'Set the properties of the Step Progressor
stepProgressor.MinRange = 0
stepProgressor.MaxRange = d
stepProgressor.StepValue = 1
stepProgressor.Message = "Copying..."

'Create the ProgressDialog. This automatically displays the dialog

Dim progressDialog2 As ESRI.ArcGIS.Framework.IProgressDialog2 = CType(stepProgressor, ESRI.ArcGIS.Framework.IProgressDialog2) ' Explict Cast

' Set the properties of the ProgressDialog
progressDialog2.CancelEnabled = True
progressDialog2.Description = "Copy Features"
progressDialog2.Title = "Copying..."
progressDialog2.Animation = ESRI.ArcGIS.Framework.esriProgressAnimationTypes.esriDownloadFile
' Step. Do your big process here.
Dim boolean_Continue As System.Boolean
boolean_Continue = True

Dim i As System.Int32
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

'Start an edit operation
pEditor.StartOperation()

pFeature = pFeatureCursor.NextFeature

Do While Not pFeature Is Nothing


'Progress Bar''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim statusBar As ESRI.ArcGIS.esriSystem.IStatusBar = TryCast(My.ArcMap.Application.StatusBar, ESRI.ArcGIS.esriSystem.IStatusBar)
statusBar.Message(0) = i.ToString
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

If pFeature.Shape.GeometryType = ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolygon Then
c = c + 1
pPolygon2 = pFeature.ShapeCopy
SimplifyGeometry(pPolygon2)


r = pPolygon2.ExteriorRingCount
If r > 0 Then
r = r - 1
ReDim pRings(r)
pPolygon2.QueryExteriorRingsEx(pPolygon2.ExteriorRingCount, pRings(0))

For i = 0 To r
pRing = pRings(i)

r2 = pPolygon2.InteriorRingCount(pRing)


If r2 > 0 Then
r2 = r2 - 1
ReDim pRings2(r2)
pPolygon2.QueryInteriorRingsEx(pRing, pPolygon2.InteriorRingCount(pRing), pRings2(0))
For i2 = 0 To r2
pRing2 = pRings2(i2)

pPolylinePointColl = New ESRI.ArcGIS.Geometry.Polyline
pPolylinePointColl.AddPointCollection(pRing2)

pPolyline = pPolylinePointColl 'QI
SimplifyGeometry(pPolyline)

SimplifyGeometry(pPolyline)
pNewFeature = destFeatureLayer.FeatureClass.CreateFeature
pNewFeature.Shape = pPolyline
CopyFieldValues(pFeature, pNewFeature) 'copy matching field values
pNewFeature.Store()
c2 = c2 + 1
Next

End If

pPolylinePointColl = New ESRI.ArcGIS.Geometry.Polyline
pPolylinePointColl.AddPointCollection(pRing)
pPolyline = pPolylinePointColl 'QI
SimplifyGeometry(pPolyline)

pNewFeature = destFeatureLayer.FeatureClass.CreateFeature

If (dest3D = True And source3D = False) Or (dest3D = True And source3D = True) Then

SetZAware(pPolyline) 'make geometry zAware
'FUTURE UPDATE = COPY or ASSIGN 3D Values to all geometry points
End If

If dest3D = False And source3D = True Then
RemoveZAware(pPolyline) 'remove zAware from geometry
End If

pNewFeature.Shape = pPolyline
CopyFieldValues(pFeature, pNewFeature) 'copy matching field values

pNewFeature.Store()
c2 = c2 + 1

'Check if the cancel button was pressed. If so, stop process
boolean_Continue = trackCancel.Continue
If Not boolean_Continue Then
Exit For
End If
Next
End If


End If

pFeature = pFeatureCursor.NextFeature
Loop

'Done
trackCancel = Nothing
stepProgressor = Nothing
progressDialog2.HideDialog()

progressDialog2 = Nothing

MsgBox("Succesfully created " & c2 & " lines from " & c & " selected polygons.")

'Complete the edit operation
pEditor.StopOperation("Copy Polygons to Polylines")

'Flag the area of the new feature for refreshing
pActiveView.Refresh()


Exit Sub

trap:
MsgBox(Err.Description)
pEditor.AbortOperation()

CancelProgressBar:
'Progress cleanup
progressDialog2.HideDialog()
trackCancel = Nothing

stepProgressor = Nothing
progressDialog2 = Nothing

End Sub

Answer



At 10.1 SP1, I am able to reproduce the System.ExecutionEngineException (which is apparently uncatchable) crash if I target .NET 3.5 and I use a polygon that has multiple exterior rings with IPolygon2.QueryExteriorRingsEx. It works fine for single exterior ring polygons.


If I target .NET 4.0 I can use QueryExteriorRingsEx without issue for polygons with multiple exterior rings.


I was able to use IPolygon4.ExteriorRingBag and InteriorRingBag without issue on both framework versions.


I also tested at 10.0 SP5 and found that QueryExteriorRingsEx works fine on both .NET 3.5 and .NET 4.0.


Here is the C# code I tested with (LINQPad versions here (10.1) and here (10.0):



using System;
using System.Collections.Generic;
using System.Linq;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;

namespace RingTest1
{
class Program
{

[STAThread()]
static void Main(string[] args)
{
if (ESRI.ArcGIS.RuntimeManager.ActiveRuntime == null)
ESRI.ArcGIS.RuntimeManager.BindLicense(ESRI.ArcGIS.ProductCode.EngineOrDesktop);

var polygon1 = CreateSinglePartPolygon();
var polygon2 = CreateDonutPolygon();
var polygon3 = CreateMultiPartPolygon();
Console.WriteLine(PolygonRingsToPolylines(polygon1).Count());

Console.WriteLine(PolygonRingsToPolylines(polygon2).Count());
Console.WriteLine(PolygonRingsToPolylines(polygon3).Count());
Console.WriteLine(QueryExteriorRingsExTest(polygon1).Count());
Console.WriteLine(QueryExteriorRingsExTest(polygon2).Count());
Console.WriteLine(QueryExteriorRingsExTest(polygon3).Count());
}

private static IEnumerable QueryExteriorRingsExTest(IPolygon polygon)
{
var polygon2 = (IPolygon2)polygon;

IRing exteriorRings;
polygon2.QueryExteriorRingsEx(polygon2.ExteriorRingCount, out exteriorRings);
for (int i = 0; i < polygon2.ExteriorRingCount; i++)
{
yield return exteriorRings;
}
}

private static IEnumerable PolygonRingsToPolylines(IPolygon polygon)
{

var polygon4 = (IPolygon4)polygon;
var exteriorRingGeometryBag = (IGeometryBag)polygon4.ExteriorRingBag;
var exteriorRingGeometryCollection = (IGeometryCollection)exteriorRingGeometryBag;
for (int i = 0; i < exteriorRingGeometryCollection.GeometryCount; i++)
{
var exteriorRingGeometry = exteriorRingGeometryCollection.get_Geometry(i);
yield return SegmentCollectionToPolyline((ISegmentCollection)exteriorRingGeometry);

var interiorRingGeometryBag = polygon4.get_InteriorRingBag((IRing)exteriorRingGeometry);
var interiorRingGeometryCollection = (IGeometryCollection)interiorRingGeometryBag;

for (int j = 0; j < interiorRingGeometryCollection.GeometryCount; j++)
{
var interiorRingGeometry = interiorRingGeometryCollection.get_Geometry(i);
yield return SegmentCollectionToPolyline((ISegmentCollection)interiorRingGeometry);
}
}
}

private static IPolyline SegmentCollectionToPolyline(ISegmentCollection segmentCollection)
{

var polyline = (IPolyline)new PolylineClass();
var geomcoll = (IGeometryCollection)polyline;
var pathcoll = (ISegmentCollection)new PathClass();
for (int i = 0; i < segmentCollection.SegmentCount; i++)
{
var segment = segmentCollection.get_Segment(i);
pathcoll.AddSegment(segment);
}
geomcoll.AddGeometry((IGeometry)pathcoll);
geomcoll.GeometriesChanged();

return polyline;
}

private static IPolygon CreateSinglePartPolygon()
{
var polygon = (IPolygon)new PolygonClass();
var pointcoll = (IPointCollection4)polygon;
var geometryBridge2 = (IGeometryBridge2)new GeometryEnvironmentClass();
var aWKSPointBuffer = new WKSPoint[5];


// Exterior ring
aWKSPointBuffer[0].X = 0;
aWKSPointBuffer[0].Y = 0;
aWKSPointBuffer[1].X = 0;
aWKSPointBuffer[1].Y = 1;
aWKSPointBuffer[2].X = 1;
aWKSPointBuffer[2].Y = 1;
aWKSPointBuffer[3].X = 1;
aWKSPointBuffer[3].Y = 0;
aWKSPointBuffer[4].X = 0;

aWKSPointBuffer[4].Y = 0;
geometryBridge2.AddWKSPoints(pointcoll, ref aWKSPointBuffer);

var topoOp = (ITopologicalOperator2)polygon;
topoOp.IsKnownSimple_2 = false;
topoOp.Simplify();
return polygon;
}

private static IPolygon CreateDonutPolygon()

{
var polygon = (IPolygon)new PolygonClass();
var pointcoll = (IPointCollection4)polygon;
var geometryBridge2 = (IGeometryBridge2)new GeometryEnvironmentClass();
var aWKSPointBuffer = new WKSPoint[5];

// Exterior ring
aWKSPointBuffer[0].X = 0;
aWKSPointBuffer[0].Y = 0;
aWKSPointBuffer[1].X = 0;

aWKSPointBuffer[1].Y = 4;
aWKSPointBuffer[2].X = 4;
aWKSPointBuffer[2].Y = 4;
aWKSPointBuffer[3].X = 4;
aWKSPointBuffer[3].Y = 0;
aWKSPointBuffer[4].X = 0;
aWKSPointBuffer[4].Y = 0;
geometryBridge2.AddWKSPoints(pointcoll, ref aWKSPointBuffer);

// Interior ring

aWKSPointBuffer[0].X = 1;
aWKSPointBuffer[0].Y = 1;
aWKSPointBuffer[1].X = 3;
aWKSPointBuffer[1].Y = 1;
aWKSPointBuffer[2].X = 3;
aWKSPointBuffer[2].Y = 3;
aWKSPointBuffer[3].X = 1;
aWKSPointBuffer[3].Y = 3;
aWKSPointBuffer[4].X = 1;
aWKSPointBuffer[4].Y = 1;

geometryBridge2.AddWKSPoints(pointcoll, ref aWKSPointBuffer);

var topoOp = (ITopologicalOperator2)polygon;
topoOp.IsKnownSimple_2 = false;
topoOp.Simplify();
return polygon;
}

private static IPolygon CreateMultiPartPolygon()
{

var polygon = (IPolygon)new PolygonClass();
var pointcoll = (IPointCollection4)polygon;
var geometryBridge2 = (IGeometryBridge2)new GeometryEnvironmentClass();
var aWKSPointBuffer = new WKSPoint[5];

// Exterior ring 1
aWKSPointBuffer[0].X = 0;
aWKSPointBuffer[0].Y = 0;
aWKSPointBuffer[1].X = 0;
aWKSPointBuffer[1].Y = 1;

aWKSPointBuffer[2].X = 1;
aWKSPointBuffer[2].Y = 1;
aWKSPointBuffer[3].X = 1;
aWKSPointBuffer[3].Y = 0;
aWKSPointBuffer[4].X = 0;
aWKSPointBuffer[4].Y = 0;
geometryBridge2.AddWKSPoints(pointcoll, ref aWKSPointBuffer);

// Exterior ring 2
aWKSPointBuffer[0].X = 2;

aWKSPointBuffer[0].Y = 2;
aWKSPointBuffer[1].X = 2;
aWKSPointBuffer[1].Y = 3;
aWKSPointBuffer[2].X = 3;
aWKSPointBuffer[2].Y = 3;
aWKSPointBuffer[3].X = 3;
aWKSPointBuffer[3].Y = 2;
aWKSPointBuffer[4].X = 2;
aWKSPointBuffer[4].Y = 2;
geometryBridge2.AddWKSPoints(pointcoll, ref aWKSPointBuffer);


var topoOp = (ITopologicalOperator2)polygon;
topoOp.IsKnownSimple_2 = false;
topoOp.Simplify();
return polygon;
}
}
}

Here is the output of the program at 10.1 SP1 when run without debugging (after closing the crash dialog) and targeting .NET 3.5:



1
2
2
1
1

Unhandled Exception: System.BadImageFormatException: Array type not expected here.
at ESRI.ArcGIS.Geometry.PolygonClass.QueryExteriorRingsEx(Int32 numExteriorRingsRequested, IRing& exteriorRings)
at RingTest1.Program.d__0.MoveNext() in C:\CSProjects\RingTest1\RingTest1\Program.cs:line 32
at System.Linq.Enumerable.Count[TSource](IEnumerable`1 source)

at RingTest1.Program.Main(String[] args) in C:\CSProjects\RingTest1\RingTest1\Program.cs:line 25

Here is the output on .NET 4.0:


1
2
2
1
1
2

No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...