Skip to content
Snippets Groups Projects
Commit cca1c9cc authored by User expired's avatar User expired
Browse files

added possibility to add multiple nodes into output at once (addNodes

beside addNode function)
 -> could be improved with java8 syntax (default method in IOutput), but
checkstyle does not support this right now
parent 9578a001
No related branches found
No related tags found
No related merge requests found
<?xml version="1.0" encoding="UTF-8"?>
<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
<fileset name="all" enabled="true" check-config-name="NKrChecks" local="false">
<file-match-pattern match-pattern="." include-pattern="true"/>
</fileset>
</fileset-config>
......@@ -2,14 +2,17 @@ package it.unibz.inf.isochrone.algorithm;
import it.unibz.inf.isochrone.config.ConfigDataset;
import it.unibz.inf.isochrone.db.Database;
import it.unibz.inf.isochrone.network.AbstractOutput;
import it.unibz.inf.isochrone.network.IOutput;
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.Output;
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.HashSet;
......@@ -38,7 +41,7 @@ public abstract class Isochrone {
* Instantiates a new isochrone object.
*
* @param config the configuration of the dataset for the isochrone
* @param db the databse object used for db communication
* @param db used for database communication
* @param query the parameters for the isochrone
* @throws AlgorithmException thrown if there are no date codes in the database for the specified query
*/
......@@ -117,41 +120,34 @@ public abstract class Isochrone {
* @param output the output in which the computed nodes and links are stored.
* @return the computed output (stored in the output given by the parameter)
*/
// TODO: Add some more tests for this method. If isochrone gets too small (2points for 15min isochrone from FUB) nothing fails ;-(
public <T extends Output> T compute(final T output) {
// 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();
if (!query.getStartNodes().isEmpty()) {
initializeStartNodes(query.getStartNodes());
} else {
final Collection<Integer> startNodes = query.getStartNodes();
if (startNodes.isEmpty()) {
initializeStartLocations(output, query.getStartLocations());
} else {
initializeStartNodes(startNodes);
}
Node node = null;
// TODO: Would it make sense to bulk-load the adjLinks? (not id by id, but the whole queue or the next 1000 ids?)
while ((node = queue.poll()) != null) {
final Collection<Link> adjacents = calcAdjLinks(node.getID());
final int nodeId = node.getID();
final Collection<Link> adjacents = calcAdjLinks(nodeId);
output.addNode(node);
node.close();
for (final Link link : adjacents) {
if (link.isContinuous()) {
updateNodeQueue(expandContinuousLink(node, link));
addLinks(output, link);
} else {
updateNodeQueue(expandDiscreteLink(node, link));
}
}
updateNodeQueue(expandLinks(node, adjacents));
addLinks(output, adjacents);
if (query.isExpireNodes() && node.isExpired()) {
removeNode(node.getID());
if (isNodeExpires && node.isExpired()) {
removeNode(nodeId);
}
}
for (final Node n : getNodes()) {
output.addNode(n);
}
output.addNodes(getNodes());
output.afterCalculation();
return output;
......@@ -185,82 +181,75 @@ public abstract class Isochrone {
// Private methods
private <T extends Output> void addLinks(final T output, final Link link) {
if (Math.abs(link.getStartOffset() - Double.MIN_VALUE) < COMPARE_PRECISION) {
output.addLink(link);
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);
}
}
}
/**
* Expand the given continuous link (that is connected to the given node)
* 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 link continuous link, that is connected to the node
* @return another node, if it can be reached in the available time (or null if time has run out)
* @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 Node expandContinuousLink(final Node node, final Link link) {
final int duration = query.getDuration().intValue();
private Collection<Node> expandLinks(final Node node, final Collection<Link> links) {
final long duration = query.getDuration().longValue();
final double walkingSpeed = query.getWalkingSpeed();
double destinationOffset;
final Node adjacentNode = getNode(link.getOppositeOf(node));
adjacentNode.visitNrAdjacentLinks((short) 1);
if (query.getDir() == Direction.INCOMING) {
destinationOffset = Math.max(0, link.getLength() - (query.getDuration() - node.getDistance()) * walkingSpeed);
link.setStartOffset(destinationOffset);
link.setEndOffset(link.getLength());
} else {
final double remainingDistance = query.getDuration() - node.getDistance() < 0 ? 0 : query.getDuration() - node.getDistance();
destinationOffset = Math.min(link.getLength(), remainingDistance * walkingSpeed);
link.setStartOffset(0);
link.setEndOffset(destinationOffset);
}
if (!adjacentNode.isClosed()) {
final double newDistance = node.getDistance() + link.getLength() / walkingSpeed;
if (newDistance <= duration && newDistance < adjacentNode.getDistance()) {
adjacentNode.setDistance(newDistance);
return adjacentNode;
final boolean isExpiring = query.isExpireNodes();
final boolean isIncoming = (Direction.INCOMING == query.getDir());
final Collection<Node> resultNodes = new ArrayList<>(links.size());
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 (isContinuous) {
double remainingDistance = duration - nodeDistance;
if (isIncoming) {
link.setStartOffset(Math.max(0, linkLength - (remainingDistance * walkingSpeed)));
link.setEndOffset(linkLength);
} else {
remainingDistance = Math.max(0, duration - nodeDistance);
link.setStartOffset(0);
link.setEndOffset(Math.min(linkLength, remainingDistance * walkingSpeed));
}
}
} else if (query.isExpireNodes() && adjacentNode.isExpired() && node.getID() != adjacentNode.getID()) {
removeNode(adjacentNode.getID());
}
return null;
}
if (adjacentNode.isClosed()) {
if (isExpiring && adjacentNode.isExpired()) {
final int adjId = adjacentNode.getID();
if (!isContinuous || adjId != node.getID()) {
removeNode(adjId);
}
}
} 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()}));
}
/**
* 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 current Node
* @param link discrete link connected to the node
* @return another node, if it can be reached in the available time (or null if there is no time to reach another node)
*/
private Node expandDiscreteLink(final Node node, final Link link) {
final Long duration = query.getDuration();
final Node adjacentNode = getNode(link.getOppositeOf(node));
adjacentNode.visitNrAdjacentLinks((short) 1);
if (!adjacentNode.isClosed()) {
final Set<Short> routes = new HashSet<>();
routes.add((short) link.getRoute());
final double newDistance = getAdjNodeCost(node, adjacentNode, routes);
if (newDistance <= duration && newDistance < adjacentNode.getDistance()) {
adjacentNode.setDistance(newDistance);
return adjacentNode;
if (newDistance <= duration && newDistance < adjacentNode.getDistance()) {
adjacentNode.setDistance(newDistance);
resultNodes.add(adjacentNode);
}
}
} else if (query.isExpireNodes() && adjacentNode.isExpired()) {
removeNode(adjacentNode.getID());
}
return null;
return resultNodes;
}
/**
......@@ -281,7 +270,7 @@ public abstract class Isochrone {
* @param routeIds the routes that should be considered
* @return the cost of traveling from the node to adjNode
*/
private double getAdjNodeCost(final Node node, final Node adjNode, final Set<Short> routeIds) {
private double getAdjNodeCost(final Node node, final Node adjNode, final Collection<Short> routeIds) {
return database.getAdjNodeCost(node, adjNode, routeIds, codes, query.getFromTime(), query.getToTime());
}
......@@ -293,7 +282,7 @@ public abstract class Isochrone {
private void initDateCodes() throws AlgorithmException {
codes = getDateCodes(query.getTime());
if (codes.size() == 0) {
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");
......@@ -306,32 +295,36 @@ public abstract class Isochrone {
* @param output the output class in which the initial links are stored
* @param locations The locations from which the isochrone should start.
*/
private void initializeStartLocations(final Output output, final Collection<Location> locations) {
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 (query.getDir() == Direction.INCOMING) {
if (isIncoming) {
node = getNode(link.getStartNode());
distance = locationOffset / query.getWalkingSpeed();
distance = locationOffset / walkingSpeed;
if (locationOffset > 0 && locationOffset < link.getLength()) {
link.setStartOffset(Math.max(0, locationOffset - query.getDuration() * query.getWalkingSpeed()));
link.setStartOffset(Math.max(0, locationOffset - duration * walkingSpeed));
link.setEndOffset(locationOffset);
output.addLink(link);
}
} else {
node = getNode(link.getEndNode());
distance = (link.getLength() - locationOffset) / query.getWalkingSpeed();
distance = (link.getLength() - locationOffset) / walkingSpeed;
if (locationOffset > 0 && locationOffset < link.getLength()) {
link.setStartOffset(locationOffset);
link.setEndOffset(Math.min(link.getLength(), Math.abs(link.getLength() - locationOffset - query.getDuration() * query.getWalkingSpeed())));
link.setEndOffset(Math.min(link.getLength(), Math.abs(link.getLength() - locationOffset - duration * walkingSpeed)));
output.addLink(link);
}
}
if (distance <= query.getDuration() && distance < node.getDistance()) {
if (distance <= duration && distance < node.getDistance()) {
node.setDistance(distance);
updateNodeQueue(node);
}
......@@ -361,4 +354,15 @@ public abstract class Isochrone {
queue.offer(node);
}
}
/**
* Sets updated nodes into the right place in the priority queue.
*
* @param nodes The nodes that were changed
*/
private void updateNodeQueue(final Collection<Node> nodes) {
for (final Node node : nodes) {
updateNodeQueue(node);
}
}
}
......@@ -225,7 +225,7 @@ public class Database {
* @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 Set<Short> routeIds, final Set<Integer> dateCodes, final long fromTime, final long toTime) {
public double getAdjNodeCost(final Node node, final Node adjNode, final Collection<Short> routeIds, final Set<Integer> dateCodes, final long fromTime, final long toTime) {
double minDistance = Double.POSITIVE_INFINITY;
ResultSet rs = null;
......@@ -332,14 +332,14 @@ public class Database {
* @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 dateCodes the date codes for which the times are calculated
* @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 Set<Short> routeIds, final Set<Integer> dateCodes, final long from, final long to) throws SQLException {
final String sql = String.format(queryEarliestArrivalTimeHomo, preparePlaceHolders(routeIds.size()), preparePlaceHolders(dateCodes.size()));
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()));
final PreparedStatement statement = getPstmt(sql);
// CHECKSTYLE:OFF MagicNumber
......@@ -348,7 +348,7 @@ public class Database {
int idx = setValues(3, statement, routeIds.toArray());
statement.setLong(idx++, from);
statement.setLong(idx++, to);
idx = setValues(idx, statement, dateCodes.toArray());
idx = setValues(idx, statement, dates.toArray());
statement.setLong(idx, to);
// CHECKSTYLE:ON MagicNumber
......@@ -361,14 +361,14 @@ public class Database {
* @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 dateCodes the date codes for which the times are calculated
* @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 Set<Short> routeIds, final Set<Integer> dateCodes, final long from, final long to) throws SQLException {
final String sql = String.format(queryLatestDepartureTimeHomo, preparePlaceHolders(routeIds.size()), preparePlaceHolders(dateCodes.size()));
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()));
final PreparedStatement statement = getPstmt(sql);
// CHECKSTYLE:OFF MagicNumber
......@@ -377,7 +377,7 @@ public class Database {
int idx = setValues(3, statement, routeIds.toArray());
statement.setLong(idx++, from);
statement.setLong(idx++, to);
idx = setValues(idx, statement, dateCodes.toArray());
idx = setValues(idx, statement, dates.toArray());
statement.setLong(idx, from);
// CHECKSTYLE:ON MagicNumber
......
package it.unibz.inf.isochrone.network;
import java.util.Collection;
public abstract class AbstractOutput implements IOutput {
/**
* This is called after all links and nodes are stored in this output.
* Can be useful to save results in the database (or similar)
*/
public void afterCalculation() { }
/**
* This is called before links and/or nodes are stored in this output.
* Can be useful to set up some datatypes (clear maps, ...).
*/
public void beforeCalculation() { }
public void addLinks(final Collection<Link> links) {
for (final Link link : links) {
addLink(link);
}
}
public void addNodes(final Collection<Node> nodes) {
for (final Node node : nodes) {
addNode(node);
}
}
}
package it.unibz.inf.isochrone.network;
/**
* Stores all the output links from the isochrone algorithm.
*/
public interface Output {
public interface IOutput {
/**
* Stores a link which belongs to the network.
......@@ -19,16 +20,4 @@ public interface Output {
*/
void addNode(Node node);
/**
* This is called after all links and nodes are stored in this output.
* Can be useful to save results in the database (or similar)
*/
void afterCalculation();
/**
* This is called before links and/or nodes are stored in this output.
* Can be useful to set up some datatypes (clear maps, ...).
*/
void beforeCalculation();
}
package it.unibz.inf.isochrone.network;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
......@@ -9,7 +10,7 @@ import java.util.Set;
* A sample output class, which simply stores all nodes and returns if needed.
* This is mainly used for testing purposes.
*/
public class MemoryOutput implements Output {
public class MemoryOutput extends AbstractOutput {
private final Map<Integer, Link> links;
private final Set<Node> nodes;
......@@ -43,13 +44,8 @@ public class MemoryOutput implements Output {
}
@Override
public void afterCalculation() {
// No post-processing of calculated isochrone
}
@Override
public void beforeCalculation() {
// No pre-processing of calculated isochrone
public void addNodes(final Collection<Node> node) {
nodes.addAll(nodes);
}
}
......@@ -7,7 +7,7 @@ package it.unibz.inf.isochrone.network;
* A link offset consists of a distance from the link start node and a distance from the link end node. The distances
* indicate the two outer link segments that cannot be reached. Moreover, these two segments delimit the reachable,
* inner part of this link.
*
*
* @author Gytis Tumas
* @author Willi Cometti
* @version 2.0
......@@ -19,7 +19,7 @@ public class Offset {
/**
* Class Constructor that uses two distances to create the <code>Offset
* </code>.
*
*
* @param startOffset the distance from the start node
* @param endOffset the distance from the end node
*/
......@@ -30,7 +30,7 @@ public class Offset {
/**
* Returns the beginning of the offset in respect to the start node.
*
*
* @return the start node offset
*/
public double getStartOffset() {
......@@ -39,10 +39,11 @@ public class Offset {
/**
* Returns the ending of the offset in respect to the start node.
*
*
* @return the end node offset
*/
public double getEndOffset() {
return endOffset;
}
}
package it.unibz.inf.isochrone.network;
/**
* Represents a Schedule for Transport systems in memory.
* Represents a Schedule for Transport systems (in memory).
*/
public class Schedule {
private long departureTime, arrivalTime;
private long arrivalTime;
private long departureTime;
// Constructors
public Schedule() { }
public void setDepartureTime(final long departureTime) {
this.departureTime = departureTime;
}
public void setArrivalTime(final long arrivalTime) {
this.arrivalTime = arrivalTime;
}
// Getters
public long getArrivalTime() {
return arrivalTime;
......@@ -24,4 +21,14 @@ public class Schedule {
return departureTime;
}
// Setters
public void setArrivalTime(final long arrivalTime) {
this.arrivalTime = arrivalTime;
}
public void setDepartureTime(final long departureTime) {
this.departureTime = departureTime;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment