-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Turn Lanes Value Check * Turn Lanes Check * Revert file * Turn Lanes Value Check * Update doc * Update doc * Turn Lanes Value Check Co-authored-by: Elaine Shum <[email protected]>
- Loading branch information
1 parent
85615cf
commit 9fde267
Showing
6 changed files
with
352 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
# Invalid Turn Lanes Value Check | ||
|
||
This check flags roads that have invalid `turn:lanes` tag values. | ||
|
||
Valid values contain keywords found in | ||
https://github.com/osmlab/atlas/blob/dev/src/main/java/org/openstreetmap/atlas/tags/TurnTag.java | ||
i.e. | ||
```java | ||
enum TurnType | ||
{ | ||
LEFT, | ||
SHARP_LEFT, | ||
SLIGHT_LEFT, | ||
THROUGH, | ||
RIGHT, | ||
SHARP_RIGHT, | ||
SLIGHT_RIGHT, | ||
REVERSE, | ||
MERGE_TO_LEFT, | ||
MERGE_TO_RIGHT, | ||
NONE; | ||
} | ||
String TURN_LANE_DELIMITER = "\\|"; | ||
String TURN_TYPE_DELIMITER = ";"; | ||
``` | ||
e.g. "turn:lanes":"through|through|right" is valid | ||
e.g. "turn:lanes":"through|through|righ" is not valid | ||
|
||
|
||
#### Live Examples | ||
|
||
1. The way [id:730457851](https://www.openstreetmap.org/way/730457851) has an invalid `turn:lanes` | ||
2. The way [id:836279618](https://www.openstreetmap.org/way/836279618) has an invalid `turn:lanes:forward` | ||
|
||
#### Code Review | ||
|
||
In [Atlas](https://github.com/osmlab/atlas), OSM elements are represented as Edges, Points, Lines, Nodes & Relations; in our case, we’re are looking at [Edges](https://github.com/osmlab/atlas/blob/dev/src/main/java/org/openstreetmap/atlas/geography/atlas/items/Edge.java). | ||
|
||
Our first goal is to validate the incoming Atlas object. Valid features for this check will satisfy the following conditions: | ||
|
||
* Is an Edge | ||
* Has a `highway` tag that is car navigable | ||
* Has a `turn:lanes` or `turn:lanes:forward` or `turn:lanes:backward` tag | ||
* Is not an OSM way that has already been flagged | ||
|
||
```java | ||
@Override | ||
public boolean validCheckForObject(final AtlasObject object) | ||
{ | ||
return TurnLanesTag.hasTurnLane(object) && HighwayTag.isCarNavigableHighway(object) | ||
&& object instanceof Edge && ((Edge) object).isMainEdge() | ||
&& !this.isFlagged(object.getOsmIdentifier()); | ||
} | ||
``` | ||
|
||
The valid objects (i.e. `turn:lanes` or `turn:lanes:forward` or `turn:lanes:backward`) | ||
are then trimmed to remove all the keywords found in | ||
https://github.com/osmlab/atlas/blob/dev/src/main/java/org/openstreetmap/atlas/tags/TurnTag.java | ||
i.e. | ||
|
||
```java | ||
enum TurnType | ||
{ | ||
LEFT, | ||
SHARP_LEFT, | ||
SLIGHT_LEFT, | ||
THROUGH, | ||
RIGHT, | ||
SHARP_RIGHT, | ||
SLIGHT_RIGHT, | ||
REVERSE, | ||
MERGE_TO_LEFT, | ||
MERGE_TO_RIGHT, | ||
NONE; | ||
} | ||
String TURN_LANE_DELIMITER = "\\|"; | ||
String TURN_TYPE_DELIMITER = ";"; | ||
``` | ||
if the resulting trimmed string is non-empty, that means `turn:lanes` is malformed | ||
|
||
```java | ||
@Override | ||
protected Optional<CheckFlag> flag(final AtlasObject object) | ||
{ | ||
final String turnLanesTag = object.getTag(TurnLanesTag.KEY).orElse(""); | ||
final String turnLanesForwardTag = object.getTag(TurnLanesForwardTag.KEY).orElse(""); | ||
final String turnLanesBackwardTag = object.getTag(TurnLanesBackwardTag.KEY).orElse(""); | ||
|
||
if (!this.trimKeywords(turnLanesTag).isEmpty() | ||
|| !this.trimKeywords(turnLanesForwardTag).isEmpty() | ||
|| !this.trimKeywords(turnLanesBackwardTag).isEmpty()) | ||
{ | ||
this.markAsFlagged(object.getOsmIdentifier()); | ||
|
||
return Optional.of(this.createFlag(new OsmWayWalker((Edge) object).collectEdges(), | ||
this.getLocalizedInstruction(0, object.getOsmIdentifier()))); | ||
} | ||
return Optional.empty(); | ||
} | ||
``` | ||
|
||
Please note that the keywords "LEFT" and "RIGHT" are trimmed towards the end so that the phrases | ||
"MERGE_TO_LEFT" AND "MERGE_TO_RIGHT" are trimmed first. | ||
```java | ||
public final String trimKeywords(final String input) | ||
{ | ||
String result = input.toLowerCase(); | ||
for (final TurnType turnType : TurnTag.TurnType.values()) | ||
{ | ||
if (turnType != TurnTag.TurnType.LEFT && turnType != TurnTag.TurnType.RIGHT) | ||
{ | ||
result = result.replaceAll(turnType.name().toLowerCase(), ""); | ||
} | ||
} | ||
result = result.replaceAll(TurnTag.TurnType.LEFT.name().toLowerCase(), ""); | ||
result = result.replaceAll(TurnTag.TurnType.RIGHT.name().toLowerCase(), ""); | ||
result = result.replaceAll(TurnTag.TURN_LANE_DELIMITER.toLowerCase(), ""); | ||
result = result.replaceAll(TurnTag.TURN_TYPE_DELIMITER.toLowerCase(), ""); | ||
return result.trim(); | ||
} | ||
``` | ||
|
||
To learn more about the code, please look at the comments in the source code for the check. | ||
[InvalidTurnLanesValueCheck](../../src/main/java/org/openstreetmap/atlas/checks/validation/tag/InvalidTurnLanesValueCheck.java) |
110 changes: 110 additions & 0 deletions
110
src/main/java/org/openstreetmap/atlas/checks/validation/tag/InvalidTurnLanesValueCheck.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package org.openstreetmap.atlas.checks.validation.tag; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import org.openstreetmap.atlas.checks.base.BaseCheck; | ||
import org.openstreetmap.atlas.checks.flag.CheckFlag; | ||
import org.openstreetmap.atlas.geography.atlas.items.AtlasObject; | ||
import org.openstreetmap.atlas.geography.atlas.items.Edge; | ||
import org.openstreetmap.atlas.geography.atlas.walker.OsmWayWalker; | ||
import org.openstreetmap.atlas.tags.HighwayTag; | ||
import org.openstreetmap.atlas.tags.TurnLanesBackwardTag; | ||
import org.openstreetmap.atlas.tags.TurnLanesForwardTag; | ||
import org.openstreetmap.atlas.tags.TurnLanesTag; | ||
import org.openstreetmap.atlas.tags.TurnTag; | ||
import org.openstreetmap.atlas.tags.TurnTag.TurnType; | ||
import org.openstreetmap.atlas.utilities.configuration.Configuration; | ||
|
||
/** | ||
* Flags {@link Edge}s that have the {@code highway} tag and a {@code lanes} tag with an invalid | ||
* value. The valid {@code lanes} values are configurable. | ||
* | ||
* @author mselaineleong | ||
*/ | ||
public class InvalidTurnLanesValueCheck extends BaseCheck<Long> | ||
{ | ||
private static final long serialVersionUID = -1459761692833694715L; | ||
|
||
private static final List<String> FALLBACK_INSTRUCTIONS = Arrays | ||
.asList("Way {0,number,#} has an invalid turn:lanes value."); | ||
|
||
/** | ||
* The default constructor that must be supplied. The Atlas Checks framework will generate the | ||
* checks with this constructor, supplying a configuration that can be used to adjust any | ||
* parameters that the check uses during operation. | ||
* | ||
* @param configuration | ||
* the JSON configuration for this check | ||
*/ | ||
public InvalidTurnLanesValueCheck(final Configuration configuration) | ||
{ | ||
super(configuration); | ||
} | ||
|
||
public final String trimKeywords(final String input) | ||
{ | ||
String result = input.toLowerCase(); | ||
for (final TurnType turnType : TurnTag.TurnType.values()) | ||
{ | ||
if (turnType != TurnTag.TurnType.LEFT && turnType != TurnTag.TurnType.RIGHT) | ||
{ | ||
result = result.replaceAll(turnType.name().toLowerCase(), ""); | ||
} | ||
} | ||
result = result.replaceAll(TurnTag.TurnType.LEFT.name().toLowerCase(), ""); | ||
result = result.replaceAll(TurnTag.TurnType.RIGHT.name().toLowerCase(), ""); | ||
result = result.replaceAll(TurnTag.TURN_LANE_DELIMITER.toLowerCase(), ""); | ||
result = result.replaceAll(TurnTag.TURN_TYPE_DELIMITER.toLowerCase(), ""); | ||
return result.trim(); | ||
} | ||
|
||
/** | ||
* This function will validate if the supplied atlas object is valid for the check. | ||
* | ||
* @param object | ||
* the atlas object supplied by the Atlas-Checks framework for evaluation | ||
* @return {@code true} if this object should be checked | ||
*/ | ||
@Override | ||
public boolean validCheckForObject(final AtlasObject object) | ||
{ | ||
return TurnLanesTag.hasTurnLane(object) && HighwayTag.isCarNavigableHighway(object) | ||
&& object instanceof Edge && ((Edge) object).isMainEdge() | ||
&& !this.isFlagged(object.getOsmIdentifier()); | ||
} | ||
|
||
/** | ||
* This is the actual function that will check to see whether the object needs to be flagged. | ||
* | ||
* @param object | ||
* the atlas object supplied by the Atlas-Checks framework for evaluation | ||
* @return an optional {@link CheckFlag} object that | ||
*/ | ||
@Override | ||
protected Optional<CheckFlag> flag(final AtlasObject object) | ||
{ | ||
final String turnLanesTag = object.getTag(TurnLanesTag.KEY).orElse(""); | ||
final String turnLanesForwardTag = object.getTag(TurnLanesForwardTag.KEY).orElse(""); | ||
final String turnLanesBackwardTag = object.getTag(TurnLanesBackwardTag.KEY).orElse(""); | ||
|
||
if (!this.trimKeywords(turnLanesTag).isEmpty() | ||
|| !this.trimKeywords(turnLanesForwardTag).isEmpty() | ||
|| !this.trimKeywords(turnLanesBackwardTag).isEmpty()) | ||
{ | ||
this.markAsFlagged(object.getOsmIdentifier()); | ||
|
||
return Optional.of(this.createFlag(new OsmWayWalker((Edge) object).collectEdges(), | ||
this.getLocalizedInstruction(0, object.getOsmIdentifier()))); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
protected List<String> getFallbackInstructions() | ||
{ | ||
return FALLBACK_INSTRUCTIONS; | ||
} | ||
|
||
} |
37 changes: 37 additions & 0 deletions
37
...st/java/org/openstreetmap/atlas/checks/validation/tag/InvalidTurnLanesValueCheckTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package org.openstreetmap.atlas.checks.validation.tag; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.openstreetmap.atlas.checks.configuration.ConfigurationResolver; | ||
import org.openstreetmap.atlas.checks.validation.verifier.ConsumerBasedExpectedCheckVerifier; | ||
|
||
/** | ||
* Tests for {@link InvalidTurnLanesValueCheck} | ||
* | ||
* @author mselaineleong | ||
*/ | ||
public class InvalidTurnLanesValueCheckTest | ||
{ | ||
@Rule | ||
public InvalidTurnLanesValueCheckTestRule setup = new InvalidTurnLanesValueCheckTestRule(); | ||
|
||
@Rule | ||
public ConsumerBasedExpectedCheckVerifier verifier = new ConsumerBasedExpectedCheckVerifier(); | ||
|
||
@Test | ||
public void invalidTurnLanesValue() | ||
{ | ||
this.verifier.actual(this.setup.invalidTurnLanesValue(), | ||
new InvalidTurnLanesValueCheck(ConfigurationResolver.emptyConfiguration())); | ||
this.verifier.globallyVerify(flags -> Assert.assertEquals(1, flags.size())); | ||
} | ||
|
||
@Test | ||
public void validTurnLanesValue() | ||
{ | ||
this.verifier.actual(this.setup.validTurnLanesValue(), | ||
new InvalidTurnLanesValueCheck(ConfigurationResolver.emptyConfiguration())); | ||
this.verifier.globallyVerify(flags -> Assert.assertEquals(0, flags.size())); | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
...ava/org/openstreetmap/atlas/checks/validation/tag/InvalidTurnLanesValueCheckTestRule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package org.openstreetmap.atlas.checks.validation.tag; | ||
|
||
import org.openstreetmap.atlas.geography.atlas.Atlas; | ||
import org.openstreetmap.atlas.utilities.testing.CoreTestRule; | ||
import org.openstreetmap.atlas.utilities.testing.TestAtlas; | ||
import org.openstreetmap.atlas.utilities.testing.TestAtlas.Edge; | ||
import org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc; | ||
import org.openstreetmap.atlas.utilities.testing.TestAtlas.Node; | ||
|
||
/** | ||
* Tests for {@link InvalidLanesTagCheck} | ||
* | ||
* @author mselaineleong | ||
*/ | ||
|
||
public class InvalidTurnLanesValueCheckTestRule extends CoreTestRule | ||
{ | ||
private static final String TEST_1 = "47.2136626201459,-122.443275382856"; | ||
private static final String TEST_2 = "47.2138327316739,-122.44258668766"; | ||
private static final String TEST_3 = "47.2136626201459,-122.441897992465"; | ||
private static final String TEST_4 = "47.2138114677627,-122.440990166979"; | ||
private static final String TEST_5 = "47.2136200921786,-122.44001973284"; | ||
private static final String TEST_6 = "47.2135137721113,-122.439127559518"; | ||
private static final String TEST_7 = "47.2136200921786,-122.438157125378"; | ||
|
||
@TestAtlas( | ||
// nodes | ||
nodes = { @Node(coordinates = @Loc(value = TEST_1)), | ||
@Node(coordinates = @Loc(value = TEST_3)), | ||
@Node(coordinates = @Loc(value = TEST_5)), | ||
@Node(coordinates = @Loc(value = TEST_7)) }, | ||
// edges | ||
edges = { @Edge(id = "1000000001", coordinates = { @Loc(value = TEST_1), | ||
@Loc(value = TEST_2), @Loc(value = TEST_3) }, tags = { "highway=motorway" }), | ||
@Edge(id = "1001000001", coordinates = { @Loc(value = TEST_3), | ||
@Loc(value = TEST_4), @Loc(value = TEST_5) }, tags = { | ||
"highway=motorway", "lanes=1.5", "turn:lanes=through|right" }), | ||
@Edge(id = "1002000001", coordinates = { @Loc(value = TEST_5), | ||
@Loc(value = TEST_6), | ||
@Loc(value = TEST_7) }, tags = { "highway=motorway" }) }) | ||
private Atlas validTurnLanesValue; | ||
|
||
@TestAtlas( | ||
// nodes | ||
nodes = { @Node(coordinates = @Loc(value = TEST_1)), | ||
@Node(coordinates = @Loc(value = TEST_3)), | ||
@Node(coordinates = @Loc(value = TEST_5)), | ||
@Node(coordinates = @Loc(value = TEST_7), tags = { "barrier=toll_booth" }) }, | ||
// edges | ||
edges = { @Edge(id = "1000000001", coordinates = { @Loc(value = TEST_1), | ||
@Loc(value = TEST_2), @Loc(value = TEST_3) }, tags = { "highway=motorway" }), | ||
@Edge(id = "1001000001", coordinates = { @Loc(value = TEST_3), | ||
@Loc(value = TEST_4), | ||
@Loc(value = TEST_5) }, tags = { "highway=motorway", "lanes=11", | ||
"turn:lanes=throug|throug|slight_right" }), | ||
@Edge(id = "1002000001", coordinates = { @Loc(value = TEST_5), | ||
@Loc(value = TEST_6), | ||
@Loc(value = TEST_7) }, tags = { "highway=motorway" }) }) | ||
private Atlas invalidTurnLanesValue; | ||
|
||
public Atlas invalidTurnLanesValue() | ||
{ | ||
return this.invalidTurnLanesValue; | ||
} | ||
|
||
public Atlas validTurnLanesValue() | ||
{ | ||
return this.validTurnLanesValue; | ||
} | ||
} |