Why is SplitLineAtPoint failing?

683
6
04-15-2011 07:53 AM
LornaMurison
Occasional Contributor
Hi everyone,
I am trying to use IPolycurve.QueryPointAndDistance to get the distance from the curve start to the location of a dam on a river.
The problem I encountered is that this seems to query existing vertices on the river, so if the particular segment has only a start and end point (and no vertex near the dam location), the distance to the dam will always be 0.
In order to get around this I am using IProximityOperator.ReturnNearestPoint to find the point on the river closest to the dam, and then using IPolycurve.SplitAtPoint to create a vertex at that point. Once that vertex is in place, QueryPointAndDistance should be able to measure the distance to that vertex.
Now, the SplitAtPoint method has a boolean output called SplitHappened and this is set to "True" when the split is successful.

I am running through this loop for 5 different dams. For the first one, SplitHappened is set to "False" and the distance to the dam is 0. But for the other 4 dams, SplitHappened is set to "True". I don't know what could be changing between the first iteration and the others to cause this difference.

Any comments will be appreciated

    
    'Create a point on the river nearest the dam
    Dim pProxOp As IProximityOperator
    Set pProxOp = pRiver ' <--- pRiver is an IFeature
    Dim pDamPoint As IPoint
    Set pDamPoint = pProxOp.ReturnNearestPoint(pDam, esriNoExtension) '<-- pDam is an IFeature
    
    'Create a vertex in the river at that point
    Dim PartIndex As Long
    Dim SegIndex As Long
    Dim SplitHap As Boolean
    pRiver.SplitAtPoint pDamPoint, True, False, SplitHap, PartIndex, SegIndex
    
    Debug.Print SplitHap ' <-- This comes up "False" the first time, and "True" all other times
    'Find the distance to the point
    Dim DistToDam As Double
    Dim NearDist As Double
    Dim Length As Double
    Dim pDamPoint2 As IPoint
    Set pDamPoint2 = New Point
    Dim Right As Boolean
    pRiver.QueryPointAndDistance esriNoExtension, pDam, False, pDamPoint2, DistToDam, NearDist, Right
0 Kudos
6 Replies
DuncanHornby
MVP Notable Contributor
So you are saying the first time it enters the loop it says false and does not do the split but the next 4 dams do split their respective polylines? May be the issue is with the polyline for the first split, may be it's multipart?

Duncan
0 Kudos
LornaMurison
Occasional Contributor
So you are saying the first time it enters the loop it says false and does not do the split but the next 4 dams do split their respective polylines? May be the issue is with the polyline for the first split, may be it's multipart?


That is exactly what I'm saying.  I just converted it to multipart and ran the code again, but it gives me the same result.

I deleted the first dam and ran it again.  The other 4 dams still work.  So it is definitely related to that particular dam/river segment and not to the fact that it is the first time through the loop.

Any other ideas?
0 Kudos
JakubSisak
Occasional Contributor III
Can you post the rest of the code?
I suspect you stepped through it and monitored all variables and parameters the first time around to make sure all is kosher?
Is the first incoming geometry simple?
0 Kudos
JakubSisak
Occasional Contributor III
The other 4 dams still work.  So it is definitely related to that particular dam/river segment and not to the fact that it is the first time through the loop.
Any other ideas?


Validate/simplify the geometry before you you attempt to split it.
0 Kudos
LornaMurison
Occasional Contributor
Here is the full code.

I have stepped through it several times and identified areas where I was setting variables inside instead of outside loops and things like that, but I can't see any more of those issues.

Thanks for your help 🙂

Private Sub Downstream_Click()

'Get each of the three layers and feature classes (Rivers, Dams, and CSL)
Dim pMxDoc As IMxDocument
Set pMxDoc = ThisDocument

Dim pMap As IMap
Set pMap = pMxDoc.FocusMap

Dim pActiveView As IActiveView
Set pActiveView = pMxDoc.ActiveView

Dim pRiversL As IFeatureLayer
Set pRiversL = pMap.Layer(0) 'Rivers
Dim pRiversFC As IFeatureClass
Set pRiversFC = pRiversL.FeatureClass

