diff --git a/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java b/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java index 6ef169d1760a17f75101be2160406ee151d1572d..a4eaa433f1ff864abf7f0daab8c03469005a00af 100644 --- a/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java +++ b/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java @@ -14,8 +14,6 @@ import it.unibz.inf.isochrone.util.Query; import java.util.Calendar; import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Map.Entry; @@ -158,17 +156,40 @@ public abstract class Isochrone { qStartNodes.initialize(output); Node node = null; + NodeConnection cConnections = null; + NodeConnection dConnections = null; while ((node = nodeQueue.poll()) != null) { final int nodeId = node.getId(); + cConnections = new NodeConnection(node); + dConnections = new NodeConnection(node); + final Collection<Link> adjacents = calcAdjLinks(nodeId); output.addNode(node); node.close(); - final NodeConnection nConnection = prepareLinkExpand(node, adjacents); - updateNodeQueue(expandContinuousLinks(nConnection)); - addLinks(output, adjacents); + for (final Link link : adjacents) { + final boolean isContLink = link.isContinuous(); + final Node targetNode = getNode(link.getOppositeOf(node)); + targetNode.visitNrAdjacentLinks((short) 1); + + if (isContLink) { + prepareContinousLink(node, link); + } + + if (!targetNode.isClosed()) { + if (isContLink) { + cConnections.addLink(targetNode, link); + } else { + dConnections.addLink(targetNode, link); + } + } else if (qIsExpiring && targetNode.isExpired()) { + removeNode(targetNode.getId()); + } + } - updateNodeQueue(expandDiscreteLinks(nConnection)); + updateNodeQueue(expandContinuousLinks(cConnections)); + addLinks(output, cConnections.getAllLinks()); + updateNodeQueue(expandDiscreteLinks(dConnections)); if (qIsExpiring && node.isExpired()) { removeNode(nodeId); @@ -209,75 +230,33 @@ public abstract class Isochrone { // Private methods - private <T extends IOutput> void addLinks(final T output, final Collection<Link> links) { - for (final Link link : links) { - if (link.isContinuous() && Math.abs(link.getStartOffset() - Double.MIN_VALUE) < COMPARE_PRECISION) { - output.addLink(link); - } - } - } - - private NodeConnection prepareLinkExpand(final Node node, final Collection<Link> links) { - final NodeConnection nConnection = new NodeConnection(node); - for (final Link link : links) { - final Node adjacentNode = getNode(link.getOppositeOf(node)); - adjacentNode.visitNrAdjacentLinks((short) 1); - - if (link.isContinuous()) { - final double nodeDistance = node.getDistance(); - final double linkLength = link.getLength(); - - if (qIsIncoming) { - link.setStartOffset(Math.max(0, linkLength - (qDuration - nodeDistance) * qWalkingSpeed)); - link.setEndOffset(linkLength); - } else { - final double remainingDistance = qDuration - nodeDistance < 0 ? 0 : qDuration - nodeDistance; - link.setStartOffset(0); - link.setEndOffset(Math.min(linkLength, remainingDistance * qWalkingSpeed)); - } - } - - if (adjacentNode.isClosed()) { - if (qIsExpiring && adjacentNode.isExpired() && adjacentNode.getId() != node.getId()) { - removeNode(adjacentNode.getId()); - } - } else { - nConnection.addTargetLink(adjacentNode, link); - } - } - - return nConnection; - } - /** * Expand the given continuous link (that is connected to the given node) * and adjusts the link offsets based on the distance of the current Node * and the maximum duration of the query. * - * @param nConnection information about the the sourceNode, its links and reachable nodes + * @param nConnection information about to where start the calculation, where to travel to and which routes are considered * @return another node, if it can be reached in the available time (or null if time has run out) */ private Collection<Node> expandContinuousLinks(final NodeConnection nConnection) { - if (!nConnection.containsContinuousTargets()) { - return Collections.emptyList(); - } - - final Set<Node> resultCollection = new HashSet<>(); - final Node sourceNode = nConnection.getSourceNode(); - final Set<Entry<Node, Collection<Link>>> entrySet = nConnection.getContinuousTargets().entrySet(); - for (final Entry<Node, Collection<Link>> e : entrySet) { - final Node adjacentNode = e.getKey(); - final Collection<Link> linkCollection = e.getValue(); - for (final Link link : linkCollection) { - final double newDistance = sourceNode.getDistance() + link.getLength() / qWalkingSpeed; - if (newDistance <= qDuration && newDistance < adjacentNode.getDistance()) { - adjacentNode.setDistance(newDistance); - resultCollection.add(adjacentNode); + final double sourceDistance = nConnection.getSourceNode().getDistance(); + final Map<Node, Collection<Link>> targets = nConnection.getTargetEntries(); + final Collection<Entry<Node, Collection<Link>>> entries = targets.entrySet(); + + final Set<Node> results = new LinkedHashSet<>(targets.size()); + for (final Entry<Node, Collection<Link>> e : entries) { + final Node targetNode = e.getKey(); + final Collection<Link> targetLinks = e.getValue(); + for (final Link l : targetLinks) { + final double newDistance = sourceDistance + l.getLength() / qWalkingSpeed; + if (newDistance <= qDuration && newDistance < targetNode.getDistance()) { + targetNode.setDistance(newDistance); + results.add(targetNode); } } } - return resultCollection; + return results; } /** @@ -286,46 +265,44 @@ public abstract class Isochrone { * distance of the current Node and the maximum duration of * the query. * - * @param nConnection information about the the sourceNode, its links and reachable nodes - * @return all nodes that can be reached in the available time (or an empty set if there is no time to reach another node) + * @param nConnection information about to where start the calculation, where to travel to and which routes are considered + * @return another node, if it can be reached in the available time (or null if there is no time to reach another node) */ private Collection<Node> expandDiscreteLinks(final NodeConnection nConnection) { - if (!nConnection.containsDiscreteTargets()) { - return Collections.emptyList(); - } - - final Map<Node, Double> newDistances = getAdjNodeCost(nConnection); - final Set<Node> resultCollection = new LinkedHashSet<>(newDistances.size()); - for (final Entry<Node, Double> e : newDistances.entrySet()) { - final Node adjacentNode = e.getKey(); - final double newDistance = e.getValue(); - if (newDistance <= qDuration && newDistance < adjacentNode.getDistance()) { - adjacentNode.setDistance(newDistance); - resultCollection.add(adjacentNode); + final Map<Node, Long> newDistances = getAdjNodeCost(nConnection); + final Collection<Entry<Node, Long>> entries = newDistances.entrySet(); + + final Set<Node> results = new LinkedHashSet<>(newDistances.size()); + for (final Entry<Node, Long> entry : entries) { + final Node targetNode = entry.getKey(); + final Long newDistance = entry.getValue(); + if (newDistance <= qDuration && newDistance < targetNode.getDistance()) { + targetNode.setDistance(newDistance); + results.add(targetNode); } } - return resultCollection; + return results; } /** - * Gets date codes for a given time. + * Get the cost to travel to an adjacent node. * - * @param time the given - * @return returns a set of date codes + * @param nConnection information about to where start the calculation, where to travel to and which routes are considered + * @return the cost of traveling from the node to the target nodes (targetNodeId == mapKey, distance == mapValue) */ - private Collection<Integer> getDateCodes(final Calendar time) { - return database.getDateCodes(time); + private Map<Node, Long> getAdjNodeCost(final NodeConnection nConnection) { + return database.getAdjNodeCost(nConnection, dateCodes, qFromTime, qToTime); } /** - * Get the cost to travel to an adjacent node. + * Gets date codes for a given time. * - * @param nConnection information about to where start the calculation, where to travel to and which routes are considered - * @return the cost of traveling from the node to adjNode + * @param time the given + * @return returns a set of date codes */ - private Map<Node, Double> getAdjNodeCost(final NodeConnection nConnection) { - return database.getAdjNodeCost(nConnection, dateCodes, qFromTime, qToTime); + private Collection<Integer> getDateCodes(final Calendar time) { + return database.getDateCodes(time); } /** @@ -377,17 +354,33 @@ public abstract class Isochrone { } } + private void prepareContinousLink(final Node node, final Link link) { + final double linkLength = link.getLength(); + final double nodeDistance = node.getDistance(); + + if (qIsIncoming) { + link.setStartOffset(Math.max(0, linkLength - (qDuration - nodeDistance) * qWalkingSpeed)); + link.setEndOffset(linkLength); + } else { + final double remainingDistance = qDuration - nodeDistance < 0 ? 0 : qDuration - nodeDistance; + link.setStartOffset(0); + link.setEndOffset(Math.min(linkLength, remainingDistance * qWalkingSpeed)); + } + } + /** - * Set an updated node into the right place in the priority + * Sets an updated node into the right place in the priority * queue. * * @param node The node that was changed */ private void updateNodeQueue(final Node node) { - if (node != null) { - nodeQueue.remove(node); - nodeQueue.offer(node); + if (node == null) { + return; } + + nodeQueue.remove(node); + nodeQueue.offer(node); } /** @@ -396,8 +389,22 @@ public abstract class Isochrone { * @param nodes The nodes that were changed */ private void updateNodeQueue(final Collection<Node> nodes) { + if (nodes == null || nodes.isEmpty()) { + return; + } + for (final Node node : nodes) { updateNodeQueue(node); } } + + // Private static methods + + private static <T extends AbstractOutput> void addLinks(final T output, final Collection<Link> links) { + for (final Link link : links) { + if (Math.abs(link.getStartOffset() - Double.MIN_VALUE) < COMPARE_PRECISION) { + output.addLink(link); + } + } + } } diff --git a/src/main/java/it/unibz/inf/isochrone/db/Database.java b/src/main/java/it/unibz/inf/isochrone/db/Database.java index 61d5560ba05e4a8473dfc8700087e600d93cabaf..d2144e24263190403510740c9d29727e79ac9aa5 100644 --- a/src/main/java/it/unibz/inf/isochrone/db/Database.java +++ b/src/main/java/it/unibz/inf/isochrone/db/Database.java @@ -20,7 +20,9 @@ import java.util.Calendar; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; /** @@ -40,10 +42,13 @@ import java.util.Set; * @version 2.2 */ public class Database { + private static final boolean USE_IN_QUERY = false; protected static final byte NW_MODE_CONTINUOUS = 0; protected static final byte NW_MODE_DISCRETE = 1; private final String queryEarliestArrivalTimeHomo; + private final String queryEarliestArrivalTimeHomoInQuery; private final String queryLatestDepartureTimeHomo; + private final String queryLatestDepartureTimeHomoInQuery; private final String queryGetAllEdges; private final String queryGetAdjLinks; private final String queryGetAdjContinuousLinks; @@ -57,13 +62,14 @@ public class Database { private final String queryGetLinksInRange; private final String queryGetRange; private final String queryLoadLinksFromIER; - protected final ConfigDataset config; - protected Mode mode; - protected boolean isIncoming; private Map<String, PreparedStatement> pstmtsCacheMap; /** The connection the the database. It is lazy-loaded by {@link #getPstmt(String)} method. */ private Connection connection; + protected final ConfigDataset config; + protected Mode mode; + protected boolean isIncoming; + // Constructor public Database(final ConfigDataset config, final Mode mode, final Direction direction) { @@ -77,12 +83,18 @@ public class Database { final String configVertex = config.getTableVertex(); final String configVertexDensity = config.getTableVertexDensity(); - queryLatestDepartureTimeHomo = "SELECT SOURCE, TARGET, TIME_D, TIME_A, ROUTE_ID FROM " + configSchedule - + " WHERE SOURCE IN (%S) AND TARGET = ? AND ROUTE_ID IN (%S) AND TIME_A>=? AND TIME_A<=? AND SERVICE_ID IN (%S) AND TIME_D >= ?"; + queryEarliestArrivalTimeHomo = "SELECT TIME_D, TIME_A, ROUTE_ID FROM " + configSchedule + + " WHERE SOURCE = ? AND TARGET = ? AND ROUTE_ID IN (%S) AND TIME_D>=? AND TIME_D<=? AND SERVICE_ID IN (%S) AND TIME_A <= ?"; - queryEarliestArrivalTimeHomo = "SELECT SOURCE, TARGET, TIME_D, TIME_A, ROUTE_ID FROM " + configSchedule + queryEarliestArrivalTimeHomoInQuery = "SELECT SOURCE, TARGET, TIME_D, TIME_A, ROUTE_ID FROM " + configSchedule + " WHERE SOURCE = ? AND TARGET IN (%S) AND ROUTE_ID IN (%S) AND TIME_D>=? AND TIME_D<=? AND SERVICE_ID IN (%S) AND TIME_A <= ?"; + queryLatestDepartureTimeHomo = "SELECT TIME_D, TIME_A, ROUTE_ID FROM " + configSchedule + + " WHERE SOURCE = ? AND TARGET = ? AND ROUTE_ID IN (%S) AND TIME_A>=? AND TIME_A<=? AND SERVICE_ID IN (%S) AND TIME_D >= ?"; + + queryLatestDepartureTimeHomoInQuery = "SELECT SOURCE, TARGET, TIME_D, TIME_A, ROUTE_ID FROM " + configSchedule + + " WHERE SOURCE IN (%S) AND TARGET = ? AND ROUTE_ID IN (%S) AND TIME_A>=? AND TIME_A<=? AND SERVICE_ID IN (%S) AND TIME_D >= ?"; + queryGetAllEdges = "SELECT ID, SOURCE, TARGET, LENGTH, EDGE_MODE, ROUTE_ID FROM " + configEdges; queryGetContinuousEdges = "SELECT ID, SOURCE, TARGET, LENGTH, EDGE_MODE, ROUTE_ID FROM " + configEdges + " WHERE EDGE_MODE=" + NW_MODE_CONTINUOUS; @@ -152,7 +164,7 @@ public class Database { // Public methods - public void addLink(final Link link, final short degree, final Map<Integer, Node> nodes, final Map<Integer, Collection<Link>> adjList) { + private void addLink(final Link link, final short degree, final Map<Integer, Node> nodes, final Map<Integer, Collection<Link>> adjList) { int n1, n2; if (isIncoming) { n1 = link.getStartNode(); @@ -218,70 +230,17 @@ public class Database { /** * Gets the cost to go from one node to another node. * - * @param nConnection information about to where start the calculation, where to travel to and which routes are considered * @param dateCodes the days that should be taken into the account * @param fromTime the earliest time for which we want to calculate the departure time * @param toTime the latest time for which we want to calculate the departure time * @return the cost of for traveling from the start node to the end node */ - public Map<Node, Double> getAdjNodeCost(final NodeConnection nConnection, final Collection<Integer> dateCodes, final long fromTime, final long toTime) { - final Map<Node, Double> resultMap = new HashMap<>(); - final Node node = nConnection.getSourceNode(); - - ResultSet rs = null; - try { - if (isIncoming) { - rs = getLatestDepartureTime(nConnection, dateCodes, fromTime, Math.round(toTime - node.getDistance())); - while (rs.next()) { - final long departureTime = rs.getLong("TIME_D"); - final long arrivalTime = rs.getLong("TIME_A"); - final int routeId = rs.getInt("ROUTE_ID"); - final int adjNodeId = rs.getInt("SOURCE"); - final Node adjNode = nConnection.getDiscreteTargetNode(adjNodeId); - adjNode.setDepartureTime(routeId, departureTime); - node.setArrivalTime(routeId, arrivalTime); - - Double minDistance = resultMap.get(adjNode); - if (minDistance == null) { - minDistance = Double.POSITIVE_INFINITY; - } - - final double distance = toTime - departureTime; - if (distance < minDistance) { - resultMap.put(adjNode, distance); - adjNode.setCheapestReachedRouteId(routeId); - } - } - } else { - rs = getEarliestArrivalTime(nConnection, dateCodes, Math.round(fromTime + node.getDistance()), toTime); - while (rs.next()) { - final long departureTime = rs.getLong("TIME_D"); - final long arrivalTime = rs.getLong("TIME_A"); - final int routeId = rs.getInt("ROUTE_ID"); - final int adjNodeId = rs.getInt("TARGET"); - final Node adjNode = nConnection.getDiscreteTargetNode(adjNodeId); - node.setDepartureTime(routeId, departureTime); - adjNode.setArrivalTime(routeId, arrivalTime); - - Double minDistance = resultMap.get(adjNode); - if (minDistance == null) { - minDistance = Double.POSITIVE_INFINITY; - } - - final double distance = arrivalTime > 0 ? arrivalTime - fromTime : Double.POSITIVE_INFINITY; - if (distance < minDistance) { - resultMap.put(adjNode, distance); - adjNode.setCheapestReachedRouteId(routeId); - } - } - } - } catch (final SQLException e) { - e.printStackTrace(); - } finally { - DbUtils.closeQuietly(rs); + public Map<Node, Long> getAdjNodeCost(final NodeConnection nConnection, final Collection<Integer> dateCodes, final long fromTime, final long toTime) { + if (USE_IN_QUERY) { + return getAdjNodeCostByInQuery(nConnection, dateCodes, fromTime, toTime); } - return resultMap; + return getAdjNodeCostByLoops(nConnection, dateCodes, fromTime, toTime); } /** @@ -342,67 +301,6 @@ public class Database { return codes; } - /** - * Gets the earliest arrival time at the target when traveled from the source. - * - * @param nConnection (contains the source node id, the targetIds and the route ids) - * @param dates the date codes for which the times are calculated - * @param from the earliest time for which we want to calculate the departure time - * @param to the latest time for which we want to calculate the departure time - * @throws SQLException thrown is the DB update statement can not be executed (or the prepared statement parameters could not be set) - * @return a resultSet with all the times that come into consideration - */ - public ResultSet getEarliestArrivalTime(final NodeConnection nConnection, final Collection<Integer> dates, final long from, final long to) throws SQLException { - final int sId = nConnection.getSourceNode().getId(); - final Collection<Integer> tIds = nConnection.getDiscreteTargetNodeIds(); - final Collection<Integer> rIds = nConnection.getDiscreteRouteIds(); - - final String sql = String.format(queryEarliestArrivalTimeHomo, preparePlaceHolders(tIds.size()), preparePlaceHolders(rIds.size()), preparePlaceHolders(dates.size())); - final PreparedStatement statement = getPstmt(sql); - - // CHECKSTYLE:OFF MagicNumber - statement.setInt(1, sId); - int idx = setValues(2, statement, tIds.toArray()); - idx = setValues(idx, statement, rIds.toArray()); - statement.setLong(idx++, from); - statement.setLong(idx++, to); - idx = setValues(idx, statement, dates.toArray()); - statement.setLong(idx, to); - // CHECKSTYLE:ON MagicNumber - - return statement.executeQuery(); - } - - /** - * Gets the latest departure time to start from source, to arrive at the target in time. - * - * @param nConnection (contains the sourceIds, the target node id and the route ids) - * @param dates the date codes for which the times are calculated - * @param from the earliest time for which we want to calculate the departure time - * @param to the latest time for which we want to calculate the departure time - * @throws SQLException thrown is the DB update statement can not be executed (or the prepared statement parameters could not be set) - * @return a resultSet with all the times that come into consideration - */ - public ResultSet getLatestDepartureTime(final NodeConnection nConnection, final Collection<Integer> dates, final long from, final long to) throws SQLException { - final int tId = nConnection.getSourceNode().getId(); - final Collection<Integer> sIds = nConnection.getDiscreteTargetNodeIds(); - final Collection<Integer> routeIds = nConnection.getDiscreteRouteIds(); - - final String sql = String.format(queryLatestDepartureTimeHomo, preparePlaceHolders(sIds.size()), preparePlaceHolders(routeIds.size()), preparePlaceHolders(dates.size())); - final PreparedStatement statement = getPstmt(sql); - - // CHECKSTYLE:OFF MagicNumber - int idx = setValues(1, statement, sIds.toArray()); - statement.setInt(idx++, tId); - idx = setValues(idx, statement, routeIds.toArray()); - statement.setLong(idx++, from); - statement.setLong(idx++, to); - idx = setValues(idx, statement, dates.toArray()); - statement.setLong(idx, from); - // CHECKSTYLE:ON MagicNumber - - return statement.executeQuery(); - } /** * Get a single link from the network and and add the source @@ -425,7 +323,12 @@ public class Database { final int targetId = rs.getInt("TARGET"); final short degree = rs.getShort("NODE_DEGREE"); final double length = rs.getDouble("LENGTH"); - addNode((isIncoming) ? sourceId : targetId, degree, nodes); + if (isIncoming) { + addNode(sourceId, degree, nodes); + } else { + addNode(targetId, degree, nodes); + } + link = new Link(linkId, sourceId, targetId, length); } } catch (final SQLException e) { @@ -714,6 +617,216 @@ public class Database { return new Link(linkId, start, end, routeId); } + private Map<Node, Long> getAdjNodeCostByInQuery(final NodeConnection nConnection, final Collection<Integer> dateCodes, final long fromTime, final long toTime) { + final Map<Node, Long> results = new LinkedHashMap<>(); + final Node node = nConnection.getSourceNode(); + + ResultSet rs = null; + try { + if (isIncoming) { + rs = getLatestDepartureTime(nConnection, dateCodes, fromTime, Math.round(toTime - node.getDistance())); + while (rs.next()) { + final long departureTime = rs.getLong("TIME_D"); + final long arrivalTime = rs.getLong("TIME_A"); + final int routeId = rs.getInt("ROUTE_ID"); + final int adjNodeId = rs.getInt("SOURCE"); + final Node adjNode = nConnection.getTargetNode(adjNodeId); + adjNode.setDepartureTime(routeId, departureTime); + node.setArrivalTime(routeId, arrivalTime); + + final long distance = toTime - departureTime; + updateDistanceMap(results, routeId, adjNode, distance); + } + } else { + rs = getEarliestArrivalTime(nConnection, dateCodes, Math.round(fromTime + node.getDistance()), toTime); + while (rs.next()) { + final long departureTime = rs.getLong("TIME_D"); + final long arrivalTime = rs.getLong("TIME_A"); + final int routeId = rs.getInt("ROUTE_ID"); + final int adjNodeId = rs.getInt("TARGET"); + final Node adjNode = nConnection.getTargetNode(adjNodeId); + node.setDepartureTime(routeId, departureTime); + adjNode.setArrivalTime(routeId, arrivalTime); + + final long distance = arrivalTime > 0 ? arrivalTime - fromTime : Long.MAX_VALUE; + updateDistanceMap(results, routeId, adjNode, distance); + } + } + } catch (final SQLException e) { + e.printStackTrace(); + } finally { + DbUtils.closeQuietly(rs); + } + + return results; + } + + private Map<Node, Long> getAdjNodeCostByLoops(final NodeConnection nConnection, final Collection<Integer> dateCodes, final long fromTime, final long toTime) { + final Node node = nConnection.getSourceNode(); + final Set<Entry<Node, Collection<Link>>> entries = nConnection.getTargetEntries().entrySet(); + final Map<Node, Long> results = new LinkedHashMap<>(entries.size()); + for (final Entry<Node, Collection<Link>> entry : entries) { + final Node targetNode = entry.getKey(); + final Collection<Integer> routes = NodeConnection.getTargetRoutes(entry.getValue()); + + ResultSet rs = null; + try { + if (isIncoming) { + rs = getLatestDepartureTime(targetNode.getId(), node.getId(), routes, dateCodes, fromTime, Math.round(toTime - node.getDistance())); + while (rs.next()) { + final long departureTime = rs.getLong("TIME_D"); + final long arrivalTime = rs.getLong("TIME_A"); + final short routeId = rs.getShort("ROUTE_ID"); + targetNode.setDepartureTime(routeId, departureTime); + node.setArrivalTime(routeId, arrivalTime); + + final long distance = toTime - departureTime; + updateDistanceMap(results, routeId, targetNode, distance); + } + } else { + rs = getEarliestArrivalTime(node.getId(), targetNode.getId(), routes, dateCodes, Math.round(fromTime + node.getDistance()), toTime); + while (rs.next()) { + final long departureTime = rs.getLong("TIME_D"); + final long arrivalTime = rs.getLong("TIME_A"); + final short routeId = rs.getShort("ROUTE_ID"); + targetNode.setArrivalTime(routeId, arrivalTime); + node.setDepartureTime(routeId, departureTime); + + final long distance = arrivalTime > 0 ? arrivalTime - fromTime : Long.MAX_VALUE; + updateDistanceMap(results, routeId, targetNode, distance); + } + } + } catch (final SQLException e) { + e.printStackTrace(); + } finally { + DbUtils.closeQuietly(rs); + } + } + + return results; + } + + /** + * Gets the earliest arrival time at the target when traveled from the source. + * + * @param sId the id of the source node + * @param tId the id of the target node + * @param rIds the identifiers of the routes that should be considered + * @param dateCodes the date codes for which the times are calculated + * @param from the earliest time for which we want to calculate the departure time + * @param to the latest time for which we want to calculate the departure time + * @throws SQLException thrown is the DB update statement can not be executed (or the prepared statement parameters could not be set) + * @return a resultSet with all the times that come into consideration + */ + private ResultSet getEarliestArrivalTime(final int sId, final int tId, final Collection<Integer> rIds, final Collection<Integer> dateCodes, final long from, final long to) throws SQLException { + final String sql = String.format(queryEarliestArrivalTimeHomo, preparePlaceHolders(rIds.size()), preparePlaceHolders(dateCodes.size())); + final PreparedStatement statement = getPstmt(sql); + + // CHECKSTYLE:OFF MagicNumber + statement.setInt(1, sId); + statement.setInt(2, tId); + int idx = setValues(3, statement, rIds.toArray()); + statement.setLong(idx++, from); + statement.setLong(idx++, to); + idx = setValues(idx, statement, dateCodes.toArray()); + statement.setLong(idx, to); + // CHECKSTYLE:ON MagicNumber + + return statement.executeQuery(); + } + + /** + * Gets the earliest arrival time at the target when traveled from the source. + * + * @param nConnection (contains the source node id, the targetIds and the route ids) + * @param dates the date codes for which the times are calculated + * @param from the earliest time for which we want to calculate the departure time + * @param to the latest time for which we want to calculate the departure time + * @throws SQLException thrown is the DB update statement can not be executed (or the prepared statement parameters could not be set) + * @return a resultSet with all the times that come into consideration + */ + private ResultSet getEarliestArrivalTime(final NodeConnection nConnection, final Collection<Integer> dates, final long from, final long to) throws SQLException { + final int sId = nConnection.getSourceNode().getId(); + final Collection<Integer> tIds = nConnection.getAllTargetNodes(); + final Collection<Integer> rIds = nConnection.getAllRoutes(); + + final String sql = String.format(queryEarliestArrivalTimeHomoInQuery, preparePlaceHolders(tIds.size()), preparePlaceHolders(rIds.size()), preparePlaceHolders(dates.size())); + final PreparedStatement statement = getPstmt(sql); + + // CHECKSTYLE:OFF MagicNumber + statement.setInt(1, sId); + int idx = setValues(2, statement, tIds.toArray()); + idx = setValues(idx, statement, rIds.toArray()); + statement.setLong(idx++, from); + statement.setLong(idx++, to); + idx = setValues(idx, statement, dates.toArray()); + statement.setLong(idx, to); + // CHECKSTYLE:ON MagicNumber + + return statement.executeQuery(); + } + + /** + * Gets the latest departure time to start from source, to arrive at the target in time. + * + * @param sId the id of the source node + * @param tId the id of the target node + * @param rIds the identifiers of the routes that should be considered + * @param dateCodes the date codes for which the times are calculated + * @param from the earliest time for which we want to calculate the departure time + * @param to the latest time for which we want to calculate the departure time + * @throws SQLException thrown is the DB update statement can not be executed (or the prepared statement parameters could not be set) + * @return a resultSet with all the times that come into consideration + */ + private ResultSet getLatestDepartureTime(final int sId, final int tId, final Collection<Integer> rIds, final Collection<Integer> dateCodes, final long from, final long to) throws SQLException { + final String sql = String.format(queryLatestDepartureTimeHomo, preparePlaceHolders(rIds.size()), preparePlaceHolders(dateCodes.size())); + final PreparedStatement statement = getPstmt(sql); + + // CHECKSTYLE:OFF MagicNumber + statement.setInt(1, sId); + statement.setInt(2, tId); + int idx = setValues(3, statement, rIds.toArray()); + statement.setLong(idx++, from); + statement.setLong(idx++, to); + idx = setValues(idx, statement, dateCodes.toArray()); + statement.setLong(idx, from); + // CHECKSTYLE:ON MagicNumber + + return statement.executeQuery(); + } + + /** + * Gets the latest departure time to start from source, to arrive at the target in time. + * + * @param nConnection (contains the sourceIds, the target node id and the route ids) + * @param dates the date codes for which the times are calculated + * @param from the earliest time for which we want to calculate the departure time + * @param to the latest time for which we want to calculate the departure time + * @throws SQLException thrown is the DB update statement can not be executed (or the prepared statement parameters could not be set) + * @return a resultSet with all the times that come into consideration + */ + private ResultSet getLatestDepartureTime(final NodeConnection nConnection, final Collection<Integer> dates, final long from, final long to) throws SQLException { + final int tId = nConnection.getSourceNode().getId(); + final Collection<Integer> sIds = nConnection.getAllTargetNodes(); + final Collection<Integer> routeIds = nConnection.getAllRoutes(); + + final String sql = String.format(queryLatestDepartureTimeHomoInQuery, preparePlaceHolders(sIds.size()), preparePlaceHolders(routeIds.size()), preparePlaceHolders(dates.size())); + final PreparedStatement statement = getPstmt(sql); + + // CHECKSTYLE:OFF MagicNumber + int idx = setValues(1, statement, sIds.toArray()); + statement.setInt(idx++, tId); + idx = setValues(idx, statement, routeIds.toArray()); + statement.setLong(idx++, from); + statement.setLong(idx++, to); + idx = setValues(idx, statement, dates.toArray()); + statement.setLong(idx, from); + // CHECKSTYLE:ON MagicNumber + + return statement.executeQuery(); + } + + // Protected static methods /** @@ -824,4 +937,16 @@ public class Database { + subQuery(loaded, idx - 1, srid) + ")"; } } + + private static void updateDistanceMap(final Map<Node, Long> results, final int routeId, final Node adjNode, final long distance) { + Long minDistance = results.get(adjNode); + if (minDistance == null) { + minDistance = Long.MAX_VALUE; + } + + if (distance < minDistance) { + results.put(adjNode, distance); + adjNode.setCheapestReachedRouteId(routeId); + } + } } diff --git a/src/main/java/it/unibz/inf/isochrone/network/NodeConnection.java b/src/main/java/it/unibz/inf/isochrone/network/NodeConnection.java index 38c92d152759e65740f14a48461e4b8caf63fde8..34ee245d6116881c4e0fe3728d6244e13c8b748b 100644 --- a/src/main/java/it/unibz/inf/isochrone/network/NodeConnection.java +++ b/src/main/java/it/unibz/inf/isochrone/network/NodeConnection.java @@ -11,103 +11,73 @@ public class NodeConnection { private static final int INITIAL_LIST_SIZE = 3; private final Node sourceNode; - private final Map<Node, Collection<Link>> continuousTargetConnections; - private final Map<Node, Collection<Link>> discreteTargetConnections; + private final Map<Node, Collection<Link>> targetRoutes; // Collections public NodeConnection(final Node sourceNode) { this.sourceNode = sourceNode; - this.continuousTargetConnections = new LinkedHashMap<>(); - this.discreteTargetConnections = new LinkedHashMap<>(); + this.targetRoutes = new LinkedHashMap<>(); } // Getters - public Node getSourceNode() { - return sourceNode; - } - - public Map<Node, Collection<Link>> getContinuousTargets() { - return continuousTargetConnections; - } - - public Collection<Integer> getContinuousRouteToTargetNode(final int nodeId) { - return getRoutesToTargetNode(continuousTargetConnections, nodeId); - } - - public Node getContinuousTargetNode(final int nodeId) { - return getNodeById(continuousTargetConnections, nodeId); - } - - public Collection<Integer> getContinuousTargetNodeIds() { - return getIdsForTargetNodes(continuousTargetConnections); - } - - public Collection<Integer> getContinuousRouteIds() { - return getIdsForTargetNodes(continuousTargetConnections); - } + public Collection<Link> getAllLinks() { + final Collection<Collection<Link>> linkSet = targetRoutes.values(); + final Set<Link> resultSet = new LinkedHashSet<>(linkSet.size()); + for (final Collection<Link> lCollection : linkSet) { + resultSet.addAll(lCollection); + } - public Map<Node, Collection<Link>> getDiscreteTargets() { - return discreteTargetConnections; + return resultSet; } - public Node getDiscreteTargetNode(final int nodeId) { - return getNodeById(discreteTargetConnections, nodeId); - } + public Collection<Integer> getAllTargetNodes() { + final Set<Node> nodeSet = targetRoutes.keySet(); + final Set<Integer> resultSet = new LinkedHashSet<>(nodeSet.size()); + for (final Node n : nodeSet) { + resultSet.add(n.getId()); + } - public Collection<Integer> getDiscreteTargetNodeIds() { - return getIdsForTargetNodes(discreteTargetConnections); + return resultSet; } - public Collection<Integer> getDiscreteRouteIds() { - return getIdsForRoutes(discreteTargetConnections); - } + public Collection<Integer> getAllRoutes() { + final Collection<Collection<Link>> linkSet = targetRoutes.values(); + final Set<Integer> resultSet = new LinkedHashSet<>(linkSet.size()); + for (final Collection<Link> lCollection : linkSet) { + for (final Link l : lCollection) { + resultSet.add(l.getRoute()); + } + } - public Collection<Integer> getDiscreteRouteToTargetNode(final int nodeId) { - return getRoutesToTargetNode(discreteTargetConnections, nodeId); + return resultSet; } - public Collection<Integer> getRoutesToTargetNode(final int nodeId) { - return getRoutesToTargetNode(getTargets(), nodeId); + public Node getSourceNode() { + return sourceNode; } - public Map<Node, Collection<Link>> getTargets() { - final Map<Node, Collection<Link>> allTargets = new LinkedHashMap<>(); - allTargets.putAll(continuousTargetConnections); - allTargets.putAll(discreteTargetConnections); - - return allTargets; + public Map<Node, Collection<Link>> getTargetEntries() { + return targetRoutes; } - public Collection<Integer> getTargetNodeIds() { - return getIdsForTargetNodes(getTargets()); + public Node getTargetNode(final int targetNodeId) { + return getNodeById(targetNodeId); } - public Collection<Integer> getRouteIds() { - return getIdsForRoutes(getTargets()); + public Collection<Integer> getTargetRoutes(final int targetNodeId) { + return getRoutesToTarget(targetNodeId); } // Public methods - public void addTargetLink(final Node targetNode, final Link link) { - if (link.isContinuous()) { - addLinkForNode(continuousTargetConnections, targetNode, link); - } else { - addLinkForNode(discreteTargetConnections, targetNode, link); - } + public void addLink(final Node targetNode, final Link link) { + addLinkForNode(targetNode, link); } public boolean containsTargets() { - return containsContinuousTargets() || containsDiscreteTargets(); - } - - public boolean containsContinuousTargets() { - return !continuousTargetConnections.isEmpty(); - } - - public boolean containsDiscreteTargets() { - return !discreteTargetConnections.isEmpty(); + return !targetRoutes.isEmpty(); } @Override @@ -115,20 +85,30 @@ public class NodeConnection { return "NodeConnection [sourceNode(id)=" + sourceNode.getId() + "]"; } - // Private static methods + // Public static methods - private static void addLinkForNode(final Map<Node, Collection<Link>> m, final Node node, final Link link) { - Collection<Link> c = m.get(node); + public static Collection<Integer> getTargetRoutes(final Collection<Link> linkSet) { + final Set<Integer> resultSet = new LinkedHashSet<>(linkSet.size()); + for (final Link l : linkSet) { + resultSet.add(l.getRoute()); + } + return resultSet; + } + + // Private methods + + private void addLinkForNode(final Node node, final Link link) { + Collection<Link> c = targetRoutes.get(node); if (c == null) { c = new ArrayList<>(INITIAL_LIST_SIZE); } c.add(link); - m.put(node, c); + targetRoutes.put(node, c); } - private static Node getNodeById(final Map<Node, Collection<Link>> map, final int nodeId) { - final Set<Node> entries = map.keySet(); + private Node getNodeById(final int nodeId) { + final Set<Node> entries = targetRoutes.keySet(); for (final Node n : entries) { if (nodeId == n.getId()) { return n; @@ -138,38 +118,13 @@ public class NodeConnection { return null; } - private static Collection<Integer> getIdsForRoutes(final Map<Node, Collection<Link>> m) { - final Collection<Collection<Link>> linkSet = m.values(); - final Set<Integer> resultSet = new LinkedHashSet<>(linkSet.size()); - for (final Collection<Link> lCollection : linkSet) { - for (final Link l : lCollection) { - resultSet.add(l.getRoute()); - } - } - - return resultSet; - } - - private static Collection<Integer> getRoutesToTargetNode(final Map<Node, Collection<Link>> m, final int nodeId) { - return getRoutesToTargetNode(m, getNodeById(m, nodeId)); + private Collection<Integer> getRoutesToTarget(final int targetNodeId) { + return getRoutesToTarget(getNodeById(targetNodeId)); } - private static Collection<Integer> getRoutesToTargetNode(final Map<Node, Collection<Link>> m, final Node node) { - final Collection<Link> linkSet = m.get(node); - final Set<Integer> resultSet = new LinkedHashSet<>(linkSet.size()); - for (final Link l : linkSet) { - resultSet.add(l.getRoute()); - } - return resultSet; + private Collection<Integer> getRoutesToTarget(final Node targetNode) { + final Collection<Link> linkSet = targetRoutes.get(targetNode); + return getTargetRoutes(linkSet); } - private static Collection<Integer> getIdsForTargetNodes(final Map<Node, Collection<Link>> m) { - final Set<Node> nodeSet = m.keySet(); - final Set<Integer> resultSet = new LinkedHashSet<>(nodeSet.size()); - for (final Node n : nodeSet) { - resultSet.add(n.getId()); - } - - return resultSet; - } }