From 9178a571768dd284d8341c57e877875b3ed89532 Mon Sep 17 00:00:00 2001 From: Nikolaus Krismer <nikolaus.krismer@uibk.ac.at> Date: Tue, 12 Aug 2014 19:23:48 +0200 Subject: [PATCH] experimenting with new sql queries for getEarliestArrivalTime and getLatestDepartureTime. This should reduce the number of sql queries by far... --- .../inf/isochrone/algorithm/Isochrone.java | 231 ++++++++++-------- .../it/unibz/inf/isochrone/db/Database.java | 65 ++--- .../it/unibz/inf/isochrone/network/Link.java | 2 +- .../inf/isochrone/network/MemoryOutput.java | 4 +- .../it/unibz/inf/isochrone/network/Node.java | 2 +- .../inf/isochrone/network/NodeConnection.java | 135 ++++++++++ 6 files changed, 310 insertions(+), 129 deletions(-) create mode 100644 src/main/java/it/unibz/inf/isochrone/network/NodeConnection.java 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 ee83cfc8..717142ff 100644 --- a/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java +++ b/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java @@ -8,14 +8,17 @@ import it.unibz.inf.isochrone.network.Link; import it.unibz.inf.isochrone.network.Location; import it.unibz.inf.isochrone.network.MemoryOutput; import it.unibz.inf.isochrone.network.Node; +import it.unibz.inf.isochrone.network.NodeConnection; import it.unibz.inf.isochrone.util.EnumContainer.Direction; import it.unibz.inf.isochrone.util.Query; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; import java.util.PriorityQueue; import java.util.Set; @@ -30,10 +33,33 @@ import java.util.Set; */ public abstract class Isochrone { private static final double COMPARE_PRECISION = 0.0000001d; - private final Query query; - private final PriorityQueue<Node> queue; - private Set<Integer> codes; - protected Database database; + private final Collection<Integer> dateCodes; + private final PriorityQueue<Node> nodeQueue; + private final long qDuration; + private final boolean qIsExpiring; + private final boolean qIsIncoming; + private final StartNodes qStartNodes; + private final long qFromTime; + private final long qToTime; + private final double qWalkingSpeed; + private final class StartNodes { + private final Collection<Integer> startNodes; + private final Collection<Location> startLocations; + + private StartNodes(final Query query) { + startNodes = query.getStartNodes(); + startLocations = query.getStartLocations(); + } + + private void initialize(final IOutput output) { + if (startNodes.isEmpty()) { + initializeStartLocations(output, startLocations); + } else { + initializeStartNodes(startNodes); + } + } + } + protected final Database database; // Constructors @@ -46,15 +72,22 @@ public abstract class Isochrone { * @throws AlgorithmException thrown if there are no date codes in the database for the specified query */ public Isochrone(final ConfigDataset config, final Database db, final Query query) throws AlgorithmException { - this.database = db; - this.query = query; - this.queue = new PriorityQueue<>(); - - if (database == null) { - database = new Database(config, query.getMode(), query.getDir()); + this.database = (db == null) ? new Database(config, query.getMode(), query.getDir()) : db; + this.dateCodes = getDateCodes(query.getTime()); + if (dateCodes.isEmpty()) { + // was solved in uniBz version by switching to unimodal mode... we do not do this any more + // (since user does not get informated about the change) + throw new AlgorithmException("Couldn't find datecodes in the databse for the given date"); } - initDateCodes(); + this.nodeQueue = new PriorityQueue<>(); + this.qDuration = query.getDuration().longValue(); + this.qIsExpiring = query.isExpireNodes(); + this.qIsIncoming = (Direction.INCOMING == query.getDir()); + this.qStartNodes = new StartNodes(query); + this.qToTime = query.getToTime(); + this.qFromTime = query.getFromTime(); + this.qWalkingSpeed = query.getWalkingSpeed(); } /** @@ -122,19 +155,12 @@ public abstract class Isochrone { */ // TODO: Add some more tests for this method. If isochrones get too small (2points for 15min isochrone from FUB) nothing fails ;-( public <T extends AbstractOutput> T compute(final T output) { - final boolean isNodeExpires = query.isExpireNodes(); output.beforeCalculation(); - - final Collection<Integer> startNodes = query.getStartNodes(); - if (startNodes.isEmpty()) { - initializeStartLocations(output, query.getStartLocations()); - } else { - initializeStartNodes(startNodes); - } + qStartNodes.initialize(output); Node node = null; - while ((node = queue.poll()) != null) { - final int nodeId = node.getID(); + while ((node = nodeQueue.poll()) != null) { + final int nodeId = node.getId(); final Collection<Link> adjacents = calcAdjLinks(nodeId); output.addNode(node); node.close(); @@ -142,7 +168,7 @@ public abstract class Isochrone { updateNodeQueue(expandLinks(node, adjacents)); addLinks(output, adjacents); - if (isNodeExpires && node.isExpired()) { + if (qIsExpiring && node.isExpired()) { removeNode(nodeId); } } @@ -189,104 +215,119 @@ public abstract class Isochrone { } } - /** - * Expand the given 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 node current node - * @param links links that are connected to the node - * @return nodes that can be reached in the available time (or an empty list if time has run out/no nodes are reachable) - */ private Collection<Node> expandLinks(final Node node, final Collection<Link> links) { - final long duration = query.getDuration().longValue(); - final double walkingSpeed = query.getWalkingSpeed(); - final boolean isExpiring = query.isExpireNodes(); - final boolean isIncoming = (Direction.INCOMING == query.getDir()); + final Collection<Node> resultNodes = new ArrayList<>(); - final Collection<Node> resultNodes = new ArrayList<>(links.size()); + final NodeConnection nConnections = new NodeConnection(node); for (final Link link : links) { final Node adjacentNode = getNode(link.getOppositeOf(node)); adjacentNode.visitNrAdjacentLinks((short) 1); - final double linkLength = link.getLength(); - final boolean isContinuous = link.isContinuous(); - final double nodeDistance = node.getDistance(); + if (link.isContinuous()) { + final double nodeDistance = node.getDistance(); + final double linkLength = link.getLength(); - if (isContinuous) { - double remainingDistance = duration - nodeDistance; - if (isIncoming) { - link.setStartOffset(Math.max(0, linkLength - (remainingDistance * walkingSpeed))); + if (qIsIncoming) { + link.setStartOffset(Math.max(0, linkLength - (qDuration - nodeDistance) * qWalkingSpeed)); link.setEndOffset(linkLength); } else { - remainingDistance = Math.max(0, duration - nodeDistance); + final double remainingDistance = qDuration - nodeDistance < 0 ? 0 : qDuration - nodeDistance; link.setStartOffset(0); - link.setEndOffset(Math.min(linkLength, remainingDistance * walkingSpeed)); + link.setEndOffset(Math.min(linkLength, remainingDistance * qWalkingSpeed)); } } if (adjacentNode.isClosed()) { - if (isExpiring && adjacentNode.isExpired()) { - final int adjId = adjacentNode.getID(); - if (!isContinuous || adjId != node.getID()) { - removeNode(adjId); - } + if (qIsExpiring && adjacentNode.isExpired() && adjacentNode.getId() != node.getId()) { + removeNode(adjacentNode.getId()); } } else { - final double newDistance; - if (isContinuous) { - newDistance = nodeDistance + linkLength / walkingSpeed; - } else { - final Set<Short> routes = new HashSet<>(); - routes.add((short) link.getRoute()); - newDistance = getAdjNodeCost(node, adjacentNode, Arrays.asList(new Short[] {(short) link.getRoute()})); - } - - if (newDistance <= duration && newDistance < adjacentNode.getDistance()) { - adjacentNode.setDistance(newDistance); - resultNodes.add(adjacentNode); - } + nConnections.addTargetLink(adjacentNode, link); } } + resultNodes.addAll(expandContinuousLinks(nConnections)); + resultNodes.addAll(expandDiscreteLinks(nConnections)); + return resultNodes; } /** - * Gets date codes for a given time. + * 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 time the given - * @return returns a set of date codes + * @param nConnection information about the the sourceNode, its links and reachable nodes + * @return another node, if it can be reached in the available time (or null if time has run out) */ - private Set<Integer> getDateCodes(final Calendar time) { - return database.getDateCodes(time); + private Collection<Node> expandContinuousLinks(final NodeConnection nConnection) { + if (!nConnection.containsContinuousTargets()) { + return Collections.emptyList(); + } + + final Set<Node> resultCollection = new HashSet<>(); + final Node sourceNode = nConnection.getSourceNode(); + + for (final Entry<Node, Link> e : nConnection.getContinuousTargets().entrySet()) { + final Node adjacentNode = e.getKey(); + final Link link = e.getValue(); + final double newDistance = sourceNode.getDistance() + link.getLength() / qWalkingSpeed; + + if (newDistance <= qDuration && newDistance < adjacentNode.getDistance()) { + adjacentNode.setDistance(newDistance); + resultCollection.add(adjacentNode); + } + } + + return resultCollection; } /** - * Get the cost to travel to an adjacent node. + * Expand the given discrete link (that is connected to the + * given node and adjust the link offsets based on the + * distance of the current Node and the maximum duration of + * the query. * - * @param node the id of the node from which the calculation is started - * @param adjNode the id of the node which is traveled to - * @param routeIds the routes that should be considered - * @return the cost of traveling from the node to adjNode + * @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) */ - private double getAdjNodeCost(final Node node, final Node adjNode, final Collection<Short> routeIds) { - return database.getAdjNodeCost(node, adjNode, routeIds, codes, query.getFromTime(), query.getToTime()); + private Collection<Node> expandDiscreteLinks(final NodeConnection nConnection) { + if (!nConnection.containsDiscreteTargets()) { + return Collections.emptyList(); + } + + final Set<Node> resultCollection = new HashSet<>(); + final Map<Node, Double> newDistances = getAdjNodeCost(nConnection); + 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); + } + } + + return resultCollection; } /** - * Initialize the datecodes for the isochrone. + * Gets date codes for a given time. * - * @throws AlgorithmException thrown if there are no date codes for the query time in the database + * @param time the given + * @return returns a set of date codes */ - private void initDateCodes() throws AlgorithmException { - codes = getDateCodes(query.getTime()); + private Collection<Integer> getDateCodes(final Calendar time) { + return database.getDateCodes(time); + } - if (codes.isEmpty()) { - // was solved in uniBz version by switching to unimodal mode... we do not do this any more - // (since user does not get informated about the change) - throw new AlgorithmException("Couldn't find datecodes in the databse for the given date"); - } + /** + * Get the cost to travel to an adjacent node. + * + * @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 + */ + private Map<Node, Double> getAdjNodeCost(final NodeConnection nConnection) { + return database.getAdjNodeCost(nConnection, dateCodes, qFromTime, qToTime); } /** @@ -296,35 +337,31 @@ public abstract class Isochrone { * @param locations The locations from which the isochrone should start. */ private void initializeStartLocations(final IOutput output, final Collection<Location> locations) { - final long duration = query.getDuration().longValue(); - final double walkingSpeed = query.getWalkingSpeed(); - final boolean isIncoming = (Direction.INCOMING == query.getDir()); - for (final Location location : locations) { final Link link = getLink(location.getLinkId()); final double locationOffset = location.getOffset(); double distance; Node node; - if (isIncoming) { + if (qIsIncoming) { node = getNode(link.getStartNode()); - distance = locationOffset / walkingSpeed; + distance = locationOffset / qWalkingSpeed; if (locationOffset > 0 && locationOffset < link.getLength()) { - link.setStartOffset(Math.max(0, locationOffset - duration * walkingSpeed)); + link.setStartOffset(Math.max(0, locationOffset - qDuration * qWalkingSpeed)); link.setEndOffset(locationOffset); output.addLink(link); } } else { node = getNode(link.getEndNode()); - distance = (link.getLength() - locationOffset) / walkingSpeed; + distance = (link.getLength() - locationOffset) / qWalkingSpeed; if (locationOffset > 0 && locationOffset < link.getLength()) { link.setStartOffset(locationOffset); - link.setEndOffset(Math.min(link.getLength(), Math.abs(link.getLength() - locationOffset - duration * walkingSpeed))); + link.setEndOffset(Math.min(link.getLength(), Math.abs(link.getLength() - locationOffset - qDuration * qWalkingSpeed))); output.addLink(link); } } - if (distance <= duration && distance < node.getDistance()) { + if (distance <= qDuration && distance < node.getDistance()) { node.setDistance(distance); updateNodeQueue(node); } @@ -350,8 +387,8 @@ public abstract class Isochrone { */ private void updateNodeQueue(final Node node) { if (node != null) { - queue.remove(node); - queue.offer(node); + nodeQueue.remove(node); + nodeQueue.offer(node); } } 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 caf37f15..f66cf793 100644 --- a/src/main/java/it/unibz/inf/isochrone/db/Database.java +++ b/src/main/java/it/unibz/inf/isochrone/db/Database.java @@ -5,6 +5,7 @@ import it.unibz.inf.isochrone.config.ConfigDataset; import it.unibz.inf.isochrone.config.ConfigIsochrone; import it.unibz.inf.isochrone.network.Link; import it.unibz.inf.isochrone.network.Node; +import it.unibz.inf.isochrone.network.NodeConnection; import it.unibz.inf.isochrone.util.EnumContainer.Direction; import it.unibz.inf.isochrone.util.EnumContainer.Mode; import it.unibz.inf.isochrone.util.Point; @@ -76,11 +77,11 @@ public class Database { final String configVertex = config.getTableVertex(); final String configVertexDensity = config.getTableVertexDensity(); - 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 >= ?"; + 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 + + " 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 <= ?"; queryGetAllEdges = "SELECT ID, SOURCE, TARGET, LENGTH, EDGE_MODE, ROUTE_ID FROM " + configEdges; @@ -217,25 +218,27 @@ public class Database { /** * Gets the cost to go from one node to another node. * - * @param node the node from which we should start calculating - * @param adjNode the node to which we want to calculate the cost - * @param routeIds the routeIds that should be taken into consideration + * @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 double getAdjNodeCost(final Node node, final Node adjNode, final Collection<Short> routeIds, final Set<Integer> dateCodes, final long fromTime, final long toTime) { + 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(); double minDistance = Double.POSITIVE_INFINITY; ResultSet rs = null; try { if (isIncoming) { - rs = getLatestDepartureTime(adjNode.getID(), node.getID(), routeIds, dateCodes, fromTime, Math.round(toTime - node.getDistance())); + 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 short routeId = rs.getShort("ROUTE_ID"); + final short adjNodeId = rs.getShort("SOURCE"); + final Node adjNode = nConnection.getDiscreteTargetNode(adjNodeId); adjNode.setDepartureTime(routeId, departureTime); node.setArrivalTime(routeId, arrivalTime); final double distance = toTime - departureTime; @@ -245,13 +248,15 @@ public class Database { } } } else { - rs = getEarliestArrivalTime(node.getID(), adjNode.getID(), routeIds, dateCodes, Math.round(fromTime + node.getDistance()), toTime); + 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 short routeId = rs.getShort("ROUTE_ID"); - adjNode.setArrivalTime(routeId, arrivalTime); + final short adjNodeId = rs.getShort("TARGET"); + final Node adjNode = nConnection.getDiscreteTargetNode(adjNodeId); node.setDepartureTime(routeId, departureTime); + adjNode.setArrivalTime(routeId, arrivalTime); final double distance = arrivalTime > 0 ? arrivalTime - fromTime : Double.POSITIVE_INFINITY; if (distance < minDistance) { minDistance = distance; @@ -265,7 +270,7 @@ public class Database { DbUtils.closeQuietly(rs); } - return minDistance; + return resultMap; } /** @@ -329,23 +334,25 @@ public class Database { /** * Gets the earliest arrival time at the target when traveled from the source. * - * @param sourceId the id of the source node - * @param targetId the id of the target node - * @param routeIds the identifiers (as set) of the routes that should be considered + * @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 int sourceId, final int targetId, final Collection<Short> routeIds, final Set<Integer> dates, final long from, final long to) throws SQLException { - final String sql = String.format(queryEarliestArrivalTimeHomo, preparePlaceHolders(routeIds.size()), preparePlaceHolders(dates.size())); + 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, sourceId); - statement.setInt(2, targetId); - int idx = setValues(3, statement, routeIds.toArray()); + 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()); @@ -358,23 +365,25 @@ public class Database { /** * Gets the latest departure time to start from source, to arrive at the target in time. * - * @param sourceId the id of the source node - * @param targetId the id of the target node - * @param routeIds the identifiers (as set) of the routes that should be considered + * @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 int sourceId, final int targetId, final Collection<Short> routeIds, final Set<Integer> dates, final long from, final long to) throws SQLException { - final String sql = String.format(queryLatestDepartureTimeHomo, preparePlaceHolders(routeIds.size()), preparePlaceHolders(dates.size())); + 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 - statement.setInt(1, sourceId); - statement.setInt(2, targetId); - int idx = setValues(3, statement, routeIds.toArray()); + 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()); diff --git a/src/main/java/it/unibz/inf/isochrone/network/Link.java b/src/main/java/it/unibz/inf/isochrone/network/Link.java index a9d15add..799fe594 100644 --- a/src/main/java/it/unibz/inf/isochrone/network/Link.java +++ b/src/main/java/it/unibz/inf/isochrone/network/Link.java @@ -145,7 +145,7 @@ public class Link { * @return opposite Node of this link (or null) */ public int getOppositeOf(final Node node) { - if (getStartNode() == node.getID()) { + if (getStartNode() == node.getId()) { return endNode; } diff --git a/src/main/java/it/unibz/inf/isochrone/network/MemoryOutput.java b/src/main/java/it/unibz/inf/isochrone/network/MemoryOutput.java index 013b9a12..2836d2bd 100644 --- a/src/main/java/it/unibz/inf/isochrone/network/MemoryOutput.java +++ b/src/main/java/it/unibz/inf/isochrone/network/MemoryOutput.java @@ -44,8 +44,8 @@ public class MemoryOutput extends AbstractOutput { } @Override - public void addNodes(final Collection<Node> node) { - nodes.addAll(nodes); + public void addNodes(final Collection<Node> nodeCollection) { + nodes.addAll(nodeCollection); } } diff --git a/src/main/java/it/unibz/inf/isochrone/network/Node.java b/src/main/java/it/unibz/inf/isochrone/network/Node.java index b0cfb06b..d3a828bc 100644 --- a/src/main/java/it/unibz/inf/isochrone/network/Node.java +++ b/src/main/java/it/unibz/inf/isochrone/network/Node.java @@ -113,7 +113,7 @@ public class Node implements Comparable<Node> { return distance; } - public int getID() { + public int getId() { return id; } diff --git a/src/main/java/it/unibz/inf/isochrone/network/NodeConnection.java b/src/main/java/it/unibz/inf/isochrone/network/NodeConnection.java new file mode 100644 index 00000000..02bb939c --- /dev/null +++ b/src/main/java/it/unibz/inf/isochrone/network/NodeConnection.java @@ -0,0 +1,135 @@ +package it.unibz.inf.isochrone.network; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class NodeConnection { + private final Node sourceNode; + private final Map<Node, Link> continuousTargetConnections; + private final Map<Node, Link> discreteTargetConnections; + + // Collections + + public NodeConnection(final Node sourceNode) { + this.sourceNode = sourceNode; + this.continuousTargetConnections = new HashMap<>(); + this.discreteTargetConnections = new HashMap<>(); + } + + // Getters + + public Node getSourceNode() { + return sourceNode; + } + + public Map<Node, Link> getContinuousTargets() { + return continuousTargetConnections; + } + + 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<Integer> getContiunousRouteIds() { + return getIdsForRoutes(continuousTargetConnections); + } + + public Map<Node, Link> getDiscreteTargets() { + return discreteTargetConnections; + } + + public Node getDiscreteTargetNode(final int nodeId) { + return getNodeById(discreteTargetConnections, nodeId); + } + + public Collection<Integer> getDiscreteTargetNodeIds() { + return getIdsForTargetNodes(discreteTargetConnections); + } + + public Collection<Integer> getDiscreteRouteIds() { + return getIdsForRoutes(discreteTargetConnections); + } + + public Map<Node, Link> getTargets() { + final Map<Node, Link> allTargets = new HashMap<>(); + allTargets.putAll(continuousTargetConnections); + allTargets.putAll(discreteTargetConnections); + + return allTargets; + } + + public Collection<Integer> getTargetNodeIds() { + return getIdsForTargetNodes(getTargets()); + } + + public Collection<Integer> getRouteIds() { + return getIdsForRoutes(getTargets()); + } + + // Public methods + + public void addTargetLink(final Node targetNode, final Link link) { + if (link.isContinuous()) { + continuousTargetConnections.put(targetNode, link); + } else { + discreteTargetConnections.put(targetNode, link); + } + } + + public boolean containsTargets() { + return containsContinuousTargets() || containsDiscreteTargets(); + } + + public boolean containsContinuousTargets() { + return !continuousTargetConnections.isEmpty(); + } + + public boolean containsDiscreteTargets() { + return !discreteTargetConnections.isEmpty(); + } + + // Private static methods + + private static Node getNodeById(final Map<Node, Link> map, final int nodeId) { + final Set<Entry<Node, Link>> entries = map.entrySet(); + for (final Entry<Node, Link> entry : entries) { + if (nodeId == entry.getKey().getId()) { + return entry.getKey(); + } + } + + return null; + } + + private static Collection<Integer> getIdsForRoutes(final Map<Node, Link> m) { + final Collection<Link> linkSet = m.values(); + final Set<Integer> resultSet = new HashSet<>(linkSet.size()); + for (final Link l : linkSet) { + resultSet.add(l.getRoute()); + } + + return resultSet; + } + + private static Collection<Integer> getIdsForTargetNodes(final Map<Node, Link> m) { + final Set<Node> nodeSet = m.keySet(); + final Set<Integer> resultSet = new HashSet<>(nodeSet.size()); + for (final Node n : nodeSet) { + resultSet.add(n.getId()); + } + + return resultSet; + } +} -- GitLab