.NET Modify Z or M without modifying curves

708
2
10-28-2011 10:28 PM
RichardFairhurst
MVP Honored Contributor
I am trying to modify Z or M values of a polyline that has nonlinear segments, such as circular arcs, eliptical arcs, and bezier curves, without converting the line to linear segments.  Densifying the curve into line segments is not an option.  My code below seems to work until I try to update the feature shape with the modified geometry.  At that point ArcMap crashes (I know bad error handling, but an error clearly is occurring the first time that code section runs and I need to overcome it).  The feature I am trying to modify is an ellipse with one part and one segment, just to see if I can get this to work at all. I'll worry about multiple parts and segments later.  Any ideas on how to properly use the ISegmentCollection or possibly the IGeometryCollection objects to achieve what I want?  There is very little documentation of these objects.

' Assume I already have a valid Polyline Featurelayer that is Z and M Aware called pFLayer
        Dim pFC As ESRI.ArcGIS.geodatabase.IFeatureClass
        pFC = pFLayer.FeatureClass ' Get FeatureClass of FeatureLayer
        Dim pFCursor As ESRI.ArcGIS.Geodatabase.IFeatureCursor
        pFCursor = pFC.Update(Nothing, False) ' Create an UpdateCursor on FeatureClass
        Dim pFeat As ESRI.ArcGIS.Geodatabase.IFeature
        pFeat = pFCursor.NextFeature ' Get First Feature
        Dim pPolyline As IPolyline = pFeat.ShapeCopy
        Dim pSegColl As ESRI.ArcGIS.Geometry.ISegmentCollection
        Dim Nonlinear As Boolean = False
        Dim SegCount As Long = 0
        Dim pInPoint As IPoint ' Current Point to process
        Dim distAlong As Double ' 3D Distance along the curve
        Dim j As Long = 0
        Dim pSegment As ISegment
        Dim NewSegments As ESRI.ArcGIS.Geometry.ISegmentCollection = New Polyline

        Do While Not pFeat Is Nothing
            pPolyline = New Polyline
            pPolyline.SpatialReference = pFeat.Shape.SpatialReference ' Make sure Polyline has Spatial Reference
            pPolyline = pFeat.Shape ' Assign geometry to Polyline
            pSegColl = pPolyline
            pSegColl.HasNonLinearSegments(Nonlinear)
            If Not Nonlinear Then
                ' All Segments are linear and code works without a problem so it is not included
            Else
                ' One or more segments of the polyline is Nonlinear
                SegCount = pSegColl.SegmentCount
                NewSegments = New Polyline
                distAlong = 0
                For j = 0 To SegCount - 1 ' read all segments
                    pSegment = pSegColl.Segment(j)
                    pInPoint = New Point
                    pInPoint.SpatialReference = pFeat.Shape.SpatialReference
                    pInPoint = pSegment.FromPoint
                    pInPoint.M = distAlong
                    pSegment.FromPoint = pInPoint
                    distAlong = Math.Sqrt(pSegment.Length ^ 2 + (pSegment.FromPoint.Z - pSegment.ToPoint.Z) ^ 2) + distAlong
                    pInPoint = New Point
                    pInPoint.SpatialReference = pFeat.Shape.SpatialReference
                    pInPoint = pSegment.ToPoint
                    pInPoint.M = distAlong
                    pSegment.ToPoint = pInPoint ' Modify ToPoint to add M value
                    MsgBox("Segment To Point M Value is " & pSegment.ToPoint.M & " pInPoint M Value is " & pInPoint.M & " and Distance Along is " & distAlong) ' Verify everything updated
                    NewSegments.AddSegment(pSegment) ' Append segment to another segmentCollection
                Next j
                pPolyline.SpatialReference = pFeat.Shape.SpatialReference
                pPolyline = CType(NewSegments, ESRI.ArcGIS.Geometry.Polyline)
                If pPolyline Is Nothing Then
                    MsgBox("Bad Segments to Polyline")
                Else
                    MsgBox("To Point M Value is " & NewSegments.Segment(0).ToPoint.M & " Geometrytype is " & NewSegments.Segment(0).GeometryType) ' Verify that NewSegment is a curve GeometryType still. It says it is an Ellipse Geometry with updated M values and everything seems to be working.
                End If
                pFeat.Shape = NewSegments ' Code causes ArcMap to crash here on the first nonlinear feature.  Also tried "pFeat.Shape = pPolyline" with same result
                pFCursor.UpdateFeature(pFeat) ' Update the cursor
            End If
            pFeat = pFCursor.NextFeature
            pPolyline = Nothing
        Loop
