Skip to content

Commit

Permalink
Fixing edge connectivity for OSM ways with differing layer tag values (
Browse files Browse the repository at this point in the history
…#220)

* Fixing edge connectivity for varying layer tags

* Consistent commenting and minor modification of return statement

* Changing back for readability - aka code tennis
  • Loading branch information
MikeGost authored and matthieun committed Sep 21, 2018
1 parent 7f098fb commit 2b343dc
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,45 +64,6 @@ public class RawAtlasIntegrationTest
@Rule
public DynamicRawAtlasSectioningTestRule setup = new DynamicRawAtlasSectioningTestRule();

@Test
public void testOverlappingNodesWithUniqueLayerTags()
{
// Based on https://www.openstreetmap.org/way/467880095 and
// https://www.openstreetmap.org/way/28247094 having two different layer tag values and
// having overlapping nodes (https://www.openstreetmap.org/node/4661272336 and
// https://www.openstreetmap.org/node/5501637097) that should not be merged.
final Location overlappingLocation = Location.forString("1.3248985,103.6452864");
final String path = RawAtlasIntegrationTest.class.getResource("layerTagTestCase.pbf")
.getPath();
final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(path));
final Atlas rawAtlas = rawAtlasGenerator.build();

// Verify both points made it into the raw atlas
Assert.assertTrue(Iterables.size(rawAtlas.pointsAt(overlappingLocation)) == 2);

// Prepare the country and boundary
final Set<String> singaporeCountry = new HashSet<>();
singaporeCountry.add("SGP");
final CountryBoundaryMap boundaryMap = CountryBoundaryMap
.fromPlainText(new InputStreamResource(() -> RawAtlasIntegrationTest.class
.getResourceAsStream("testNodesWithDifferentLayerTagsBoundaryMap.txt")));

final Atlas slicedRawAtlas = new RawAtlasCountrySlicer(singaporeCountry, boundaryMap)
.slice(rawAtlas);
final Atlas finalAtlas = new WaySectionProcessor(slicedRawAtlas,
AtlasLoadingOption.createOptionWithAllEnabled(boundaryMap)).run();

// Make sure there is no sectioning happening between the two ways with different layer tag
// values. There is a one-way overpass and a bi-directional residential street, resulting in
// 3 total edges and 4 nodes (one on both ends of the two segments)
Assert.assertEquals(3, finalAtlas.numberOfEdges());
Assert.assertEquals(4, finalAtlas.numberOfNodes());

// Again, verify there is no node at the duplicated location
Assert.assertTrue(Iterables.size(finalAtlas.nodesAt(overlappingLocation)) == 0);
Assert.assertEquals(0, finalAtlas.numberOfPoints());
}

@Test
public void testPbfToSlicedAtlasWithExpansion()
{
Expand Down Expand Up @@ -340,6 +301,130 @@ public void testStandAloneNodeIngest()
Assert.assertEquals(0, finalAtlas.numberOfRelations());
}

@Test
public void testTwoWaysWithDifferentLayersIntersectingAtEnd()
{
// Based on https://www.openstreetmap.org/way/26071941 and
// https://www.openstreetmap.org/way/405246856 having two different layer tag values and
// having a shared node (https://www.openstreetmap.org/node/281526976) at which one of the
// ways ends. This is a fairly common OSM use-case, where two roads (often ramps or links)
// having different layer tags should be connected.
final Location intersection = Location.forString("55.0480165, 82.9406646");
final String path = RawAtlasIntegrationTest.class
.getResource("twoWaysWithDifferentLayersIntersectingAtEnd.pbf").getPath();
final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(path));
final Atlas rawAtlas = rawAtlasGenerator.build();

// Prepare the country and boundary
final Set<String> countries = new HashSet<>();
countries.add("RUS");
final CountryBoundaryMap boundaryMap = CountryBoundaryMap
.fromPlainText(new InputStreamResource(() -> RawAtlasIntegrationTest.class
.getResourceAsStream("layerIntersectionAtEndBoundaryMap.txt")));

final Atlas slicedRawAtlas = new RawAtlasCountrySlicer(countries, boundaryMap)
.slice(rawAtlas);
final Atlas finalAtlas = new WaySectionProcessor(slicedRawAtlas,
AtlasLoadingOption.createOptionWithAllEnabled(boundaryMap)).run();

// Make sure there are exactly three edges created. Both ways are one-way and one of them
// gets way-sectioned into two edges.
Assert.assertEquals(3, finalAtlas.numberOfEdges());

// Make sure there are exactly 4 nodes
Assert.assertEquals(4, finalAtlas.numberOfNodes());

