From 514319b6a09928314532edcd431a86c4a78d3fa3 Mon Sep 17 00:00:00 2001
From: Nikolaus Krismer <nikolaus.krismer@uibk.ac.at>
Date: Thu, 29 Sep 2016 14:39:36 +0200
Subject: [PATCH] worked on supporing isodistance (issue #5)

---
 CHANGELOG.md                                  |   1 +
 .../unibz/inf/isochrone/AlgorithmRunner.java  |  11 +-
 .../algorithm/AbstractAlgorithm.java          | 116 +++++++++++-------
 .../unibz/inf/isochrone/algorithm/MineR.java  |  13 +-
 .../inf/isochrone/config/ConfigAlgorithm.java |  98 +++++++--------
 .../it/unibz/inf/isochrone/db/Database.java   |  15 +--
 .../inf/isochrone/db/DbQueryPostgres.java     |   6 +-
 .../unibz/inf/isochrone/db/DbQuerySqlite.java |   3 +-
 .../it/unibz/inf/isochrone/network/IEdge.java |   4 +-
 .../it/unibz/inf/isochrone/network/INode.java |   6 +-
 .../unibz/inf/isochrone/network/Location.java |  16 ++-
 .../it/unibz/inf/isochrone/network/Node.java  |  22 ++--
 .../inf/isochrone/network/NodeWired.java      |   2 +-
 .../it/unibz/inf/isochrone/DatasetHelper.java |   2 +-
 .../it/unibz/inf/isochrone/SampleRunner.java  |  38 +++---
 .../isochrone/algorithm/QLocationRWTest.java  |   2 +-
 .../inf/isochrone/db/QueryQuantifierTest.java |   6 +-
 .../db/QueryRuntimeConfigurationProvider.java |   4 +-
 .../inf/isochrone/db/QueryRuntimeTest.java    |  10 +-
 .../inf/isochrone/AlgorithmRunnerTest.java    |   4 +-
 .../inf/isochrone/ConfigAlgorithmHelper.java  |  14 +--
 .../inf/isochrone/TestConfiguration.java      |   2 +-
 .../it/unibz/inf/isochrone/TestGeoHelper.java |   2 +-
 .../isochrone/algorithm/AlgorithmTest.java    |  73 +++++++----
 .../isochrone/algorithm/ComparisonTest.java   |  15 +--
 .../algorithm/EdgeOrientationTest.java        |   3 +-
 .../isochrone/algorithm/IncrementalTest.java  |  18 +--
 .../isochrone/algorithm/ProfilingTest.java    |   3 +-
 .../isochrone/algorithm/ReachabilityTest.java |   3 +-
 .../isochrone/config/ConfigAlgorithmTest.java |  25 +++-
 .../inf/isochrone/db/RangeNodeCountTest.java  |   2 +-
 .../isochrone/network/StringifierTest.java    |   2 +-
 32 files changed, 301 insertions(+), 240 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24b10e5d..ec3ec907 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
 Upcoming version:
 -----------------
+  - worked on supporing isodistance (issue #5) (Nikolaus Krismer)
   - fixed findbugs warnings (Nikolaus Krismer)
   - reworking TileNumber class (Nikolaus Krismer)
   - added support for 3d points/linestring reading (Nikolaus Krismer)
diff --git a/src/main/java/it/unibz/inf/isochrone/AlgorithmRunner.java b/src/main/java/it/unibz/inf/isochrone/AlgorithmRunner.java
index 31af040e..961b6cca 100644
--- a/src/main/java/it/unibz/inf/isochrone/AlgorithmRunner.java
+++ b/src/main/java/it/unibz/inf/isochrone/AlgorithmRunner.java
@@ -26,6 +26,7 @@ import it.unibz.inf.isochrone.algorithm.AlgorithmFactory;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.ConfigAlgorithmBuilder;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.Direction;
+import it.unibz.inf.isochrone.config.ConfigAlgorithm.LimitType;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.Mode;
 import it.unibz.inf.isochrone.config.ConfigDataset;
 import it.unibz.inf.isochrone.db.Database;
@@ -57,7 +58,7 @@ public final class AlgorithmRunner {
 	@Option(name = "-mr", usage = "the number of nodes that are loaded by one range query. Only useful for MINER/MINERX algorithm. 0 value means \"unlimited\".")
 	private Long maxRangeSize = 0L;
 	@Option(name = "-m", usage = "the network mode to use for calculation")
-	private Mode mode = Mode.MULTIMODAL;
+	private Mode mode = Mode.ISOCHRONE_MULTIMODAL;
 	@Option(name = "-q", handler = QueryPointOptionHandler.class, usage = "node to start the calculation from (format \"lng,lat\").")
 	private Point2D.Double queryPoint = new Point2D.Double(11.3525539631585d, 46.4975205d);
 	@DecimalMin("0.1")
@@ -138,14 +139,14 @@ public final class AlgorithmRunner {
 		final ConfigAlgorithmBuilder configBuilder = new ConfigAlgorithmBuilder(queryPoint)
 			.dateTime(dateTime)
 			.direction(direction)
-			.duration(dmax)
+			.limit(LimitType.DURATION, dmax)
 			.mode(mode)
 			.walkingSpeed(walkingSpeed);
 
-		final ConfigAlgorithm algorithmConfig = configBuilder.build();
-		try (final Database database = new Database(dataset, algorithmConfig.getMode(), algorithmConfig.getDirection())) {
+		final ConfigAlgorithm configAlgorithm = configBuilder.build();
+		try (final Database database = new Database(dataset, configAlgorithm.getMode(), configAlgorithm.getDirection())) {
 			final long l0 = System.currentTimeMillis();
-			final AbstractAlgorithm algorithm = algorithmName.getInstance(database, algorithmConfig, maxMemorySize, maxRangeSize);
+			final AbstractAlgorithm algorithm = algorithmName.getInstance(database, configAlgorithm, maxMemorySize, maxRangeSize);
 			final MemoryOutput output = algorithm.getResult();
 			LOGGER.info("Calculation time (in msec): {}", (System.currentTimeMillis() - l0));
 			LOGGER.info("#edges: {}", output.getEdges().size());
diff --git a/src/main/java/it/unibz/inf/isochrone/algorithm/AbstractAlgorithm.java b/src/main/java/it/unibz/inf/isochrone/algorithm/AbstractAlgorithm.java
index a5da8560..b918d229 100644
--- a/src/main/java/it/unibz/inf/isochrone/algorithm/AbstractAlgorithm.java
+++ b/src/main/java/it/unibz/inf/isochrone/algorithm/AbstractAlgorithm.java
@@ -19,6 +19,7 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import it.unibz.inf.isochrone.config.ConfigAlgorithm;
+import it.unibz.inf.isochrone.config.ConfigAlgorithm.LimitType;
 import it.unibz.inf.isochrone.config.ConfigDataset;
 import it.unibz.inf.isochrone.db.Database;
 import it.unibz.inf.isochrone.network.Edge;
@@ -47,11 +48,11 @@ public abstract class AbstractAlgorithm {
 	 * Flag controlling algorithm cache usage. Set this to false to deactivate isochrone caching.
 	 */
 	private static final boolean USE_CACHE = true;
-	protected final Long cDuration;
-	protected final Long cMaxNodes;
-	protected final boolean cIsIncoming;
 	protected final Long cFromTime;
+	protected final boolean cIsIncoming;
 	protected final boolean cIsMultimodal;
+	protected final long cLimit;
+	protected final LimitType cLimitType;
 	protected final boolean cOutputOnlyExpandedEdges;
 	protected final Collection<Point2D.Double> cQueryPoints;
 	protected final Integer cQueryPointsSRID;
@@ -87,10 +88,6 @@ public abstract class AbstractAlgorithm {
 	 * @throws AlgorithmException thrown if there are no date codes in the database for the specified dataset or if it is not valid or a unimodal one should be used for multimodal calculation
 	 */
 	public AbstractAlgorithm(final Database database, final ConfigAlgorithm config) throws AlgorithmException {
-		if (config.getDateTime() == null) {
-			throw new AlgorithmException("Invalid algorithm configuration given!");
-		}
-
 		if (LOGGER.isTraceEnabled()) {
 			LOGGER.trace("Creating algorithm {}", this.getClass().getSimpleName());
 		}
@@ -102,17 +99,21 @@ public abstract class AbstractAlgorithm {
 			activateStatistics();
 		}
 
-		this.cDuration = config.getDuration();
+		this.cFromTime = config.getTimeFrom();
 		this.cIsIncoming = config.isIncoming();
 		this.cIsMultimodal = config.isMultimodal();
-		this.cMaxNodes = config.getNodes();
+		this.cLimit = config.getLimit();
+		this.cLimitType = config.getLimitType();
 		this.cOutputOnlyExpandedEdges = config.isOutputOnlyExpandedEdges();
 		this.cQueryPoints = config.getQueryPoints();
 		this.cQueryPointsSRID = config.getQueryPointSRID();
 		this.cToTime = config.getTimeTo();
-		this.cFromTime = config.getTimeFrom();
 		this.cWalkingSpeed = (float) (config.getWalkingSpeed().floatValue() / CONVERSION_FACTOR_TO_METER_PER_SEC);
 
+		if (cIsMultimodal && config.getDateTime() == null) {
+			throw new AlgorithmException("Invalid algorithm configuration given!");
+		}
+
 		final ConfigDataset ds = database.getConfigDataset();
 		if (!ds.isValid()) {
 			throw new AlgorithmException("Can not create an isochrone algorithm for invalid dataset \"" + ds.getDatasetName() + "\"!");
@@ -121,13 +122,13 @@ public abstract class AbstractAlgorithm {
 			throw new AlgorithmException("Can not calculate multimodal isochrone within unimodal dataset \"" + ds.getDatasetName() +  "\"!");
 		}
 
-		if (cDuration == null || !cIsMultimodal) {
+		if (cLimitType != LimitType.DURATION || !cIsMultimodal) {
 			// we do not need serviceIds from the calendar table (since unimodal mode is used or no maxDuration is set)
 			this.serviceIds = null;
 		} else {
 			this.serviceIds = getServiceIds();
 			if (serviceIds == null || serviceIds.isEmpty()) {
-				// empty calendars were solved in uniBz version by switching computation mode to unimodal mode...
+				// empty calendars were solved in UniBZ version by switching computation mode to unimodal mode...
 				// ... we do not do this any more (since user does not get informed about the change)
 				//  => we throw this (new) exception here
 				final String msg = "Could not find calendars in the database for dataset \"" + ds.getDatasetName() + "\" and date \"" + config.getDateTime() + "\"";
@@ -271,6 +272,7 @@ public abstract class AbstractAlgorithm {
 	 * @throws AlgorithmException if calculation could not be performed (by now only used if MDijkstra would need more memory than available)
 	 */
 	protected boolean calculate() throws AlgorithmException {
+		final boolean limitNodeCount = (cLimitType == LimitType.NODE_COUNT);
 		final Collection<Location> startLocations = getStartLocations();
 		if (startLocations.isEmpty()) {
 			throw new AlgorithmException("Can not calculate isochrone, since start locations could not be projected onto street network");
@@ -278,7 +280,7 @@ public abstract class AbstractAlgorithm {
 		initStartLocations(startLocations);
 
 		running.set(true);
-		while (running.get() && !nodeQueue.isEmpty() && (cMaxNodes == null || expandedNodes < cMaxNodes)) {
+		while (running.get() && !nodeQueue.isEmpty() && (!limitNodeCount || expandedNodes < cLimit)) {
 			expandNode(nodeQueue.poll());
 			++expandedNodes;
 		}
@@ -403,14 +405,14 @@ public abstract class AbstractAlgorithm {
 		} else if (offset > 0) {
 			// duration in cache is bigger than requested duration => we need to shrink the already known isochrone
 			for (final INode node : nodes) {
-				final double nDistance = node.getDistance();
-				if (cDuration == null || nDistance <= cDuration) {
+				final float nDuration = node.getDuration();
+				if (isWithinDuration(nDuration)) {
 					output.addNode(node);
 					// TODO: Re-implement incremental caching!
 //					final Collection<IEdge> nodeEdges = node.getEdges();
 //					if (nodeEdges != null) {
 //						nodeEdges.stream().forEach(edge -> {
-//							prepareEdgeOffsets(edge, nDistance);
+//							prepareEdgeOffsets(edge, nDuration);
 //							output.addEdge(edge);
 //						});
 //					}
@@ -457,7 +459,7 @@ public abstract class AbstractAlgorithm {
 		}
 
 		final INode sourceNode = nConnection.getSourceNode();
-		final float sourceDistance = sourceNode.getDistance();
+		final float sourceDuration = sourceNode.getDuration();
 		final Map<INode, Collection<IEdge>> targets = nConnection.getTargetEntries();
 		final Collection<Entry<INode, Collection<IEdge>>> entries = targets.entrySet();
 
@@ -466,14 +468,15 @@ public abstract class AbstractAlgorithm {
 			final INode targetNode = entry.getKey();
 			final Collection<IEdge> targetEdges = entry.getValue();
 			for (final IEdge edge : targetEdges) {
-				final float newDistance = sourceDistance + (edge.getLength() / cWalkingSpeed);
-				if (targetNode.isNewMinDistance(newDistance)) {
+				final float eLength = edge.getLength();
+				final float newDuration = sourceDuration + (eLength / getMaxSpeed(edge));
+				if (targetNode.isNewMinDuration(newDuration)) {
 					if (cOutputOnlyExpandedEdges) {
 						output.addEdge(edge);
 					}
 
-					if (isWithinDuration(newDistance)) {
-						targetNode.setDistance(newDistance);
+					if (isWithinDuration(newDuration)) {
+						targetNode.setDuration(newDuration);
 						results.add(targetNode);
 					}
 				}
@@ -501,8 +504,8 @@ public abstract class AbstractAlgorithm {
 		for (final Entry<INode, Long> entry : entries) {
 			final INode targetNode = entry.getKey();
 			final long newDistance = entry.getValue();
-			if (targetNode.isNewMinDistance(newDistance) && isWithinDuration(newDistance)) {
-				targetNode.setDistance(newDistance);
+			if (targetNode.isNewMinDuration(newDistance) && isWithinDuration(newDistance)) {
+				targetNode.setDuration(newDistance);
 				results.add(targetNode);
 			}
 		}
@@ -527,19 +530,28 @@ public abstract class AbstractAlgorithm {
 		}
 	}
 
+	private float getMaxSpeed(final IEdge edge) {
+		if (cLimitType == LimitType.DISTANCE) {
+			return 1.0f;
+		}
+
+//		return Math.min(cWalkingSpeed, edge.getMaxSpeed());
+		return cWalkingSpeed;
+	}
+
 	/**
 	 * Gets valid service ids (from the calendar table) for a given dateTime.
 	 *
 	 * @return returns a set of date codes
 	 */
 	private Collection<ServiceCalendar> getServiceIds() {
-		if (cDuration == null) {
+		if (cLimitType != LimitType.DURATION) {
 			return null;
 		}
 
 		final LocalDateTime dateTime = config.getDateTime();
-		final LocalDate endDate = (cIsIncoming) ? dateTime.toLocalDate() : dateTime.plus(cDuration, ChronoUnit.SECONDS).toLocalDate();
-		final LocalDate startDate = (cIsIncoming) ? dateTime.minus(cDuration, ChronoUnit.SECONDS).toLocalDate() : dateTime.toLocalDate();
+		final LocalDate endDate = (cIsIncoming) ? dateTime.toLocalDate() : dateTime.plus(cLimit, ChronoUnit.SECONDS).toLocalDate();
+		final LocalDate startDate = (cIsIncoming) ? dateTime.minus(cLimit, ChronoUnit.SECONDS).toLocalDate() : dateTime.toLocalDate();
 
 		return database.getCalendar(startDate, endDate);
 	}
@@ -587,27 +599,33 @@ public abstract class AbstractAlgorithm {
 	}
 
 	private void initStartLocations(final Collection<Location> locations) {
-		final double walkingDuration = (cDuration == null) ? 0 : cDuration * cWalkingSpeed;
 		for (final Location location : locations) {
 			final IEdge edge = getSingleEdge(location.getEdgeId());
+			// time to first edge is always computed using cWalkingSpeed only (no max speed)
+			final float durationToEdge = (cLimitType == LimitType.DURATION) ? (float) (cWalkingSpeed * location.getDistance()) : location.getDistance();
+			// all other times are computed regarding a max speed
+			// (at least if cLimitType is set to DURATION, which is check inside the function getMaxSpeed)
+			final double maxDistance = (cLimitType == LimitType.DURATION) ? cLimit * getMaxSpeed(edge) : 0.0d;
+			final float eLength = edge.getLength();
 			final float locationOffset = location.getOffset();
-			final float newDistance;
+			final float newDuration;
+			final boolean usesEdgeOffset = (locationOffset > 0 && locationOffset < eLength);
 			final INode node;
 
 			if (cIsIncoming) {
 				node = getNode(edge.getStartNode());
-				newDistance = locationOffset / cWalkingSpeed;
-				if (locationOffset > 0 && locationOffset < edge.getLength()) {
-					edge.setStartOffset((float) Math.max(0, locationOffset - walkingDuration));
+				newDuration = locationOffset / getMaxSpeed(edge) + durationToEdge;
+				if (usesEdgeOffset) {
+					edge.setStartOffset((float) Math.max(0, locationOffset - maxDistance));
 					edge.setEndOffset(locationOffset);
 				}
 			} else {
-				final float d = edge.getLength() - locationOffset;
+				final float d = eLength - locationOffset;
 				node = getNode(edge.getEndNode());
-				newDistance = d / cWalkingSpeed;
-				if (locationOffset > 0 && locationOffset < edge.getLength()) {
+				newDuration = d / getMaxSpeed(edge) + durationToEdge;
+				if (usesEdgeOffset) {
 					edge.setStartOffset(locationOffset);
-					edge.setEndOffset((float) Math.min(edge.getLength(), Math.abs(d - walkingDuration)));
+					edge.setEndOffset((float) Math.min(eLength, Math.abs(d - maxDistance)));
 				}
 			}
 
@@ -616,21 +634,25 @@ public abstract class AbstractAlgorithm {
 				output.addEdge(edge);
 			}
 
-			if (node.isNewMinDistance(newDistance)) {
+			if (node.isNewMinDuration(newDuration)) {
 				if (cOutputOnlyExpandedEdges) {
 					output.addEdge(edge);
 				}
 
-				if (isWithinDuration(newDistance)) {
-					node.setDistance(newDistance);
+				if (isWithinDuration(newDuration)) {
+					node.setDuration(newDuration);
 					updateNodeQueue(node);
 				}
 			}
 		}
 	}
 
-	private boolean isWithinDuration(final float distance) {
-		return cDuration == null || distance <= cDuration;
+	private boolean isWithinDuration(final float duration) {
+		if (cLimitType != LimitType.DURATION) {
+			return true;
+		}
+
+		return duration <= cLimit;
 	}
 
 	private NodeConnection prepareStreetEdges(final INode node, final Collection<IEdge> edges) {
@@ -640,7 +662,7 @@ public abstract class AbstractAlgorithm {
 		}
 
 		edges.stream().filter(predicateContinuous).forEachOrdered(edge -> {
-			prepareEdgeOffsets(edge, node.getDistance());
+			prepareEdgeOffsets(edge, node.getDuration());
 			addTargetNode(nConnection, edge.getOppositeOf(node.getId()), edge);
 		});
 
@@ -664,17 +686,17 @@ public abstract class AbstractAlgorithm {
 		return nConnection;
 	}
 
-	private void prepareEdgeOffsets(final IEdge edge, final double nodeDistance) {
+	private void prepareEdgeOffsets(final IEdge edge, final double nodeDuration) {
 		final float eLength = edge.getLength();
-		final double maxDistance = (cDuration == null) ? 0 : (cDuration - nodeDistance);
+		final double maxDuration = cLimit - nodeDuration;
 
 		if (cIsIncoming) {
-			edge.setStartOffset((float) Math.max(0, eLength - maxDistance * cWalkingSpeed));
+			edge.setStartOffset((float) Math.max(0, eLength - maxDuration * getMaxSpeed(edge)));
 			edge.setEndOffset(eLength);
 		} else {
-			final double remainingDistance = Math.max(0, maxDistance);
-			edge.setStartOffset(0);
-			edge.setEndOffset((float) Math.min(eLength, remainingDistance * cWalkingSpeed));
+			final double remainingDuration = Math.max(0, maxDuration);
+			edge.setStartOffset(0f);
+			edge.setEndOffset((float) Math.min(eLength, remainingDuration * getMaxSpeed(edge)));
 		}
 	}
 
diff --git a/src/main/java/it/unibz/inf/isochrone/algorithm/MineR.java b/src/main/java/it/unibz/inf/isochrone/algorithm/MineR.java
index 69add434..6a22b04d 100644
--- a/src/main/java/it/unibz/inf/isochrone/algorithm/MineR.java
+++ b/src/main/java/it/unibz/inf/isochrone/algorithm/MineR.java
@@ -14,6 +14,7 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import it.unibz.inf.isochrone.config.ConfigAlgorithm;
+import it.unibz.inf.isochrone.config.ConfigAlgorithm.LimitType;
 import it.unibz.inf.isochrone.config.ConfigDataset;
 import it.unibz.inf.isochrone.db.Database;
 import it.unibz.inf.isochrone.network.IEdge;
@@ -31,7 +32,7 @@ public class MineR extends Mine {
 	/** Memory that is used at max by nodes (other memory is reserved for route collections, edges, ...). */
 	private static final float MAX_MEMORY_PERCENT = 0.3f;
 	/** Flag indicating if the loadable nodes is limited by algorithm-configuration value "cMaxNodes" (meaning that at max these node count will be in memory). */
-	private static final boolean NODE_LIMIT_BY_MAX_NODES = false;
+	private static final boolean LIMIT_NODE_COUNT_BY_CONFIG = false;
 	private final Map<Integer, Collection<IEdge>> loadedEdges;
 	private final Set<Seed> loadingSeeds;
 	/**
@@ -54,12 +55,12 @@ public class MineR extends Mine {
 			throw new AlgorithmException(getClass().getSimpleName() + " does not work without a density database table!");
 		}
 
-		if (NODE_LIMIT_BY_MAX_NODES) {
+		if (LIMIT_NODE_COUNT_BY_CONFIG) {
 			// calculate node limit from given parameter and cMaxNodes (set in super-constructor)
 			Long tmpLimit = null;
 			tmpLimit = (nodeLimit == null || nodeLimit <= 0) ? dsConfig.getAlgorithmNodeLimit() : nodeLimit;
-			if (cMaxNodes != null) {
-				tmpLimit = (tmpLimit == null) ? cMaxNodes : Math.min(tmpLimit, cMaxNodes);
+			if (cLimitType == LimitType.NODE_COUNT) {
+				tmpLimit = (tmpLimit == null) ? cLimit : Math.min(tmpLimit, cLimit);
 			}
 			this.nodeLimit = tmpLimit;
 		} else {
@@ -193,11 +194,11 @@ public class MineR extends Mine {
 	 * @return the remaining distance
 	 */
 	private double getRemainingDistance(final INode node) {
-		if (cDuration == null) {
+		if (cLimitType != LimitType.DURATION) {
 			return Double.MAX_VALUE;
 		}
 
-		return (cDuration - node.getDistance()) * cWalkingSpeed;
+		return (cLimit - node.getDuration()) * cWalkingSpeed;
 	}
 
 	/**
diff --git a/src/main/java/it/unibz/inf/isochrone/config/ConfigAlgorithm.java b/src/main/java/it/unibz/inf/isochrone/config/ConfigAlgorithm.java
index 7caacd29..b1acab72 100644
--- a/src/main/java/it/unibz/inf/isochrone/config/ConfigAlgorithm.java
+++ b/src/main/java/it/unibz/inf/isochrone/config/ConfigAlgorithm.java
@@ -21,16 +21,17 @@ import javax.annotation.concurrent.Immutable;
  */
 @Immutable
 public final class ConfigAlgorithm implements Serializable {
+	public enum LimitType { DISTANCE, DURATION, NODE_COUNT };
 	public enum Direction { INCOMING, OUTGOING }
-	public enum Mode { UNIMODAL, MULTIMODAL };
+	public enum Mode { ISODISTANCE, ISOCHRONE_UNIMODAL, ISOCHRONE_MULTIMODAL };
 
 	private static final long serialVersionUID = 701390363941349843L;
 	private final boolean activateStatistics;
 	private final LocalDateTime dateTime;
 	private final Direction direction;
-	private final Long duration;
+	private final long limit;
+	private final LimitType limitType;
 	private final Mode mode;
-	private final Long nodes;
 	/**
 	 * Flag indicating if the edges stored in the output represent reachable nodes (default; flag-value = false) or only expanded ones.
 	 * The difference is that reachable edges are for example stored in both directions and for non-minimal distance ways, while
@@ -54,9 +55,9 @@ public final class ConfigAlgorithm implements Serializable {
 		private boolean activateStatistics = false;
 		private Direction direction = Direction.INCOMING;
 		private LocalDateTime dateTime = LocalDateTime.of(2015, Month.AUGUST, 17, 15, 0, 0, 0);
-		private Long duration = 3600L;
-		private Mode mode = Mode.MULTIMODAL;
-		private Long nodes = null;
+		private LimitType limitType = LimitType.DURATION;
+		private Long limit = 3600L;
+		private Mode mode = Mode.ISOCHRONE_MULTIMODAL;
 		private boolean outputOnlyExpandedEdges = false;
 		private Integer queryPointSRID = null;
 		private boolean useCache = false;
@@ -77,14 +78,8 @@ public final class ConfigAlgorithm implements Serializable {
 		}
 
 		public ConfigAlgorithm build() {
-			if (duration == null && nodes == null) {
-				throw new IllegalStateException("Either duration or nodes (or both) has to be set!");
-			}
-			if (duration != null && duration < 0) {
-				throw new IllegalStateException("Duration value has to be a positive value!");
-			}
-			if (nodes != null && nodes < 0) {
-				throw new IllegalStateException("Nodes value has to be a positive value!");
+			if (dateTime == null && mode == Mode.ISOCHRONE_MULTIMODAL) {
+				throw new IllegalStateException("Can not calculate multimodal isochrone without dateTime set!");
 			}
 
 			return new ConfigAlgorithm(this);
@@ -111,13 +106,16 @@ public final class ConfigAlgorithm implements Serializable {
 			return this;
 		}
 
-		public ConfigAlgorithmBuilder duration(final Long duration) {
-			this.duration = duration;
-			return this;
-		}
+		public ConfigAlgorithmBuilder limit(final LimitType limitType, final long limit) {
+			if (limitType == null) {
+				throw new NullPointerException("LimitType can not be set to null!");
+			}
+			if (limit <= 0) {
+				throw new IllegalStateException("Limit value has to be a positive value!");
+			}
 
-		public ConfigAlgorithmBuilder nodes(final Long nodes) {
-			this.nodes = nodes;
+			this.limitType = limitType;
+			this.limit = limit;
 			return this;
 		}
 
@@ -166,9 +164,9 @@ public final class ConfigAlgorithm implements Serializable {
 		this.activateStatistics = builder.activateStatistics;
 		this.dateTime = builder.dateTime;
 		this.direction = builder.direction;
-		this.duration = builder.duration;
+		this.limit = builder.limit;
+		this.limitType = builder.limitType;
 		this.mode = builder.mode;
-		this.nodes = builder.nodes;
 		this.outputOnlyExpandedEdges = builder.outputOnlyExpandedEdges;
 		this.queryPointSRID = builder.queryPointSRID;
 		this.useCache = builder.useCache;
@@ -176,17 +174,17 @@ public final class ConfigAlgorithm implements Serializable {
 		this.queryPoints = builder.queryPoints;
 		this.walkingSpeed = builder.walkingSpeed;
 
-		if (dateTime == null) {
-			toTime = null;
-			fromTime = null;
-		} else {
+		if (dateTime != null && limitType == LimitType.DURATION) {
 			if (getDirection() == Direction.INCOMING) {
 				toTime = getSecondsTillMidnight(dateTime);
-				fromTime = (duration == null) ? null : Math.max(0, toTime - duration);
+				fromTime = Math.max(0, toTime - limit);
 			} else {
 				fromTime = getSecondsTillMidnight(dateTime);
-				toTime = (duration == null) ? null : fromTime + duration;
+				toTime = fromTime + limit;
 			}
+		} else {
+			toTime = null;
+			fromTime = null;
 		}
 	}
 
@@ -200,16 +198,16 @@ public final class ConfigAlgorithm implements Serializable {
 		return direction;
 	}
 
-	public Long getDuration() {
-		return duration;
+	public long getLimit() {
+		return limit;
 	}
 
-	public Mode getMode() {
-		return mode;
+	public LimitType getLimitType() {
+		return limitType;
 	}
 
-	public Long getNodes() {
-		return nodes;
+	public Mode getMode() {
+		return mode;
 	}
 
 	public Collection<Point2D.Double> getQueryPoints() {
@@ -249,7 +247,7 @@ public final class ConfigAlgorithm implements Serializable {
 	}
 
 	public boolean isMultimodal() {
-		return mode == Mode.MULTIMODAL;
+		return mode == Mode.ISOCHRONE_MULTIMODAL;
 	}
 
 	public boolean isStatisticsActivated() {
@@ -283,13 +281,6 @@ public final class ConfigAlgorithm implements Serializable {
 		if (direction != other.direction) {
 			return false;
 		}
-		if (duration == null) {
-			if (other.duration != null) {
-				return false;
-			}
-		} else if (!duration.equals(other.duration)) {
-			return false;
-		}
 		if (fromTime == null) {
 			if (other.fromTime != null) {
 				return false;
@@ -297,14 +288,13 @@ public final class ConfigAlgorithm implements Serializable {
 		} else if (!fromTime.equals(other.fromTime)) {
 			return false;
 		}
-		if (mode != other.mode) {
+		if (limit != other.limit) {
 			return false;
 		}
-		if (nodes == null) {
-			if (other.nodes != null) {
-				return false;
-			}
-		} else if (!nodes.equals(other.nodes)) {
+		if (limitType != other.limitType) {
+			return false;
+		}
+		if (mode != other.mode) {
 			return false;
 		}
 		if (outputOnlyExpandedEdges != other.outputOnlyExpandedEdges) {
@@ -356,10 +346,10 @@ public final class ConfigAlgorithm implements Serializable {
 		result = prime * result + (activateStatistics ? 1231 : 1237);
 		result = prime * result + ((dateTime == null) ? 0 : dateTime.hashCode());
 		result = prime * result + ((direction == null) ? 0 : direction.hashCode());
-		result = prime * result + ((duration == null) ? 0 : duration.hashCode());
 		result = prime * result + ((fromTime == null) ? 0 : fromTime.hashCode());
+		result = prime * result + (int) (limit ^ (limit >>> 32));
+		result = prime * result + ((limitType == null) ? 0 : limitType.hashCode());
 		result = prime * result + ((mode == null) ? 0 : mode.hashCode());
-		result = prime * result + ((nodes == null) ? 0 : nodes.hashCode());
 		result = prime * result + (outputOnlyExpandedEdges ? 1231 : 1237);
 		result = prime * result + ((queryPointSRID == null) ? 0 : queryPointSRID.hashCode());
 		result = prime * result + ((queryPoints == null) ? 0 : queryPoints.hashCode());
@@ -376,12 +366,12 @@ public final class ConfigAlgorithm implements Serializable {
 	public String toString() {
 		return "ConfigAlgorithm [activateStatistics=" + activateStatistics
 			+ ", direction=" + direction
-			+ ", date=" + dateTime.toString()
-			+ ", maxNodes=" + nodes
+			+ ", date=" + dateTime
+			+ ", limit=" + limit
+			+ ", limitType=" + limitType
 			+ ", mode=" + mode
 			+ ", walkingSpeed=" + walkingSpeed
-			+ ", queryPoints=[" + queryPoints.stream().map(p -> "[" + p.getX() + "," + p.getY() + "]").collect(Collectors.joining(", ")) + "]"
-			+ ", duration=" + duration
+			+ ", queryPoints=[" + ((queryPoints == null) ? "null" : queryPoints.stream().map(p -> "[" + p.getX() + "," + p.getY() + "]").collect(Collectors.joining(", "))) + "]"
 			+ "]";
 	}
 
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 feaa9c8f..19836a32 100644
--- a/src/main/java/it/unibz/inf/isochrone/db/Database.java
+++ b/src/main/java/it/unibz/inf/isochrone/db/Database.java
@@ -101,8 +101,8 @@ public class Database implements AutoCloseable {
 
 	protected Database(final ConfigDataset config, final Mode mode, final Direction direction, final boolean fetchGeometries) {
 		this.config = config;
-		this.isIncoming = direction == Direction.INCOMING;
-		this.isMultimodal = mode == Mode.MULTIMODAL;
+		this.isIncoming = (direction == Direction.INCOMING);
+		this.isMultimodal = (mode == Mode.ISOCHRONE_MULTIMODAL);
 		this.mode = (isMultimodal) ? null : IEdge.Mode.STREET;
 		this.seedReader = new WKBReader(new GeometryFactory(new PrecisionModel(), config.getServerSRID()));
 		this.wkbReader = (fetchGeometries) ? new WKBReader(new GeometryFactory(new PrecisionModel(), config.getServerSRID())) : null;
@@ -306,7 +306,7 @@ public class Database implements AutoCloseable {
 
 			try {
 				if (isIncoming) {
-					e = getLatestDepartureTime(targetNode.getId(), node.getId(), arrRoutes, arrServiceCalendar, (fromTime == null) ? null : fromTime, (long) Math.round(toTime - node.getDistance()));
+					e = getLatestDepartureTime(targetNode.getId(), node.getId(), arrRoutes, arrServiceCalendar, (fromTime == null) ? null : fromTime, (long) Math.round(toTime - node.getDuration()));
 					try (final ResultSet rs = e.getValue().executeQuery()) {
 						while (rs.next()) {
 							final long departureTime = rs.getLong("time_d");
@@ -332,7 +332,7 @@ public class Database implements AutoCloseable {
 						}
 					}
 				} else {
-					e = getEarliestArrivalTime(node.getId(), targetNode.getId(), arrRoutes, arrServiceCalendar, (fromTime == null) ? null : (long) Math.round(fromTime + node.getDistance()), toTime);
+					e = getEarliestArrivalTime(node.getId(), targetNode.getId(), arrRoutes, arrServiceCalendar, (fromTime == null) ? null : (long) Math.round(fromTime + node.getDuration()), toTime);
 					try (final ResultSet rs = e.getValue().executeQuery()) {
 						while (rs.next()) {
 							final long departureTime = rs.getLong("time_d");
@@ -519,9 +519,10 @@ public class Database implements AutoCloseable {
 
 			rSet = statement.executeQuery();
 			while (rSet.next()) {
-				final int edgeId = rSet.getInt("ID");
-				final float offset = rSet.getFloat("OFFSET");
-				locations.add(new Location(edgeId, offset));
+				final int edgeId = rSet.getInt("id");
+				final float offset = rSet.getFloat("offset");
+				final float distance = rSet.getFloat("distance");
+				locations.add(new Location(edgeId, offset, distance));
 			}
 		} catch (final SQLException e) {
 			handleSqlException(e);
diff --git a/src/main/java/it/unibz/inf/isochrone/db/DbQueryPostgres.java b/src/main/java/it/unibz/inf/isochrone/db/DbQueryPostgres.java
index 35d15e26..33e7c159 100644
--- a/src/main/java/it/unibz/inf/isochrone/db/DbQueryPostgres.java
+++ b/src/main/java/it/unibz/inf/isochrone/db/DbQueryPostgres.java
@@ -102,8 +102,8 @@ class DbQueryPostgres extends AbstractDbQuery {
 			+ " INNER JOIN " + configNodeTileInformation + " n ON (n.id = " + (isIncoming ? "target" : "source") + " AND n.ZOOM = ?)"
 			+ " WHERE n.x = ? AND n.y = ?";
 
-		final String nearestEdge = "SELECT e.id AS id, ST_Line_Locate_Point(e.geo::geometry, d.point_geometry::geometry) * e.length AS offset, "
-			+ " ST_Distance(e.geo, d.point_geometry) AS distance"
+		final String nearestEdge = "SELECT e.id AS id, ST_Line_Locate_Point(e.geo::geometry, d.point_geometry::geometry) * e.length AS offset,"
+			+ " ST_Distance(" + DbUtils.getGeography("e.geo", serverSRID) + ", " + DbUtils.getGeography("d.point_geometry", serverSRID) + ") AS distance"
 			+ " FROM %1$s E, ("
 			+ "     SELECT p.geometry AS point_geometry, e.geo <-> p.geometry AS upperDistBound"
 			+ "     FROM %1$s AS E, ("
@@ -119,7 +119,7 @@ class DbQueryPostgres extends AbstractDbQuery {
 			+ " FROM " + configNode
 			+ " WHERE id=?";
 
-		final String projectToLocation = "SELECT ne.id, ne.offset FROM (" + nearestEdge + ") AS ne"
+		final String projectToLocation = "SELECT ne.id, ne.offset, ne.distance FROM (" + nearestEdge + ") AS ne"
 			+ " WHERE ne.distance = (SELECT MIN(distance) FROM (" + nearestEdge + ") AS ne)"
 			+ " ORDER BY ne.offset ASC";
 
diff --git a/src/main/java/it/unibz/inf/isochrone/db/DbQuerySqlite.java b/src/main/java/it/unibz/inf/isochrone/db/DbQuerySqlite.java
index c810a77a..ada00f3a 100644
--- a/src/main/java/it/unibz/inf/isochrone/db/DbQuerySqlite.java
+++ b/src/main/java/it/unibz/inf/isochrone/db/DbQuerySqlite.java
@@ -119,7 +119,8 @@ class DbQuerySqlite extends AbstractDbQuery {
 //			+ " WHERE e.mode=%<s AND PtDistWithin(e.geo, D.point_geometry, D.min_distance)"
 //			+ " ORDER BY ST_Distance(e.geo, p.geometry) ASC, ST_Line_Locate_Point(e.geo, D.point_geometry) * e.length ASC"; // order by offset
 
-		final String projectToLocation = "SELECT e.id AS id, ST_Line_Locate_Point(e.geo, e.point_geometry) * e.length AS offset"
+		final String projectToLocation = "SELECT e.id AS id, ST_Line_Locate_Point(e.geo, e.point_geometry) * e.length AS offset,"
+			+ " ST_Distance(" + DbUtils.getGeometry("e.geo", serverSRID, 4326) + ", " + DbUtils.getGeometry("p.point_geometry", serverSRID, 4326) + ", true) AS distance"
 			+ " FROM ("
 			+ "     SELECT e.id, e.geo, e.length, p.point_geometry FROM %s AS e, ("
 			+ "         SELECT ST_Transform(ST_PointFromText(?, ?), " + serverSRID + ") AS point_geometry"
diff --git a/src/main/java/it/unibz/inf/isochrone/network/IEdge.java b/src/main/java/it/unibz/inf/isochrone/network/IEdge.java
index 9777be5b..8f885d7a 100644
--- a/src/main/java/it/unibz/inf/isochrone/network/IEdge.java
+++ b/src/main/java/it/unibz/inf/isochrone/network/IEdge.java
@@ -30,16 +30,18 @@ public interface IEdge extends INetworkEntity {
 		}
 	};
 
+	// Getter
 	int getEndNode();
 	float getEndOffset();
 	@Override LineString getGeometry();
 	float getLength();
 	Mode getMode();
+	int getOppositeOf(int nodeId);
 	Route getRoute();
 	int getStartNode();
 	float getStartOffset();
 	boolean isFullEdge();
-	int getOppositeOf(int nodeId);
+	// Setter
 	void setEndOffset(float endOffset);
 	void setStartOffset(float startOffset);
 }
diff --git a/src/main/java/it/unibz/inf/isochrone/network/INode.java b/src/main/java/it/unibz/inf/isochrone/network/INode.java
index 811260cb..c2029605 100644
--- a/src/main/java/it/unibz/inf/isochrone/network/INode.java
+++ b/src/main/java/it/unibz/inf/isochrone/network/INode.java
@@ -11,7 +11,7 @@ public interface INode extends INetworkEntity {
 	int MEMORY_SIZE = 72;
 
 	Route getCheapestReachedRoute();
-	float getDistance();
+	float getDuration();
 	short getEdgeCout();
 	@Override Point getGeometry();
 	Map<Route, Schedule> getRouteSchedules();
@@ -19,12 +19,12 @@ public interface INode extends INetworkEntity {
 	boolean isChangedSinceLastAcknowledge();
 	boolean isClosed();
 	boolean isExpired();
-	boolean isNewMinDistance(final float newDistance);
+	boolean isNewMinDuration(final float duration);
 	boolean isOpen();
 
 	void setSchedule(final Route route, final Schedule s);
 	void setCheapestReachedRoute(final Route cheapestReachedRoute);
-	void setDistance(final float distance);
+	void setDuration(final float distance);
 	void setGeometry(final Point geometry);
 
 	void acknowledge();
diff --git a/src/main/java/it/unibz/inf/isochrone/network/Location.java b/src/main/java/it/unibz/inf/isochrone/network/Location.java
index 0a67cd7a..fd553696 100644
--- a/src/main/java/it/unibz/inf/isochrone/network/Location.java
+++ b/src/main/java/it/unibz/inf/isochrone/network/Location.java
@@ -15,25 +15,35 @@ public class Location implements Serializable {
 	private static final long serialVersionUID = -5904462447962312476L;
 	private final int edgeId;
 	private final float offset;
+	private final float distance;
 
 	// Constructor
 
-	public Location(final int edgeId, final float offset) {
+	public Location(final int edgeId, final float offset, final float distance) {
 		this.edgeId = edgeId;
 		this.offset = offset;
+		this.distance = distance;
 	}
 
 	// Getter
 
 	/**
-	 * @return the edgeId belonging to the location
+	 * @return the distance from the location to the edge.
+	 * The exact point on the edge the distance is measured to is specified by the offset.
+	 */
+	public float getDistance() {
+		return distance;
+	}
+
+	/**
+	 * @return the id of the edge the location belongs to
 	 */
 	public int getEdgeId() {
 		return edgeId;
 	}
 
 	/**
-	 * @return the offset in respect to the source node
+	 * @return the offset of the location in respect to the source node (between 0 and edge.getLength())
 	 */
 	public float getOffset() {
 		return offset;
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 95a13d64..a8fa776c 100644
--- a/src/main/java/it/unibz/inf/isochrone/network/Node.java
+++ b/src/main/java/it/unibz/inf/isochrone/network/Node.java
@@ -18,7 +18,7 @@ public class Node extends NetworkEntity implements INode {
 	private boolean changedSinceAcknowledge;
 	private Route cheapestReachedRoute;
 	private boolean closed;
-	private float distance;
+	private float duration;
 	private Point geometry;
 	private Map<Route, Schedule> routes;
 
@@ -29,7 +29,7 @@ public class Node extends NetworkEntity implements INode {
 
 		this.changedSinceAcknowledge = true;
 		this.closed = false;
-		this.distance = Float.POSITIVE_INFINITY;
+		this.duration = Float.POSITIVE_INFINITY;
 		this.nrEdges = nrEdges;
 	}
 
@@ -41,8 +41,8 @@ public class Node extends NetworkEntity implements INode {
 	}
 
 	@Override
-	public float getDistance() {
-		return distance;
+	public float getDuration() {
+		return duration;
 	}
 
 	@Override
@@ -92,8 +92,8 @@ public class Node extends NetworkEntity implements INode {
 	}
 
 	@Override
-	public boolean isNewMinDistance(final float newDistance) {
-		return newDistance < getDistance();
+	public boolean isNewMinDuration(final float newDistance) {
+		return newDistance < getDuration();
 	}
 
 	@Override
@@ -119,8 +119,8 @@ public class Node extends NetworkEntity implements INode {
 	}
 
 	@Override
-	public void setDistance(final float distance) {
-		this.distance = distance;
+	public void setDuration(final float duration) {
+		this.duration = duration;
 	}
 
 	@Override
@@ -150,10 +150,10 @@ public class Node extends NetworkEntity implements INode {
 		}
 
 		final INode n = (INode) other;
-		if (getDistance() < n.getDistance()) {
+		if (getDuration() < n.getDuration()) {
 			return -1;
 		}
-		if (getDistance() > n.getDistance()) {
+		if (getDuration() > n.getDuration()) {
 			return 1;
 		}
 
@@ -181,7 +181,7 @@ public class Node extends NetworkEntity implements INode {
 
 	@Override
 	public String toString() {
-		return "Node [id=" + getId() + ", distance=" + distance + "]";
+		return "Node [id=" + getId() + ", duration=" + duration + "]";
 	}
 
 	@Override
diff --git a/src/main/java/it/unibz/inf/isochrone/network/NodeWired.java b/src/main/java/it/unibz/inf/isochrone/network/NodeWired.java
index 4118361a..e0972b1a 100644
--- a/src/main/java/it/unibz/inf/isochrone/network/NodeWired.java
+++ b/src/main/java/it/unibz/inf/isochrone/network/NodeWired.java
@@ -43,7 +43,7 @@ public class NodeWired extends Node {
 
 	@Override
 	public String toString() {
-		return "NodeWired [id=" + getId() + ", distance=" + getDistance() + ", nrEdges=" + getEdgeCount() + "]";
+		return "NodeWired [id=" + getId() + ", duration=" + getDuration() + ", nrEdges=" + getEdgeCount() + "]";
 	}
 
 
diff --git a/src/performance/java/it/unibz/inf/isochrone/DatasetHelper.java b/src/performance/java/it/unibz/inf/isochrone/DatasetHelper.java
index a354b0f2..91d92f81 100644
--- a/src/performance/java/it/unibz/inf/isochrone/DatasetHelper.java
+++ b/src/performance/java/it/unibz/inf/isochrone/DatasetHelper.java
@@ -82,7 +82,7 @@ public class DatasetHelper {
 			throw new IllegalStateException("Can not create dataset \"" + dsName + "\"");
 		}
 
-		final Mode m = (dsConfig.isMultimodal()) ? Mode.MULTIMODAL : Mode.UNIMODAL;
+		final Mode m = (dsConfig.isMultimodal()) ? Mode.ISOCHRONE_MULTIMODAL : Mode.ISOCHRONE_UNIMODAL;
 		return new Database(dsConfig, m, Direction.INCOMING);
 	}
 
diff --git a/src/performance/java/it/unibz/inf/isochrone/SampleRunner.java b/src/performance/java/it/unibz/inf/isochrone/SampleRunner.java
index 38c23762..a43cf63d 100644
--- a/src/performance/java/it/unibz/inf/isochrone/SampleRunner.java
+++ b/src/performance/java/it/unibz/inf/isochrone/SampleRunner.java
@@ -8,6 +8,7 @@ import java.util.Collections;
 import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -24,6 +25,7 @@ import it.unibz.inf.isochrone.algorithm.AlgorithmStatistics;
 import it.unibz.inf.isochrone.algorithm.AlgorithmStatistics.Statistic;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.ConfigAlgorithmBuilder;
+import it.unibz.inf.isochrone.config.ConfigAlgorithm.LimitType;
 import it.unibz.inf.isochrone.config.ConfigDataset;
 import it.unibz.inf.isochrone.output.MemoryOutput;
 
@@ -256,18 +258,16 @@ public final class SampleRunner {
 				propertyNodeSums.put(algorithm, pn);
 			}
 
-			final Collection<ConfigAlgorithm> algorithmConfigurations = dataset.getValue();
-			for (final ConfigAlgorithm config : algorithmConfigurations) {
+			final Collection<ConfigAlgorithm> configAlgorithms = dataset.getValue();
+			for (final ConfigAlgorithm config : configAlgorithms) {
 				for (final Class<? extends AbstractAlgorithm> algorithm : classes) {
 					final String algortihmName = algorithm.getSimpleName() + nameSuffix;
+					final LimitType limitType = config.getLimitType();
+					final long limitValue = config.getLimit();
+					final String limitTypeName = limitType.name().toLowerCase(Locale.ENGLISH);
+
 					final TestConfiguration testConf = new TestConfiguration(algorithm, dsName, config, params);
-					if (config.getDuration() == null) {
-						logger.info("Executing \"{}\" for dataset \"{}\" (nodes: {})", algortihmName, dsName, config.getNodes());
-					} else if (config.getNodes() == null) {
-						logger.info("Executing \"{}\" for dataset \"{}\" (duration: {})", algortihmName, dsName, config.getDuration());
-					}  else {
-						logger.info("Executing \"{}\" for dataset \"{}\" (duration: {}; nodes: {})", algortihmName, dsName, config.getDuration(), config.getNodes());
-					}
+					logger.info("Executing \"{}\" for dataset \"{}\" ({}: {})", algortihmName, dsName, limitTypeName, limitValue);
 					logger.trace("   testConfiguration is {}", testConf);
 
 					final long startTime = System.currentTimeMillis();
@@ -275,28 +275,22 @@ public final class SampleRunner {
 					final AlgorithmStatistics stats = result.getStatistics();
 					final long runtime = System.currentTimeMillis() - startTime;
 					if (logger.isTraceEnabled()) {
-						if (config.getDuration() == null) {
-							logger.trace("   runtime for nodes \"{}\": {}msec", config.getNodes(), runtime);
-						} else if (config.getNodes() == null) {
-							logger.trace("   runtime for duration \"{}\": {}msec", config.getDuration(), runtime);
-						}  else {
-							logger.trace("   runtime for duration \"{}\" and nodes \"{}\": {}msec", config.getDuration(), config.getNodes(), runtime);
-						}
+						logger.trace("   runtime for {} \"{}\": {}msec", limitTypeName, limitValue, runtime);
 						logger.trace("   nodes (in memory): {}", stats.getNumericStat(Statistic.LOADED_NODES_CURRENT).longValue());
 						logger.trace("   nodes (in output): {}", result.getOutput().getNodes().size());
 						logger.trace("   edges (in output): {}", result.getOutput().getEdges().size());
 					}
 
-					final boolean containsDurationSamples = (config.getDuration() != null);
-					final boolean containsNodeSamples = (config.getNodes() != null);
+					final boolean containsDurationSamples = (limitType == LimitType.DURATION);
+					final boolean containsNodeSamples = (limitType == LimitType.NODE_COUNT);
 					for (final SampleProperty p : sampleProperties) {
 						final Long pValue = (p == SampleProperty.RUNTIME) ? runtime : p.getSamplingInformation(stats);
 						if (containsDurationSamples) {
-							samples.get(SampleType.DURATION).get(p).add(config.getDuration(), algortihmName, dsName, pValue.doubleValue());
+							samples.get(SampleType.DURATION).get(p).add(config.getLimit(), algortihmName, dsName, pValue.doubleValue());
 							propertyDurationSums.get(algorithm).put(p, propertyDurationSums.get(algorithm).get(p) + pValue);
 						}
 						if (containsNodeSamples) {
-							samples.get(SampleType.NODES).get(p).add(config.getNodes(), algortihmName, dsName, pValue.doubleValue());
+							samples.get(SampleType.NODES).get(p).add(config.getLimit(), algortihmName, dsName, pValue.doubleValue());
 							propertyNodeSums.get(algorithm).put(p, propertyNodeSums.get(algorithm).get(p) + pValue);
 						}
 					}
@@ -356,7 +350,7 @@ public final class SampleRunner {
 			for (final Long duration : durationSteps) {
 				try {
 					final ConfigAlgorithmBuilder bClones = builder.clone();
-					dsParams.add(bClones.duration(duration).nodes(null).build());
+					dsParams.add(bClones.limit(LimitType.DURATION, duration).build());
 				} catch (final CloneNotSupportedException e) {
 					// in case of an exception we simply do not add the builder to the dsConfigurations
 				}
@@ -367,7 +361,7 @@ public final class SampleRunner {
 			for (final Long nodes : nodeSteps) {
 				try {
 					final ConfigAlgorithmBuilder bClones = builder.clone();
-					dsParams.add(bClones.duration(null).nodes(nodes).build());
+					dsParams.add(bClones.limit(LimitType.NODE_COUNT, nodes).build());
 				} catch (final CloneNotSupportedException e) {
 					// in case of an exception we simply do not add the builder to the dsConfigurations
 				}
diff --git a/src/performance/java/it/unibz/inf/isochrone/algorithm/QLocationRWTest.java b/src/performance/java/it/unibz/inf/isochrone/algorithm/QLocationRWTest.java
index 6a021b0c..7beb92e2 100644
--- a/src/performance/java/it/unibz/inf/isochrone/algorithm/QLocationRWTest.java
+++ b/src/performance/java/it/unibz/inf/isochrone/algorithm/QLocationRWTest.java
@@ -60,7 +60,7 @@ public class QLocationRWTest {
 		// Constructor
 
 		DatabaseQueryPointRandomizer(final ConfigDataset ds) {
-			super(ds, Mode.MULTIMODAL, Direction.INCOMING);
+			super(ds, Mode.ISOCHRONE_MULTIMODAL, Direction.INCOMING);
 		}
 
 		// Protected methods
diff --git a/src/performance/java/it/unibz/inf/isochrone/db/QueryQuantifierTest.java b/src/performance/java/it/unibz/inf/isochrone/db/QueryQuantifierTest.java
index 6155ebb4..b5c2d68a 100644
--- a/src/performance/java/it/unibz/inf/isochrone/db/QueryQuantifierTest.java
+++ b/src/performance/java/it/unibz/inf/isochrone/db/QueryQuantifierTest.java
@@ -76,17 +76,17 @@ public class QueryQuantifierTest {
 	public void setup() {
 		LOGGER.info("Starting test(s) in class \"{}\"", QueryQuantifierTest.class.getSimpleName());
 		sampleRunnerMMIn = new SampleRunnerBuilder(SAMPLE_PROPERTIES_MM_IN)
-			.addDatasets(ConfigAlgorithmHelper.getAllDatasetsRW(Mode.MULTIMODAL, Direction.INCOMING))
+			.addDatasets(ConfigAlgorithmHelper.getAllDatasetsRW(Mode.ISOCHRONE_MULTIMODAL, Direction.INCOMING))
 			.addMaxDuration(TestConfiguration.DEFAULT_MAX_DURATION)
 			.addMaxNodes(TestConfiguration.DEFAULT_MAX_NODES)
 			.build();
 		sampleRunnerMMOut = new SampleRunnerBuilder(SAMPLE_PROPERTIES_MM_OUT)
-			.addDatasets(ConfigAlgorithmHelper.getAllDatasetsRW(Mode.MULTIMODAL, Direction.OUTGOING))
+			.addDatasets(ConfigAlgorithmHelper.getAllDatasetsRW(Mode.ISOCHRONE_MULTIMODAL, Direction.OUTGOING))
 			.addMaxDuration(TestConfiguration.DEFAULT_MAX_DURATION)
 			.addMaxNodes(TestConfiguration.DEFAULT_MAX_NODES)
 			.build();
 		sampleRunnerUM = new SampleRunnerBuilder(SAMPLE_PROPERTIES_UM)
-			.addDatasets(ConfigAlgorithmHelper.getAllDatasetsRW(Mode.UNIMODAL))
+			.addDatasets(ConfigAlgorithmHelper.getAllDatasetsRW(Mode.ISOCHRONE_UNIMODAL))
 			.addMaxDuration(TestConfiguration.DEFAULT_MAX_DURATION)
 			.addMaxNodes(TestConfiguration.DEFAULT_MAX_NODES)
 			.build();
diff --git a/src/performance/java/it/unibz/inf/isochrone/db/QueryRuntimeConfigurationProvider.java b/src/performance/java/it/unibz/inf/isochrone/db/QueryRuntimeConfigurationProvider.java
index b6e78d1f..a34904c5 100644
--- a/src/performance/java/it/unibz/inf/isochrone/db/QueryRuntimeConfigurationProvider.java
+++ b/src/performance/java/it/unibz/inf/isochrone/db/QueryRuntimeConfigurationProvider.java
@@ -46,8 +46,8 @@ class QueryRuntimeConfigurationProvider implements AutoCloseable {
 		}
 
 		this.dataset = dataset;
-		dbOutgoing = new Database(dataset, Mode.MULTIMODAL, Direction.OUTGOING);
-		dbIncoming = new Database(dataset, Mode.MULTIMODAL, Direction.INCOMING);
+		dbOutgoing = new Database(dataset, Mode.ISOCHRONE_MULTIMODAL, Direction.OUTGOING);
+		dbIncoming = new Database(dataset, Mode.ISOCHRONE_MULTIMODAL, Direction.INCOMING);
 
 		testEdges = new ArrayList<>();
 		testNodes = new ArrayList<>();
diff --git a/src/performance/java/it/unibz/inf/isochrone/db/QueryRuntimeTest.java b/src/performance/java/it/unibz/inf/isochrone/db/QueryRuntimeTest.java
index cc0b585a..374a3e37 100644
--- a/src/performance/java/it/unibz/inf/isochrone/db/QueryRuntimeTest.java
+++ b/src/performance/java/it/unibz/inf/isochrone/db/QueryRuntimeTest.java
@@ -394,7 +394,7 @@ public class QueryRuntimeTest {
 				final INode randomNode = nodes.get(getRandomId(arrNodeKeys, -1));
 				final Point seedCoordinate = dbIncoming.getNodeCoordinates(randomNode.getId());
 				final Point randomNodeCoordinate = dbIncoming.getNodeCoordinates(randomNode.getId());
-				randomNode.setDistance((float) seedCoordinate.distance(randomNodeCoordinate));
+				randomNode.setDuration((float) seedCoordinate.distance(randomNodeCoordinate));
 				intersectionNodes[j] = new Seed(randomNode.getId(), seedCoordinate, getLoadableRadius(randomNode));
 			}
 
@@ -402,7 +402,7 @@ public class QueryRuntimeTest {
 			final INode randomNode = nodes.get(getRandomId(arrNodeKeys, -1));
 			final Point seedCoordinate = dbIncoming.getNodeCoordinates(randomNode.getId());
 			final Point randomNodeCoordinate = dbIncoming.getNodeCoordinates(randomNode.getId());
-			randomNode.setDistance((float) seedCoordinate.distance(randomNodeCoordinate));
+			randomNode.setDuration((float) seedCoordinate.distance(randomNodeCoordinate));
 			final Seed seed = new Seed(randomNode.getId(), seedCoordinate, getLoadableRadius(randomNode));
 
 			final String disjunctAreaString = "ST_Difference("
@@ -431,7 +431,7 @@ public class QueryRuntimeTest {
 			final INode randomNode = nodes.get(getRandomId(arrNodeKeys, -1));
 			final Point seedCoordinate = dbIncoming.getNodeCoordinates(randomNode.getId());
 			final Point randomNodeCoordinate = dbIncoming.getNodeCoordinates(randomNode.getId());
-			randomNode.setDistance((float) seedCoordinate.distance(randomNodeCoordinate));
+			randomNode.setDuration((float) seedCoordinate.distance(randomNodeCoordinate));
 
 			final Seed seed = new Seed(randomNode.getId(), seedCoordinate, getLoadableRadius(randomNode));
 
@@ -796,10 +796,10 @@ public class QueryRuntimeTest {
 	 * @return the radius that should be used for range queries (max == remaining distance; actual value depends on free memory)
 	 */
 	private static double getLoadableRadius(final INode node) {
-		final double r = (DURATION - node.getDistance()) * (WALKING_SPEED / AbstractAlgorithm.CONVERSION_FACTOR_TO_METER_PER_SEC);
+		final double r = (DURATION - node.getDuration()) * (WALKING_SPEED / AbstractAlgorithm.CONVERSION_FACTOR_TO_METER_PER_SEC);
 
 		// loadable radius must be positive... so we return node.getDistance as a minimum
-		return Math.max(r, node.getDistance());
+		return Math.max(r, node.getDuration());
 	}
 
 	private static boolean shouldRuntimeBeAdded(final int i, final ResultSet r, final int rsCount) throws SQLException {
diff --git a/src/test/java/it/unibz/inf/isochrone/AlgorithmRunnerTest.java b/src/test/java/it/unibz/inf/isochrone/AlgorithmRunnerTest.java
index 023a31f9..7ec0c44d 100644
--- a/src/test/java/it/unibz/inf/isochrone/AlgorithmRunnerTest.java
+++ b/src/test/java/it/unibz/inf/isochrone/AlgorithmRunnerTest.java
@@ -37,7 +37,7 @@ public class AlgorithmRunnerTest {
 			"-dt", "2015-08-03T09:00",
 			"-dmax", "600",
 			"-mm", "200",
-			"-m", Mode.MULTIMODAL.name(),
+			"-m", Mode.ISOCHRONE_MULTIMODAL.name(),
 			"-q", "11.3525539631585,46.4975205",
 			"-ws", "4.2"
 		});
@@ -104,7 +104,7 @@ public class AlgorithmRunnerTest {
 	@Test
 	public void testModeParameter() throws Throwable {
 		testRunnerParameter(new String[] {
-			"-m", Mode.UNIMODAL.name()
+			"-m", Mode.ISOCHRONE_UNIMODAL.name()
 		});
 	}
 
diff --git a/src/test/java/it/unibz/inf/isochrone/ConfigAlgorithmHelper.java b/src/test/java/it/unibz/inf/isochrone/ConfigAlgorithmHelper.java
index 40c64893..a69bbdc0 100644
--- a/src/test/java/it/unibz/inf/isochrone/ConfigAlgorithmHelper.java
+++ b/src/test/java/it/unibz/inf/isochrone/ConfigAlgorithmHelper.java
@@ -73,18 +73,18 @@ public final class ConfigAlgorithmHelper {
 			// take care of mode parameter
 			final ConfigDataset dsConfig = ConfigDataset.getInstance(datasetName);
 			final boolean isUnimodalDataset = !dsConfig.isMultimodal();
-			if (mode == Mode.UNIMODAL) {
-				builder.mode(Mode.UNIMODAL);
-			} else if (mode == Mode.MULTIMODAL) {
+			if (mode == Mode.ISOCHRONE_UNIMODAL) {
+				builder.mode(Mode.ISOCHRONE_UNIMODAL);
+			} else if (mode == Mode.ISOCHRONE_MULTIMODAL) {
 				if (isUnimodalDataset) {
 					// multimodality for unimodal dataset requested -> skip dataset
 					continue;
 				}
 
-				builder.mode(Mode.MULTIMODAL);
+				builder.mode(Mode.ISOCHRONE_MULTIMODAL);
 			} else if (isUnimodalDataset) {
 				// auto mode (if mode parameter is null) -> multimodal for datasets supporting it, unimodal otherwise
-				builder.mode(Mode.UNIMODAL);
+				builder.mode(Mode.ISOCHRONE_UNIMODAL);
 			}
 			// take care of direction parameter
 			if (direction != null) {
@@ -118,7 +118,7 @@ public final class ConfigAlgorithmHelper {
 		final int gCenter = (gLevel / 2) * length;
 		final int gx = Math.max(0, gCenter - (length * levelOffset));
 		final int gy = gCenter;
-		final ConfigAlgorithmBuilder configGrid = new ConfigAlgorithmBuilder(new Point2D.Double(gx, gy)).activateStatistics().queryPointSRID(SRID_SYNTHETIC).mode(Mode.UNIMODAL);
+		final ConfigAlgorithmBuilder configGrid = new ConfigAlgorithmBuilder(new Point2D.Double(gx, gy)).activateStatistics().queryPointSRID(SRID_SYNTHETIC).mode(Mode.ISOCHRONE_UNIMODAL);
 		builderMap.put("grid_s" + gLevel, configGrid);
 
 		// spider network
@@ -126,7 +126,7 @@ public final class ConfigAlgorithmHelper {
 		final int sCenter = 0;
 		final int sx = Math.max(-1 * sLevel * length, sCenter - (length * levelOffset));
 		final int sy = sCenter;
-		final ConfigAlgorithmBuilder configSpider = new ConfigAlgorithmBuilder(new Point2D.Double(sx, sy)).activateStatistics().queryPointSRID(SRID_SYNTHETIC).mode(Mode.UNIMODAL);
+		final ConfigAlgorithmBuilder configSpider = new ConfigAlgorithmBuilder(new Point2D.Double(sx, sy)).activateStatistics().queryPointSRID(SRID_SYNTHETIC).mode(Mode.ISOCHRONE_UNIMODAL);
 		builderMap.put("spider_l" + sLevel, configSpider);
 
 		final String strSkipped = SystemHelper.getProperty(PROPERTY_SKIP_DATASETS);
diff --git a/src/test/java/it/unibz/inf/isochrone/TestConfiguration.java b/src/test/java/it/unibz/inf/isochrone/TestConfiguration.java
index 184a268d..e323048f 100644
--- a/src/test/java/it/unibz/inf/isochrone/TestConfiguration.java
+++ b/src/test/java/it/unibz/inf/isochrone/TestConfiguration.java
@@ -92,7 +92,7 @@ public class TestConfiguration {
 
 	@Override
 	public String toString() {
-		return "TestConfiguration [algorithmName=" + algorithmName + ", dataset=" + datasetName + ", duration=" + algorithmConfig.getDuration() + "]";
+		return "TestConfiguration [algorithmName=" + algorithmName + ", dataset=" + datasetName + ", limit=" + algorithmConfig.getLimit() + ", limitType=" + algorithmConfig.getLimitType() + "]";
 	}
 
 }
diff --git a/src/test/java/it/unibz/inf/isochrone/TestGeoHelper.java b/src/test/java/it/unibz/inf/isochrone/TestGeoHelper.java
index 9e714efb..81fb46bf 100644
--- a/src/test/java/it/unibz/inf/isochrone/TestGeoHelper.java
+++ b/src/test/java/it/unibz/inf/isochrone/TestGeoHelper.java
@@ -53,7 +53,7 @@ public final class TestGeoHelper {
 
 		// for finding the nearest edge the direction of the algorithm is not relevant (since we need all edges with the minimal distance from p)
 		// -> we simply assume Direction.INCOMING here
-		try (final Database db = new Database(ds, Mode.MULTIMODAL, Direction.INCOMING)) {
+		try (final Database db = new Database(ds, Mode.ISOCHRONE_MULTIMODAL, Direction.INCOMING)) {
 			final List<Location> locations = db.projectOnStreetEdges(p);
 			if (!locations.isEmpty()) {
 				for (final Location l : locations) {
diff --git a/src/test/java/it/unibz/inf/isochrone/algorithm/AlgorithmTest.java b/src/test/java/it/unibz/inf/isochrone/algorithm/AlgorithmTest.java
index 5c871f3a..171be835 100644
--- a/src/test/java/it/unibz/inf/isochrone/algorithm/AlgorithmTest.java
+++ b/src/test/java/it/unibz/inf/isochrone/algorithm/AlgorithmTest.java
@@ -21,6 +21,7 @@ import it.unibz.inf.isochrone.algorithm.AlgorithmStatistics.Statistic;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.ConfigAlgorithmBuilder;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.Direction;
+import it.unibz.inf.isochrone.config.ConfigAlgorithm.LimitType;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.Mode;
 import it.unibz.inf.isochrone.config.ConfigDataset;
 import it.unibz.inf.isochrone.network.INode;
@@ -54,20 +55,38 @@ public class AlgorithmTest {
 	}
 
 	@Test(expectedExceptions = IllegalStateException.class)
-	public void testInvalidConfigurationDuration() throws AlgorithmException {
+	public void testInvalidConfigurationDurationNegative() throws AlgorithmException {
 		LOGGER.info("Running test regarding IllegalStateException");
 		LOGGER.info("-----------------------------");
 
-		final ConfigAlgorithm invalidConfiguration = ConfigAlgorithmHelper.getDefaultDatasetBuilder().duration(-1L).build();
+		final ConfigAlgorithm invalidConfiguration = ConfigAlgorithmHelper.getDefaultDatasetBuilder().limit(LimitType.DURATION, -1L).build();
 		TestHelper.runUnwrapped(MDijkstra.class, ConfigAlgorithmHelper.getDefaultDatasetName(), invalidConfiguration);
 	}
 
 	@Test(expectedExceptions = IllegalStateException.class)
-	public void testInvalidConfigurationNodes() throws AlgorithmException {
+	public void testInvalidConfigurationNodesNegative() throws AlgorithmException {
 		LOGGER.info("Running test regarding IllegalStateException");
 		LOGGER.info("-----------------------------");
 
-		final ConfigAlgorithm invalidConfiguration = ConfigAlgorithmHelper.getDefaultDatasetBuilder().nodes(-1L).build();
+		final ConfigAlgorithm invalidConfiguration = ConfigAlgorithmHelper.getDefaultDatasetBuilder().limit(LimitType.NODE_COUNT, -1L).build();
+		TestHelper.runUnwrapped(MDijkstra.class, ConfigAlgorithmHelper.getDefaultDatasetName(), invalidConfiguration);
+	}
+
+	@Test(expectedExceptions = IllegalStateException.class)
+	public void testInvalidConfigurationDurationZero() throws AlgorithmException {
+		LOGGER.info("Running test regarding IllegalStateException");
+		LOGGER.info("-----------------------------");
+
+		final ConfigAlgorithm invalidConfiguration = ConfigAlgorithmHelper.getDefaultDatasetBuilder().limit(LimitType.DURATION, 0L).build();
+		TestHelper.runUnwrapped(MDijkstra.class, ConfigAlgorithmHelper.getDefaultDatasetName(), invalidConfiguration);
+	}
+
+	@Test(expectedExceptions = IllegalStateException.class)
+	public void testInvalidConfigurationNodesZero() throws AlgorithmException {
+		LOGGER.info("Running test regarding IllegalStateException");
+		LOGGER.info("-----------------------------");
+
+		final ConfigAlgorithm invalidConfiguration = ConfigAlgorithmHelper.getDefaultDatasetBuilder().limit(LimitType.NODE_COUNT, 0L).build();
 		TestHelper.runUnwrapped(MDijkstra.class, ConfigAlgorithmHelper.getDefaultDatasetName(), invalidConfiguration);
 	}
 
@@ -76,16 +95,16 @@ public class AlgorithmTest {
 		LOGGER.info("Running test regarding AlgorithmException");
 		LOGGER.info("-----------------------------");
 
-		final ConfigAlgorithm invalidConfiguration = ConfigAlgorithmHelper.getDefaultDatasetBuilder().dateTime(null).build();
+		final ConfigAlgorithm invalidConfiguration = ConfigAlgorithmHelper.getDefaultDatasetBuilder().mode(Mode.ISOCHRONE_MULTIMODAL).dateTime(null).build();
 		TestHelper.runUnwrapped(MDijkstra.class, ConfigAlgorithmHelper.getDefaultDatasetName(), invalidConfiguration);
 	}
 
 	@Test(expectedExceptions = IllegalStateException.class)
-	public void testInvalidNoBounds() throws AlgorithmException {
+	public void testInvalidLimitType() throws AlgorithmException {
 		LOGGER.info("Running test regarding IllegalStateException");
 		LOGGER.info("-----------------------------");
 
-		final ConfigAlgorithm invalidConfiguration = ConfigAlgorithmHelper.getDefaultDatasetBuilder().duration(null).nodes(null).build();
+		final ConfigAlgorithm invalidConfiguration = ConfigAlgorithmHelper.getDefaultDatasetBuilder().limit(null, 1).build();
 		TestHelper.runUnwrapped(MDijkstra.class, ConfigAlgorithmHelper.getDefaultDatasetName(), invalidConfiguration);
 	}
 
@@ -101,7 +120,7 @@ public class AlgorithmTest {
 		for (final Entry<String, ConfigAlgorithmBuilder> builderEntry : builderSet) {
 			final String dsName = builderEntry.getKey();
 			final ConfigAlgorithmBuilder builder = builderEntry.getValue();
-			final ConfigAlgorithm c = builder.duration(testDuration).build();
+			final ConfigAlgorithm c = builder.limit(LimitType.DURATION, testDuration).build();
 
 			for (final Class<? extends AbstractAlgorithm> a : algorithms) {
 				final AlgorithmResult<MemoryOutput> result = TestHelper.run(a, dsName, c);
@@ -144,7 +163,7 @@ public class AlgorithmTest {
 		LOGGER.info("-----------------------------");
 		final Collection<Class<? extends AbstractAlgorithm>> algorithms = TestHelper.getAllAlgorithms();
 		for (final Class<? extends AbstractAlgorithm> a : algorithms) {
-			run(a, ConfigAlgorithmHelper.getDefaultDatasetName(), ConfigAlgorithmHelper.getDefaultDatasetBuilder().mode(Mode.UNIMODAL).useCache().build());
+			run(a, ConfigAlgorithmHelper.getDefaultDatasetName(), ConfigAlgorithmHelper.getDefaultDatasetBuilder().mode(Mode.ISOCHRONE_UNIMODAL).useCache().build());
 		}
 	}
 
@@ -155,7 +174,7 @@ public class AlgorithmTest {
 		final Collection<Class<? extends AbstractAlgorithm>> algorithms = TestHelper.getAllAlgorithms();
 		final String dsName = ConfigAlgorithmHelper.getDefaultDatasetName();
 		for (final Class<? extends AbstractAlgorithm> a : algorithms) {
-			run(a, dsName, ConfigAlgorithmHelper.getDefaultDatasetBuilder().duration(DURATION_LOCAL).useCache().build());
+			run(a, dsName, ConfigAlgorithmHelper.getDefaultDatasetBuilder().limit(LimitType.DURATION, DURATION_LOCAL).useCache().build());
 		}
 	}
 
@@ -166,7 +185,7 @@ public class AlgorithmTest {
 		final Collection<Class<? extends AbstractAlgorithm>> algorithms = TestHelper.getAllAlgorithms();
 		final String dsName = ConfigAlgorithmHelper.getDefaultDatasetName();
 		for (final Class<? extends AbstractAlgorithm> a : algorithms) {
-			run(a, dsName, ConfigAlgorithmHelper.getDefaultDatasetBuilder().direction(Direction.OUTGOING).duration(DURATION_LOCAL).useCache().build());
+			run(a, dsName, ConfigAlgorithmHelper.getDefaultDatasetBuilder().direction(Direction.OUTGOING).limit(LimitType.DURATION, DURATION_LOCAL).useCache().build());
 		}
 	}
 
@@ -177,7 +196,7 @@ public class AlgorithmTest {
 		final Collection<Class<? extends AbstractAlgorithm>> algorithms = TestHelper.getAllAlgorithms();
 		final String dsName = ConfigAlgorithmHelper.getDefaultDatasetName();
 		for (final Class<? extends AbstractAlgorithm> a : algorithms) {
-			run(a, dsName, ConfigAlgorithmHelper.getDefaultDatasetBuilder().duration(DURATION_LOCAL).mode(Mode.UNIMODAL).useCache().build());
+			run(a, dsName, ConfigAlgorithmHelper.getDefaultDatasetBuilder().limit(LimitType.DURATION, DURATION_LOCAL).mode(Mode.ISOCHRONE_UNIMODAL).useCache().build());
 		}
 	}
 
@@ -190,12 +209,12 @@ public class AlgorithmTest {
 
 		final long startTimeMillis = System.currentTimeMillis();
 		final String dsName = ConfigAlgorithmHelper.getDefaultDatasetName();
-		final ConfigAlgorithm algorithmConfig = ConfigAlgorithmHelper.getDefaultDataset();
+		final ConfigAlgorithm configAlgorithm = ConfigAlgorithmHelper.getDefaultDataset();
 
-		final AlgorithmResult<MemoryOutput> resultMineR = TestHelper.run(MineR.class, dsName, algorithmConfig, maxMemory);
-		logResult(resultMineR, algorithmConfig, startTimeMillis);
-		final AlgorithmResult<MemoryOutput> resultMineRX = TestHelper.run(MineRX.class, dsName, algorithmConfig, maxMemory);
-		logResult(resultMineRX, algorithmConfig, startTimeMillis);
+		final AlgorithmResult<MemoryOutput> resultMineR = TestHelper.run(MineR.class, dsName, configAlgorithm, maxMemory);
+		logResult(resultMineR, configAlgorithm, startTimeMillis);
+		final AlgorithmResult<MemoryOutput> resultMineRX = TestHelper.run(MineRX.class, dsName, configAlgorithm, maxMemory);
+		logResult(resultMineRX, configAlgorithm, startTimeMillis);
 	}
 
 	@Test
@@ -207,28 +226,28 @@ public class AlgorithmTest {
 
 		final long startTimeMillis = System.currentTimeMillis();
 		final String dsName = ConfigAlgorithmHelper.getDefaultDatasetName();
-		final ConfigAlgorithm algorithmConfig = ConfigAlgorithmHelper.getDefaultDatasetBuilder().queryPoints(QUERYPOINT_BUS).build();
+		final ConfigAlgorithm configAlgorithm = ConfigAlgorithmHelper.getDefaultDatasetBuilder().queryPoints(QUERYPOINT_BUS).build();
 
-		final AlgorithmResult<MemoryOutput> resultMineR = TestHelper.run(MineR.class, dsName, algorithmConfig, maxMemory);
-		logResult(resultMineR, algorithmConfig, startTimeMillis);
-		final AlgorithmResult<MemoryOutput> resultMineRX = TestHelper.run(MineRX.class, dsName, algorithmConfig, maxMemory);
-		logResult(resultMineRX, algorithmConfig, startTimeMillis);
+		final AlgorithmResult<MemoryOutput> resultMineR = TestHelper.run(MineR.class, dsName, configAlgorithm, maxMemory);
+		logResult(resultMineR, configAlgorithm, startTimeMillis);
+		final AlgorithmResult<MemoryOutput> resultMineRX = TestHelper.run(MineRX.class, dsName, configAlgorithm, maxMemory);
+		logResult(resultMineRX, configAlgorithm, startTimeMillis);
 	}
 
 
 	// Private static methods
 
-	private static void run(final Class<? extends AbstractAlgorithm> algorithmClass, final String dsName, final ConfigAlgorithm algorithmConfig) {
+	private static void run(final Class<? extends AbstractAlgorithm> algorithmClass, final String dsName, final ConfigAlgorithm configAlgorithm) {
 		LOGGER.info("Executing \"{}\"", algorithmClass.getSimpleName());
 		final long startTimeMillis = System.currentTimeMillis();
-		final AlgorithmResult<MemoryOutput> result = TestHelper.run(algorithmClass, dsName, algorithmConfig);
-		logResult(result, algorithmConfig, startTimeMillis);
+		final AlgorithmResult<MemoryOutput> result = TestHelper.run(algorithmClass, dsName, configAlgorithm);
+		logResult(result, configAlgorithm, startTimeMillis);
 	}
 
-	private static void logResult(final AlgorithmResult<MemoryOutput> result, final ConfigAlgorithm algorithmConfig, final long startTimeMillis) {
+	private static void logResult(final AlgorithmResult<MemoryOutput> result, final ConfigAlgorithm configAlgorithm, final long startTimeMillis) {
 		if (LOGGER.isInfoEnabled()) {
 			final long runTime = System.currentTimeMillis() - startTimeMillis;
-			LOGGER.info("   runtime for duration \"{}\": {}msec", algorithmConfig.getDuration(), runTime);
+			LOGGER.info("   runtime for {} \"{}\": {}msec", configAlgorithm.getLimitType(), configAlgorithm.getLimit(), runTime);
 			LOGGER.info("   nodes (in memory): {}", result.getStatistics().getNumericStat(Statistic.LOADED_NODES_CURRENT).longValue());
 			LOGGER.info("   nodes (in output): {}", result.getOutput().getNodes().size());
 			LOGGER.info("   edges (in output): {}", result.getOutput().getEdges().size());
diff --git a/src/test/java/it/unibz/inf/isochrone/algorithm/ComparisonTest.java b/src/test/java/it/unibz/inf/isochrone/algorithm/ComparisonTest.java
index f1e9dbef..30aa8d91 100644
--- a/src/test/java/it/unibz/inf/isochrone/algorithm/ComparisonTest.java
+++ b/src/test/java/it/unibz/inf/isochrone/algorithm/ComparisonTest.java
@@ -22,6 +22,7 @@ import it.unibz.inf.isochrone.ConfigAlgorithmHelper;
 import it.unibz.inf.isochrone.TestHelper;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.ConfigAlgorithmBuilder;
+import it.unibz.inf.isochrone.config.ConfigAlgorithm.LimitType;
 import it.unibz.inf.isochrone.config.ConfigDataset;
 import it.unibz.inf.isochrone.network.IEdge;
 import it.unibz.inf.isochrone.network.INetworkEntity;
@@ -79,7 +80,7 @@ public final class ComparisonTest {
 		final Set<Entry<String, Collection<ConfigAlgorithm>>> datasets = dsConfigurations.entrySet();
 		for (final Entry<String, Collection<ConfigAlgorithm>> dsEntry : datasets) {
 			final String dsName = dsEntry.getKey();
-			final Collection<ConfigAlgorithm> algorithmConfigurations = dsEntry.getValue();
+			final Collection<ConfigAlgorithm> configAlgorithms = dsEntry.getValue();
 
 			for (final Class<? extends AbstractAlgorithm> class1 : algorithms) {
 				for (final Class<? extends AbstractAlgorithm> class2 : algorithms) {
@@ -91,9 +92,9 @@ public final class ComparisonTest {
 					final String c2 = class2.getSimpleName();
 					LOGGER.info("Comparing \"{}\" and \"{}\" on dataset \"{}\"", c1, c2, dsName);
 
-					for (final ConfigAlgorithm config : algorithmConfigurations) {
-						final double duration = config.getDuration();
-						LOGGER.debug("   isochrone duration: {}", duration);
+					for (final ConfigAlgorithm config : configAlgorithms) {
+						final long limit = config.getLimit();
+						LOGGER.debug("   isochrone {}: {}", config.getLimitType().name().toLowerCase(Locale.ENGLISH), limit);
 						if (LOGGER.isTraceEnabled()) {
 							System.gc();
 
@@ -117,8 +118,8 @@ public final class ComparisonTest {
 						final Collection<IEdge> e1 = TestHelper.getSortedCollection(o1.getEdges());
 						final Collection<IEdge> e2 = TestHelper.getSortedCollection(o2.getEdges());
 
-						assertEquals(n1, n2, dsName, duration, c1, c2);
-						assertEquals(e1, e2, dsName, duration, c1, c2);
+						assertEquals(n1, n2, dsName, limit, c1, c2);
+						assertEquals(e1, e2, dsName, limit, c1, c2);
 					}
 
 					LOGGER.info("-----------------------------");
@@ -165,7 +166,7 @@ public final class ComparisonTest {
 
 		final List<ConfigAlgorithm> dsParams = new ArrayList<>(durationSteps.size());
 		for (final Long duration : durationSteps) {
-			dsParams.add(builder.duration(duration).useCache().build());
+			dsParams.add(builder.limit(LimitType.DURATION, duration).useCache().build());
 		}
 
 		dsConfigurations.put(datasetName, dsParams);
diff --git a/src/test/java/it/unibz/inf/isochrone/algorithm/EdgeOrientationTest.java b/src/test/java/it/unibz/inf/isochrone/algorithm/EdgeOrientationTest.java
index dbe54cba..c3e89944 100644
--- a/src/test/java/it/unibz/inf/isochrone/algorithm/EdgeOrientationTest.java
+++ b/src/test/java/it/unibz/inf/isochrone/algorithm/EdgeOrientationTest.java
@@ -15,6 +15,7 @@ import it.unibz.inf.isochrone.TestHelper;
 import it.unibz.inf.isochrone.TestHelper.AlgorithmResult;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.ConfigAlgorithmBuilder;
+import it.unibz.inf.isochrone.config.ConfigAlgorithm.LimitType;
 import it.unibz.inf.isochrone.config.ConfigDataset;
 import it.unibz.inf.isochrone.network.IEdge;
 import it.unibz.inf.isochrone.network.Location;
@@ -46,7 +47,7 @@ public class EdgeOrientationTest {
 			final String dsName = builderEntry.getKey();
 			LOGGER.info("Checking edge orientations in dataset \"{}\"", dsName);
 			final ConfigAlgorithmBuilder builder = builderEntry.getValue();
-			final ConfigAlgorithm c = builder.duration(DURATION).outputOnlyExpandedEdges().build();
+			final ConfigAlgorithm c = builder.limit(LimitType.DURATION, DURATION).outputOnlyExpandedEdges().build();
 
 			for (final Class<? extends AbstractAlgorithm> a : algorithms) {
 				LOGGER.debug("   using algorithm \"{}\"", a.getSimpleName());
diff --git a/src/test/java/it/unibz/inf/isochrone/algorithm/IncrementalTest.java b/src/test/java/it/unibz/inf/isochrone/algorithm/IncrementalTest.java
index cd9c562b..aa2fd760 100644
--- a/src/test/java/it/unibz/inf/isochrone/algorithm/IncrementalTest.java
+++ b/src/test/java/it/unibz/inf/isochrone/algorithm/IncrementalTest.java
@@ -1,6 +1,7 @@
 package it.unibz.inf.isochrone.algorithm;
 
 import java.util.Collection;
+import java.util.Locale;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -12,6 +13,7 @@ import it.unibz.inf.isochrone.TestHelper;
 import it.unibz.inf.isochrone.TestHelper.AlgorithmResult;
 import it.unibz.inf.isochrone.algorithm.AlgorithmStatistics.Statistic;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm;
+import it.unibz.inf.isochrone.config.ConfigAlgorithm.LimitType;
 import it.unibz.inf.isochrone.config.ConfigDataset;
 import it.unibz.inf.isochrone.output.MemoryOutput;
 
@@ -21,9 +23,9 @@ public class IncrementalTest {
 	private static final long DURATION_15 = 900L;
 	private static final long DURATION_30 = 1800L;
 	private static final String TEST_DS_NAME = ConfigAlgorithmHelper.getDefaultDatasetName();
-	private static final ConfigAlgorithm TEST_CONFIG_DEFAULT_10 = ConfigAlgorithmHelper.getDefaultDatasetBuilder().duration(DURATION_10).useCache().useIncrementalCache().build();
-	private static final ConfigAlgorithm TEST_CONFIG_DEFAULT_15 = ConfigAlgorithmHelper.getDefaultDatasetBuilder().duration(DURATION_15).useCache().useIncrementalCache().build();
-	private static final ConfigAlgorithm TEST_CONFIG_DEFAULT_30 = ConfigAlgorithmHelper.getDefaultDatasetBuilder().duration(DURATION_30).useCache().useIncrementalCache().build();
+	private static final ConfigAlgorithm TEST_CONFIG_DEFAULT_10 = ConfigAlgorithmHelper.getDefaultDatasetBuilder().limit(LimitType.DURATION, DURATION_10).useCache().useIncrementalCache().build();
+	private static final ConfigAlgorithm TEST_CONFIG_DEFAULT_15 = ConfigAlgorithmHelper.getDefaultDatasetBuilder().limit(LimitType.DURATION, DURATION_15).useCache().useIncrementalCache().build();
+	private static final ConfigAlgorithm TEST_CONFIG_DEFAULT_30 = ConfigAlgorithmHelper.getDefaultDatasetBuilder().limit(LimitType.DURATION, DURATION_30).useCache().useIncrementalCache().build();
 
 	// Public methods
 
@@ -65,18 +67,18 @@ public class IncrementalTest {
 
 	// Private static methods
 
-	private static void run(final Class<? extends AbstractAlgorithm> algorithmClass, final String dsName, final ConfigAlgorithm algorithmConfig) {
+	private static void run(final Class<? extends AbstractAlgorithm> algorithmClass, final String dsName, final ConfigAlgorithm configAlgorithm) {
 		LOGGER.info("Executing \"{}\"", algorithmClass.getSimpleName());
 		final long startTimeMillis = System.currentTimeMillis();
-		final AlgorithmResult<MemoryOutput> result = TestHelper.run(algorithmClass, dsName, algorithmConfig);
-		logResult(result, algorithmConfig, startTimeMillis);
+		final AlgorithmResult<MemoryOutput> result = TestHelper.run(algorithmClass, dsName, configAlgorithm);
+		logResult(result, configAlgorithm, startTimeMillis);
 	}
 
-	private static void logResult(final AlgorithmResult<MemoryOutput> result, final ConfigAlgorithm algorithmConfig, final long startTimeMillis) {
+	private static void logResult(final AlgorithmResult<MemoryOutput> result, final ConfigAlgorithm configAlgorithm, final long startTimeMillis) {
 		if (LOGGER.isInfoEnabled()) {
 			final long runTime = System.currentTimeMillis() - startTimeMillis;
 			final AlgorithmStatistics stats = result.getStatistics();
-			LOGGER.info("   runtime for duration \"{}\": {}msec", algorithmConfig.getDuration(), runTime);
+			LOGGER.info("   runtime for {} \"{}\": {}msec", configAlgorithm.getLimitType().name().toLowerCase(Locale.ENGLISH), configAlgorithm.getLimit(), runTime);
 			LOGGER.info("   nodes (in memory): {}", (stats == null) ? 0 : stats.getNumericStat(Statistic.LOADED_NODES_CURRENT).longValue());
 			LOGGER.info("   nodes (in output): {}", result.getOutput().getNodes().size());
 			LOGGER.info("   edges (in output): {}", result.getOutput().getEdges().size());
diff --git a/src/test/java/it/unibz/inf/isochrone/algorithm/ProfilingTest.java b/src/test/java/it/unibz/inf/isochrone/algorithm/ProfilingTest.java
index 45c7fc52..dd947e9e 100644
--- a/src/test/java/it/unibz/inf/isochrone/algorithm/ProfilingTest.java
+++ b/src/test/java/it/unibz/inf/isochrone/algorithm/ProfilingTest.java
@@ -2,6 +2,7 @@ package it.unibz.inf.isochrone.algorithm;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Locale;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -77,7 +78,7 @@ public class ProfilingTest {
 
 		if (LOGGER.isInfoEnabled()) {
 			final long runTime = System.currentTimeMillis() - startTime;
-			LOGGER.info("   runtime for duration \"{}\": {}msec", configAlgorithm.getDuration(), runTime);
+			LOGGER.info("   runtime for {} \"{}\": {}msec", configAlgorithm.getLimitType().name().toLowerCase(Locale.ENGLISH), configAlgorithm.getLimit(), runTime);
 			LOGGER.info("   nodes (in memory): {}", result.getStatistics().getNumericStat(Statistic.LOADED_NODES_CURRENT).longValue());
 
 			if (USE_CLEARING) {
diff --git a/src/test/java/it/unibz/inf/isochrone/algorithm/ReachabilityTest.java b/src/test/java/it/unibz/inf/isochrone/algorithm/ReachabilityTest.java
index ffaee140..551aa57b 100644
--- a/src/test/java/it/unibz/inf/isochrone/algorithm/ReachabilityTest.java
+++ b/src/test/java/it/unibz/inf/isochrone/algorithm/ReachabilityTest.java
@@ -16,6 +16,7 @@ import it.unibz.inf.isochrone.TestGeoHelper;
 import it.unibz.inf.isochrone.TestHelper;
 import it.unibz.inf.isochrone.TestHelper.AlgorithmResult;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm;
+import it.unibz.inf.isochrone.config.ConfigAlgorithm.LimitType;
 import it.unibz.inf.isochrone.config.ConfigDataset;
 import it.unibz.inf.isochrone.network.Edge;
 import it.unibz.inf.isochrone.network.IEdge;
@@ -33,7 +34,7 @@ public class ReachabilityTest {
 	private static final long DURATION = 900L;
 	private static final BigDecimal WALKING_SPEED = BigDecimal.valueOf(1.8d);
 	private static final String TEST_DS_NAME = ConfigAlgorithmHelper.getDefaultDatasetName();
-	private static final ConfigAlgorithm TEST_CONFIG = ConfigAlgorithmHelper.getDefaultDatasetBuilder().duration(DURATION).walkingSpeed(WALKING_SPEED).useCache().build();
+	private static final ConfigAlgorithm TEST_CONFIG = ConfigAlgorithmHelper.getDefaultDatasetBuilder().limit(LimitType.DURATION, DURATION).walkingSpeed(WALKING_SPEED).useCache().build();
 	private int nodeBusStationFagenstr;
 	private int nodeLoretoBridge;
 
diff --git a/src/test/java/it/unibz/inf/isochrone/config/ConfigAlgorithmTest.java b/src/test/java/it/unibz/inf/isochrone/config/ConfigAlgorithmTest.java
index 3f986e6f..bf6aa053 100644
--- a/src/test/java/it/unibz/inf/isochrone/config/ConfigAlgorithmTest.java
+++ b/src/test/java/it/unibz/inf/isochrone/config/ConfigAlgorithmTest.java
@@ -11,16 +11,24 @@ import org.testng.annotations.Test;
 
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.ConfigAlgorithmBuilder;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.Direction;
+import it.unibz.inf.isochrone.config.ConfigAlgorithm.LimitType;
 import it.unibz.inf.isochrone.config.ConfigAlgorithm.Mode;
 
 public class ConfigAlgorithmTest {
 	private static final LocalDateTime DATETIME = LocalDateTime.of(2013, Month.FEBRUARY, 1, 15, 30);
 	private static final BigDecimal SPEED =  BigDecimal.valueOf(1.5d);
 	private static final Direction DIRECTION = Direction.INCOMING;
-	private static final Long DURATION = 50L;
-	private static final Mode MODE = Mode.MULTIMODAL;
+	private static final long DURATION = 50L;
+	private static final LimitType LIMIT_TYPE = LimitType.DURATION;
+	private static final Mode MODE = Mode.ISOCHRONE_MULTIMODAL;
 	private static final Point2D.Double QUERYPOINT = new Point2D.Double(11.352554d, 46.497520d);
-	private static final ConfigAlgorithm TEST_PARAMETER = new ConfigAlgorithmBuilder(QUERYPOINT).dateTime(DATETIME).direction(DIRECTION).duration(DURATION).mode(MODE).walkingSpeed(SPEED).build();
+	private static final ConfigAlgorithm TEST_PARAMETER = new ConfigAlgorithmBuilder(QUERYPOINT)
+		.dateTime(DATETIME)
+		.direction(DIRECTION)
+		.limit(LIMIT_TYPE, DURATION)
+		.mode(MODE)
+		.walkingSpeed(SPEED)
+		.build();
 
 	// Test methods
 
@@ -30,13 +38,18 @@ public class ConfigAlgorithmTest {
 	}
 
 	@Test
-	public void testDuration() {
-		Assert.assertEquals(TEST_PARAMETER.getDuration(), DURATION, "Duration unexpected");
+	public void testLimitType() {
+		Assert.assertEquals(TEST_PARAMETER.getLimitType(), LIMIT_TYPE, "LimitType unexpected");
+	}
+
+	@Test
+	public void testMaxDuration() {
+		Assert.assertEquals(TEST_PARAMETER.getLimit(), DURATION, "Duration unexpected");
 	}
 
 	@Test
 	public void testMode() {
-		Assert.assertEquals(TEST_PARAMETER.getMode(), Mode.MULTIMODAL, "Mode unexpected");
+		Assert.assertEquals(TEST_PARAMETER.getMode(), Mode.ISOCHRONE_MULTIMODAL, "Mode unexpected");
 	}
 
 	@Test
diff --git a/src/test/java/it/unibz/inf/isochrone/db/RangeNodeCountTest.java b/src/test/java/it/unibz/inf/isochrone/db/RangeNodeCountTest.java
index c8e92c66..1f68cbce 100644
--- a/src/test/java/it/unibz/inf/isochrone/db/RangeNodeCountTest.java
+++ b/src/test/java/it/unibz/inf/isochrone/db/RangeNodeCountTest.java
@@ -100,7 +100,7 @@ public class RangeNodeCountTest {
 	}
 
 	public double getRange(final int nodeId, final long density) {
-		final Database db = new Database(ds, Mode.MULTIMODAL, Direction.INCOMING);
+		final Database db = new Database(ds, Mode.ISOCHRONE_MULTIMODAL, Direction.INCOMING);
 		final double range = db.getRangeByNodeCount(nodeId, density);
 		db.close();
 
diff --git a/src/test/java/it/unibz/inf/isochrone/network/StringifierTest.java b/src/test/java/it/unibz/inf/isochrone/network/StringifierTest.java
index bc856e0b..7a1989c8 100644
--- a/src/test/java/it/unibz/inf/isochrone/network/StringifierTest.java
+++ b/src/test/java/it/unibz/inf/isochrone/network/StringifierTest.java
@@ -23,7 +23,7 @@ public class StringifierTest {
 
 	@Test
 	public void testLocation() {
-		final Location l = new Location(123, 0.1f);
+		final Location l = new Location(123, 0.1f, 100.3f);
 		Assert.assertFalse(l.toString().contains("@"));
 	}
 
-- 
GitLab