/** * 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 java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.HashMap; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import org.h2.jdbc.JdbcSQLException; import org.h2gis.functions.factory.H2GISDBFactory; import org.h2gis.functions.factory.H2GISFunctions; import static org.h2gis.unitTest.GeometryAsserts.assertGeometryEquals; import org.junit.*; /** * @author Adam Gouge * @author Erwan Bocher */ public class ST_ShortestPathTreeTest { private static Connection connection; private Statement st; private static final double TOLERANCE = 0.0; private static final String DO = "'directed - edge_orientation'"; private static final String RO = "'reversed - edge_orientation'"; private static final String U = "'undirected'"; private static final String W = "'weight'"; private static final String CORMEN = "CORMEN_EDGES_ALL"; @BeforeClass public static void setUp() throws Exception { // Keep a connection alive to not close the DataBase on each unit test connection = H2GISDBFactory.createSpatialDataBase("ST_ShortestPathTreeTest", true); H2GISFunctions.registerFunction(connection.createStatement(), new ST_ShortestPathTree(), ""); GraphCreatorTest.registerCormenGraph(connection); } @Before public void setUpStatement() throws Exception { st = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); } @After public void tearDownStatement() throws Exception { st.close(); } @AfterClass public static void tearDown() throws Exception { connection.close(); } @Test public void DO() throws Exception { // Edges // | 1 | 2 | 3 | 4 | 5 | // |----------|---------------|--------|----------------|-------| // | * | (1) | (5) | (5,6), (-10,9) | (-10) | // | (3,7,10) | * | (3) | (3,6) | (3,7) | // | (7,10) | (4) | * | (6) | (7) | // | (8,10) | (2) | (2,3) | * | (8) | // | (10) | (10,1), (9,2) | (10,5) | (9) | * | // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'directed - edge_orientation', 1) check(oneToAll(CORMEN, DO, 1), new Tree() .add(-10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 1.0)) .add(1, new TreeEdge("LINESTRING (0 1, 1 2)", 1, 2, 1.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 1, 3, 1.0)) .add(6, new TreeEdge("LINESTRING (1 0, 2 2)", 3, 4, 1.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 5, 4, 1.0)) ); check(oneToAll(CORMEN, DO, 2), new Tree() .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 1.0)) .add(6, new TreeEdge("LINESTRING (1 0, 2 2)", 3, 4, 1.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 1.0)) ); check(oneToAll(CORMEN, DO, 3), new Tree() .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 3, 2, 1.0)) .add(6, new TreeEdge("LINESTRING (1 0, 2 2)", 3, 4, 1.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 1.0)) ); check(oneToAll(CORMEN, DO, 4), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 4, 2, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 1.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 4, 5, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 1.0)) ); check(oneToAll(CORMEN, DO, 5), new Tree() .add(1, new TreeEdge("LINESTRING (0 1, 1 2)", 1, 2, 1.0)) .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 4, 2, 1.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 1, 3, 1.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 5, 4, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 1.0)) ); } @Test public void DOlimitBy1point1() throws Exception { // Edges // | 1 | 2 | 3 | 4 | 5 | // |----------|---------------|--------|----------------|-------| // | * | (1) | (5) | (5,6), (-10,9) | (-10) | // | (3,7,10) | * | (3) | (3,6) | (3,7) | // | (7,10) | (4) | * | (6) | (7) | // | (8,10) | (2) | (2,3) | * | (8) | // | (10) | (10,1), (9,2) | (10,5) | (9) | * | // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'directed - edge_orientation', 1, 1.1) check(oneToAll(CORMEN, DO, 1, 1.1), new Tree() .add(-10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 1.0)) .add(1, new TreeEdge("LINESTRING (0 1, 1 2)", 1, 2, 1.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 1, 3, 1.0)) ); check(oneToAll(CORMEN, DO, 2, 1.1), new Tree() .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 1.0)) ); check(oneToAll(CORMEN, DO, 3, 1.1), new Tree() .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 3, 2, 1.0)) .add(6, new TreeEdge("LINESTRING (1 0, 2 2)", 3, 4, 1.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 1.0)) ); check(oneToAll(CORMEN, DO, 4, 1.1), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 4, 2, 1.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 4, 5, 1.0)) ); check(oneToAll(CORMEN, DO, 5, 1.1), new Tree() .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 5, 4, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 1.0)) ); } @Test public void WDO() throws SQLException { // Edges // | 1 | 2 | 3 | 4 | 5 | // |----------|-------|---------|------------------|--------------| // | * | (5,4) | (5) | (5,7,9), (-10,9) | (5,7), (-10) | // | (3,7,10) | * | (3) | (3,7,9) | (3,7) | // | (7,10) | (4) | * | (7,9) | (7) | // | (8,10) | (2) | (2,3) | * | (8) | // | (10) | (9,2) | (9,2,3) | (9) | * | // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'directed - edge_orientation', 'weight, 1) check(oneToAll(CORMEN, DO, W, 1), new Tree() .add(-10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 7.0)) .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 3, 2, 3.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 1, 3, 5.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 2.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 5, 4, 6.0)) ); check(oneToAll(CORMEN, DO, W, 2), new Tree() .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 2.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 2.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 5, 4, 6.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 7.0)) ); check(oneToAll(CORMEN, DO, W, 3), new Tree() .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 7.0)) .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 3, 2, 3.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 2.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 5, 4, 6.0)) ); check(oneToAll(CORMEN, DO, W, 4), new Tree() .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 7.0)) .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 4, 2, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 2.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 4, 5, 4.0)) ); check(oneToAll(CORMEN, DO, W, 5), new Tree() .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 7.0)) .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 4, 2, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 2.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 5, 4, 6.0)) ); } @Test public void WDOlimitedBy6point1() throws SQLException { // Edges // | 1 | 2 | 3 | 4 | 5 | // |----------|-------|---------|------------------|--------------| // | * | (5,4) | (5) | (5,7,9), (-10,9) | (5,7), (-10) | // | (3,7,10) | * | (3) | (3,7,9) | (3,7) | // | (7,10) | (4) | * | (7,9) | (7) | // | (8,10) | (2) | (2,3) | * | (8) | // | (10) | (9,2) | (9,2,3) | (9) | * | // Distances: // {0.0, 8.0, 5.0, 13.0, 7.0} // {11.0, 0.0, 2.0, 10.0, 4.0} // {9.0, 3.0, 0.0, 8.0, 2.0} // {11.0, 1.0, 3.0, 0.0, 4.0} // {7.0, 7.0, 9.0, 6.0, 0.0} // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'directed - edge_orientation', 'weight, 1, 6.1) check(oneToAll(CORMEN, DO, W, 1, 6.1), new Tree() .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 1, 3, 5.0)) ); check(oneToAll(CORMEN, DO, W, 2, 6.1), new Tree() .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 2.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 2.0)) ); check(oneToAll(CORMEN, DO, W, 3, 6.1), new Tree() .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 3, 2, 3.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 2.0)) ); check(oneToAll(CORMEN, DO, W, 4, 6.1), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 4, 2, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 2.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 4, 5, 4.0)) ); check(oneToAll(CORMEN, DO, W, 5, 6.1), new Tree() .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 5, 4, 6.0)) ); } @Test public void RO() throws Exception { // Edges // | 1 | 2 | 3 | 4 | 5 | // |----------------|----------|--------|--------|---------------| // | * | (10,7,3) | (10,7) | (10,8) | (10) | // | (1) | * | (4) | (2) | (1,10), (2,9) | // | (5) | (3) | * | (3,2) | (5,10) | // | (6,5), (9,-10) | (6,3) | (6) | * | (9) | // | (-10) | (7,3) | (7) | (8) | * | // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'reversed - edge_orientation', 1) check(oneToAll(CORMEN, RO, 1), new Tree() .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 1.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 5, 3, 1.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 5, 4, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 1.0)) ); check(oneToAll(CORMEN, RO, 2), new Tree() .add(1, new TreeEdge("LINESTRING (0 1, 1 2)", 2, 1, 1.0)) .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 2, 3, 1.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 4, 5, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 1.0)) ); check(oneToAll(CORMEN, RO, 3), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 1.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 1.0)) ); check(oneToAll(CORMEN, RO, 4), new Tree() .add(-10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 1.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 1.0)) .add(6, new TreeEdge("LINESTRING (1 0, 2 2)", 4, 3, 1.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 4, 5, 1.0)) ); check(oneToAll(CORMEN, RO, 5), new Tree() .add(-10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 1.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 5, 3, 1.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 5, 4, 1.0)) ); } @Test public void ROlimitedBy1point1() throws Exception { // Edges // | 1 | 2 | 3 | 4 | 5 | // |----------------|----------|--------|--------|---------------| // | * | (10,7,3) | (10,7) | (10,8) | (10) | // | (1) | * | (4) | (2) | (1,10), (2,9) | // | (5) | (3) | * | (3,2) | (5,10) | // | (6,5), (9,-10) | (6,3) | (6) | * | (9) | // | (-10) | (7,3) | (7) | (8) | * | // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'reversed - edge_orientation', 1, 1.1) check(oneToAll(CORMEN, RO, 1, 1.1), new Tree() .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 1.0)) ); check(oneToAll(CORMEN, RO, 2, 1.1), new Tree() .add(1, new TreeEdge("LINESTRING (0 1, 1 2)", 2, 1, 1.0)) .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 2, 3, 1.0)) ); check(oneToAll(CORMEN, RO, 3, 1.1), new Tree() .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 1.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 1.0)) ); check(oneToAll(CORMEN, RO, 4, 1.1), new Tree() .add(6, new TreeEdge("LINESTRING (1 0, 2 2)", 4, 3, 1.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 4, 5, 1.0)) ); check(oneToAll(CORMEN, RO, 5, 1.1), new Tree() .add(-10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 1.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 5, 3, 1.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 5, 4, 1.0)) ); } @Test public void WRO() throws Exception { // Edges // | 1 | 2 | 3 | 4 | 5 | // |------------------|----------|--------|--------|---------| // | * | (10,7,3) | (10,7) | (10,8) | (10) | // | (4,5) | * | (4) | (2) | (2,9) | // | (5) | (3) | * | (3,2) | (3,2,9) | // | (9,-10), (9,7,5) | (9,7,3) | (9,7) | * | (9) | // | (-10), (7,5) | (7,3) | (7) | (8) | * | // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'reversed - edge_orientation', 'weight', 1) check(oneToAll(CORMEN, RO, W, 1), new Tree() .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 2.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 5, 3, 2.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 5, 4, 4.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 7.0)) ); check(oneToAll(CORMEN, RO, W, 2), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 2, 3, 3.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 5.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 4, 5, 6.0)) ); check(oneToAll(CORMEN, RO, W, 3), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 2.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 5.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 4, 5, 6.0)) ); check(oneToAll(CORMEN, RO, W, 4), new Tree() .add(-10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 7.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 2.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 5.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 5, 3, 2.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 4, 5, 6.0)) ); check(oneToAll(CORMEN, RO, W, 5), new Tree() .add(-10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 7.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 2.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 5.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 5, 3, 2.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 5, 4, 4.0)) ); } @Test public void WROlimitedBy6point1() throws Exception { // Edges // | 1 | 2 | 3 | 4 | 5 | // |------------------|----------|--------|--------|---------| // | * | (10,7,3) | (10,7) | (10,8) | (10) | // | (4,5) | * | (4) | (2) | (2,9) | // | (5) | (3) | * | (3,2) | (3,2,9) | // | (9,-10), (9,7,5) | (9,7,3) | (9,7) | * | (9) | // | (-10), (7,5) | (7,3) | (7) | (8) | * | // Distances: // {0.0, 11.0, 9.0, 11.0, 7.0} // {8.0, 0.0, 3.0, 1.0, 7.0} // {5.0, 2.0, 0.0, 3.0, 9.0} // {13.0, 10.0, 8.0, 0.0, 6.0} // {7.0, 4.0, 2.0, 4.0, 0.0} // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'reversed - edge_orientation', 'weight', 1, 6.1) check(oneToAll(CORMEN, RO, W, 1, 6.1), new Tree() ); check(oneToAll(CORMEN, RO, W, 2, 6.1), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 2, 3, 3.0)) ); check(oneToAll(CORMEN, RO, W, 3, 6.1), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 2.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 5.0)) ); check(oneToAll(CORMEN, RO, W, 4, 6.1), new Tree() .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 4, 5, 6.0)) ); // We might expect edge 3 to be included in the SPT but it is not. // This is because when we limit Dijkstra by a radius of 6.1, // it stops after its first iteration after finding vertices // 3 (distance 2.0), 4 (distance 4.0) and 1 (distance 7.0) // since 7.0 > 6.1. // TODO: Rethink how we limit Dijkstra to give the expected result? check(oneToAll(CORMEN, RO, W, 5, 6.1), new Tree() .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 5, 3, 2.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 5, 4, 4.0)) ); } @Test public void U() throws Exception { // Edges // | 1 | 2 | // |------------------------------|------------------------------------| // | * | (1) | // | (1) | * | // | (5) | (3), (4) | // | (2,1), (6,5), (8,10), (9,10) | (2) | // | (10) | (10,1), (7,3), (7,4), (8,2), (9,2) | // // | 3 | 4 | 5 | // |----------|------------------------------|------------------------------------| // | (5) | (1,2), (5,6), (10,8), (10,9) | (10) | // | (3), (4) | (2) | (1,10), (3,7), (4,7), (2,8), (2,9) | // | * | (6) | (7) | // | (6) | * | (8), (9) | // | (7) | (8), (9) | * | // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'undirected', 1) check(oneToAll(CORMEN, U, 1), new Tree() .add(1, new TreeEdge("LINESTRING (0 1, 1 2)", 1, 2, 1.0)) .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 1, 3, 1.0)) .add(6, new TreeEdge("LINESTRING (1 0, 2 2)", 3, 4, 1.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 5, 4, 1.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 5, 4, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 1.0)) ); check(oneToAll(CORMEN, U, 2), new Tree() .add(1, new TreeEdge("LINESTRING (0 1, 1 2)", 2, 1, 1.0)) .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 1.0)) .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 2, 3, 1.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 1.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 4, 5, 1.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 4, 5, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 1.0)) ); check(oneToAll(CORMEN, U, 3), new Tree() .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 1.0)) .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 3, 2, 1.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 1.0)) .add(6, new TreeEdge("LINESTRING (1 0, 2 2)", 3, 4, 1.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 1.0)) ); check(oneToAll(CORMEN, U, 4), new Tree() .add(1, new TreeEdge("LINESTRING (0 1, 1 2)", 2, 1, 1.0)) .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 4, 2, 1.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 1.0)) .add(6, new TreeEdge("LINESTRING (1 0, 2 2)", 4, 3, 1.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 4, 5, 1.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 4, 5, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 1.0)) ); check(oneToAll(CORMEN, U, 5), new Tree() .add(1, new TreeEdge("LINESTRING (0 1, 1 2)", 1, 2, 1.0)) .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 4, 2, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 1.0)) .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 3, 2, 1.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 5, 3, 1.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 5, 4, 1.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 5, 4, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 1.0)) ); } @Test public void UlimitedBy1point1() throws Exception { // Edges // | 1 | 2 | // |------------------------------|------------------------------------| // | * | (1) | // | (1) | * | // | (5) | (3), (4) | // | (2,1), (6,5), (8,10), (9,10) | (2) | // | (10) | (10,1), (7,3), (7,4), (8,2), (9,2) | // // | 3 | 4 | 5 | // |----------|------------------------------|------------------------------------| // | (5) | (1,2), (5,6), (10,8), (10,9) | (10) | // | (3), (4) | (2) | (1,10), (3,7), (4,7), (2,8), (2,9) | // | * | (6) | (7) | // | (6) | * | (8), (9) | // | (7) | (8), (9) | * | // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'undirected', 1, 1.1) check(oneToAll(CORMEN, U, 1, 1.1), new Tree() .add(1, new TreeEdge("LINESTRING (0 1, 1 2)", 1, 2, 1.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 1, 3, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 1.0)) ); check(oneToAll(CORMEN, U, 2, 1.1), new Tree() .add(1, new TreeEdge("LINESTRING (0 1, 1 2)", 2, 1, 1.0)) .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 1.0)) .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 2, 3, 1.0)) ); check(oneToAll(CORMEN, U, 3, 1.1), new Tree() .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 1.0)) .add(4, new TreeEdge("LINESTRING (1 0, 1.25 1, 1 2)", 3, 2, 1.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 1.0)) .add(6, new TreeEdge("LINESTRING (1 0, 2 2)", 3, 4, 1.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 1.0)) ); check(oneToAll(CORMEN, U, 4, 1.1), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 4, 2, 1.0)) .add(6, new TreeEdge("LINESTRING (1 0, 2 2)", 4, 3, 1.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 4, 5, 1.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 4, 5, 1.0)) ); check(oneToAll(CORMEN, U, 5, 1.1), new Tree() .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 5, 3, 1.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 5, 4, 1.0)) .add(9, new TreeEdge("LINESTRING (2 0, 2.25 1, 2 2)", 5, 4, 1.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 1.0)) ); } @Test public void WU() throws Exception { // Edges // | 1 | 2 | 3 | 4 | 5 | // |-------------|-------|-------|---------|-------------| // | * | (5,3) | (5) | (5,3,2) | (10), (5,7) | // | (3,5) | * | (3) | (2) | (3,7) | // | (5) | (3) | * | (3,2) | (7) | // | (2,3,5) | (2) | (2,3) | * | (8) | // | (10), (7,5) | (7,3) | (7) | (8) | * | // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'undirected', 'weight', 1) check(oneToAll(CORMEN, U, W, 1), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 2.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 1, 3, 5.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 2.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 1, 5, 7.0)) ); check(oneToAll(CORMEN, U, W, 2), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 2.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 5.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 2.0)) ); check(oneToAll(CORMEN, U, W, 3), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 2.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 5.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 2.0)) ); check(oneToAll(CORMEN, U, W, 4), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 4, 2, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 2.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 5.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 4, 5, 4.0)) ); check(oneToAll(CORMEN, U, W, 5), new Tree() .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 2.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 5.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 5, 3, 2.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 5, 4, 4.0)) .add(10, new TreeEdge("LINESTRING (2 0, 0 1)", 5, 1, 7.0)) ); } @Test public void WUlimitedBy6point1() throws Exception { // Edges // | 1 | 2 | 3 | 4 | 5 | // |-------------|-------|-------|---------|-------------| // | * | (5,3) | (5) | (5,3,2) | (10), (5,7) | // | (3,5) | * | (3) | (2) | (3,7) | // | (5) | (3) | * | (3,2) | (7) | // | (2,3,5) | (2) | (2,3) | * | (8) | // | (10), (7,5) | (7,3) | (7) | (8) | * | // Distances: // {0.0, 7.0, 5.0, 8.0, 7.0} // {7.0, 0.0, 2.0, 1.0, 4.0} // {5.0, 2.0, 0.0, 3.0, 2.0} // {8.0, 1.0, 3.0, 0.0, 4.0} // {7.0, 4.0, 2.0, 4.0, 0.0} // // SELECT * FROM ST_ShortestPathTree('CORMEN_EDGES_ALL', // 'undirected', 'weight', 1, 6.1) check(oneToAll(CORMEN, U, W, 1, 6.1), new Tree() .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 1, 3, 5.0)) ); check(oneToAll(CORMEN, U, W, 2, 6.1), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 2.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 2.0)) ); check(oneToAll(CORMEN, U, W, 3, 6.1), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 2, 4, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 3, 2, 2.0)) .add(5, new TreeEdge("LINESTRING (0 1, 1 0)", 3, 1, 5.0)) .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 3, 5, 2.0)) ); check(oneToAll(CORMEN, U, W, 4, 6.1), new Tree() .add(2, new TreeEdge("LINESTRING (1 2, 2 2)", 4, 2, 1.0)) .add(3, new TreeEdge("LINESTRING (1 2, 0.75 1, 1 0)", 2, 3, 2.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 4, 5, 4.0)) ); // Note: Edge 3 is not included here for the same reason as in // {@link #WROlimitedBy6point1}. check(oneToAll(CORMEN, U, W, 5, 6.1), new Tree() .add(7, new TreeEdge("LINESTRING (1 0, 2 0)", 5, 3, 2.0)) .add(8, new TreeEdge("LINESTRING (2 2, 1.75 1, 2 0)", 5, 4, 4.0)) ); } @Test public void WDODisconnectedGraph() throws SQLException { // This test shows that when ST_ShortestPathTree is called on a graph // that is not connected, it will create the largest SPT it can while // staying in the same (strongly) connected component as the source // vertex. check(oneToAll("COPY_EDGES_ALL", DO, W, 6), new Tree() .add(11, new TreeEdge("LINESTRING (3 1, 4 2)", 6, 7, 1.0)) .add(12, new TreeEdge("LINESTRING (4 2, 5 2)", 7, 8, 2.0)) ); } @Test public void WDODisconnectedGraphLimitedBy1point1() throws SQLException { check(oneToAll("COPY_EDGES_ALL", DO, W, 6, 1.1), new Tree() .add(11, new TreeEdge("LINESTRING (3 1, 4 2)", 6, 7, 1.0)) ); } @Test(expected = IllegalArgumentException.class) public void unrecognizedArg4() throws Throwable { // The source vertex must be given as an INT and not a DOUBLE. try { st.executeQuery( "SELECT * FROM ST_ShortestPathTree('COPY_EDGES_ALL', " + DO + ", 1.0, 1)"); } catch (JdbcSQLException e) { assertTrue(e.getMessage().contains(GraphFunction.ARG_ERROR + "1.0")); throw e.getOriginalCause(); } } @Test(expected = IllegalArgumentException.class) public void unrecognizedArg5() throws Throwable { // The radius must be given as a DOUBLE and not an INT. try { st.executeQuery( "SELECT * FROM ST_ShortestPathTree('COPY_EDGES_ALL', " + DO + ", 1, 5)"); } catch (JdbcSQLException e) { assertTrue(e.getMessage().contains(GraphFunction.ARG_ERROR + "5")); throw e.getOriginalCause(); } } @Test(expected = IllegalArgumentException.class) public void otherUnrecognizedArg5() throws Throwable { // The source vertex must be given as an INT and not a DOUBLE. try { st.executeQuery( "SELECT * FROM ST_ShortestPathTree('COPY_EDGES_ALL', " + DO + ", " + W + ", 1.0)"); } catch (JdbcSQLException e) { assertTrue(e.getMessage().contains(GraphFunction.ARG_ERROR + "1.0")); throw e.getOriginalCause(); } } private ResultSet oneToAll(String table, String orientation, int source) throws SQLException { return oneToAll(table, orientation, null, source, Double.POSITIVE_INFINITY); } private ResultSet oneToAll(String table, String orientation, int source, double radius) throws SQLException { return oneToAll(table, orientation, null, source, radius); } private ResultSet oneToAll(String table, String orientation, String weight, int source) throws SQLException { return oneToAll(table, orientation, weight, source, Double.POSITIVE_INFINITY); } private ResultSet oneToAll(String table, String orientation, String weight, int source, double radius) throws SQLException { return st.executeQuery( "SELECT * FROM ST_ShortestPathTree('" + table + "', " + orientation + ((weight != null) ? ", " + weight : "") + ", " + source + ((radius < Double.POSITIVE_INFINITY) ? ", " + radius : "") + ")" ); } @Test public void testNoGeometryField() throws Throwable { // This test shows that if the input has no geometry column, // neither will the output. st.execute("DROP TABLE IF EXISTS NO_GEOM;" + "CREATE TABLE NO_GEOM AS " + "SELECT EDGE_ID, START_NODE, END_NODE, WEIGHT " + "FROM CORMEN_EDGES_ALL;"); final ResultSet resultSet = st.executeQuery("SELECT * FROM ST_ShortestPathTree(" + "'NO_GEOM', 'UNDIRECTED', 3);"); checkNoGeom(resultSet, new Tree() .add(3, new TreeEdge(3, 2, 1.0)) .add(4, new TreeEdge(3, 2, 1.0)) .add(5, new TreeEdge(3, 1, 1.0)) .add(6, new TreeEdge(3, 4, 1.0)) .add(7, new TreeEdge(3, 5, 1.0)) ); } private void check(ResultSet rs, Tree tree) throws SQLException { check(rs, tree, true); } private void checkNoGeom(ResultSet rs, Tree tree) throws SQLException { check(rs, tree, false); } private void check(ResultSet rs, Tree tree, boolean checkGeom) throws SQLException { int count = 0; while (rs.next()) { count++; TreeEdge e = tree.get(rs.getInt(GraphConstants.EDGE_ID)); if (checkGeom) { assertGeometryEquals(e.getGeom(), rs.getBytes(GraphConstants.THE_GEOM)); } assertEquals(e.getSource(), rs.getInt(GraphConstants.SOURCE)); assertEquals(e.getDestination(), rs.getInt(GraphConstants.DESTINATION)); assertEquals(e.getWeight(), rs.getDouble(GraphConstants.WEIGHT), TOLERANCE); } assertEquals(tree.size(), count); rs.close(); } private class Tree extends HashMap<Integer, TreeEdge> { public Tree add(Integer i, TreeEdge e) { super.put(i, e); return this; } } private class TreeEdge { private String geom; private int source; private int destination; private double weight; public TreeEdge(int source, int destination, double weight) { this(null, source, destination, weight); } public TreeEdge(String geom, int source, int destination, double weight) { this.geom = geom; this.source = source; this.destination = destination; this.weight = weight; } public String getGeom() { return geom; } public int getSource() { return source; } public int getDestination() { return destination; } public double getWeight() { return weight; } } }