// Explicitly check for a single node at the intersection location
Assert.assertEquals(1, Iterables.size(finalAtlas.nodesAt(intersection)));

// Explicitly check that the layer=0 link is connected to both the layer=-1 trunk edges
Assert.assertEquals(2, finalAtlas.edge(26071941000000L).connectedEdges().size());
}

@Test
public void testTwoWaysWithDifferentLayersIntersectingAtStart()
{
// Based on https://www.openstreetmap.org/way/551411163 and partial piece of
// https://www.openstreetmap.org/way/67803311 having two different layer tag values and
// having a shared node (https://www.openstreetmap.org/node/5325270497) at which one of the
// ways ends. This is a fairly common OSM use-case, where two roads (often ramps or links)
// having different layer tags should be connected. In this case, we also check that the
// trunk link is connected to the trunk at both the start and end nodes.
final Location intersection = Location.forString("52.4819691, 38.7603042");
final String path = RawAtlasIntegrationTest.class
.getResource("twoWaysWithDifferentLayersIntersectingAtStart.pbf").getPath();
final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(path));
final Atlas rawAtlas = rawAtlasGenerator.build();

// Prepare the country and boundary
final Set<String> countries = new HashSet<>();
countries.add("RUS");
final CountryBoundaryMap boundaryMap = CountryBoundaryMap
.fromPlainText(new InputStreamResource(() -> RawAtlasIntegrationTest.class
.getResourceAsStream("layerIntersectionAtStartBoundaryMap.txt")));

final Atlas slicedRawAtlas = new RawAtlasCountrySlicer(countries, boundaryMap)
.slice(rawAtlas);
final Atlas finalAtlas = new WaySectionProcessor(slicedRawAtlas,
AtlasLoadingOption.createOptionWithAllEnabled(boundaryMap)).run();

// Make sure there are exactly six edges created. The trunk link (551411163) is
// way-sectioned into 2 pieces - at an intermediate crossing, while the trunk (67803311) is
// sectioned into 4 pieces - once at the start of the link, once at an intermediate crossing
// and again at the end of the link.
Assert.assertEquals(6, finalAtlas.numberOfEdges());

// Make sure there are exactly 6 nodes
Assert.assertEquals(6, finalAtlas.numberOfNodes());

// Explicitly check for a single node at the intersection location
Assert.assertEquals(1, Iterables.size(finalAtlas.nodesAt(intersection)));

// Explicitly check that the layer=0 link is connected to both the layer=1 trunk edges and
// its own sectioned edge
Assert.assertEquals(3, finalAtlas.edge(551411163000001L).connectedEdges().size());
Assert.assertEquals(3, finalAtlas.edge(551411163000002L).connectedEdges().size());
}

@Test
public void testTwoWaysWithDifferentLayersIntersectingInMiddle()
{
// Based on https://www.openstreetmap.org/way/467880095 and
// https://www.openstreetmap.org/way/28247094 having two different layer tag values and
// having overlapping nodes (https://www.openstreetmap.org/node/4661272336 and
// https://www.openstreetmap.org/node/5501637097) that should not be merged.
final Location overlappingLocation = Location.forString("1.3248985,103.6452864");
final String path = RawAtlasIntegrationTest.class
.getResource("twoWaysWithDifferentLayersIntersectingInMiddle.pbf").getPath();
final RawAtlasGenerator rawAtlasGenerator = new RawAtlasGenerator(new File(path));
final Atlas rawAtlas = rawAtlasGenerator.build();

// Verify both points made it into the raw atlas
Assert.assertTrue(Iterables.size(rawAtlas.pointsAt(overlappingLocation)) == 2);

// Prepare the country and boundary
final Set<String> singaporeCountry = new HashSet<>();
singaporeCountry.add("SGP");
final CountryBoundaryMap boundaryMap = CountryBoundaryMap
.fromPlainText(new InputStreamResource(() -> RawAtlasIntegrationTest.class
.getResourceAsStream("layerIntersectionInMiddleBoundaryMap.txt")));

final Atlas slicedRawAtlas = new RawAtlasCountrySlicer(singaporeCountry, boundaryMap)
.slice(rawAtlas);
final Atlas finalAtlas = new WaySectionProcessor(slicedRawAtlas,
AtlasLoadingOption.createOptionWithAllEnabled(boundaryMap)).run();

// Make sure there is no sectioning happening between the two ways with different layer tag
// values. There is a one-way overpass and a bi-directional residential street, resulting in
// 3 total edges and 4 nodes (one on both ends of the two segments)
Assert.assertEquals(3, finalAtlas.numberOfEdges());
Assert.assertEquals(4, finalAtlas.numberOfNodes());

// Again, verify there is no node at the duplicated location
Assert.assertTrue(Iterables.size(finalAtlas.nodesAt(overlappingLocation)) == 0);
Assert.assertEquals(0, finalAtlas.numberOfPoints());
}

