/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.opentripplanner.graph_builder.module.osm; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import junit.framework.TestCase; import org.junit.Test; import org.opentripplanner.openstreetmap.impl.FileBasedOpenStreetMapProviderImpl; import org.opentripplanner.routing.edgetype.ParkAndRideEdge; import org.opentripplanner.routing.edgetype.ParkAndRideLinkEdge; import org.opentripplanner.routing.graph.Edge; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.graph.Vertex; import org.opentripplanner.routing.vertextype.ParkAndRideVertex; public class TestUnconnectedAreas extends TestCase { /** * The P+R.osm.gz file contains 2 park and ride, one a single way area and the other a * multipolygon with a hole. Both are not linked to any street, apart from three roads that * crosses the P+R with w/o common nodes. * * This test just make sure we correctly link those P+R with the street network by creating * virtual nodes at the place where the street intersects the P+R areas. See ticket #1562. */ @Test public void testUnconnectedParkAndRide() throws Exception { Graph gg = new Graph(); OpenStreetMapModule loader = new OpenStreetMapModule(); loader.setDefaultWayPropertySetSource(new DefaultWayPropertySetSource()); FileBasedOpenStreetMapProviderImpl provider = new FileBasedOpenStreetMapProviderImpl(); File file = new File(getClass().getResource("P+R.osm.gz").getFile()); provider.setPath(file); loader.setProvider(provider); loader.buildGraph(gg, new HashMap<Class<?>, Object>()); assertEquals(1, gg.getBuilderAnnotations().size()); int nParkAndRide = 0; int nParkAndRideLink = 0; for (Vertex v : gg.getVertices()) { if (v instanceof ParkAndRideVertex) { nParkAndRide++; } } for (Edge e : gg.getEdges()) { if (e instanceof ParkAndRideLinkEdge) { nParkAndRideLink++; } } assertEquals(2, nParkAndRide); assertEquals(10, nParkAndRideLink); } /** * This test ensures that if a Park and Ride has a node that is exactly atop a node on a way, the graph * builder will not loop forever trying to split it. The hackett-pr.osm.gz file contains a park-and-ride lot in * Hackettstown, NJ, which demonstrates this behavior. See discussion in ticket 1605. */ @Test public void testCoincidentNodeUnconnectedParkAndRide () throws Exception { Graph g = new Graph(); OpenStreetMapModule loader = new OpenStreetMapModule(); loader.setDefaultWayPropertySetSource(new DefaultWayPropertySetSource()); FileBasedOpenStreetMapProviderImpl provider = new FileBasedOpenStreetMapProviderImpl(); File file = new File(getClass().getResource("hackett_pr.osm.gz").getFile()); provider.setPath(file); loader.setProvider(provider); loader.buildGraph(g, new HashMap<Class<?>, Object>()); Vertex washTwp = null; int nParkAndRide = 0; int nParkAndRideLink = 0; for (Vertex v : g.getVertices()) { if (v instanceof ParkAndRideVertex) { nParkAndRide++; washTwp = v; } } for (Edge e : g.getEdges()) { if (e instanceof ParkAndRideLinkEdge) { nParkAndRideLink++; } } assertEquals(1, nParkAndRide); // the P+R should get four connections, since the entrance road is duplicated as well, and crosses twice // since there are in and out edges, that brings the total to 8 per P+R. // Even though the park and rides get merged, duplicate edges remain from when they were separate. // FIXME: we shouldn't have duplicate edges. assertEquals(16, nParkAndRideLink); assertNotNull(washTwp); List<String> connections = new ArrayList<String>(); for (Edge e : washTwp.getOutgoing()) { if (e instanceof ParkAndRideEdge) continue; assertTrue(e instanceof ParkAndRideLinkEdge); connections.add(e.getToVertex().getLabel()); } // symmetry for (Edge e : washTwp.getIncoming()) { if (e instanceof ParkAndRideEdge) continue; assertTrue(e instanceof ParkAndRideLinkEdge); assertTrue(connections.contains(e.getFromVertex().getLabel())); } assertTrue(connections.contains("osm:node:3096570222")); assertTrue(connections.contains("osm:node:3094264704")); assertTrue(connections.contains("osm:node:3094264709")); assertTrue(connections.contains("osm:node:3096570227")); } /** * Test the situation where a road passes over a node of a park and ride but does not have a node there. */ @Test public void testRoadPassingOverNode () throws Exception { List<String> connections = testGeometricGraphWithClasspathFile("coincident_pr.osm", 1, 2); assertTrue(connections.contains("osm:node:-10")); } /** * Test the situation where a park and ride passes over the node of a road but does not have a node there. * Additionally, the node of the road is duplicated to test this corner case. */ @Test public void testAreaPassingOverNode () throws Exception { List<String> connections = testGeometricGraphWithClasspathFile("coincident_pr.osm", 1, 2); assertTrue(connections.contains("osm:node:-10")); } /** * Test the situation where a road passes over a node of a park and ride but does not have a node there. * Additionally, the node of the ring is duplicated to test this corner case. */ @Test public void testRoadPassingOverDuplicatedNode () throws Exception { List<String> connections = testGeometricGraphWithClasspathFile("coincident_pr.osm", 1, 2); // depending on what order everything comes out of the spatial index, we will inject one of // the duplicated nodes into the way. When we get to the other ringsegments, we will just inject // the node that has already been injected into the way. So either of these cases are valid. assertTrue(connections.contains("osm:node:-10") || connections.contains("osm:node:-100")); } /** * We've written several OSM files that exhibit different situations but should show the same results. Test with this code. */ public List<String> testGeometricGraphWithClasspathFile(String fn, int prCount, int prlCount) { Graph g = new Graph(); OpenStreetMapModule loader = new OpenStreetMapModule(); loader.setDefaultWayPropertySetSource(new DefaultWayPropertySetSource()); FileBasedOpenStreetMapProviderImpl provider = new FileBasedOpenStreetMapProviderImpl(); File file = new File(getClass().getResource(fn).getFile()); provider.setPath(file); loader.setProvider(provider); loader.buildGraph(g, new HashMap<Class<?>, Object>()); Vertex pr = null; int nParkAndRide = 0; int nParkAndRideLink = 0; for (Vertex v : g.getVertices()) { if (v instanceof ParkAndRideVertex) { nParkAndRide++; pr = v; } } for (Edge e : g.getEdges()) { if (e instanceof ParkAndRideLinkEdge) { nParkAndRideLink++; } } assertEquals(prCount, nParkAndRide); assertEquals(prlCount, nParkAndRideLink); assertNotNull(pr); // make sure it is connected List<String> connections = new ArrayList<String>(); for (Edge e : pr.getOutgoing()) { if (e instanceof ParkAndRideEdge) continue; assertTrue(e instanceof ParkAndRideLinkEdge); connections.add(e.getToVertex().getLabel()); } // symmetry for (Edge e : pr.getIncoming()) { if (e instanceof ParkAndRideEdge) continue; assertTrue(e instanceof ParkAndRideLinkEdge); assertTrue(connections.contains(e.getFromVertex().getLabel())); } return connections; } }