Dim pDamsL As IFeatureLayer
Set pDamsL = pMap.Layer(1) 'Dams
Dim pDamsFC As IFeatureClass
Set pDamsFC = pDamsL.FeatureClass

Dim pCSLL As IFeatureLayer
Set pCSLL = pMap.Layer(2) 'CSL
Dim pCSLFC As IFeatureClass
Set pCSLFC = pCSLL.FeatureClass

'Begin looping through the Dams
Dim pDamCursor As IFeatureCursor
Set pDamCursor = pDamsFC.Search(Nothing, False)

Dim pDamF As IFeature
Set pDamF = pDamCursor.NextFeature

Dim DamNumber As Integer
DamNumber = 0
Do Until pDamF Is Nothing
    DamNumber = DamNumber + 1
    Debug.Print "Beginning Dam Number " & DamNumber & "..."

    'Find the river segment that intersects with the dam
    Dim pDam As IPoint
    Set pDam = pDamF.Shape
    
    Dim pTopoOp As ITopologicalOperator
    Set pTopoOp = pDam
    
    Dim BufferPoly As IPolygon
    Set BufferPoly = pTopoOp.Buffer(pMxDoc.SearchTolerance)
    
    Dim pSF As ISpatialFilter
    Set pSF = New SpatialFilter
    
    With pSF
        Set .Geometry = BufferPoly
        .GeometryField = "Shape"
        .SpatialRel = esriSpatialRelIndexIntersects
    End With
    
    Dim pRivCursor As IFeatureCursor
    Set pRivCursor = pRiversFC.Search(pSF, False)
    
    Dim pRiverF As IFeature
    Set pRiverF = pRivCursor.NextFeature
    
    'Get the length of the current segment
    Dim pRiver As IPolycurve
    Set pRiver = pRiverF.Shape
    
    Dim RiverLength As Double
    RiverLength = pRiver.Length
    
    'Create a point on the river nearest the dam
    Dim pProxOp As IProximityOperator
    Set pProxOp = pRiver
    Dim pDamPoint As IPoint
    Set pDamPoint = pProxOp.ReturnNearestPoint(pDam, esriNoExtension)
    
    'Create a vertex in the river at that point
    Dim PartIndex As Long
    Dim SegIndex As Long
    Dim SplitHap As Boolean
    pRiver.SplitAtPoint pDamPoint, True, False, SplitHap, PartIndex, SegIndex
    
    Debug.Print SplitHap
    'Find the distance to the point
    Dim DistToDam As Double
    Dim NearDist As Double
    Dim Length As Double
    Dim pDamPoint2 As IPoint
    Set pDamPoint2 = New Point
    Dim Right As Boolean
    pRiver.QueryPointAndDistance esriNoExtension, pDam, False, pDamPoint2, DistToDam, NearDist, Right

    Length = RiverLength - DistToDam
    Debug.Print "Distance to Dam #" & DamNumber & ": " & DistToDam
    
    Dim pRiverSeg As IPolycurve
    Set pRiverSeg = pRiver
    Dim pNextRiver As IPolyline
    Dim pNextFromPoint As IPoint
    Dim pNextToPoint As IPoint
    Dim pToPoint As IPoint
    Dim LoopCount1 As Integer
    Dim pTopoOpUnion As ITopologicalOperator
    Set pTopoOpUnion = pRiverSeg
    LoopCount1 = 0
    
    'Keep going until length of segments downstream of the dam is greater than 500m
    Do Until Length >= 500
        LoopCount1 = LoopCount1 + 1
        Debug.Print "Finding downstream segments, Iteration # " & LoopCount1 & "..."
        If LoopCount1 = 1 Then
            Set pToPoint = pRiver.ToPoint
        ElseIf LoopCount1 > 1 Then
            'Do Nothing
        End If
        
        'Find segments that intersect with the last segment's to-point
        Set pTopoOp = pToPoint
        Set BufferPoly = pTopoOp.Buffer(pMxDoc.SearchTolerance)
        
        With pSF
            Set .Geometry = BufferPoly
            .GeometryField = "Shape"
            .SpatialRel = esriSpatialRelIndexIntersects
        End With
        
        Dim pNextCursor As IFeatureCursor
        Set pNextCursor = pRiversFC.Search(pSF, False)
        
        Dim pNextRiverF As IFeature
        Set pNextRiverF = pNextCursor.NextFeature
        
        
        
        Do Until pNextRiverF Is Nothing
            'Get the geometry attributes of the intersecting segment
            Set pNextRiver = pNextRiverF.Shape
            Set pNextFromPoint = pNextRiver.FromPoint
            Set pNextToPoint = pNextRiver.ToPoint
            'Find out if the intersecting segment is the next downstream segment
            If Round(pToPoint.X, 5) = Round(pNextFromPoint.X, 5) Then
                If Round(pToPoint.Y, 5) = Round(pNextFromPoint.Y, 5) Then
                    Debug.Print "Downstream segment found"
                    Length = Length + pNextRiver.Length 'And add the length of the downstream segment to the total
                    Set pRiverSeg = pTopoOpUnion.Union(pNextRiver)
                    'pRiverSeg should now contain the entire length of river
                    Set pToPoint = pNextRiver.ToPoint
                    Debug.Print "Total length: " & pRiverSeg.Length
                    Debug.Print "Length downstream from dam: " & Length
                Else:
                    'Do nothing
                End If
            Else:
                'Do nothing
            End If
            
            Set pNextRiverF = pNextCursor.NextFeature
            
        Loop
        Debug.Print "Downstream segment confirmed"
     Loop
     Debug.Print "Downstream segments compiled to be at least 500 metres"
                
    'Get the Subcurve
    Dim pSubCurve As IPolycurve
    pRiverSeg.GetSubcurve DistToDam, DistToDam + 500, False, pSubCurve
    
    'Select any CSL that intersects with the Subcurve (This curve starts at the dam and extends 500 metres downstream)
    Dim pFeatureSelection As IFeatureSelection
    Set pFeatureSelection = pCSLL
    
    Set pTopoOp = pSubCurve
    Set BufferPoly = pTopoOp.Buffer(pMxDoc.SearchTolerance)
    
    With pSF
        Set .Geometry = BufferPoly
        .GeometryField = "Shape"
        .SpatialRel = esriSpatialRelIndexIntersects
    End With
    
    Debug.Print "Searching for CSL intersecting with 500 metre downstream segment..."
    Dim pCSLCursor As IFeatureCursor
    Set pCSLCursor = pCSLFC.Search(pSF, False)
    
    Dim pCSLF As IFeature
    Set pCSLF = pCSLCursor.NextFeature
    
    Dim LoopCount As Integer
    LoopCount = 0
    Dim Query As String
    Do Until pCSLF Is Nothing 'All of these are CSL intersecting with the Subcurve
        LoopCount = LoopCount + 1
        Debug.Print "Found downstream CSL # " & LoopCount
        Dim pCSL As IPoint
        Set pCSL = pCSLF.Shape

        If LoopCount = 1 Then
            Query = "OBJECTID = " & pCSLF.OID
        ElseIf LoopCount > 1 Then
            Query = Query & " OR OBJECTID = " & pCSLF.OID
        End If
        Set pCSLF = pCSLCursor.NextFeature
    Loop
    Debug.Print LoopCount & " Downstream CSL found"
    If LoopCount > 0 Then 'Only perform the selection if intersecting CSL were found
        Dim pQueryFilter As IQueryFilter
        Set pQueryFilter = New QueryFilter
        pQueryFilter.WhereClause = Query
        pFeatureSelection.SelectFeatures pQueryFilter, esriSelectionResultAdd, False
        Debug.Print LoopCount & " Downstream CSL selected"
    Else:
        'Do Nothing
    End If
    
    Set pDamF = pDamCursor.NextFeature
    Debug.Print " "
    Debug.Print " "
Loop

Debug.Print "completed downstream CSL selection for " & DamNumber & " dams"
pActiveView.Refresh
MsgBox "Done!"
End Sub
0 Kudos
DuncanHornby
MVP Notable Contributor
Lorna,

Try setting your spatialfilter SpatialRel property to esriSpatialRelIntersects?

Duncan
0 Kudos