Subscribe to this thread
Home - General / All posts - Fixing direction of parallel lanes for left-hand traffic
atomek

422 post(s)
#20-Oct-15 11:28

Hi guys,

I'm developing a network model using Open Street Maps data which (after processing in Manifold) I use for batch routing (outside Manifold). Dual carriageways are represented in that dataset with separate lines for each direction of traffic but single-carriageway bi-directional roads are single lines (with an appropriate attribute indicating it's a two-way road).

For the level of detail of analysis I'm doing I need each direction to be modelled separately. I'm able to create two parallel lines pointing in opposite directions for each single-carriageway road but I can't get around fixing (reversing both of) them where they are aligned opposite to how traffic moves here (UK left-hand traffic). It's not a problem for the analysis to run, but a big issue for presentation of the results.

I attached a sample .map of what I'm working with (I circled what is considered "good"/"bad" representation), hoping some of you may have a smart idea for automating the selection of lines for reversing.

Thanks,

Tom

Attachments:
LaneDirection.map

tjhb
10,094 post(s)
#21-Oct-15 00:25

This is beautiful work Tom.

Though on behalf of roughly 1 in 15 European males (for example), please don't use red and green to show contrasting classes in cartography! Avoidable grief.

How about joining all lines for a given road_index into a ring (AllBranches() in SQL does not trigger normalization, then if IsRing() you can confidently ConvertToArea(), all without affecting original direction), then measuring whether ring direction is clockwise (correct) or anti-clockwise (incorrect)?

To measure that you *could* use the surveyor's method a.k.a. Gauss's method—or you could just determine whether the area boundary is reversed on normalization. Normalize() in SQL always makes area boundaries run anti-clockwise (with thanks to Adam).

Tim

tjhb
10,094 post(s)
#21-Oct-15 01:32

It was a little bit more complicate than that, but not much.

This is a start. It produces clockwise rings (lines) for each road in [OneWays] joined with its connectors in [Connectors].

OPTIONS CoordSys("OneWays" AS COMPONENT)

;

SELECT

    [road_index],

    --[Joined],

    CASE

        WHEN IsRing([Branch])

            THEN ReverseLine(ConvertToLine(

                Normalize(ConvertToArea([Branch]))

                -- Normalize() -> area directed anticlockwise

                ))

        ELSE NULL -- check data

    END AS [Clockwise ring]

FROM

    (SELECT

        [road_index],

        Normalize(AllBranches([ID])) AS [Joined]

            -- Normalize() is required to join contiguous branches

            -- It does not preserve or enforce line direction

            -- Here we don't mind since we are about to correct it

    FROM 

        (SELECT [ID][road_index]

        FROM [OneWays]

        UNION

        SELECT [ID][road_index]

        FROM [Connectors]

        )

    GROUP BY [road_index]

    )

SPLIT BY Branches([Joined]AS [Branch]

    -- To subdivide any self-intersecting joined lines

    -- into separate rings

    -- (There is just one in the example data

    -- where a road crosses a line in Roads having atr = 512)

;

From here there are at least two ways to go.

You can either join each line in [OneWays] and [Connectors] to its containing [Clockwise ring] in the result, split the [Clockwise ring] with the ends of the contained line using IntersectLine() (there's a slightly tricky aspect to that) then check whether their ends match, reversing the contained line if necessary. (IntersectLine() does not disrupt line direction.) That's OK though fiddly.

Or, perhaps, you could just Explode each [Clockwise ring] then use each segment as it is (restoring attributes, perhaps via centroids, then sorting back into the two drawings). That looks simpler but could be more fiddly overall.

Attachments:
Draw clockwise rings.txt

atomek

422 post(s)
#21-Oct-15 20:48

Thank you so much Tim!

I followed your first suggestion to progress with the results of your query, intersecting them with both RoadsNodes and OneWayNodes. The query produced lines (loops) that in some cases started not where they were expected to start (i.e. not where input lines started; visible when line style is an arrow, see results of your query: lines with index e.g. 125, 147, 97), thus intersectLine resulted in some segments being split into two. To identify these instances I created an update query that set a [count] column of these [FinalOneWays] to contain the number of lines with a given index number (so if there were, as expected, 6 lines with a given index, thats 2 lanes and 4 connectors, it set 6 as count, but a different number, >6, where additional segments were created) that I could then thematically format (using blue and purple ;)) to see where manual handling is required.

I'll see how workable is that 'manual handling' with a full dataset, but being able to clearly distinguish 'bad apples' is a plus.

Mr Zappa ;) thank you for the intellectual workout your thought experiment required me to perform

tjhb
10,094 post(s)
#21-Oct-15 21:57

The query produced lines (loops) that in some cases started not where they were expected to start (i.e. not where input lines started...) thus intersectLine resulted in some segments being split into two.

I hadn't thought of that Tom and I should have, since I've struck the same issue (not really an issue, perfectly normal, but something that requires a bit more work) in a context completely different from yours, yet oddly similar, as tends to be the way with geometry.

So I believe I know how to handle it efficiently. Either leave it with me or if you like post the code you're currently working with and I'll add a bit.

firsttube


1,439 post(s)
#21-Oct-15 17:00

And now for a less-elegant multiple-step idea. Just thinking out loud here...

It seems that all the incorrect lines in OneWays have another line within 5 metres to the left of themselves. You could create a midpoint for each line in OneWays. Then use a modified version of Tim's "Snap points to nearest line with threshold" query to move each midpoint to the closest line in OneWays having a different ID within 5 metres. We could then create new lines from the original midpoint to its moved midpoint (call it MidPointLines). Now we can extract the bearing of the OneWays line at its midpoint and compare it to the bearing of the MidPointLines. If the MidPointLines have a bearing that is -90 degrees that of the OneWays line, then that OneWays line should be reversed, as well as it's pair. Some checking would have to be done before comparing the bearings, i.e. if the bearing of the OneWays line was 290, then the bearing of the correct MidPointLines would be 20. Similarly, if the OneWays line has a bearing of 40 then the incorrect MidPointLines would be 310.

One trick will be to get the bearing of the OneWays line at its midpoint. My idea was to create a tiny buffer around the midpoint and use ClipIntersect, but this sometimes reverses the clipped portion of the line. Perhaps one could create a tiny buffer, then create the buffer's boundary line, and use IntersectLines?


"The blessing in life is finding the torture you are comfortable with." - Jerry Seinfeld, 6/26/2013

Manifold User Community Use Agreement Copyright (C) 2007-2021 Manifold Software Limited. All rights reserved.