/** * 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 com.vividsolutions.jts.geom.Geometry; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.Map; import org.h2.tools.SimpleResultSet; import org.h2.value.Value; import org.h2.value.ValueDecimal; import org.h2.value.ValueInt; import org.h2.value.ValueString; import org.h2gis.api.ScalarFunction; import static org.h2gis.network.functions.GraphConstants.DESTINATION; import static org.h2gis.network.functions.GraphConstants.EDGE_ID; import static org.h2gis.network.functions.GraphConstants.SOURCE; import static org.h2gis.network.functions.GraphConstants.THE_GEOM; import static org.h2gis.network.functions.GraphConstants.WEIGHT; import static org.h2gis.utilities.TableUtilities.isColumnListConnection; import org.h2gis.utilities.TableLocation; import org.h2gis.utilities.TableUtilities; import org.javanetworkanalyzer.alg.Dijkstra; import org.javanetworkanalyzer.data.VDijkstra; import org.javanetworkanalyzer.model.Edge; import org.javanetworkanalyzer.model.KeyedGraph; import org.javanetworkanalyzer.model.TraversalGraph; /** * Calculates the shortest path(s) between vertices in a JGraphT graph produced * from the input_edges table produced by ST_Graph. * * @author Adam Gouge */ public class ST_ShortestPathTree extends GraphFunction implements ScalarFunction { public static final String REMARKS = "Calculates the shortest path tree from a given vertex of a\n" + "graph using Dijkstra's algorithm.\n" + "Possible signatures:\n" + "* `ST_ShortestPathTree('INPUT_EDGES', 'o[ - eo]', s)`\n" + "* `ST_ShortestPathTree('INPUT_EDGES', 'o[ - eo]', s, r)`\n" + "* `ST_ShortestPathTree('INPUT_EDGES', 'o[ - eo]', 'w', s)`\n" + "* `ST_ShortestPathTree('INPUT_EDGES', 'o[ - eo]', 'w', s, r)`\n" + "\n" + "where\n" + "* `INPUT_EDGES` = Edges table produced by `ST_Graph` from table `INPUT`\n" + "* `o` = Global orientation (directed, reversed or undirected)\n" + "* `eo` = Edge orientation (1 = directed, -1 = reversed, 0 = undirected).\n" + " Required if global orientation is directed or reversed.\n" + "* `s` = Source vertex id\n" + "* `r` = Radius by which to limit the search (a `DOUBLE`)\n" + "* `w` = Name of column containing edge weights as `DOUBLES`\n"; /** * Constructor */ public ST_ShortestPathTree() { addProperty(PROP_REMARKS, REMARKS); } @Override public String getJavaStaticMethod() { return "getShortestPathTree"; } /** * @param connection connection * @param inputTable Edges table produced by ST_Graph * @param orientation Orientation string * @param source Source vertex id * @return Shortest path tree * @throws SQLException */ public static ResultSet getShortestPathTree(Connection connection, String inputTable, String orientation, int source) throws SQLException { return oneToAll(connection, inputTable, orientation, null, source, Double.POSITIVE_INFINITY); } /** * @param connection connection * @param inputTable Edges table produced by ST_Graph * @param orientation Orientation string * @param arg4 Source vertex id or Weight * @param arg5 Search radius or Source vertex id * @return Shortest path tree * @throws SQLException */ public static ResultSet getShortestPathTree(Connection connection, String inputTable, String orientation, Value arg4, Value arg5) throws SQLException { if (arg4 instanceof ValueInt) { final int source = arg4.getInt(); if (arg5 instanceof ValueDecimal) { final double radius = arg5.getDouble(); return oneToAll(connection, inputTable, orientation, null, source, radius); } else { throw new IllegalArgumentException(ARG_ERROR + arg5); } } else if (arg4 instanceof ValueString) { final String weight = arg4.getString(); if (arg5 instanceof ValueInt) { final int source = arg5.getInt(); return oneToAll(connection, inputTable, orientation, weight, source, Double.POSITIVE_INFINITY); } else { throw new IllegalArgumentException(ARG_ERROR + arg5); } } else { throw new IllegalArgumentException(ARG_ERROR + arg4); } } /** * @param connection connection * @param inputTable Edges table produced by ST_Graph * @param orientation Orientation string * @param weight Weight * @param source Source vertex id * @param radius Search radius * @return Shortest path tree * @throws SQLException */ public static ResultSet getShortestPathTree(Connection connection, String inputTable, String orientation, String weight, int source, double radius) throws SQLException { return oneToAll(connection, inputTable, orientation, weight, source, radius); } private static ResultSet oneToAll(Connection connection, String inputTable, String orientation, String weight, int source, double radius) throws SQLException { final TableLocation tableName = TableUtilities.parseInputTable(connection, inputTable); final String firstGeometryField = ST_ShortestPath.getFirstGeometryField(connection, tableName); final boolean containsGeomField = firstGeometryField != null; final SimpleResultSet output = prepareResultSet(containsGeomField); if (isColumnListConnection(connection)) { return output; } // Do the calculation. final KeyedGraph<VDijkstra, Edge> graph = prepareGraph(connection, inputTable, orientation, weight, VDijkstra.class, Edge.class); final Dijkstra<VDijkstra, Edge> dijkstra = new Dijkstra<VDijkstra, Edge>(graph); final VDijkstra vSource = graph.getVertex(source); final TraversalGraph<VDijkstra, Edge> shortestPathTree; if (radius < Double.POSITIVE_INFINITY) { dijkstra.calculate(vSource, radius); shortestPathTree = dijkstra.reconstructTraversalGraph(radius); } else { dijkstra.calculate(vSource); shortestPathTree = dijkstra.reconstructTraversalGraph(); } if (containsGeomField) { final Map<Integer, Geometry> edgeGeometryMap = ST_ShortestPath.getEdgeGeometryMap(connection, tableName, firstGeometryField); for (Edge e : shortestPathTree.edgeSet()) { final Edge baseGraphEdge = e.getBaseGraphEdge(); final int id = baseGraphEdge.getID(); output.addRow(edgeGeometryMap.get(Math.abs(id)), id, shortestPathTree.getEdgeSource(e).getID(), shortestPathTree.getEdgeTarget(e).getID(), graph.getEdgeWeight(baseGraphEdge)); } } else { for (Edge e : shortestPathTree.edgeSet()) { final Edge baseGraphEdge = e.getBaseGraphEdge(); final int id = baseGraphEdge.getID(); output.addRow(id, shortestPathTree.getEdgeSource(e).getID(), shortestPathTree.getEdgeTarget(e).getID(), graph.getEdgeWeight(baseGraphEdge)); } } return output; } /** * Return a new {@link org.h2.tools.SimpleResultSet} with SOURCE, * DESTINATION and DISTANCE columns. * @return a new {@link org.h2.tools.SimpleResultSet} with SOURCE, * DESTINATION and DISTANCE columns * * @param includeGeomColumn True if we include a Geometry column */ private static SimpleResultSet prepareResultSet(boolean includeGeomColumn) { SimpleResultSet output = new SimpleResultSet(); if (includeGeomColumn) { output.addColumn(THE_GEOM, Types.JAVA_OBJECT, "GEOMETRY", 0, 0); } output.addColumn(EDGE_ID, Types.INTEGER, 10, 0); output.addColumn(SOURCE, Types.INTEGER, 10, 0); output.addColumn(DESTINATION, Types.INTEGER, 10, 0); output.addColumn(WEIGHT, Types.DOUBLE, 10, 0); return output; } }