/** * H2GIS is a library that brings spatial support to the H2 Database Engine * <http://www.h2database.com>. H2GIS is developed by CNRS * <http://www.cnrs.fr/>. * * This code is part of the H2GIS project. H2GIS 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; * version 3.0 of the License. * * H2GIS 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 Lesser General Public License * for more details <http://www.gnu.org/licenses/>. * * * For more information, please consult: <http://www.h2gis.org/> * or contact directly: info_at_h2gis.org */ package org.h2gis.network.functions; import junit.framework.Assert; import org.javanetworkanalyzer.data.VDijkstra; import org.javanetworkanalyzer.model.*; import org.junit.BeforeClass; import org.junit.Test; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.Set; import static junit.framework.Assert.assertTrue; import org.h2gis.functions.factory.H2GISDBFactory; import static org.junit.Assert.assertEquals; /** * Tests the graph creators under all possible configurations. * * @author Adam Gouge * @author Erwan Bocher */ public class GraphCreatorTest { private static Connection connection; private static final double TOLERANCE = 0.0; @BeforeClass public static void tearUp() throws Exception { // Keep a connection alive to not close the DataBase on each unit test connection = H2GISDBFactory.createSpatialDataBase("GraphCreatorTest"); registerCormenGraph(connection); } public static void registerCormenGraph(Connection connection) throws SQLException { final Statement st = connection.createStatement(); // 2:1 // >2 <----------- 4 // / |^ ->|^ // 1:10/ / | 6:9 / / | // / | | ----- | | // /3:2| |4:3 / 8:4| |9:6 // 1<--------------- | | // \ | | / 10:7\ | | // 5:5\ | / / \ | / // \ v| / 7:2 >v| // > 3 -----------> 5 // CORMEN st.execute("CREATE TABLE cormen(road LINESTRING, id INT AUTO_INCREMENT PRIMARY KEY, weight DOUBLE, edge_orientation INT);" + "INSERT INTO cormen VALUES " + "('LINESTRING (0 1, 1 2)', DEFAULT, 10.0, 1)," + "('LINESTRING (1 2, 2 2)', DEFAULT, 1.0, -1)," + "('LINESTRING (1 2, 0.75 1, 1 0)', DEFAULT, 2.0, 1)," + "('LINESTRING (1 0, 1.25 1, 1 2)', DEFAULT, 3.0, 1)," + "('LINESTRING (0 1, 1 0)', DEFAULT, 5.0, 1)," + "('LINESTRING (1 0, 2 2)', DEFAULT, 9.0, 1)," + "('LINESTRING (1 0, 2 0)', DEFAULT, 2.0, 1)," + "('LINESTRING (2 2, 1.75 1, 2 0)', DEFAULT, 4.0, 1)," + "('LINESTRING (2 0, 2.25 1, 2 2)', DEFAULT, 6.0, 1)," + "('LINESTRING (2 0, 0 1)', DEFAULT, 7.0, 0);"); // In order to not depend on ST_Graph, we simply simulate the output of ST_Graph // on the Cormen graph. st.execute("CREATE TABLE cormen_nodes(node_id int auto_increment primary key, the_geom point);" + "INSERT INTO cormen_nodes(the_geom) VALUES " + "('POINT (0 1)')," + "('POINT (1 2)')," + "('POINT (2 2)')," + "('POINT (1 0)')," + "('POINT (2 0)');"); // cormen_nodes // NODE_ID THE_GEOM // 1 POINT (0 1) // 2 POINT (1 2) // 3 POINT (2 2) // 4 POINT (1 0) // 5 POINT (2 0) st.execute("CREATE TABLE CORMEN_EDGES(EDGE_ID INT AUTO_INCREMENT PRIMARY KEY, START_NODE INT, END_NODE INT);" + "INSERT INTO CORMEN_EDGES(START_NODE, END_NODE) VALUES " + "(1, 2)," + "(2, 4)," + "(2, 3)," + "(3, 2)," + "(1, 3)," + "(3, 4)," + "(3, 5)," + "(4, 5)," + "(5, 4)," + "(5, 1);"); // CORMEN_EDGES: // EDGE_ID START_NODE END_NODE // 1 1 2 // 2 2 4 // 3 2 3 // 4 3 2 // 5 1 3 // 6 3 4 // 7 3 5 // 8 4 5 // 9 5 4 // 10 5 1 // Quick fix to recover other columns: st.execute("DROP TABLE IF EXISTS CORMEN_EDGES_ALL;" + "CREATE TABLE CORMEN_EDGES_ALL AS SELECT " + "A.*, B.* FROM CORMEN A, CORMEN_EDGES B WHERE A.ID=B.EDGE_ID;"); // CORMEN_EDGES_ALL: // ROAD ID EIGHT EDGE_ORIENTATION EDGE_ID START_NODE END_NODE // LINESTRING (0 1, 1 2) 1 0.0 1 1 1 2 // LINESTRING (1 2, 2 2) 2 1.0 -1 2 2 4 // LINESTRING (1 2, 0.75 1, 1 0) 3 2.0 1 3 2 3 // LINESTRING (1 0, 1.25 1, 1 2) 4 3.0 1 4 3 2 // LINESTRING (0 1, 1 0) 5 5.0 1 5 1 3 // LINESTRING (1 0, 2 2) 6 9.0 1 6 3 4 // LINESTRING (1 0, 2 0) 7 2.0 1 7 3 5 // LINESTRING (2 2, 1.75 1, 2 0) 8 4.0 1 8 4 5 // LINESTRING (2 0, 2.25 1, 2 2) 9 6.0 1 9 5 4 // LINESTRING (2 0, 0 1) 10 7.0 0 10 5 1 // We add another connected component consisting of edges w(6, 7)=1.0 and w(7, 8) = 2.0. // (Simulating ST_Graph). st.execute("DROP TABLE IF EXISTS copy_nodes"); st.execute("CREATE TABLE copy_nodes AS SELECT * FROM cormen_nodes"); st.execute("INSERT INTO copy_nodes VALUES " + "(6, 'POINT (3 1)')," + "(7, 'POINT (4 2)')," + "(8, 'POINT (5 2)');"); st.execute("DROP TABLE IF EXISTS COPY_EDGES_ALL"); st.execute("CREATE TABLE COPY_EDGES_ALL AS SELECT * FROM CORMEN_EDGES_ALL"); st.execute("INSERT INTO COPY_EDGES_ALL VALUES ('LINESTRING (3 1, 4 2)', 11, 1.0, 1, 11, 6, 7)," + "('LINESTRING (4 2, 5 2)', 12, 2.0, 1, 12, 7, 8)"); st.execute("ALTER TABLE COPY_EDGES_ALL ALTER COLUMN ID SET NOT NULL"); st.execute("CREATE PRIMARY KEY ON COPY_EDGES_ALL(ID)"); // Here we create a copy with edges 3, 4, 6, 8, 10 having a weight of // Infinity. Setting an edge's weight to Infinity is equivalent to // deleting the edge from the graph. // 2:1 // >2 <----------- 4 // / ^ // 1:10/ | // / | // / |9:6 // 1 | // \ | // 5:5\ / // \ 7:2 | // > 3 -----------> 5 // INF_EDGES_ALL st.execute("DROP TABLE IF EXISTS INF_EDGES_ALL"); st.execute("CREATE TABLE INF_EDGES_ALL AS SELECT * FROM CORMEN_EDGES_ALL"); st.execute("UPDATE INF_EDGES_ALL SET WEIGHT=POWER(0, -1) WHERE " + "EDGE_ID=3 OR EDGE_ID=4 OR EDGE_ID=6 OR EDGE_ID=8 OR EDGE_ID=10;"); st.execute("ALTER TABLE INF_EDGES_ALL ALTER COLUMN ID SET NOT NULL"); st.execute("CREATE PRIMARY KEY ON INF_EDGES_ALL(ID)"); } @Test public void testDO() throws SQLException { GraphCreator<VDijkstra, Edge> graphCreator = new GraphCreator<VDijkstra, Edge>(connection, "CORMEN_EDGES_ALL", GraphFunctionParser.Orientation.DIRECTED, "edge_orientation", null, VDijkstra.class, Edge.class); final KeyedGraph<VDijkstra,Edge> graph = graphCreator.prepareGraph(); assertTrue(graph instanceof DirectedPseudoG); assertEquals(5, graph.vertexSet().size()); Assert.assertEquals(11, graph.edgeSet().size()); checkVertices(graph, 1, 2, 3, 4, 5); checkEdge(graph, 1, 1, 2); checkEdge(graph, 2, 4, 2); checkEdge(graph, 3, 2, 3); checkEdge(graph, 4, 3, 2); checkEdge(graph, 5, 1, 3); checkEdge(graph, 6, 3, 4); checkEdge(graph, 7, 3, 5); checkEdge(graph, 8, 4, 5); checkEdge(graph, 9, 5, 4); checkEdge(graph, 10, 5, 1); checkEdge(graph, -10, 1, 5); } @Test public void testWDO() throws SQLException { GraphCreator<VDijkstra, Edge> graphCreator = new GraphCreator<VDijkstra, Edge>(connection, "CORMEN_EDGES_ALL", GraphFunctionParser.Orientation.DIRECTED, "edge_orientation", "weight", VDijkstra.class, Edge.class); final KeyedGraph<VDijkstra,Edge> graph = graphCreator.prepareGraph(); assertTrue(graph instanceof DirectedWeightedPseudoG); assertEquals(5, graph.vertexSet().size()); Assert.assertEquals(11, graph.edgeSet().size()); checkVertices(graph, 1, 2, 3, 4, 5); checkEdge(graph, 1, 1, 2, 10.0); checkEdge(graph, 2, 4, 2, 1.0); checkEdge(graph, 3, 2, 3, 2.0); checkEdge(graph, 4, 3, 2, 3.0); checkEdge(graph, 5, 1, 3, 5.0); checkEdge(graph, 6, 3, 4, 9.0); checkEdge(graph, 7, 3, 5, 2.0); checkEdge(graph, 8, 4, 5, 4.0); checkEdge(graph, 9, 5, 4, 6.0); checkEdge(graph, 10, 5, 1, 7.0); checkEdge(graph, -10, 1, 5, 7.0); } @Test public void testRO() throws SQLException { GraphCreator<VDijkstra, Edge> graphCreator = new GraphCreator<VDijkstra, Edge>(connection, "CORMEN_EDGES_ALL", GraphFunctionParser.Orientation.REVERSED, "edge_orientation", null, VDijkstra.class, Edge.class); final KeyedGraph<VDijkstra,Edge> graph = graphCreator.prepareGraph(); assertTrue(graph instanceof DirectedPseudoG); assertEquals(5, graph.vertexSet().size()); Assert.assertEquals(11, graph.edgeSet().size()); checkVertices(graph, 1, 2, 3, 4, 5); checkEdge(graph, 1, 2, 1); checkEdge(graph, 2, 2, 4); checkEdge(graph, 3, 3, 2); checkEdge(graph, 4, 2, 3); checkEdge(graph, 5, 3, 1); checkEdge(graph, 6, 4, 3); checkEdge(graph, 7, 5, 3); checkEdge(graph, 8, 5, 4); checkEdge(graph, 9, 4, 5); checkEdge(graph, 10, 1, 5); checkEdge(graph, -10, 5, 1); } @Test public void testWRO() throws SQLException { GraphCreator<VDijkstra, Edge> graphCreator = new GraphCreator<VDijkstra, Edge>(connection, "CORMEN_EDGES_ALL", GraphFunctionParser.Orientation.REVERSED, "edge_orientation", "weight", VDijkstra.class, Edge.class); final KeyedGraph<VDijkstra,Edge> graph = graphCreator.prepareGraph(); assertTrue(graph instanceof DirectedWeightedPseudoG); assertEquals(5, graph.vertexSet().size()); Assert.assertEquals(11, graph.edgeSet().size()); checkVertices(graph, 1, 2, 3, 4, 5); checkEdge(graph, 1, 2, 1, 10.0); checkEdge(graph, 2, 2, 4, 1.0); checkEdge(graph, 3, 3, 2, 2.0); checkEdge(graph, 4, 2, 3, 3.0); checkEdge(graph, 5, 3, 1, 5.0); checkEdge(graph, 6, 4, 3, 9.0); checkEdge(graph, 7, 5, 3, 2.0); checkEdge(graph, 8, 5, 4, 4.0); checkEdge(graph, 9, 4, 5, 6.0); checkEdge(graph, 10, 1, 5, 7.0); checkEdge(graph, -10, 5, 1, 7.0); } @Test public void testU() throws SQLException { GraphCreator<VDijkstra, Edge> graphCreator = new GraphCreator<VDijkstra, Edge>(connection, "CORMEN_EDGES_ALL", GraphFunctionParser.Orientation.UNDIRECTED, null, null, VDijkstra.class, Edge.class); final KeyedGraph<VDijkstra,Edge> graph = graphCreator.prepareGraph(); assertTrue(graph instanceof PseudoG); assertEquals(5, graph.vertexSet().size()); Assert.assertEquals(10, graph.edgeSet().size()); checkVertices(graph, 1, 2, 3, 4, 5); checkEdge(graph, 1, 1, 2); checkEdge(graph, 2, 2, 4); final Set<Edge> edges23 = graph.getAllEdges(graph.getVertex(2), graph.getVertex(3)); Assert.assertEquals(2, edges23.size()); for (Edge e : edges23) { if (e.getID() != 4) { assertEquals(3, e.getID()); } } checkEdge(graph, 5, 1, 3); checkEdge(graph, 6, 3, 4); checkEdge(graph, 7, 3, 5); final Set<Edge> edges45 = graph.getAllEdges(graph.getVertex(4), graph.getVertex(5)); Assert.assertEquals(2, edges45.size()); for (Edge e : edges45) { if (e.getID() != 8) { assertEquals(9, e.getID()); } } checkEdge(graph, 10, 5, 1); } @Test public void testWU() throws SQLException { GraphCreator<VDijkstra, Edge> graphCreator = new GraphCreator<VDijkstra, Edge>(connection, "CORMEN_EDGES_ALL", GraphFunctionParser.Orientation.UNDIRECTED, null, "weight", VDijkstra.class, Edge.class); final KeyedGraph<VDijkstra,Edge> graph = graphCreator.prepareGraph(); assertTrue(graph instanceof WeightedPseudoG); assertEquals(5, graph.vertexSet().size()); Assert.assertEquals(10, graph.edgeSet().size()); checkVertices(graph, 1, 2, 3, 4, 5); checkEdge(graph, 1, 1, 2, 10.0); checkEdge(graph, 2, 2, 4, 1.0); final Set<Edge> edges23 = graph.getAllEdges(graph.getVertex(2), graph.getVertex(3)); Assert.assertEquals(2, edges23.size()); for (Edge e : edges23) { if (e.getID() == 4) { assertEquals(3.0, graph.getEdgeWeight(e), TOLERANCE); } else { assertEquals(3, e.getID()); assertEquals(2.0, graph.getEdgeWeight(e), TOLERANCE); } } checkEdge(graph, 5, 1, 3, 5.0); checkEdge(graph, 6, 3, 4, 9.0); checkEdge(graph, 7, 3, 5, 2.0); final Set<Edge> edges45 = graph.getAllEdges(graph.getVertex(4), graph.getVertex(5)); Assert.assertEquals(2, edges45.size()); for (Edge e : edges45) { if (e.getID() == 8) { assertEquals(4.0, graph.getEdgeWeight(e), TOLERANCE); } else { assertEquals(9, e.getID()); assertEquals(6.0, graph.getEdgeWeight(e), TOLERANCE); } } checkEdge(graph, 10, 5, 1, 7.0); } private void checkEdge(KeyedGraph<VDijkstra, Edge> graph, int id, int source, int dest) { checkEdge(graph, id, source, dest, 1.0); } private void checkEdge(KeyedGraph<VDijkstra, Edge> graph, int id, int source, int dest, double weight) { final Edge edge = graph.getEdge(graph.getVertex(source), graph.getVertex(dest)); assertEquals(id, edge.getID()); assertTrue(graph.containsEdge(edge)); assertEquals(weight, graph.getEdgeWeight(edge), TOLERANCE); } private void checkVertices(KeyedGraph<VDijkstra, Edge> graph, int... vertices) { for (int i : vertices) { assertTrue(graph.containsVertex(graph.getVertex(i))); } } @Test(expected = IllegalArgumentException.class) public void testNullOrientation() throws SQLException { testOrientation("NULL"); } @Test(expected = IllegalArgumentException.class) public void testInvalidOrientation() throws SQLException { testOrientation("2"); } private void testOrientation(String newOrientation) throws SQLException { final Statement st = connection.createStatement(); st.execute("DROP TABLE IF EXISTS COPY; CREATE TABLE COPY AS SELECT * FROM CORMEN_EDGES_ALL"); st.execute("UPDATE COPY SET edge_orientation=" + newOrientation + " WHERE edge_id=1"); GraphCreator<VDijkstra, Edge> graphCreator = new GraphCreator<VDijkstra, Edge>(connection, "COPY", GraphFunctionParser.Orientation.DIRECTED, "edge_orientation", "weight", VDijkstra.class, Edge.class); try { graphCreator.prepareGraph(); } finally { st.execute("DROP TABLE COPY"); st.close(); } } }