private void assertAllEntitiesHaveCountryCode(final Atlas atlas)
{
atlas.lines().forEach(line ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RUS||POLYGON((82.9358393 55.0504618, 82.9440158 55.0514274, 82.9463965 55.0445087, 82.9378292 55.0434533, 82.9358393 55.0504618))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RUS||POLYGON((38.7492934 52.4698156, 38.7907408 52.4800608, 38.8020636 52.4476597, 38.7715000 52.4466020, 38.7492934 52.4698156))
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,9 @@ private void identifyEdgesNodesAndAreasFromLines(final WaySectionChangeSet chang

// Identify all nodes. We care about three cases: 1. self-intersections, if the way
// contains a repeated location 2. sectioning based on tagging (ex. at a barrier) 3.
// at an intersection with another edge
// at an intersection with another edge. Case 3 breaks down into pieces: a. An
// intersection with another edge of the same layer. b. An intersection at the start
// or end of an edge of a different layer
for (int index = 0; index < polyLine.size(); index++)
{
final Location shapePoint = polyLine.get(index);
Expand All @@ -625,11 +627,18 @@ private void identifyEdgesNodesAndAreasFromLines(final WaySectionChangeSet chang
continue;
}

// 3. Check if there is an edge intersection of the same layer at this location
// 3a. Check for an intersecting edge, of the same layer at this location
if (locationIsPartOfAnIntersectingEdgeOfTheSameLayer(shapePoint, line))
{
addPointToNodeList(shapePoint, nodesForEdge);
}

// 3b. Check for an intersecting edge, of a different layer at this location -
// the intersecting edge must start or end here
if (locationIsAnEndPointOfAnIntersectingEdgeOfDifferentLayer(shapePoint, line))
{
addPointToNodeList(shapePoint, nodesForEdge);
}
}

if (line.isClosed() && nodesForEdge.size() == 0)
Expand Down Expand Up @@ -779,6 +788,42 @@ private boolean isAtlasPoint(final WaySectionChangeSet changeSet, final Point po
return false;
}

/**
* Determines whether the given {@link Line} has any intersecting {@link Line}s, with a
* different layer tag value that meet the definition of an {@link Edge}, starting or ending at
* the given {@link Location}.
*
* @param location
* The {@link Location} to use
* @param line
* The {@link Line} to use
* @return {@code true} if there is at least one {@link Edge} with a different layer tag value
* intersecting the given line and having an end point at the given location
*/
private boolean locationIsAnEndPointOfAnIntersectingEdgeOfDifferentLayer(
final Location location, final Line line)
{
final long targetLayerValue = LayerTag.getTaggedOrImpliedValue(line, 0L);

return Iterables
// Find all intersecting edges
.stream(this.rawAtlas.linesContaining(location,
target -> target.getIdentifier() != line.getIdentifier()
&& isAtlasEdge(target) && target.asPolyLine().contains(location)))
// Check whether that edge has a different layer value as the line we're looking at
// and that our point is its start or end node
.anyMatch(candidate ->
{
final long layerValue = LayerTag.getTaggedOrImpliedValue(candidate, 0L);
final boolean edgesOnDifferentLayers = targetLayerValue != layerValue;
final PolyLine candidatePolyline = candidate.asPolyLine();
final boolean intersectionIsAtEndPoint = candidatePolyline.first()
.equals(location) || candidatePolyline.last().equals(location);

return edgesOnDifferentLayers && intersectionIsAtEndPoint;
});
}

/**
* Determines whether the given {@link Line} has any intersecting {@link Line}s, with the same
* layer tag value that meet the definition of an {@link Edge}, running through it at the given
Expand All @@ -796,8 +841,6 @@ private boolean locationIsPartOfAnIntersectingEdgeOfTheSameLayer(final Location
{
final long targetLayerValue = LayerTag.getTaggedOrImpliedValue(line, 0L);

// TODO - Getting non-intersecting lines from the spatial query results.
// So explicitly specifying "contains shapePoint". Need to resolve this!
return Iterables
// Find all intersecting edges
.stream(this.rawAtlas.linesContaining(location,
Expand Down

0 comments on commit 2b343dc

Please sign in to comment.