[GIS] Updating a column with line direction in MapInfo

directionlinemapbasicmapinfo

I was wondering if there is a way to update a column in MapInfo with the digitised line direction. I have two TAB files, one is complete lines and the other is sections of the same lines (both have a unique ID). I was wondering if it would be possible to add a column in both files with the direction of the line so I can check if the directions are identical, e.g:

enter image description here

Thanks,
Alan

Best Answer

I would run they each of the individual segments and for each find the matching node in the complex polyline for the start as well as the end node of the section.

Now you can check if the end node number is larger than the start node number. If so the line direction of the section is the same as the direction of the polyline.

If not, the direction is reverted.

Here's some code that can help you find the node number within a polyline/polygon with only one segment:

'******************************
Function OBJFindNode(     ByVal oObj As Object      'The object (polyline or polygon) to search in, only the first segment of the object will be searched
                        , ByVal oNode As Object     'Node position to look for
                        , ByVal fTolerance As Float 'Search tolerance, in meters, distance is measured using Cartesian calculation
                        ) As Integer                'Returns the first matching node number, or 0 if no found

Dim nSegm, nNode As Integer

OnError GoTo ErrorOccured

OBJFindNode = 0

If OBJFindSegmentAndNode(oObj, oNode, fTolerance, nSegm, nNode) Then
    If nSegm = 1 Then
        OBJFindNode = nNode
    End If
End If

Exit Function
'-------------------------
ErrorOccured:
    Note Err() + " " + Error$() + ": OBJFindNode"
End Function

'************************************
Function OBJFindSegmentAndNode( ByVal oObj As Object        'The object (polyline or polygon) to search in
                              , ByVal oNode As Object       'Node position to look for
                              , ByVal fTolerance As Float   'Search tolerance, in meters, distance is measured using Cartesian calculation
                              , nSegmentFound As Integer    'The Segment number where the node was found
                              , nNodeFound As Integer       'The matching node number
                                  ) As Logical              'Returns TRUE if a matching node was found, otherwise FALSE

Dim nSegm, nNode As Integer,
    fNodeX, fNodeY, fX, fY, fDistance As Float

OnError GoTo ErrorOccured

OBJFindSegmentAndNode = FALSE

If NOT ObjectInfo(oObj, OBJ_INFO_TYPE) IN (OBJ_TYPE_PLINE, OBJ_TYPE_REGION) Then
    Exit Function
End If

fNodeX  = CentroidX(oNode)
fNodeY  = CentroidY(oNode)

For nSegm = 1 To ObjectInfo(oObj, OBJ_INFO_NPOLYGONS)
    For nNode = 1 To ObjectInfo(oObj, OBJ_INFO_NPOLYGONS + nSegm)
        fX  = ObjectNodeX(oObj, nSegm, nNode)
        fY  = ObjectNodeY(oObj, nSegm, nNode)
        fDistance   = CartesianDistance(fNodeX, fNodeY, fX, fY, "m")
        If fDistance <= fTolerance Then
           nSegmentFound    = nSegm
           nNodeFound   = nNode
           OBJFindSegmentAndNode = TRUE
           Exit Function
        End If
   Next     'nNode
Next        'nSegm

Exit Function
'-------------------------
ErrorOccured:
    Note Err() + " " + Error$() + " OBJFindSegmentAndNode"
End Function

'******************************
Function OBJFindNodeBinary(   ByVal oObj As Object      'The object (polyline or polygon) to search in, only the first segment of the object will be searched
                            , ByVal oNode As Object     'Node position to look for
                            , ByVal fTolerance As Float 'Search tolerance, in meters, distance is measured using Cartesian calculation
                            , ByVal nOffset As Integer  'Offset of 1st node in polyline based on original
                            ) As Integer                'Returns the first matching node number, or 0 if no found

Dim objLow, objHigh As Object,
    nPnts, nNode, nMid As Integer

OnError GoTo ErrorOccured

OBJFindNodeBinary = 0

nPnts = ObjectInfo(oObj, OBJ_INFO_NPNTS)

If nPnts < 100 Then
    nNode = OBJFindNode( oObj, oNode, fTolerance)
    If nNode > 0 Then
        OBJFindNodeBinary = (nNode + nOffset)
    End If
    Exit Function
End If

'Determine the middle point
nMid = nPnts \ 2 + nPnts Mod 2

'Start with the lower half
objLow = ExtractNodes( oObj, 1, 1, nMid, FALSE)
If oNode Within CartesianBuffer(MBR(objLow), 25, fTolerance, "m") Then
    nNode = OBJFindNodeBinary(objLow, oNode, fTolerance, nOffset)
    OBJFindNodeBinary = nNode
Else
    ' Forget this branch
    nNode = 0
End If

' If we couldn't find it in the lower, then check the upper half
If nNode = 0 Then
    objHigh = ExtractNodes(oObj, 1, nMid + 1, nPnts, FALSE )
    If oNode Within CartesianBuffer(MBR(objHigh), 25, fTolerance, "m") Then
        nNode = OBJFindNodeBinary( objHigh, oNode, fTolerance, nOffset + nMid)
    Else
        ' Forget this branch
        nNode = 0
    End If
End If

OBJFindNodeBinary = nNode

Exit Function
'-------------------------
ErrorOccured:
    Print Err() + " " + Error$() + " OBJFindNodeBinary: nNode: " + nNode
End Function

The fundemental structure of the Binary search function has been made by Bill Thoen who shared it on MapInfo-L more than a year ago.

This is how to use the code above:

Dim oPolyline, oPoint, oLine As Object,
    nOrder, nTolerance, nNode1, nNode2, nRowID As Integer

nTolerance = 0.2 'meters

Set CoordSys Table TABLE_WITH_LINES

Fetch First From TABLE_WITH_POLYLINES
oPolyline = TABLE_WITH_POLYLINES.OBJ

Fetch First From TABLE_WITH_LINES
Do Until EOT(TABLE_WITH_LINES)
    oLine = TABLE_WITH_LINES.OBJ
    nRowID = TABLE_WITH_LINES.ROWID 

    oPoint = CreatePoint(ObjectGeography(oLine, OBJ_GEO_LINEBEGX), ObjectGeography(oLine, OBJ_GEO_LINEBEGY))
    nNode1  = OBJFindNodeBinary(oPolyline, oPoint, 0.2, 0)

    oPoint = CreatePoint(ObjectGeography(oLine, OBJ_GEO_LINEENDX), ObjectGeography(oLine, OBJ_GEO_LINEENDY))
    nNode2  = OBJFindNodeBinary(oPolyline, oPoint, 0.2, 0)

    nOrder = 0
    If nNode1 = 0 or nNode2 = 0 Then
        'First of last point could not be matched to a node within the tolerance given
    ElseIf nNode1 < nNode2 Then
        nOrder = 1
    ElseIf nNode1 > nNode2 Then
        nOrder = -1
    End If

    Update TABLE_WITH_LINES
        Set Direction = nOrder
        Where ROWID = nROWID

    Fetch Next From TABLE_WITH_LINES
Loop

Notice that in the sample above I'm expected you to only have one polyline. If that's not the case you need to figure out how to link the current line to the matching polyline. If the line table holds a reference ID for the polyline table, you can use this.