0 Kudos
2 Replies
RichardFairhurst
MVP Honored Contributor
For those who are interested I got the code to work.  I modified it based on the example l for How to modify a specific Segment of a polyline.  The key was to use the original SegmentCollection's SegmentsChanged method and to pass the original segmentCollection back to the feature's shape.

Below is the main loop that worked.  I have tested this code with a polyline that had one part with one curved segment and another that had two parts with one curved segment each.  It worked for both features.  The concepts used in this code should apply to both polylines and polygons.

        ' Main loop
        Do While Not pFeat Is Nothing
            pPolyline = New Polyline
            pPolyline.SpatialReference = pFeat.Shape.SpatialReference ' Make sure Polyline has Spatial Reference
            pPolyline = pFeat.Shape ' Assign geometry to Polyline
            pSegColl = pPolyline ' Assign polyliine geometry to a SegmentCollection
            pSegColl.HasNonLinearSegments(Nonlinear) ' Determine if any segments are nonlinear
            If Nonlinear Then
                ' One or more segments is nonlinear.  Code below preserves curved segments while changing Z or M values.
                SegCount = pSegColl.SegmentCount
                distAlong = 0
                For j = 0 To SegCount - 1
                    pSegment = pSegColl.Segment(j)
                    pInPoint = New Point
                    pInPoint.SpatialReference = pFeat.Shape.SpatialReference
                    pInPoint = pSegment.FromPoint
                    pInPoint.M = distAlong ' Update either Z or M value here
                    pSegment.FromPoint = pInPoint ' Update Segment's FromPoint so it has new Z or M value
                    pSegColl.SegmentsChanged() Use SegmentsChanges method to update collection
                    pInPoint = Nothing ' Clean up Point Object
                    distAlong = Math.Sqrt(pSegment.Length ^ 2 + (pSegment.FromPoint.Z - pSegment.ToPoint.Z) ^ 2) + distAlong
                    pInPoint = New Point
                    pInPoint.SpatialReference = pFeat.Shape.SpatialReference
                    pInPoint = pSegment.ToPoint
                    pInPoint.M = distAlong ' Update either Z or M value here
                    pSegment.ToPoint = pInPoint ' Update Segment's ToPoint so it has new Z or M value
                    pSegColl.SegmentsChanged() Use SegmentsChanges method to update collection
                    pInPoint = Nothing ' Clean up Point Object
                    ' Uncomment line below to see changes to curved segments
                    ' MsgBox("Segment M Values are: From - " & pSegColl.Segment(j).FromPoint.M & ", To - " & pSegColl.Segment(j).ToPoint.M & " Geometrytype is " & pSegColl.Segment(j).GeometryType)
                Next j
                pFeat.Shape = pSegColl
                pFCursor.UpdateFeature(pFeat) ' Update the cursor
            Else
                ' Segments are all linear.  Code is not shown but it involved an I3DCuve object which won't operate on curved polylines.
            End If
            pFeat = pFCursor.NextFeature
            pPolyline = Nothing 'Clean up Polyline Object
            i = i + 1 ' Counter for features processed
            If i Mod 1000 = 0 Then ' flush edits after every 1,000 features
                pFCursor.Flush()
            End If
        Loop


If anyone knows about better clean up practices for .NET please let me know.  Now I need to add code that will check which part each segment is in to optionally calculate a gap dstance for my M values (the default behavior of the code is to ignore gaps between parts).

I hope this helps others who want to build custom code for manipulating Z or M values on nonlinear polylines or polygons.
0 Kudos
RichardFairhurst
MVP Honored Contributor
I found this thread on working with vertical polylines and it says:

"The ArcGIS geometry object model supports parametric curve segments, such as circular arcs and Bezier curves, but the behavior of these segments is not defined in 3D. However, you can use them in 3D polylines if you simply want to use their Z values as attributes and otherwise treat the segment as 2D."

This explains why the ICurve3D interface throws an error and usually crashes ArcMap when I attempt to use it on curved polylines.  This also means that when the link above says that "Two-dimensional (2D) relational operators have been extended to deal reasonably with such polylines.", the extensions only work with linear 3D polylines and not nonlinear 3D polylines.  So Densifying the curved 3D polylines into linear 3D polylines is required if the objects need to use the extended relational operators for selections that take the 3D geometry into account, but if preserving the curve is more important and being limited to 2D relational operations is acceptable, than the code above would work.
0 Kudos