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 a4eaa433f1ff864abf7f0daab8c03469005a00af..0acc79603e88d3e3db52329d6bf8cd6a23ef197b 100644 --- a/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java +++ b/src/main/java/it/unibz/inf/isochrone/algorithm/Isochrone.java @@ -14,6 +14,7 @@ import it.unibz.inf.isochrone.util.Query; import java.util.Calendar; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.Map; import java.util.Map.Entry; @@ -160,37 +161,19 @@ public abstract class Isochrone { NodeConnection dConnections = null; while ((node = nodeQueue.poll()) != null) { final int nodeId = node.getId(); - cConnections = new NodeConnection(node); - dConnections = new NodeConnection(node); + new NodeConnection(node); - final Collection<Link> adjacents = calcAdjLinks(nodeId); + final Collection<Link> targets = calcAdjLinks(nodeId); output.addNode(node); node.close(); - 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()); - } - } - + cConnections = prepareContinuousLinks(node, targets); updateNodeQueue(expandContinuousLinks(cConnections)); - addLinks(output, cConnections.getAllLinks()); + + dConnections = prepareDiscreteLinks(node, targets); updateNodeQueue(expandDiscreteLinks(dConnections)); + addLinks(output, targets); if (qIsExpiring && node.isExpired()) { removeNode(nodeId); } @@ -230,16 +213,34 @@ public abstract class Isochrone { // Private methods + private void addTargetNode(final NodeConnection nConnection, final Node node, final Link link) { + final Node targetNode = getNode(link.getOppositeOf(node)); + targetNode.visitNrAdjacentLinks((short) 1); + + if (targetNode.isClosed()) { + if (qIsExpiring && targetNode.isExpired() && targetNode.getId() != node.getId()) { + removeNode(targetNode.getId()); + } + } else { + nConnection.addLink(targetNode, link); + } + } + /** * 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 to where start the calculation, where to travel to and which routes are considered + * @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 Collection<Node> expandContinuousLinks(final NodeConnection nConnection) { - final double sourceDistance = nConnection.getSourceNode().getDistance(); + if (!nConnection.containsTargets()) { + return Collections.emptyList(); + } + + final Node sourceNode = nConnection.getSourceNode(); + final double sourceDistance = sourceNode.getDistance(); final Map<Node, Collection<Link>> targets = nConnection.getTargetEntries(); final Collection<Entry<Node, Collection<Link>>> entries = targets.entrySet(); @@ -247,8 +248,8 @@ public abstract class Isochrone { 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; + for (final Link link : targetLinks) { + final double newDistance = sourceDistance + link.getLength() / qWalkingSpeed; if (newDistance <= qDuration && newDistance < targetNode.getDistance()) { targetNode.setDistance(newDistance); results.add(targetNode); @@ -265,17 +266,21 @@ public abstract class Isochrone { * distance of the current Node and the maximum duration of * the query. * - * @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) + * @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 Collection<Node> expandDiscreteLinks(final NodeConnection nConnection) { + if (!nConnection.containsTargets()) { + return Collections.emptyList(); + } + 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(); + for (final Entry<Node, Long> e : entries) { + final Node targetNode = e.getKey(); + final long newDistance = e.getValue(); if (newDistance <= qDuration && newDistance < targetNode.getDistance()) { targetNode.setDistance(newDistance); results.add(targetNode); @@ -285,16 +290,6 @@ public abstract class Isochrone { return results; } - /** - * 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 the target nodes (targetNodeId == mapKey, distance == mapValue) - */ - private Map<Node, Long> getAdjNodeCost(final NodeConnection nConnection) { - return database.getAdjNodeCost(nConnection, dateCodes, qFromTime, qToTime); - } - /** * Gets date codes for a given time. * @@ -305,6 +300,16 @@ public abstract class Isochrone { return database.getDateCodes(time); } + /** + * 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, Long> getAdjNodeCost(final NodeConnection nConnection) { + return database.getAdjNodeCost(nConnection, dateCodes, qFromTime, qToTime); + } + /** * Initializes all the locations from which the isochrone should be calculated. * @@ -354,10 +359,34 @@ public abstract class Isochrone { } } - private void prepareContinousLink(final Node node, final Link link) { - final double linkLength = link.getLength(); + private NodeConnection prepareContinuousLinks(final Node node, final Collection<Link> links) { + final NodeConnection nConnection = new NodeConnection(node); final double nodeDistance = node.getDistance(); + for (final Link link : links) { + if (link.isContinuous()) { + prepareLinkOffsets(link, nodeDistance); + addTargetNode(nConnection, node, link); + } + } + + return nConnection; + } + + private NodeConnection prepareDiscreteLinks(final Node node, final Collection<Link> links) { + final NodeConnection nConnection = new NodeConnection(node); + for (final Link link : links) { + if (!link.isContinuous()) { + addTargetNode(nConnection, node, link); + } + } + + return nConnection; + } + + private void prepareLinkOffsets(final Link link, final double nodeDistance) { + final double linkLength = link.getLength(); + if (qIsIncoming) { link.setStartOffset(Math.max(0, linkLength - (qDuration - nodeDistance) * qWalkingSpeed)); link.setEndOffset(linkLength); @@ -400,9 +429,9 @@ public abstract class Isochrone { // Private static methods - private static <T extends AbstractOutput> void addLinks(final T output, final Collection<Link> links) { + private static <T extends IOutput> void addLinks(final T output, final Collection<Link> links) { for (final Link link : links) { - if (Math.abs(link.getStartOffset() - Double.MIN_VALUE) < COMPARE_PRECISION) { + if (link.isContinuous() && Math.abs(link.getStartOffset() - Double.MIN_VALUE) < COMPARE_PRECISION) { output.addLink(link); } }