From 9c92c4b6b07bf3b596d63574a100348036b10c6d Mon Sep 17 00:00:00 2001
From: rakow <rakow@users.noreply.github.com>
Date: Tue, 28 May 2024 11:17:57 +0200
Subject: [PATCH 1/2] MATSim Application: Improve network params estimators
 (#3292)

* adding todos for network param improvements

* more flexible reference models

* prepare capacity models, use the new predictor interface

* handle nan values

* Added hbs.py to calculate sapacity

* implemented capacities from HBS

* change parentheses

* update calculation

* update comments

* also create features from link attributes

* adding features useful for intersections

* update features

* handle non existing links

* catch errors in sample network

* round capacity

* also round freespeed factor

* use correct lane attribute

* warn if number of lanes does not match

* add more standard features

* don't classify secondary roads as residential

* use capacity bounds as in the old model

* refactored, option to only decease existing values

* fix calcCurvature bug

* remove TODO

* added curvature to hbs.py (only the structure, not the calculation)

* calculate geh value for traffic counts

* add capacities based on cuvature for landstrassen, add cuvature calculation

---------

Co-authored-by: frievoe97 <friedrich.voelkers@me.com>
---
 .../traffic/CountComparisonAnalysis.java      |  18 +-
 .../matsim/application/prepare/Predictor.java |   1 +
 .../prepare/network/SampleNetwork.java        |  15 +-
 .../network/params/ApplyNetworkParams.java    | 178 +++++++++----
 .../network/params/EvalFreespeedParams.java   |  11 +-
 .../network/params/FeatureRegressor.java      |  24 +-
 .../network/params/FreespeedOptServer.java    |   2 +-
 .../prepare/network/params/NetworkModel.java  |  14 +-
 .../network/params/NetworkParamsOpt.java      |  64 ++++-
 .../params/hbs/HBSMotorwayCapacity.java       |  44 ++++
 .../network/params/hbs/HBSNetworkParams.java  |  31 +++
 .../network/params/hbs/HBSRoadCapacity.java   | 144 +++++++++++
 .../params/ref/DecisionTreeParams.java        |   2 +-
 .../params/ref/GermanyNetworkParams.java      |   4 +-
 .../src/main/python/capacity/hbs.py           | 234 ++++++++++++++++++
 .../params/ApplyNetworkParamsTest.java        |   1 -
 .../network/params/HBSNetworkParamsTest.java  |  44 ++++
 .../sumo/SumoNetworkFeatureExtractor.java     | 104 +++++++-
 .../sumo/SumoNetworkFeatureExtractorTest.java |  81 ++++++
 19 files changed, 929 insertions(+), 87 deletions(-)
 create mode 100644 contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSMotorwayCapacity.java
 create mode 100644 contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java
 create mode 100644 contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java
 create mode 100644 contribs/application/src/main/python/capacity/hbs.py
 create mode 100644 contribs/application/src/test/java/org/matsim/application/prepare/network/params/HBSNetworkParamsTest.java
 create mode 100644 contribs/sumo/src/test/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractorTest.java

diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/CountComparisonAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/CountComparisonAnalysis.java
index adc9a86f435..e0c02a41831 100644
--- a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/CountComparisonAnalysis.java
+++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/CountComparisonAnalysis.java
@@ -76,6 +76,16 @@ private static int[] sum(int[] a, int[] b) {
 		return counts;
 	}
 
+	/**
+	 * Calculate the geh value for simulated and reference count
+	 */
+	private static double geh(double simulated, double observed) {
+		final double diff = simulated - observed;
+		final double sum = simulated + observed;
+
+		return Math.sqrt(2 * diff * diff / sum);
+	}
+
 	@Override
 	public Integer call() throws Exception {
 
@@ -114,14 +124,16 @@ private Table writeOutput(Counts<Link> counts, Network network, VolumesAnalyzer
 			StringColumn.create("road_type"),
 			IntColumn.create("hour"),
 			DoubleColumn.create("observed_traffic_volume"),
-			DoubleColumn.create("simulated_traffic_volume")
+			DoubleColumn.create("simulated_traffic_volume"),
+			DoubleColumn.create("geh")
 		);
 
 		Table dailyTrafficVolume = Table.create(StringColumn.create("link_id"),
 			StringColumn.create("name"),
 			StringColumn.create("road_type"),
 			DoubleColumn.create("observed_traffic_volume"),
-			DoubleColumn.create("simulated_traffic_volume")
+			DoubleColumn.create("simulated_traffic_volume"),
+			DoubleColumn.create("geh")
 		);
 
 		for (Map.Entry<Id<Link>, MeasurementLocation<Link>> entry : counts.getMeasureLocations().entrySet()) {
@@ -170,6 +182,7 @@ private Table writeOutput(Counts<Link> counts, Network network, VolumesAnalyzer
 					row.setInt("hour", hour);
 					row.setDouble("observed_traffic_volume", observedTrafficVolumeAtHour);
 					row.setDouble("simulated_traffic_volume", simulatedTrafficVolumeAtHour);
+					row.setDouble("geh", geh(simulatedTrafficVolumeAtHour, observedTrafficVolumeAtHour));
 				}
 			} else {
 				// Get the daily values
@@ -183,6 +196,7 @@ private Table writeOutput(Counts<Link> counts, Network network, VolumesAnalyzer
 			row.setString("road_type", type);
 			row.setDouble("observed_traffic_volume", observedTrafficVolumeByDay);
 			row.setDouble("simulated_traffic_volume", simulatedTrafficVolumeByDay);
+			row.setDouble("geh", geh(simulatedTrafficVolumeByDay, observedTrafficVolumeByDay));
 		}
 
 		DoubleColumn relError = dailyTrafficVolume.doubleColumn("simulated_traffic_volume")
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/Predictor.java b/contribs/application/src/main/java/org/matsim/application/prepare/Predictor.java
index 460d1a41abe..473048a99ed 100644
--- a/contribs/application/src/main/java/org/matsim/application/prepare/Predictor.java
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/Predictor.java
@@ -11,6 +11,7 @@ public interface Predictor {
 
 	/**
 	 * Predict value from given features.
+	 * @return predicted value, maybe NaN if no prediction is possible.
 	 */
 	double predict(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories);
 
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/SampleNetwork.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/SampleNetwork.java
index 82a7a278e32..242667bec49 100644
--- a/contribs/application/src/main/java/org/matsim/application/prepare/network/SampleNetwork.java
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/SampleNetwork.java
@@ -144,6 +144,10 @@ public Integer call() throws Exception {
 
 				List<? extends Node> list = e.getValue();
 
+				// for now only consider traffic lights
+				if (!e.getKey().equals("traffic_light"))
+					continue;
+
 				log.info("Sampling {} out of {} intersections for type {}", sample, list.size(), e.getKey());
 
 				for (int i = 0; i < sample && !list.isEmpty(); i++) {
@@ -193,7 +197,7 @@ public Integer call() throws Exception {
 
 		RandomizedTravelTime tt = new RandomizedTravelTime(rnd);
 
-		LeastCostPathCalculator router = createRandomizedRouter(network, tt);
+		LeastCostPathCalculator router = createRandomizedRouter(cityNetwork, tt);
 
 		sampleCityRoutes(cityNetwork, router, tt, rnd);
 
@@ -223,9 +227,16 @@ private void sampleCityRoutes(Network network, LeastCostPathCalculator router, R
 
 				Link to = NetworkUtils.getNearestLink(network, dest);
 
+				// Links could be on the very edge so that nodes are outside the network
+				if (to == null || !network.getNodes().containsKey(link.getFromNode().getId()) ||
+					!network.getNodes().containsKey(to.getToNode().getId())) {
+					i--;
+					continue;
+				}
+
 				LeastCostPathCalculator.Path path = router.calcLeastCostPath(link.getFromNode(), to.getToNode(), 0, null, null);
 
-				if (path.nodes.size() < 2) {
+				if (path == null || path.nodes.size() < 2) {
 					i--;
 					continue;
 				}
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ApplyNetworkParams.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ApplyNetworkParams.java
index deef01dfe5e..048e2fa81b7 100644
--- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ApplyNetworkParams.java
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ApplyNetworkParams.java
@@ -4,7 +4,6 @@
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.PropertyAccessor;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.matsim.api.core.v01.Id;
@@ -14,12 +13,16 @@
 import org.matsim.application.MATSimAppCommand;
 import org.matsim.application.options.InputOptions;
 import org.matsim.application.options.OutputOptions;
+import org.matsim.application.prepare.Predictor;
 import org.matsim.application.prepare.network.params.NetworkParamsOpt.Feature;
 import org.matsim.core.network.NetworkUtils;
 import org.matsim.core.utils.io.IOUtils;
 import picocli.CommandLine;
 
 import java.io.BufferedReader;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -40,6 +43,7 @@ public class ApplyNetworkParams implements MATSimAppCommand {
 	@CommandLine.Mixin
 	private final OutputOptions output = OutputOptions.ofCommand(ApplyNetworkParams.class);
 
+
 	@CommandLine.Parameters(arity = "1..*", description = "Type of parameters to apply. Available: ${COMPLETION-CANDIDATES}")
 	private Set<NetworkAttribute> params;
 
@@ -52,6 +56,19 @@ public class ApplyNetworkParams implements MATSimAppCommand {
 	@CommandLine.Option(names = "--factor-bounds", split = ",", description = "Speed factor limits (lower,upper bound)", defaultValue = NetworkParamsOpt.DEFAULT_FACTOR_BOUNDS)
 	private double[] speedFactorBounds;
 
+	@CommandLine.Option(names = "--capacity-bounds", split = ",", defaultValue = "0.4,0.6,0.8",
+		description = "Minimum relative capacity against theoretical max (traffic light,right before left, priority)")
+	private List<Double> capacityBounds;
+
+	@CommandLine.Option(names = "--road-types", split = ",", description = "Road types to apply changes to")
+	private Set<String> roadTypes;
+
+	@CommandLine.Option(names = "--junction-types", split = ",", description = "Junction types to apply changes to")
+	private Set<String> junctionTypes;
+
+	@CommandLine.Option(names = "--decrease-only", description = "Only set values if the are lower than the current value", defaultValue = "false")
+	private boolean decrease;
+
 	private NetworkModel model;
 	private NetworkParams paramsOpt;
 
@@ -62,14 +79,14 @@ public static void main(String[] args) {
 	}
 
 	/**
-	 * Theoretical capacity.
+	 * Theoretical capacity assuming fixed car length and headway. This should usually not be exceeded.
 	 */
-	private static double capacityEstimate(double v) {
+	public static double capacityEstimate(double v) {
 
 		// headway
 		double tT = 1.2;
 
-		// car length
+		// car length + buffer
 		double lL = 7.0;
 
 		double Qc = v / (v * tT + lL);
@@ -94,13 +111,19 @@ public Integer call() throws Exception {
 			}
 		}
 
-		Map<Id<Link>, Feature> features = NetworkParamsOpt.readFeatures(input.getPath("features.csv"), network.getLinks().size());
+		Map<Id<Link>, Feature> features = NetworkParamsOpt.readFeatures(input.getPath("features.csv"), network.getLinks());
 
 		for (Link link : network.getLinks().values()) {
 			Feature ft = features.get(link.getId());
 
+			if (roadTypes != null && !roadTypes.isEmpty() && !roadTypes.contains(ft.highwayType()))
+				continue;
+
+			if (junctionTypes != null && !junctionTypes.isEmpty() && !junctionTypes.contains(ft.junctionType()))
+				continue;
+
 			try {
-				applyChanges(link, ft.junctionType(), ft.features());
+				applyChanges(link, ft);
 			} catch (IllegalArgumentException e) {
 				warn++;
 				log.warn("Error processing link {}", link.getId(), e);
@@ -117,67 +140,126 @@ public Integer call() throws Exception {
 	/**
 	 * Apply speed and capacity models and apply changes.
 	 */
-	private void applyChanges(Link link, String junctionType, Object2DoubleMap<String> features) {
-
-		String type = NetworkUtils.getHighwayType(link);
+	private void applyChanges(Link link, Feature ft) {
 
 		boolean modified = false;
 
 		if (params.contains(NetworkAttribute.capacity)) {
+			modified = applyCapacity(link, ft);
+		}
+
+		if (params.contains(NetworkAttribute.freespeed)) {
+			modified |= applyFreeSpeed(link, ft);
+		}
 
-			FeatureRegressor capacity = model.capacity(junctionType);
+		if (modified)
+			warn++;
+	}
 
-			double perLane = capacity.predict(features);
+	private boolean applyCapacity(Link link, Feature ft) {
 
-			double cap = capacityEstimate(features.getDouble("speed"));
+		Predictor capacity = model.capacity(ft.junctionType(), ft.highwayType());
+		// No operation performed if not supported
+		if (capacity == null) {
+			return false;
+		}
 
-			// Minimum thresholds
-			double threshold = switch (junctionType) {
-				// traffic light can reduce capacity at least to 50% (with equal green split)
-				case "traffic_light" -> 0.4;
-				case "right_before_left" -> 0.6;
-				// Motorways are kept at their max theoretical capacity
-				case "priority" -> type.startsWith("motorway") ? 1 : 0.8;
-				default -> 0;
-			};
+		double perLane = capacity.predict(ft.features(), ft.categories());
+		if (Double.isNaN(perLane)) {
+			return true;
+		}
 
-			if (perLane < cap * threshold) {
-				log.warn("Increasing capacity per lane on {} ({}, {}) from {} to {}", link.getId(), type, junctionType, perLane, cap * threshold);
-				perLane = cap * threshold;
-				modified = true;
-			}
+		double cap = capacityEstimate(ft.features().getDouble("speed"));
 
-			link.setCapacity(link.getNumberOfLanes() * perLane);
+		if (capacityBounds.isEmpty())
+			capacityBounds.add(0.0);
+
+		// Fill up to 3 elements if not provided
+		if (capacityBounds.size() < 3) {
+			capacityBounds.add(capacityBounds.get(0));
+			capacityBounds.add(capacityBounds.get(1));
 		}
 
+		// Minimum thresholds
+		double threshold = switch (ft.junctionType()) {
+			case "traffic_light" -> capacityBounds.get(0);
+			case "right_before_left" -> capacityBounds.get(1);
+			case "priority" -> capacityBounds.get(2);
+			default -> 0;
+		};
 
-		if (params.contains(NetworkAttribute.freespeed)) {
+		boolean modified = false;
 
-			double speedFactor = 1.0;
-			FeatureRegressor speedModel = model.speedFactor(junctionType);
+		if (perLane < cap * threshold) {
+			log.warn("Increasing capacity per lane on {} ({}, {}) from {} to {}",
+				link.getId(), ft.highwayType(), ft.junctionType(), perLane, cap * threshold);
+			perLane = cap * threshold;
+			modified = true;
+		}
 
-			speedFactor =  paramsOpt != null ?
-				speedModel.predict(features, paramsOpt.getParams(junctionType)) :
-				speedModel.predict(features);
+		if (perLane > cap) {
+			log.warn("Reducing capacity per lane on {} ({}, {}) from {} to {}",
+				link.getId(), ft.highwayType(), ft.junctionType(), perLane, cap);
+			perLane = cap;
+			modified = true;
+		}
 
-			if (speedFactor > speedFactorBounds[1]) {
-				log.warn("Reducing speed factor on {} from {} to {}", link.getId(), speedFactor, speedFactorBounds[1]);
-				speedFactor = speedFactorBounds[1];
-				modified = true;
-			}
+		if (ft.features().getOrDefault("num_lanes", link.getNumberOfLanes()) != link.getNumberOfLanes())
+			log.warn("Number of lanes for link {} does not match the feature file", link.getId());
 
-			// Threshold for very low speed factors
-			if (speedFactor < speedFactorBounds[0]) {
-				log.warn("Increasing speed factor on {} from {} to {}", link, speedFactor, speedFactorBounds[0]);
-				speedFactor = speedFactorBounds[0];
-				modified = true;
-			}
+		int totalCap = BigDecimal.valueOf(link.getNumberOfLanes() * perLane).setScale(0, RoundingMode.HALF_UP).intValue();
+
+		if (decrease && totalCap > link.getCapacity())
+			return false;
+
+		link.setCapacity(totalCap);
+
+		return modified;
+	}
+
+	private boolean applyFreeSpeed(Link link, Feature ft) {
 
-			link.setFreespeed((double) link.getAttributes().getAttribute("allowed_speed") * speedFactor);
-			link.getAttributes().putAttribute("speed_factor", speedFactor);
+		Predictor speedModel = model.speedFactor(ft.junctionType(), ft.highwayType());
+
+		// No operation performed if not supported
+		if (speedModel == null) {
+			return false;
 		}
 
-		if (modified)
-			warn++;
+		double speedFactor = paramsOpt != null ?
+			speedModel.predict(ft.features(), ft.categories(), paramsOpt.getParams(ft.junctionType())) :
+			speedModel.predict(ft.features(), ft.categories());
+
+		if (Double.isNaN(speedFactor)) {
+			return false;
+		}
+
+		boolean modified = false;
+
+		if (speedFactor > speedFactorBounds[1]) {
+			log.warn("Reducing speed factor on {} from {} to {}", link.getId(), speedFactor, speedFactorBounds[1]);
+			speedFactor = speedFactorBounds[1];
+			modified = true;
+		}
+
+		// Threshold for very low speed factors
+		if (speedFactor < speedFactorBounds[0]) {
+			log.warn("Increasing speed factor on {} from {} to {}", link, speedFactor, speedFactorBounds[0]);
+			speedFactor = speedFactorBounds[0];
+			modified = true;
+		}
+
+		double freeSpeed = (double) link.getAttributes().getAttribute("allowed_speed") * speedFactor;
+
+		freeSpeed = BigDecimal.valueOf(freeSpeed).setScale(3, RoundingMode.HALF_EVEN).doubleValue();
+
+		if (decrease && freeSpeed > link.getFreespeed())
+			return false;
+
+		link.setFreespeed(freeSpeed);
+		link.getAttributes().putAttribute("speed_factor", freeSpeed);
+
+		return modified;
 	}
+
 }
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/EvalFreespeedParams.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/EvalFreespeedParams.java
index 014d2615549..cd2ba4e016a 100644
--- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/EvalFreespeedParams.java
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/EvalFreespeedParams.java
@@ -18,6 +18,7 @@
 import org.matsim.application.analysis.traffic.traveltime.SampleValidationRoutes;
 import org.matsim.application.options.InputOptions;
 import org.matsim.application.options.OutputOptions;
+import org.matsim.application.prepare.Predictor;
 import org.matsim.contrib.osm.networkReader.LinkProperties;
 import org.matsim.core.network.NetworkUtils;
 import picocli.CommandLine;
@@ -104,7 +105,7 @@ static Result applyAndEvaluateParams(
 						continue;
 					}
 
-					FeatureRegressor speedModel = model.speedFactor(ft.junctionType());
+					Predictor speedModel = model.speedFactor(ft.junctionType(), ft.highwayType());
 
 					if (speedModel == null) {
 						link.setFreespeed(allowedSpeed);
@@ -115,15 +116,15 @@ static Result applyAndEvaluateParams(
 
 					if (request.hasParams()) {
 						double[] p = request.getParams(ft.junctionType());
-						speedFactor = speedModel.predict(ft.features(), p);
+						speedFactor = speedModel.predict(ft.features(), ft.categories(), p);
 					} else
-						speedFactor = speedModel.predict(ft.features());
+						speedFactor = speedModel.predict(ft.features(), ft.categories());
 
 					// apply lower and upper bound
 					speedFactor = Math.max(speedFactorBounds[0], speedFactor);
 					speedFactor = Math.min(speedFactorBounds[1], speedFactor);
 
-					attributes.put(link.getId(), speedModel.getData(ft.features()));
+					attributes.put(link.getId(), speedModel.getData(ft.features(), ft.categories()));
 
 					link.setFreespeed(allowedSpeed * speedFactor);
 					link.getAttributes().putAttribute("speed_factor", speedFactor);
@@ -151,7 +152,7 @@ public Integer call() throws Exception {
 		mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
 
 		validationSet = readValidation(validationFiles, refHours);
-		features = readFeatures(input.getPath("features.csv"), network.getLinks().size());
+		features = readFeatures(input.getPath("features.csv"), network.getLinks());
 
 		CSVPrinter csv;
 		Path out = output.getPath("eval.csv");
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FeatureRegressor.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FeatureRegressor.java
index 051135e89b6..cade7eb1693 100644
--- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FeatureRegressor.java
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FeatureRegressor.java
@@ -1,11 +1,15 @@
 package org.matsim.application.prepare.network.params;
 
 import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
+import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
+import org.matsim.application.prepare.Predictor;
 
 /**
  * Predictor interface for regression.
+ * @deprecated Use {@link Predictor} instead.
  */
-public interface FeatureRegressor {
+@Deprecated
+public interface FeatureRegressor extends Predictor {
 
 
 	/**
@@ -28,4 +32,22 @@ default double[] getData(Object2DoubleMap<String> ft) {
 		throw new UnsupportedOperationException("Not implemented");
 	}
 
+	default double predict(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories) {
+		return predict(features);
+	}
+
+	/**
+	 * Predict values with adjusted model params.
+	 */
+	default double predict(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories, double[] params) {
+		return predict(features, params);
+	}
+
+	/**
+	 * Return data that is used for internal prediction function (normalization already applied).
+	 */
+	default double[] getData(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories) {
+		return getData(features);
+	}
+
 }
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FreespeedOptServer.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FreespeedOptServer.java
index 387824839f4..fef19850305 100644
--- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FreespeedOptServer.java
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FreespeedOptServer.java
@@ -102,7 +102,7 @@ public Integer call() throws Exception {
 		}
 
 		validationSet = NetworkParamsOpt.readValidation(validationFiles, refHours);
-		features = NetworkParamsOpt.readFeatures(input.getPath("features.csv"), network.getLinks().size());
+		features = NetworkParamsOpt.readFeatures(input.getPath("features.csv"), network.getLinks());
 
 		log.info("Initial score:");
 		applyAndEvaluateParams(null, "init");
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkModel.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkModel.java
index f98bb9bd41e..f88c4183c58 100644
--- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkModel.java
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkModel.java
@@ -1,22 +1,24 @@
 package org.matsim.application.prepare.network.params;
 
+import org.matsim.application.prepare.Predictor;
+
 /**
  * A model for estimating network parameters.
  */
 public interface NetworkModel {
 
 	/**
-	 * Flow Capacity (per lane)
+	 * Flow Capacity (per lane).
 	 */
-	default FeatureRegressor capacity(String junctionType) {
-		return null;
+	default Predictor capacity(String junctionType, String highwayType) {
+		throw new UnsupportedOperationException("Capacity model not implemented for class: " + getClass().getName());
 	}
 
 	/**
-	 * Speed factor (relative to free flow speed).
+	 * Speed factor (relative to allowed speed).
 	 */
-	default FeatureRegressor speedFactor(String junctionType) {
-		return null;
+	default Predictor speedFactor(String junctionType, String highwayType) {
+		throw new UnsupportedOperationException("Speed factor model not implemented for class: " + getClass().getName());
 	}
 
 }
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkParamsOpt.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkParamsOpt.java
index 2c69e7c6498..e9185fc7a0f 100644
--- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkParamsOpt.java
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkParamsOpt.java
@@ -4,6 +4,8 @@
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
 import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
 import org.apache.commons.csv.CSVFormat;
 import org.apache.commons.csv.CSVParser;
 import org.apache.commons.csv.CSVPrinter;
@@ -15,6 +17,7 @@
 import org.matsim.api.core.v01.network.Network;
 import org.matsim.api.core.v01.network.Node;
 import org.matsim.application.analysis.traffic.traveltime.SampleValidationRoutes;
+import org.matsim.core.network.NetworkUtils;
 import org.matsim.core.router.DijkstraFactory;
 import org.matsim.core.router.costcalculators.OnlyTimeDependentTravelDisutility;
 import org.matsim.core.router.util.LeastCostPathCalculator;
@@ -55,9 +58,14 @@ static NetworkModel load(Class<? extends NetworkModel> modelClazz) {
 	/**
 	 * Read network edge features from csv.
 	 */
-	static Map<Id<Link>, Feature> readFeatures(String input, int expectedLinks) throws IOException {
+	static Map<Id<Link>, Feature> readFeatures(String input, Map<Id<Link>, ? extends Link> links) throws IOException {
 
-		Map<Id<Link>, Feature> features = new IdMap<>(Link.class, expectedLinks);
+		Map<Id<Link>, Feature> features = new IdMap<>(Link.class, links.size());
+
+		// Create features from link attributes
+		for (Link link : links.values()) {
+			features.put(link.getId(), createDefaultFeature(link));
+		}
 
 		try (CSVParser reader = new CSVParser(IOUtils.getBufferedReader(input),
 			CSVFormat.DEFAULT.builder().setHeader().setSkipHeaderRecord(true).build())) {
@@ -67,27 +75,65 @@ static Map<Id<Link>, Feature> readFeatures(String input, int expectedLinks) thro
 			for (CSVRecord row : reader) {
 
 				Id<Link> id = Id.createLinkId(row.get("linkId"));
-
-				Object2DoubleOpenHashMap<String> ft = new Object2DoubleOpenHashMap<>();
-				ft.defaultReturnValue(Double.NaN);
+				Link link = links.get(id);
+				Feature ft = features.computeIfAbsent(id, (k) -> createDefaultFeature(link));
 
 				for (String column : header) {
 					String v = row.get(column);
 					try {
-						ft.put(column, Double.parseDouble(v));
+						ft.features.put(column, Double.parseDouble(v));
 					} catch (NumberFormatException e) {
 						// every not equal to True will be false
-						ft.put(column, Boolean.parseBoolean(v) ? 1 : 0);
+						ft.features.put(column, Boolean.parseBoolean(v) ? 1 : 0);
+						ft.categories.put(column, v);
 					}
 				}
 
-				features.put(id, new Feature(row.get("junction_type"), ft));
+				String highwayType = header.contains(NetworkUtils.TYPE) ? row.get(NetworkUtils.TYPE) :
+					(link != null ? NetworkUtils.getHighwayType(link) : null);
+
+				features.put(id, new Feature(row.get("junction_type").intern(), highwayType, ft.features, ft.categories));
 			}
 		}
 
 		return features;
 	}
 
+	/**
+	 * Create default feature based on link attributes.
+	 */
+	private static Feature createDefaultFeature(Link link) {
+		Object2DoubleOpenHashMap<String> ft = new Object2DoubleOpenHashMap<>();
+		ft.defaultReturnValue(Double.NaN);
+		Object2ObjectMap<String, String> categories = new Object2ObjectOpenHashMap<>();
+
+		// Link might not be present in the network
+		if (link == null)
+			return new Feature("", "", ft, categories);
+
+		String highwayType = NetworkUtils.getHighwayType(link);
+		categories.put("highway_type", highwayType);
+		ft.put("speed", NetworkUtils.getAllowedSpeed(link));
+		ft.put("num_lanes", link.getNumberOfLanes());
+		ft.put("length", link.getLength());
+		ft.put("capacity", link.getCapacity());
+		ft.put("freespeed", link.getFreespeed());
+
+		for (Map.Entry<String, Object> e : link.getAttributes().getAsMap().entrySet()) {
+			String key = e.getKey();
+			Object value = e.getValue();
+			if (value instanceof Number) {
+				ft.put(key, ((Number) value).doubleValue());
+			} else if (value instanceof Boolean) {
+				ft.put(key, (Boolean) value ? 1 : 0);
+			} else {
+				categories.put(key, value.toString());
+			}
+		}
+
+		return new Feature("", highwayType, ft, categories);
+	}
+
 	/**
 	 * Read validation files and calc target speed.
 	 */
@@ -170,7 +216,7 @@ static Result evaluate(Network network, Object2DoubleMap<SampleValidationRoutes.
 		return new Result(rmse.getMean(), mse.getMean(), data);
 	}
 
-	record Feature(String junctionType, Object2DoubleMap<String> features) {
+	record Feature(String junctionType, String highwayType, Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories) {
 	}
 
 	record Result(double rmse, double mae, Map<String, List<Data>> data) {
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSMotorwayCapacity.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSMotorwayCapacity.java
new file mode 100644
index 00000000000..4a8bb92e219
--- /dev/null
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSMotorwayCapacity.java
@@ -0,0 +1,44 @@
+package org.matsim.application.prepare.network.params.hbs;
+
+import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
+import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
+import org.matsim.application.prepare.Predictor;
+
+/**
+ * Capacity for motorways.
+ */
+public class HBSMotorwayCapacity implements Predictor {
+	@Override
+	public double predict(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories) {
+
+		// speed in km/h
+		int speed = (int) Math.round(features.getDouble("speed") * 3.6);
+		int lanes = (int) features.getOrDefault("num_lanes", 1);
+
+		// Capacity for 1 lane motorways is not defined in HBS
+		double capacity = 2000;
+		if (lanes == 2) {
+			if (speed >= 130)
+				capacity = 3700.0;
+			else if (speed >= 120)
+				capacity = 3800;
+			else
+				capacity = 3750;
+		} else if (lanes == 3) {
+			if (speed >= 130)
+				capacity = 5300;
+			else if (speed >= 120)
+				capacity = 5400;
+			else
+				capacity = 5350;
+		} else if (lanes >= 4) {
+			if (speed >= 130)
+				capacity = 7300;
+			else
+				capacity = 7400;
+		}
+
+		// Return capacity per lane
+		return capacity / lanes;
+	}
+}
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java
new file mode 100644
index 00000000000..faff8379a3c
--- /dev/null
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java
@@ -0,0 +1,31 @@
+package org.matsim.application.prepare.network.params.hbs;
+
+import org.matsim.application.prepare.Predictor;
+import org.matsim.application.prepare.network.params.NetworkModel;
+
+/**
+ * Capacity params calculated according to
+ * "Handbuch für die Bemessung von Straßenverkehrsanlagen“ (HBS)
+ */
+public class HBSNetworkParams implements NetworkModel {
+
+	private static final Predictor MOTORWAY = new HBSMotorwayCapacity();
+	private static final Predictor ROAD = new HBSRoadCapacity();
+
+	@Override
+	public Predictor capacity(String junctionType, String highwayType) {
+
+		// Traffic lights are not considered
+		if (junctionType.equals("traffic_light")) {
+			return null;
+		}
+
+		if (highwayType.startsWith("motorway")) {
+			return MOTORWAY;
+		}
+
+		// All lower category roads
+		return ROAD;
+	}
+
+}
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java
new file mode 100644
index 00000000000..3e07362db8f
--- /dev/null
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java
@@ -0,0 +1,144 @@
+package org.matsim.application.prepare.network.params.hbs;
+
+import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
+import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
+import org.matsim.application.prepare.Predictor;
+
+/**
+ * Capacity for general roads, that are not motorways or residential roads.
+ */
+public class HBSRoadCapacity implements Predictor {
+	/**
+	 * Capacity on "Landstraße", often osm secondary. These numbers are taken from the HBS (see contribs/application/src/main/python/capacity/hbs.py)
+	 */
+	private static double capacityLandStr(int lanes, int curvature) {
+
+
+		if (lanes == 1) {
+			if (curvature == 1) return 1369.532383465354;
+
+			if (curvature == 2) return 1117.1498589355958;
+
+			if (curvature == 3) return 1048.5840399296935;
+
+			if (curvature == 4) return 956.0314100959505;
+		}
+
+		if (lanes == 2) return 1956.6719999999998;
+
+
+		// Own assumption of increasing capacity with more lanes
+		// This is not covered by the HBS and is a very rare case
+		return (1956.6719999999998 * 1.3) / lanes;
+	}
+
+	/**
+	 * Bundesstraße with at least 70km/h, often osm primary or trunk
+	 */
+	private static double capacityBundesStr(int lanes) {
+
+		if (lanes == 1)
+			return 2033.868926820213;
+
+		if (lanes == 2)
+			return 3902.4390243902435 / 2;
+
+		return (3902.4390243902435 * 1.3) / lanes;
+	}
+
+	/**
+	 * Capacity on a side road merging into a main road at a junction.
+	 *
+	 * @param qP the vehicle volume of the main road
+	 */
+	private static double capacityMerging(double qP) {
+
+		// See HBS page 5-20, eq. S5-12 table S5-5
+
+		// mean Folgezeitlücken of the different combinations
+		double tf = 3.5;
+
+		// mean Grenzzeitlücke
+		double tg = 6.36;
+
+
+		return Math.exp((-qP / 3600) * (tg - tf / 2)) * 3600 / tf;
+	}
+
+	private static int curvatureCategory(double length, double curvature) {
+
+		// for too short segment, curvature is not relevant
+		if (length < 50 || curvature == 0)
+			return 1;
+
+		double sumChanges = curvature * (length / 1000);
+
+		// Scale length of segment to at least 300m, because the manual recommends at least a certain segment length
+		double ku = sumChanges / Math.max(0.3, length / 1000);
+
+		if (ku > 150)
+			return 4;
+		if (ku > 100)
+			return 3;
+		if (ku > 50)
+			return 2;
+		return 1;
+
+	}
+
+	/**
+	 * Capacity of a side road of type merging into a main road.
+	 *
+	 * @param roadType type of the target road
+	 * @param mainType type of the higher priority road
+	 */
+	private static double capacityMerging(String roadType, String mainType) {
+
+		if (mainType.equals("trunk") || mainType.equals("primary") || roadType.contains("residential"))
+			// ~600 veh/h
+			return capacityMerging(600);
+
+		// ~800 veh/h
+		return capacityMerging(400);
+	}
+
+	@Override
+	public double predict(Object2DoubleMap<String> features, Object2ObjectMap<String, String> categories) {
+
+		// Speed in km/h
+		int speed = (int) Math.round(features.getDouble("speed") * 3.6);
+		int lanes = (int) features.getOrDefault("num_lanes", 1);
+		int curvature = curvatureCategory(features.getDouble("length"), features.getOrDefault("curvature", 0));
+		String type = categories.get("highway_type");
+
+		// Primary and trunk roads are often Bundesstraßen,
+		// but only if they have a speed limit of 70 or more, this calculation is valid
+		// From OSM alone it is not possible to distinguish anbaufreie Hauptverkehrsstraßen and Landstraße clearly
+		if (speed >= 90 || ((type.contains("primary") || type.contains("trunk")) && speed >= 70)) {
+			return capacityBundesStr(lanes);
+		} else if (speed >= 70) {
+			return capacityLandStr(lanes, curvature);
+		}
+
+		String merging = categories.get("is_merging_into");
+
+		// Only merging with a single lane road is considered
+		if (!merging.isEmpty() && lanes == 1) {
+			return capacityMerging(type, merging);
+		}
+
+		// Capacity for city roads
+		if (speed >= 40 || lanes >= 2 || features.getDouble("is_secondary_or_higher") == 1) {
+			return switch (lanes) {
+				case 1 -> 1139.0625;
+				case 2 -> 2263.438914027149 / 2;
+				// Own assumption, rare edge-case
+				default -> 2263.438914027149 * 1.3 / lanes;
+			};
+		}
+
+		// Remaining are residential which are assumed to have high urbanisation
+		return 800.0 / lanes;
+	}
+
+}
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ref/DecisionTreeParams.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ref/DecisionTreeParams.java
index 4bb84fa6577..7dab8d49135 100644
--- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ref/DecisionTreeParams.java
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ref/DecisionTreeParams.java
@@ -18,7 +18,7 @@ public final class DecisionTreeParams implements NetworkModel {
 	private static final FeatureRegressor INSTANCE = new Model();
 
 	@Override
-	public FeatureRegressor speedFactor(String junctionType) {
+	public FeatureRegressor speedFactor(String junctionType, String highwayType) {
 		return INSTANCE;
 	}
 
diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ref/GermanyNetworkParams.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ref/GermanyNetworkParams.java
index ffe7250b2f4..80541a82a7e 100644
--- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ref/GermanyNetworkParams.java
+++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ref/GermanyNetworkParams.java
@@ -9,7 +9,7 @@
  */
 public final class GermanyNetworkParams implements NetworkModel {
 	@Override
-	public FeatureRegressor capacity(String junctionType) {
+	public FeatureRegressor capacity(String junctionType, String highwayType) {
 		return switch (junctionType) {
 			case "traffic_light" -> GermanyNetworkParams_capacity_traffic_light.INSTANCE;
 			case "right_before_left" -> GermanyNetworkParams_capacity_right_before_left.INSTANCE;
@@ -19,7 +19,7 @@ public FeatureRegressor capacity(String junctionType) {
 	}
 
 	@Override
-	public FeatureRegressor speedFactor(String junctionType) {
+	public FeatureRegressor speedFactor(String junctionType, String highwayType) {
 		return switch (junctionType) {
 			case "traffic_light" -> GermanyNetworkParams_speedRelative_traffic_light.INSTANCE;
 			case "right_before_left" -> GermanyNetworkParams_speedRelative_right_before_left.INSTANCE;
diff --git a/contribs/application/src/main/python/capacity/hbs.py b/contribs/application/src/main/python/capacity/hbs.py
new file mode 100644
index 00000000000..aad41f1a45e
--- /dev/null
+++ b/contribs/application/src/main/python/capacity/hbs.py
@@ -0,0 +1,234 @@
+import math
+import matplotlib.pyplot as plt
+import plotly.graph_objects as go
+
+
+def capacity_estimate(v):
+    # headway
+    tT = 1.2
+    # car length
+    lL = 7.0
+    Qc = v / (v * tT + lL)
+    return 3600 * Qc
+
+
+def merge(qp=400):
+    tf = 3.5
+    tg = 6.36
+    return math.exp((-qp / 3600) * (tg - tf / 2)) * 3600 / tf
+
+
+class Street:
+    def __init__(self, osm_type, lanes, speed, curvature):
+        self.osm_type = osm_type
+        self.lanes = lanes
+        self.speed = speed
+        self.curvature = curvature
+
+
+def calc_capacity_stadtstrasse(street):
+    # Source: HSB S3
+    k = 0
+    a = 54
+    b = 0.850
+
+    if street.lanes == 1:
+        f = 1.0
+        # Assume middle-high "Erschließungsintensität"
+        if street.speed == 30:
+            k = 45
+            a = 38
+            b = 0.715
+        elif street.speed == 50:
+            k = 45
+            a = 54
+            b = 0.850
+        elif street.speed == 70:
+            k = 40
+            a = 89
+            b = 0.846
+
+        return (-1.0 * b * math.pow(k, 3 / 2) * math.sqrt(4 * a * f + b * b * k) + 2 * a * f * k + b * b * k * k) / (
+                2 * f * f)
+    elif street.lanes == 2:
+        f = 0.7
+        if street.speed == 50:
+            k = 45
+            a = -0.009
+            b = 55.58
+        elif street.speed == 70:
+            f = 0.5
+            k = 40
+            a = -0.008
+            b = 80
+
+        return (b * k) / (f - a * k)
+
+
+def calc_capacity_landstrasse(street):
+    # Source: HSB table L3-4
+    if street.lanes == 1:
+        k = 20
+
+        a = 0
+        b = 0
+
+        # Table L3-4
+        if street.curvature == 1:
+            a = 98.73
+            b = 0.8175
+        elif street.curvature == 2:
+            a = 83.88
+            b = 0.8384
+        elif street.curvature == 3:
+            a = 74.41
+            b = 0.6788
+        elif street.curvature == 4:
+            a = 68.02
+            b = 0.6539
+        else:
+            raise ValueError(f"Unknown curvature {street.curvature}")
+
+        return 0.5 * (-b * math.pow(k, 3 / 2) * math.sqrt(4 * a + b * b * k) + 2 * a * k + b * b * k * k)
+    if street.lanes == 2:
+        k = 48
+        a = 55.5
+        b = -0.614
+
+        # Divide by 2 because the formula is for both directions
+        return k * (2 * a + b * k) / 2
+
+
+def calc_capacity_autobahn(street):
+    # Source: HSB A3
+    anzahl_fahrstreifen = 1
+    if street.lanes <= 2:
+        if street.speed >= 130:
+            return 3700 / anzahl_fahrstreifen
+        elif street.speed == 120:
+            return 3800 / anzahl_fahrstreifen
+        elif street.speed <= 120:
+            return 3750 / anzahl_fahrstreifen
+    elif street.lanes == 3:
+        if street.speed >= 130:
+            return 5300 / anzahl_fahrstreifen
+        elif street.speed == 120:
+            return 5400 / anzahl_fahrstreifen
+        elif street.speed <= 120:
+            return 5350 / anzahl_fahrstreifen
+    else:
+        if street.speed >= 130:
+            return 7300 / anzahl_fahrstreifen
+        elif street.speed == 120:
+            return 7400 / anzahl_fahrstreifen
+        elif street.speed <= 120:
+            return 7400 / anzahl_fahrstreifen
+
+
+osm_types = {
+    'residential': 'Stadtstraßen',
+    'unclassified': 'Landstraßen',
+    'motorway': 'Autobahnen'
+}
+
+speeds = [30, 50, 70, 80, 90, 100, 120, 130]
+curvatures = [1, 2, 3, 4]
+
+capacities = {}
+streets = []
+
+for osm_type, street_type in osm_types.items():
+    capacities[osm_type] = {}
+    for lanes in range(1, 5):
+        capacities[osm_type][lanes] = {}
+        for curvature in curvatures:
+            capacities[osm_type][lanes][curvature] = {}
+            for speed in speeds:
+                if (osm_type == 'residential' and speed > 70) or (osm_type == 'residential' and speed < 30) or (
+                        osm_type == 'residential' and lanes > 2):
+                    continue
+                if (osm_type == 'unclassified' and speed > 100) or (osm_type == 'unclassified' and speed <= 50) or (
+                        osm_type == 'unclassified' and lanes > 2):
+                    continue
+                if (osm_type == 'motorway' and lanes == 1) or (osm_type == 'motorway' and speed < 80):
+                    continue
+                street = Street(osm_type, lanes, speed, curvature)
+                streets.append(street)
+                capacities[osm_type][lanes][curvature][speed] = 0
+
+for street in streets:
+    capacity = 0
+    if street.osm_type == 'residential':
+        capacity = calc_capacity_stadtstrasse(street)
+    elif street.osm_type == 'motorway':
+        capacity = calc_capacity_autobahn(street)
+    else:
+        capacity = calc_capacity_landstrasse(street)
+    capacities[street.osm_type][street.lanes][street.curvature][street.speed] = capacity
+
+for street_type, lanes_capacity in capacities.items():
+    for lanes, speeds_capacity in lanes_capacity.items():
+        print(f"{osm_types[street_type]}, Fahrstreifen: {lanes}:")
+        for curvature, capacity in speeds_capacity.items():
+            for speed, capacity in capacity.items():
+                print(f"Geschwindigkeit {speed} km/h: Kurvigkeit {curvature} Verkehrskapazität {capacity} ")
+
+plt.figure(figsize=(14, 10))
+for street_type, lanes_capacity in capacities.items():
+    for lanes, speeds_capacity in lanes_capacity.items():
+        for curvature, capacity in speeds_capacity.items():
+            plt.plot(list(capacity.keys()), list(capacity.values()),
+                     label=f"{osm_types[street_type]}, Fahrstreifen: {lanes} Kurvigkeit: {curvature}")
+
+# plt.figure(figsize=(14, 10))
+# for street_type, lanes_capacity in capacities.items():
+#     for lanes, speeds_capacity in lanes_capacity.items():
+#         for speed, curvatures_capacity in speeds_capacity.items():
+#             for curvature, capacity in curvatures_capacity.items():
+#                 plt.plot(speed, capacity,
+#                          label=f"{osm_types[street_type]}, Fahrstreifen: {lanes}, Kurvigkeit: {curvature}")
+
+speeds_compare = []
+capacity_compare = []
+for v in range(30, 130, 10):
+    speeds_compare.append(v)
+    capacity_compare.append(capacity_estimate(v))
+plt.plot(speeds_compare, capacity_compare, label="Referenz")
+
+plt.xlabel('Geschwindigkeit (km/h)')
+plt.ylabel('Verkehrskapazität')
+plt.title('Verkehrskapazität nach Straßentyp, Fahrstreifen und Geschwindigkeit')
+plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.2), fancybox=True, ncol=4)
+plt.subplots_adjust(bottom=0.3)
+plt.show()
+
+# fig = go.Figure()
+#
+# for street_type, lanes_capacity in capacities.items():
+#     for lanes, speeds_capacity in lanes_capacity.items():
+#         fig.add_trace(go.Scatter(x=list(speeds_capacity.keys()), y=list(speeds_capacity.values()),
+#                                  mode='lines',
+#                                  name=f"{osm_types[street_type]}, Fahrstreifen: {lanes}"))
+#
+# speeds_compare = []
+# capacity_compare = []
+# for v in range(50, 140, 10):
+#     speeds_compare.append(v)
+#     capacity_compare.append(capacity_estimate(v))
+# fig.add_trace(go.Scatter(x=speeds_compare, y=capacity_compare, mode='lines', showlegend=True, name="REFERENZ"))
+#
+# for qp in (0, 200, 400, 600):
+#     kontenpunkt_y = []
+#     kontenpunkt_y.append(merge(qp))
+#     kontenpunkt_y.append(merge(qp))
+#     fig.add_trace(go.Scatter(x=(30, 50), y=kontenpunkt_y, mode='lines', showlegend=True, name="Knotenpunkt %d" % qp))
+#
+# fig.update_layout(
+#     title='Verkehrskapazität nach Straßentyp, Fahrstreifen und Geschwindigkeit',
+#     xaxis_title='Geschwindigkeit (km/h)',
+#     yaxis_title='Verkehrskapazität',
+#     legend=dict(x=0.5, y=-0.2, orientation='h'),
+#     margin=dict(b=100)
+# )
+#
+# fig.show()
diff --git a/contribs/application/src/test/java/org/matsim/application/prepare/network/params/ApplyNetworkParamsTest.java b/contribs/application/src/test/java/org/matsim/application/prepare/network/params/ApplyNetworkParamsTest.java
index b25737d6c40..e9f723d6db8 100644
--- a/contribs/application/src/test/java/org/matsim/application/prepare/network/params/ApplyNetworkParamsTest.java
+++ b/contribs/application/src/test/java/org/matsim/application/prepare/network/params/ApplyNetworkParamsTest.java
@@ -40,6 +40,5 @@ void apply() throws Exception {
 		);
 
 		assertThat(output.resolve("network-opt.xml")).exists();
-
 	}
 }
diff --git a/contribs/application/src/test/java/org/matsim/application/prepare/network/params/HBSNetworkParamsTest.java b/contribs/application/src/test/java/org/matsim/application/prepare/network/params/HBSNetworkParamsTest.java
new file mode 100644
index 00000000000..ab6172db208
--- /dev/null
+++ b/contribs/application/src/test/java/org/matsim/application/prepare/network/params/HBSNetworkParamsTest.java
@@ -0,0 +1,44 @@
+package org.matsim.application.prepare.network.params;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.matsim.contrib.sumo.SumoNetworkConverter;
+import org.matsim.testcases.MatsimTestUtils;
+
+import java.nio.file.Path;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class HBSNetworkParamsTest {
+
+	@RegisterExtension
+	MatsimTestUtils utils = new MatsimTestUtils();
+
+	@Test
+	void apply() throws Exception {
+
+		Path networkPath = Path.of(utils.getPackageInputDirectory()).resolve("osm.net.xml");
+
+		Path output = Path.of(utils.getOutputDirectory());
+
+		SumoNetworkConverter converter = SumoNetworkConverter.newInstance(List.of(networkPath),
+			output.resolve("network.xml"),
+			"EPSG:4326", "EPSG:4326");
+
+		converter.call();
+
+		assertThat(output.resolve("network.xml")).exists();
+		assertThat(output.resolve("network-ft.csv")).exists();
+
+		new ApplyNetworkParams().execute(
+			"capacity",
+			"--network", output.resolve("network.xml").toString(),
+			"--input-features", output.resolve("network-ft.csv").toString(),
+			"--output", output.resolve("network-hbs.xml").toString(),
+			"--model", "org.matsim.application.prepare.network.params.hbs.HBSNetworkParams"
+		);
+
+		assertThat(output.resolve("network-hbs.xml")).exists();
+	}
+}
diff --git a/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractor.java b/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractor.java
index 7bf1154bf2d..ee8c9a45b87 100644
--- a/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractor.java
+++ b/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractor.java
@@ -1,5 +1,7 @@
 package org.matsim.contrib.sumo;
 
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
 import org.apache.commons.csv.CSVPrinter;
 import org.matsim.contrib.osm.networkReader.LinkProperties;
 
@@ -28,7 +30,7 @@ class SumoNetworkFeatureExtractor {
 		incomingEdges = new HashMap<>();
 
 		for (SumoNetworkHandler.Edge edge : this.handler.edges.values()) {
-			incomingEdges.computeIfAbsent(edge.to, (k) -> new ArrayList<>())
+			incomingEdges.computeIfAbsent(edge.to, k -> new ArrayList<>())
 				.add(edge);
 		}
 	}
@@ -77,6 +79,60 @@ private static Set<Character> directionSet(SumoNetworkHandler.Connection c) {
 		return set;
 	}
 
+	/**
+	 * Calculate the curvature of an edge. One gon is 1/400 of a full circle.
+	 * The formula is: KU = (Sum of the curvature of the subsegments) / (Length of the edge)
+	 *
+	 * @return curvature in gon/km
+	 */
+	static double calcCurvature(SumoNetworkHandler.Edge edge) {
+		double totalGon = 0;
+		List<double[]> coordinates = edge.shape;
+
+		for (int i = 2; i < coordinates.size(); i++) {
+			double[] pointA = coordinates.get(i - 2);
+			double[] pointB = coordinates.get(i - 1);
+			double[] pointC = coordinates.get(i);
+
+			double[] vectorAB = {pointB[0] - pointA[0], pointB[1] - pointA[1]};
+			double[] vectorBC = {pointC[0] - pointB[0], pointC[1] - pointB[1]};
+
+			double dotProduct = calcDotProduct(vectorAB, vectorBC);
+			double magnitudeAB = calcMagnitude(vectorAB);
+			double magnitudeBC = calcMagnitude(vectorBC);
+
+			double cosine = dotProduct / (magnitudeAB * magnitudeBC);
+			double angleRadians = Math.acos(cosine);
+			double angleDegrees = Math.toDegrees(angleRadians);
+
+			totalGon += Math.abs((angleDegrees / 360) * 400);
+		}
+
+		return totalGon / (edge.getLength() / 1000);
+	}
+
+	/**
+	 * Calculate the dot product of two vectors.
+	 *
+	 * @param vec1 vector 1
+	 * @param vec2 vector 2
+	 * @return dot product
+	 */
+	private static double calcDotProduct(double[] vec1, double[] vec2) {
+		return vec1[0] * vec2[0] + vec1[1] * vec2[1];
+	}
+
+	/**
+	 * Calculate the magnitude of a vector.
+	 *
+	 * @param vec vector
+	 * @return magnitude
+	 */
+	private static double calcMagnitude(double[] vec) {
+		return Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]);
+	}
+
+
 	/**
 	 * Get priority. Higher is more important.
 	 */
@@ -86,9 +142,12 @@ private int getPrio(SumoNetworkHandler.Edge edge) {
 
 	public List<String> getHeader() {
 		return List.of("linkId", "highway_type", "speed", "length", "num_lanes", "change_num_lanes", "change_speed", "num_to_links", "num_conns",
-			"num_response", "num_foes", "dir_multiple_s", "dir_l", "dir_r", "dir_s", "dir_exclusive",
+			"num_response", "num_foes", "dir_multiple_s", "dir_l", "dir_r", "dir_s", "dir_exclusive", "curvature",
 			"junction_type", "junction_inc_lanes", "priority_higher", "priority_equal", "priority_lower",
-			"is_secondary_or_higher", "is_primary_or_higher", "is_motorway", "is_link");
+			"is_secondary_or_higher", "is_primary_or_higher", "is_motorway",
+			"is_link", "has_merging_link", "is_merging_into",
+			"num_left", "num_right", "num_straight"
+		);
 	}
 
 	public void print(CSVPrinter out) {
@@ -106,7 +165,7 @@ public void print(CSVPrinter out, String linkId, SumoNetworkHandler.Edge edge) t
 		String highwayType = getHighwayType(edge.type);
 
 		SumoNetworkHandler.Junction junction = handler.junctions.get(edge.to);
-		List<SumoNetworkHandler.Connection> connections = handler.connections.computeIfAbsent(edge.id, (k) -> new ArrayList<>());
+		List<SumoNetworkHandler.Connection> connections = handler.connections.computeIfAbsent(edge.id, k -> new ArrayList<>());
 
 		Set<SumoNetworkHandler.Edge> toEdges = connections.stream()
 			.filter(c -> !c.dir.equals("t"))
@@ -129,12 +188,18 @@ public void print(CSVPrinter out, String linkId, SumoNetworkHandler.Edge edge) t
 		Set<Character> dirs = new HashSet<>();
 		boolean multipleDirS = false;
 		boolean exclusiveDirs = true;
+
+		// Number of connections per direction
+		Object2IntMap<Character> numConnections = new Object2IntOpenHashMap<>();
+
 		for (SumoNetworkHandler.Connection c : connections) {
 
 			Set<Character> d = directionSet(c);
 			if (dirs.contains('s') && d.contains('s'))
 				multipleDirS = true;
 
+			d.forEach(dir -> numConnections.mergeInt(dir, 1, Integer::sum));
+
 			Set<Character> intersection = new HashSet<>(dirs); // use the copy constructor
 			intersection.retainAll(d);
 			if (!intersection.isEmpty())
@@ -152,12 +217,27 @@ public void print(CSVPrinter out, String linkId, SumoNetworkHandler.Edge edge) t
 			.mapToInt(e -> e.lanes.size())
 			.sum();
 
-		boolean geq_secondary = switch (highwayType) {
+		boolean merging = incomingEdges.get(junction.id).stream()
+			.anyMatch(e -> e.type.contains("link"));
+
+		OptionalInt highestPrio = incomingEdges.get(junction.id).stream()
+			.mapToInt(this::getPrio).max();
+
+		// Find category of the highest merging lane
+		String mergingHighest = "";
+		if (highestPrio.isPresent() && highestPrio.getAsInt() > getPrio(edge)) {
+			Optional<Map.Entry<String, LinkProperties>> m = osm.entrySet().stream().filter(e -> e.getValue().getHierarchyLevel() == -highestPrio.getAsInt())
+				.findFirst();
+			if (m.isPresent())
+				mergingHighest = m.get().getKey();
+		}
+
+		boolean geqSecondary = switch (highwayType) {
 			case "secondary", "primary", "trunk", "motorway" -> true;
 			default -> false;
 		};
 
-		boolean geq_primary = switch (highwayType) {
+		boolean geqPrimary = switch (highwayType) {
 			case "primary", "trunk", "motorway" -> true;
 			default -> false;
 		};
@@ -170,7 +250,7 @@ public void print(CSVPrinter out, String linkId, SumoNetworkHandler.Edge edge) t
 		out.print(Math.max(-3, Math.min(3, maxLanes - edge.lanes.size())));
 		out.print(maxSpeed - edge.lanes.get(0).speed);
 		out.print(toEdges.size());
-		out.print(Math.min(6, handler.connections.get(edge.id).size()));
+		out.print(Math.min(6, connections.size()));
 		out.print(Math.min(12, aggr.response().cardinality()));
 		out.print(Math.min(12, aggr.foes().cardinality()));
 		out.print(bool(multipleDirS));
@@ -178,15 +258,21 @@ public void print(CSVPrinter out, String linkId, SumoNetworkHandler.Edge edge) t
 		out.print(bool(dirs.contains('r')));
 		out.print(bool(dirs.contains('s')));
 		out.print(bool(exclusiveDirs));
+		out.print(calcCurvature(edge));
 		out.print(junction.type);
 		out.print(Math.min(12, incomingLanes));
 		out.print(bool("higher".equals(prio)));
 		out.print(bool("equal".equals(prio)));
 		out.print(bool("lower".equals(prio)));
-		out.print(bool(geq_secondary));
-		out.print(bool(geq_primary));
+		out.print(bool(geqSecondary));
+		out.print(bool(geqPrimary));
 		out.print(bool("motorway".equals(highwayType)));
 		out.print(bool(highwayType.contains("link")));
+		out.print(bool(merging));
+		out.print(mergingHighest);
+		out.print(numConnections.getInt('l'));
+		out.print(numConnections.getInt('r'));
+		out.print(numConnections.getInt('s'));
 
 		out.println();
 	}
diff --git a/contribs/sumo/src/test/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractorTest.java b/contribs/sumo/src/test/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractorTest.java
new file mode 100644
index 00000000000..e8b41e359da
--- /dev/null
+++ b/contribs/sumo/src/test/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractorTest.java
@@ -0,0 +1,81 @@
+package org.matsim.contrib.sumo;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class SumoNetworkFeatureExtractorTest {
+
+	@Test
+	void twoCoordsBackAndForth() {
+		String[] coords = {"0,0", "0,100", "0,0"};
+		int lentgh = 200;
+
+		SumoNetworkHandler.Edge edge = new SumoNetworkHandler.Edge("1", "2", "3", "highway", 0, "name", coords);
+		edge.lanes.add(new SumoNetworkHandler.Lane("1", 0, lentgh, 50 / 3.6, null, null));
+
+		double ku = SumoNetworkFeatureExtractor.calcCurvature(edge);
+
+		// Length: 0.2 km
+		// Gon: 200
+		// Curvature: 200 / 0.2 = 1000
+
+		Assertions.assertEquals(1000, ku, 0.000001);
+
+		System.out.println(ku);
+	}
+
+
+	@Test
+	void nintyDegreeCorner() {
+
+		String[] coords = {"0,0", "0,100", "100,100"};
+		int lentgh = 200;
+
+		SumoNetworkHandler.Edge edge = new SumoNetworkHandler.Edge("1", "2", "3", "highway", 0, "name", coords);
+		edge.lanes.add(new SumoNetworkHandler.Lane("1", 0, lentgh, 50 / 3.6, null, null));
+
+		double ku = SumoNetworkFeatureExtractor.calcCurvature(edge);
+
+		// Length: 0.2 km
+		// Gon: 100
+		// Curvature: 100 / 0.2 = 500
+
+		Assertions.assertEquals(500, ku, 0.000001);
+	}
+
+	@Test
+	void twoCorners() {
+
+		String[] coords = {"0,0", "0,100", "100,100", "100,200"};
+		int lentgh = 300;
+
+		SumoNetworkHandler.Edge edge = new SumoNetworkHandler.Edge("1", "2", "3", "highway", 0, "name", coords);
+		edge.lanes.add(new SumoNetworkHandler.Lane("1", 0, lentgh, 50 / 3.6, null, null));
+
+		double ku = SumoNetworkFeatureExtractor.calcCurvature(edge);
+
+		// Length: 0.3 km
+		// Gon: 100 + 100 = 200
+		// Curvature: 200 / 0.3 = 666.6666666666666
+
+		Assertions.assertEquals((200 / 0.3), ku, 0.000001);
+	}
+
+	@Test
+	void rectangle() {
+
+		String[] coords = {"0,0", "0,100", "100,100", "100,0", "0,0"};
+		int lentgh = 400;
+
+		SumoNetworkHandler.Edge edge = new SumoNetworkHandler.Edge("1", "2", "3", "highway", 0, "name", coords);
+		edge.lanes.add(new SumoNetworkHandler.Lane("1", 0, lentgh, 50 / 3.6, null, null));
+
+		double ku = SumoNetworkFeatureExtractor.calcCurvature(edge);
+
+		// Length: 0.4 km
+		// Gon: 100 + 100 + 100 = 300
+		// Curvature: 300 / 0.4 = 666.6666666666666
+
+		Assertions.assertEquals((300 / 0.4), ku, 0.000001);
+	}
+}

From f4e4a6f1590f09b7ae989b6d19551dbbd60d5084 Mon Sep 17 00:00:00 2001
From: NicDul <71266428+NicDul@users.noreply.github.com>
Date: Tue, 28 May 2024 11:30:46 +0200
Subject: [PATCH 2/2] Added a test scenario for a construction site between
 Morges and Renens on the line Lausanne - Geneva (#3283)

Co-authored-by: u238053 <nicolas.dulex@sbb.ch>
Co-authored-by: rakow <rakow@users.noreply.github.com>
---
 .../integration/RailsimIntegrationTest.java   |  11 +-
 .../config.xml                                |  39 ++
 .../trainNetwork.xml                          | 319 +++++++++
 .../transitSchedule.xml                       | 605 ++++++++++++++++++
 .../transitVehicles.xml                       | 191 ++++++
 5 files changed, 1162 insertions(+), 3 deletions(-)
 create mode 100644 contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/config.xml
 create mode 100644 contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/trainNetwork.xml
 create mode 100644 contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/transitSchedule.xml
 create mode 100644 contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/transitVehicles.xml

diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/RailsimIntegrationTest.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/RailsimIntegrationTest.java
index 7adbb41fc31..4bf88ef4c05 100644
--- a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/RailsimIntegrationTest.java
+++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/RailsimIntegrationTest.java
@@ -373,9 +373,9 @@ void testScenarioKelheim() {
 			type.setMaximumVelocity(30);
 			type.setLength(100);
 		}
-		
+
 		// simplify the activity types, e.g. home_3600 -> home
-		Set<String> activityTypes = new HashSet<>();	
+		Set<String> activityTypes = new HashSet<>();
 		for (Person person : scenario.getPopulation().getPersons().values()) {
 			for (Plan plan : person.getPlans()) {
 				for (PlanElement pE : plan.getPlanElements()) {
@@ -388,7 +388,7 @@ void testScenarioKelheim() {
 				}
 			}
 		}
-		
+
 		for (String type : activityTypes) {
 			config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams(type).setTypicalDuration(1234.));
 		}
@@ -405,6 +405,11 @@ void testScenarioMicroMesoCombination() {
 		EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "scenarioMicroMesoCombination"));
 	}
 
+	@Test
+	void testScenarioMicroMesoConstructionSiteLsGe() {
+		EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "scenarioMicroMesoConstructionSiteLsGe"));
+	}
+
 	private EventsCollector runSimulation(File scenarioDir) {
 		return runSimulation(scenarioDir, null);
 	}
diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/config.xml
new file mode 100644
index 00000000000..da3761b5c4e
--- /dev/null
+++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/config.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" ?>
+<!DOCTYPE config SYSTEM "http://www.matsim.org/files/dtd/config_v2.dtd">
+<config>
+
+	<module name="global">
+		<param name="randomSeed" value="4711"/>
+		<param name="coordinateSystem" value="Atlantis"/>
+	</module>
+
+	<module name="controler">
+		<param name="runId" value="test"/>
+		<param name="firstIteration" value="0"/>
+	</module>
+
+	<module name="qsim">
+		<!-- "start/endTime" of MobSim (00:00:00 == take earliest activity time/ run as long as active vehicles exist) -->
+		<param name="startTime" value="00:00:00"/>
+		<param name="endTime" value="24:00:00"/>
+
+		<param name="snapshotperiod" value="00:00:00"/> <!-- 00:00:00 means NO snapshot writing -->
+		<param name="mainMode" value="car"/>
+
+		<!-- time in seconds.  Time after which the frontmost vehicle on a link is called `stuck' if it does not move. -->
+		<param name="stuckTime" value="999999."/>
+	</module>
+
+	<module name="transit">
+		<param name="routingAlgorithmType" value="SwissRailRaptor"/>
+		<param name="transitScheduleFile" value="transitSchedule.xml"/>
+		<param name="useTransit" value="true"/>
+		<param name="usingTransitInMobsim" value="true"/>
+		<param name="vehiclesFile" value="transitVehicles.xml"/>
+	</module>
+
+	<module name="network">
+		<param name="inputNetworkFile" value="trainNetwork.xml"/>
+	</module>
+
+</config>
diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/trainNetwork.xml
new file mode 100644
index 00000000000..731eca24217
--- /dev/null
+++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/trainNetwork.xml
@@ -0,0 +1,319 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE network SYSTEM "http://www.matsim.org/files/dtd/network_v2.dtd">
+<network name="test network">
+
+	<!-- ====================================================================== -->
+
+	<!-- Network Lausanne - Genève, the whole line is mesoscopic except the portion between MOR and REN, where it is divided into two microscopic links.
+	All the stations are 400m long
+	The following convention applies to all stations : (Node : Station1) - (Stop : Station) - (Node : Station2)
+																																		            /<=1=>\
+	GE1 <=7=> GE2 <=3=> COP1 <=3=> COP2 <=2=> NY1 <=3=> NY2 <=2=> GLA1 <=2=> GLA2 <=2=> ROL1 <=2=> ROL2 <=2=> ALL1 <=3=> ALL2 <=2=> MOR1 <=4=> MOR2        REN1 <=5=> REN2 <=4=> LS1 <=7=> LS2
+																																					\<=1=>/
+
+	-->
+
+	<!-- ====================================================================== -->
+
+	<nodes>
+		<node id="GE1" x="-60660" y="0"></node>
+		<node id="GE2" x="-60260" y="0"></node>
+		<node id="COP1" x="-47200" y="0"></node>
+		<node id="COP2" x="-46800" y="0"></node>
+		<node id="NY1" x="-38730" y="0"></node>
+		<node id="NY2" x="-38330" y="0"></node>
+		<node id="GLA1" x="-33990" y="0"></node>
+		<node id="GLA2" x="-33590" y="0"></node>
+		<node id="ROL1" x="-26860" y="0"></node>
+		<node id="ROL2" x="-26460" y="0"></node>
+		<node id="ALL1" x="-21680" y="0"></node>
+		<node id="ALL2" x="-21280" y="0"></node>
+		<node id="MOR1" x="-12700" y="0"></node>
+		<node id="MOR2" x="-12300" y="0"></node>
+		<node id="REN1" x="-4710" y="0"></node>
+		<node id="REN2" x="-4310" y="0"></node>
+		<node id="LS1" x="0" y="0"></node>
+		<node id="LS2" x="400" y="0"></node>
+	</nodes>
+
+	<!-- ====================================================================== -->
+
+	<links capperiod="01:00:00" effectivecellsize="7.5" effectivelanewidth="3.75">
+		<link id="GE1-GE2" from="GE1" to="GE2" length="400" freespeed="32" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">GE1GE2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">7</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="GE2-GE1" from="GE2" to="GE1" length="400" freespeed="32" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">GE1GE2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">7</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="GE2-COP1" from="GE2" to="COP1" length="13060" freespeed="32" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">GE2COP1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">3</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="COP1-GE2" from="COP1" to="GE2" length="13060" freespeed="32" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">GE2COP1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">3</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="COP1-COP2" from="COP1" to="COP2" length="400" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">COP1COP2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">3</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="COP2-COP1" from="COP2" to="COP1" length="400" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">COP1COP2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">3</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+
+		<link id="COP2-NY1" from="COP2" to="NY1" length="8070" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">COP2NY1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="NY1-COP2" from="NY1" to="COP2" length="8070" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">COP2NY1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="NY1-NY2" from="NY1" to="NY2" length="400" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">NY1NY2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">3</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="NY2-NY1" from="NY2" to="NY1" length="400" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">NY1NY2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">3</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="NY2-GLA1" from="NY2" to="GLA1" length="4340" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">NY2GLA1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="GLA1-NY2" from="GLA1" to="NY2" length="4340" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">NY2GLA1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="GLA1-GLA2" from="GLA1" to="GLA2" length="400" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">GLA1GLA2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="GLA2-GLA1" from="GLA2" to="GLA1" length="400" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">GLA1GLA2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="GLA2-ROL1" from="GLA2" to="ROL1" length="6730" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">GLA2ROL1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="ROL1-GLA2" from="ROL1" to="GLA2" length="6730" freespeed="38" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">GLA2ROL1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+
+		<link id="ROL1-ROL2" from="ROL1" to="ROL2" length="400" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">ROL1ROL2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="ROL2-ROL1" from="ROL2" to="ROL1" length="400" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">ROL1ROL2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="ROL2-ALL1" from="ROL2" to="ALL1" length="4780" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">ROL2ALL1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="ALL1-ROL2" from="ALL1" to="ROL2" length="4780" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">ROL2ALL1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="ALL1-ALL2" from="ALL1" to="ALL2" length="400" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">ALL1ALL2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">3</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="ALL2-ALL1" from="ALL2" to="ALL1" length="400" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">ALL1ALL2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">3</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="ALL2-MOR1" from="ALL2" to="MOR1" length="8580" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">ALL2MOR1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="MOR1-ALL2" from="MOR1" to="ALL2" length="8580" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">ALL2MOR1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="MOR1-MOR2" from="MOR1" to="MOR2" length="400" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">MOR1MOR2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">4</attribute>
+				<attribute name="railsimEntry" class="java.lang.Boolean">true</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="MOR2-MOR1" from="MOR2" to="MOR1" length="400" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">MOR1MOR2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">4</attribute>
+				<attribute name="railsimExit" class="java.lang.Boolean">true</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="MOR2_REN1-1" from="MOR2" to="REN1" length="7590" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">MOR2REN1-1</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="REN1-MOR2-1" from="REN1" to="MOR2" length="7590" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">MOR2REN1-1</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="MOR2_REN1-2" from="MOR2" to="REN1" length="7590" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">MOR2REN1-2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="REN1-MOR2-2" from="REN1" to="MOR2" length="7590" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">MOR2REN1-2</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+
+		<link id="REN1-REN2" from="REN1" to="REN2" length="400" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">REN1REN2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">5</attribute>
+				<attribute name="railsimExit" class="java.lang.Boolean">true</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="REN2-REN1" from="REN2" to="REN1" length="400" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">REN1REN2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">5</attribute>
+				<attribute name="railsimEntry" class="java.lang.Boolean">true</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="REN2-LS1" from="REN2" to="LS1" length="4310" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">REN2LS1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">4</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="LS1-REN2" from="LS1" to="REN2" length="4310" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">REN2LS1</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">4</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+		<link id="LS1-LS2" from="LS1" to="LS2" length="400" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">LS1LS2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">7</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+		<link id="LS2-LS1" from="LS2" to="LS1" length="400" freespeed="29" capacity="3600.0" permlanes="1.0" oneway="1" modes="rail">
+			<attributes>
+				<attribute name="railsimResourceId" class="java.lang.String">LS1LS2</attribute>
+				<attribute name="railsimTrainCapacity" class="java.lang.Integer">7</attribute>
+				<attribute name="railsimResourceType" class="java.lang.String">fixedBlock</attribute>
+			</attributes>
+		</link>
+
+
+	</links>
+
+	<!-- ====================================================================== -->
+
+</network>
diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/transitSchedule.xml
new file mode 100644
index 00000000000..19b28847fff
--- /dev/null
+++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/transitSchedule.xml
@@ -0,0 +1,605 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE transitSchedule SYSTEM "http://www.matsim.org/files/dtd/transitSchedule_v2.dtd">
+
+<transitSchedule>
+
+	<!-- ====================================================================== -->
+
+	<!-- The schedule is taken from : https://network.sbb.ch/en/
+	Naming convention : Line-DepartureViaArrival-departure
+
+	In order to model a construction site between Morges and Renens, a new stop is created in the middle of the link, and a construction train stops there during the planned time, thus reducing the capacity of the link.
+
+	-->
+
+	<!-- ====================================================================== -->
+
+	<transitStops>
+		<stopFacility id="LS" x="0" y="0" linkRefId="LS1-LS2" stopAreaId="LS">
+		</stopFacility>
+		<stopFacility id="REN" x="-4510" y="0" linkRefId="REN1-REN2" stopAreaId="REN">
+		</stopFacility>
+		<stopFacility id="MOR" x="-12500" y="0" linkRefId="MOR1-MOR2" stopAreaId="MOR">
+		</stopFacility>
+		<stopFacility id="ALL" x="-21480" y="0" linkRefId="ALL1-ALL2" stopAreaId="ALL">
+		</stopFacility>
+		<stopFacility id="ROL" x="-26660" y="0" linkRefId="ROL1-ROL2" stopAreaId="ROL">
+		</stopFacility>
+		<stopFacility id="GLA" x="-33790" y="0" linkRefId="GLA1-GLA2" stopAreaId="GLA">
+		</stopFacility>
+		<stopFacility id="NY" x="-38530" y="0" linkRefId="NY1-NY2" stopAreaId="NY">
+		</stopFacility>
+		<stopFacility id="COP" x="-47000" y="0" linkRefId="COP1-COP2" stopAreaId="COP">
+		</stopFacility>
+		<stopFacility id="GE" x="-60260" y="0" linkRefId="GE1-GE2" stopAreaId="GE">
+		</stopFacility>
+		<stopFacility id="LS1" x="0" y="0" linkRefId="LS2-LS1" stopAreaId="LS">
+		</stopFacility>
+		<stopFacility id="REN1" x="-4510" y="0" linkRefId="REN2-REN1" stopAreaId="REN">
+		</stopFacility>
+		<stopFacility id="MOR1" x="-12500" y="0" linkRefId="MOR2-MOR1" stopAreaId="MOR">
+		</stopFacility>
+		<stopFacility id="ALL1" x="-21480" y="0" linkRefId="ALL2-ALL1" stopAreaId="ALL">
+		</stopFacility>
+		<stopFacility id="ROL1" x="-26660" y="0" linkRefId="ROL2-ROL1" stopAreaId="ROL">
+		</stopFacility>
+		<stopFacility id="GLA1" x="-33790" y="0" linkRefId="GLA2-GLA1" stopAreaId="GLA">
+		</stopFacility>
+		<stopFacility id="NY1" x="-38530" y="0" linkRefId="NY2-NY1" stopAreaId="NY">
+		</stopFacility>
+		<stopFacility id="COP1" x="-47000" y="0" linkRefId="COP2-COP1" stopAreaId="COP">
+		</stopFacility>
+		<stopFacility id="GE1" x="-60260" y="0" linkRefId="GE2-GE1" stopAreaId="GE">
+		</stopFacility>
+
+
+		<stopFacility id="Construction_Site_MOR_REN" x="-10000" y="0" linkRefId="MOR2_REN1-1" stopAreaId="BAU">
+		</stopFacility>
+		<stopFacility id="Construction_Site_MOR_REN2" x="-10000" y="0" linkRefId="MOR2_REN1-2" stopAreaId="BAU">
+		</stopFacility>
+
+	</transitStops>
+
+	<transitLine id="RE33">
+		<transitRoute id="RE33-ANNGELS">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="GE" arrivalOffset="00:00:00" departureOffset="00:03:00" awaitDeparture="true"/>
+				<stop refId="COP" arrivalOffset="00:12:00" departureOffset="00:13:00" awaitDeparture="true"/>
+				<stop refId="NY" arrivalOffset="00:18:00" departureOffset="00:19:00" awaitDeparture="true"/>
+				<stop refId="GLA" arrivalOffset="00:22:00" departureOffset="00:23:00" awaitDeparture="true"/>
+				<stop refId="ROL" arrivalOffset="00:27:00" departureOffset="00:28:00" awaitDeparture="true"/>
+				<stop refId="ALL" arrivalOffset="00:31:00" departureOffset="00:32:00" awaitDeparture="true"/>
+				<stop refId="MOR" arrivalOffset="00:37:00" departureOffset="00:38:00" awaitDeparture="true"/>
+				<stop refId="REN" arrivalOffset="00:44:00" departureOffset="00:45:00" awaitDeparture="true"/>
+				<stop refId="LS" arrivalOffset="00:51:00" departureOffset="00:54:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="GE1-GE2"/>
+				<link refId="GE2-COP1"/>
+				<link refId="COP1-COP2"/>
+				<link refId="COP2-NY1"/>
+				<link refId="NY1-NY2"/>
+				<link refId="NY2-GLA1"/>
+				<link refId="GLA1-GLA2"/>
+				<link refId="GLA2-ROL1"/>
+				<link refId="ROL1-ROL2"/>
+				<link refId="ROL2-ALL1"/>
+				<link refId="ALL1-ALL2"/>
+				<link refId="ALL2-MOR1"/>
+				<link refId="MOR1-MOR2"/>
+				<link refId="MOR2_REN1-1"/>
+				<link refId="REN1-REN2"/>
+				<link refId="REN2-LS1"/>
+				<link refId="LS1-LS2"/>
+			</route>
+			<departures>
+				<departure id="RE33-ANNGELS-8" departureTime="08:47:00" vehicleRefId="RE33-ANNGELS-8"/>
+				<departure id="RE33-ANNGELS-9" departureTime="09:47:00" vehicleRefId="RE33-ANNGELS-9"/>
+				<departure id="RE33-ANNGELS-10" departureTime="10:47:00" vehicleRefId="RE33-ANNGELS-10"/>
+				<departure id="RE33-ANNGELS-11" departureTime="11:47:00" vehicleRefId="RE33-ANNGELS-11"/>
+				<departure id="RE33-ANNGELS-12" departureTime="12:47:00" vehicleRefId="RE33-ANNGELS-12"/>
+			</departures>
+		</transitRoute>
+		<transitRoute id="RE33-ANNGESM">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="GE" arrivalOffset="00:00:00" departureOffset="00:03:00" awaitDeparture="true"/>
+				<stop refId="COP" arrivalOffset="00:12:00" departureOffset="00:13:00" awaitDeparture="true"/>
+				<stop refId="NY" arrivalOffset="00:18:00" departureOffset="00:19:00" awaitDeparture="true"/>
+				<stop refId="GLA" arrivalOffset="00:22:00" departureOffset="00:23:00" awaitDeparture="true"/>
+				<stop refId="ROL" arrivalOffset="00:27:00" departureOffset="00:28:00" awaitDeparture="true"/>
+				<stop refId="ALL" arrivalOffset="00:31:00" departureOffset="00:32:00" awaitDeparture="true"/>
+				<stop refId="MOR" arrivalOffset="00:37:00" departureOffset="00:38:00" awaitDeparture="true"/>
+				<stop refId="REN" arrivalOffset="00:44:00" departureOffset="00:45:00" awaitDeparture="true"/>
+				<stop refId="LS" arrivalOffset="00:51:00" departureOffset="00:54:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="GE1-GE2"/>
+				<link refId="GE2-COP1"/>
+				<link refId="COP1-COP2"/>
+				<link refId="COP2-NY1"/>
+				<link refId="NY1-NY2"/>
+				<link refId="NY2-GLA1"/>
+				<link refId="GLA1-GLA2"/>
+				<link refId="GLA2-ROL1"/>
+				<link refId="ROL1-ROL2"/>
+				<link refId="ROL2-ALL1"/>
+				<link refId="ALL1-ALL2"/>
+				<link refId="ALL2-MOR1"/>
+				<link refId="MOR1-MOR2"/>
+				<link refId="MOR2_REN1-1"/>
+				<link refId="REN1-REN2"/>
+				<link refId="REN2-LS1"/>
+				<link refId="LS1-LS2"/>
+			</route>
+			<departures>
+				<departure id="RE33-ANNGESM-8" departureTime="08:17:00" vehicleRefId="RE33-ANNGESM-8"/>
+				<departure id="RE33-ANNGESM-9" departureTime="09:17:00" vehicleRefId="RE33-ANNGESM-9"/>
+				<departure id="RE33-ANNGESM-10" departureTime="10:17:00" vehicleRefId="RE33-ANNGESM-10"/>
+				<departure id="RE33-ANNGESM-11" departureTime="11:17:00" vehicleRefId="RE33-ANNGESM-11"/>
+				<departure id="RE33-ANNGESM-12" departureTime="12:17:00" vehicleRefId="RE33-ANNGESM-12"/>
+			</departures>
+		</transitRoute>
+
+		<transitRoute id="RE33-VVGEANN">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="LS1" arrivalOffset="00:00:00" departureOffset="00:02:00" awaitDeparture="true"/>
+				<stop refId="REN1" arrivalOffset="00:08:00" departureOffset="00:08:00" awaitDeparture="true"/>
+				<stop refId="MOR1" arrivalOffset="00:15:00" departureOffset="00:16:00" awaitDeparture="true"/>
+				<stop refId="ALL1" arrivalOffset="00:21:00" departureOffset="00:22:00" awaitDeparture="true"/>
+				<stop refId="ROL1" arrivalOffset="00:25:00" departureOffset="00:26:00" awaitDeparture="true"/>
+				<stop refId="GLA1" arrivalOffset="00:31:00" departureOffset="00:32:00" awaitDeparture="true"/>
+				<stop refId="NY1" arrivalOffset="00:35:00" departureOffset="00:36:00" awaitDeparture="true"/>
+				<stop refId="COP1" arrivalOffset="00:41:00" departureOffset="00:42:00" awaitDeparture="true"/>
+				<stop refId="GE1" arrivalOffset="00:51:00" departureOffset="00:52:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="LS2-LS1"/>
+				<link refId="LS1-REN2"/>
+				<link refId="REN2-REN1"/>
+				<link refId="REN1-MOR2-2"/>
+				<link refId="MOR2-MOR1"/>
+				<link refId="MOR1-ALL2"/>
+				<link refId="ALL2-ALL1"/>
+				<link refId="ALL1-ROL2"/>
+				<link refId="ROL2-ROL1"/>
+				<link refId="ROL1-GLA2"/>
+				<link refId="GLA2-GLA1"/>
+				<link refId="GLA1-NY2"/>
+				<link refId="NY2-NY1"/>
+				<link refId="NY1-COP2"/>
+				<link refId="COP2-COP1"/>
+				<link refId="COP1-GE2"/>
+				<link refId="GE2-GE1"/>
+			</route>
+			<departures>
+				<departure id="RE33-VVGEANN-8" departureTime="08:19:00" vehicleRefId="RE33-VVGEANN-8"/>
+				<departure id="RE33-VVGEANN-9" departureTime="09:19:00" vehicleRefId="RE33-VVGEANN-9"/>
+				<departure id="RE33-VVGEANN-10" departureTime="10:19:00" vehicleRefId="RE33-VVGEANN-10"/>
+				<departure id="RE33-VVGEANN-11" departureTime="11:19:00" vehicleRefId="RE33-VVGEANN-11"/>
+				<departure id="RE33-VVGEANN-12" departureTime="12:19:00" vehicleRefId="RE33-VVGEANN-12"/>
+			</departures>
+		</transitRoute>
+
+		<transitRoute id="RE33-SMGEANN">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="LS1" arrivalOffset="00:00:00" departureOffset="00:03:00" awaitDeparture="true"/>
+				<stop refId="REN1" arrivalOffset="00:09:00" departureOffset="00:09:00" awaitDeparture="true"/>
+				<stop refId="MOR1" arrivalOffset="00:16:00" departureOffset="00:17:00" awaitDeparture="true"/>
+				<stop refId="ALL1" arrivalOffset="00:22:00" departureOffset="00:23:00" awaitDeparture="true"/>
+				<stop refId="ROL1" arrivalOffset="00:26:00" departureOffset="00:27:00" awaitDeparture="true"/>
+				<stop refId="GLA1" arrivalOffset="00:32:00" departureOffset="00:33:00" awaitDeparture="true"/>
+				<stop refId="NY1" arrivalOffset="00:36:00" departureOffset="00:37:00" awaitDeparture="true"/>
+				<stop refId="COP1" arrivalOffset="00:42:00" departureOffset="00:43:00" awaitDeparture="true"/>
+				<stop refId="GE1" arrivalOffset="00:52:00" departureOffset="00:54:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="LS2-LS1"/>
+				<link refId="LS1-REN2"/>
+				<link refId="REN2-REN1"/>
+				<link refId="REN1-MOR2-2"/>
+				<link refId="MOR2-MOR1"/>
+				<link refId="MOR1-ALL2"/>
+				<link refId="ALL2-ALL1"/>
+				<link refId="ALL1-ROL2"/>
+				<link refId="ROL2-ROL1"/>
+				<link refId="ROL1-GLA2"/>
+				<link refId="GLA2-GLA1"/>
+				<link refId="GLA1-NY2"/>
+				<link refId="NY2-NY1"/>
+				<link refId="NY1-COP2"/>
+				<link refId="COP2-COP1"/>
+				<link refId="COP1-GE2"/>
+				<link refId="GE2-GE1"/>
+			</route>
+			<departures>
+				<departure id="RE33-SMGEANN-8" departureTime="08:48:00" vehicleRefId="RE33-SMGEANN-8"/>
+				<departure id="RE33-SMGEANN-9" departureTime="09:48:00" vehicleRefId="RE33-SMGEANN-9"/>
+				<departure id="RE33-SMGEANN-10" departureTime="10:48:00" vehicleRefId="RE33-SMGEANN-10"/>
+				<departure id="RE33-SMGEANN-11" departureTime="11:48:00" vehicleRefId="RE33-SMGEANN-11"/>
+				<departure id="RE33-SMGEANN-12" departureTime="12:48:00" vehicleRefId="RE33-SMGEANN-12"/>
+			</departures>
+		</transitRoute>
+
+	</transitLine>
+
+	<transitLine id="IR90">
+		<transitRoute id="IR90-GEAPGEBR1">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="GE" arrivalOffset="00:00:00" departureOffset="00:02:00" awaitDeparture="true"/>
+				<stop refId="LS" arrivalOffset="00:39:00" departureOffset="00:42:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="GE1-GE2"/>
+				<link refId="GE2-COP1"/>
+				<link refId="COP1-COP2"/>
+				<link refId="COP2-NY1"/>
+				<link refId="NY1-NY2"/>
+				<link refId="NY2-GLA1"/>
+				<link refId="GLA1-GLA2"/>
+				<link refId="GLA2-ROL1"/>
+				<link refId="ROL1-ROL2"/>
+				<link refId="ROL2-ALL1"/>
+				<link refId="ALL1-ALL2"/>
+				<link refId="ALL2-MOR1"/>
+				<link refId="MOR1-MOR2"/>
+				<link refId="MOR2_REN1-1"/>
+				<link refId="REN1-REN2"/>
+				<link refId="REN2-LS1"/>
+				<link refId="LS1-LS2"/>
+			</route>
+			<departures>
+				<departure id="IR90-GEAPGEBR1-8" departureTime="08:08:00" vehicleRefId="IR90-GEAPGEBR1-8"/>
+				<departure id="IR90-GEAPGEBR1-9" departureTime="09:08:00" vehicleRefId="IR90-GEAPGEBR1-9"/>
+				<departure id="IR90-GEAPGEBR1-10" departureTime="10:08:00" vehicleRefId="IR90-GEAPGEBR1-10"/>
+				<departure id="IR90-GEAPGEBR1-11" departureTime="11:08:00" vehicleRefId="IR90-GEAPGEBR1-11"/>
+				<departure id="IR90-GEAPGEBR1-12" departureTime="12:08:00" vehicleRefId="IR90-GEAPGEBR1-12"/>
+			</departures>
+		</transitRoute>
+		<transitRoute id="IR90-GEAPGEBR2">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="GE" arrivalOffset="00:00:00" departureOffset="00:03:00" awaitDeparture="true"/>
+				<stop refId="NY" arrivalOffset="00:17:00" departureOffset="00:17:00" awaitDeparture="true"/>
+				<stop refId="MOR" arrivalOffset="00:34:00" departureOffset="00:34:00" awaitDeparture="true"/>
+				<stop refId="LS" arrivalOffset="00:45:00" departureOffset="00:55:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="GE1-GE2"/>
+				<link refId="GE2-COP1"/>
+				<link refId="COP1-COP2"/>
+				<link refId="COP2-NY1"/>
+				<link refId="NY1-NY2"/>
+				<link refId="NY2-GLA1"/>
+				<link refId="GLA1-GLA2"/>
+				<link refId="GLA2-ROL1"/>
+				<link refId="ROL1-ROL2"/>
+				<link refId="ROL2-ALL1"/>
+				<link refId="ALL1-ALL2"/>
+				<link refId="ALL2-MOR1"/>
+				<link refId="MOR1-MOR2"/>
+				<link refId="MOR2_REN1-1"/>
+				<link refId="REN1-REN2"/>
+				<link refId="REN2-LS1"/>
+				<link refId="LS1-LS2"/>
+			</route>
+			<departures>
+				<departure id="IR90-GEAPGEBR2-8" departureTime="08:26:00" vehicleRefId="IR90-GEAPGEBR2-8"/>
+				<departure id="IR90-GEAPGEBR2-9" departureTime="09:26:00" vehicleRefId="IR90-GEAPGEBR2-9"/>
+				<departure id="IR90-GEAPGEBR2-10" departureTime="10:26:00" vehicleRefId="IR90-GEAPGEBR2-10"/>
+				<departure id="IR90-GEAPGEBR2-11" departureTime="11:26:00" vehicleRefId="IR90-GEAPGEBR2-11"/>
+				<departure id="IR90-GEAPGEBR2-12" departureTime="12:26:00" vehicleRefId="IR90-GEAPGEBR2-12"/>
+			</departures>
+		</transitRoute>
+
+		<transitRoute id="IR90-BRGEGEAP1">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="LS1" arrivalOffset="00:00:00" departureOffset="00:03:00" awaitDeparture="true"/>
+				<stop refId="GE1" arrivalOffset="00:40:00" departureOffset="00:42:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="LS2-LS1"/>
+				<link refId="LS1-REN2"/>
+				<link refId="REN2-REN1"/>
+				<link refId="REN1-MOR2-2"/>
+				<link refId="MOR2-MOR1"/>
+				<link refId="MOR1-ALL2"/>
+				<link refId="ALL2-ALL1"/>
+				<link refId="ALL1-ROL2"/>
+				<link refId="ROL2-ROL1"/>
+				<link refId="ROL1-GLA2"/>
+				<link refId="GLA2-GLA1"/>
+				<link refId="GLA1-NY2"/>
+				<link refId="NY2-NY1"/>
+				<link refId="NY1-COP2"/>
+				<link refId="COP2-COP1"/>
+				<link refId="COP1-GE2"/>
+				<link refId="GE2-GE1"/>
+			</route>
+			<departures>
+				<departure id="IR90-BRGEGEAP1-8" departureTime="08:10:00" vehicleRefId="IR90-BRGEGEAP1-8"/>
+				<departure id="IR90-BRGEGEAP1-9" departureTime="09:10:00" vehicleRefId="IR90-BRGEGEAP1-9"/>
+				<departure id="IR90-BRGEGEAP1-10" departureTime="10:10:00" vehicleRefId="IR90-BRGEGEAP1-10"/>
+				<departure id="IR90-BRGEGEAP1-11" departureTime="11:10:00" vehicleRefId="IR90-BRGEGEAP1-11"/>
+				<departure id="IR90-BRGEGEAP1-12" departureTime="12:10:00" vehicleRefId="IR90-BRGEGEAP1-12"/>
+			</departures>
+		</transitRoute>
+		<transitRoute id="IR90-BRGEGEAP2">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="LS1" arrivalOffset="00:00:00" departureOffset="00:09:00" awaitDeparture="true"/>
+				<stop refId="MOR1" arrivalOffset="00:20:00" departureOffset="00:20:00" awaitDeparture="true"/>
+				<stop refId="NY1" arrivalOffset="00:36:00" departureOffset="00:37:00" awaitDeparture="true"/>
+				<stop refId="GE1" arrivalOffset="00:52:00" departureOffset="00:55:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="LS2-LS1"/>
+				<link refId="LS1-REN2"/>
+				<link refId="REN2-REN1"/>
+				<link refId="REN1-MOR2-2"/>
+				<link refId="MOR2-MOR1"/>
+				<link refId="MOR1-ALL2"/>
+				<link refId="ALL2-ALL1"/>
+				<link refId="ALL1-ROL2"/>
+				<link refId="ROL2-ROL1"/>
+				<link refId="ROL1-GLA2"/>
+				<link refId="GLA2-GLA1"/>
+				<link refId="GLA1-NY2"/>
+				<link refId="NY2-NY1"/>
+				<link refId="NY1-COP2"/>
+				<link refId="COP2-COP1"/>
+				<link refId="COP1-GE2"/>
+				<link refId="GE2-GE1"/>
+			</route>
+			<departures>
+				<departure id="IR90-BRGEGEAP2-8" departureTime="08:39:00" vehicleRefId="IR90-BRGEGEAP2-8"/>
+				<departure id="IR90-BRGEGEAP2-9" departureTime="09:39:00" vehicleRefId="IR90-BRGEGEAP2-9"/>
+				<departure id="IR90-BRGEGEAP2-10" departureTime="10:39:00" vehicleRefId="IR90-BRGEGEAP2-10"/>
+				<departure id="IR90-BRGEGEAP2-11" departureTime="11:39:00" vehicleRefId="IR90-BRGEGEAP2-11"/>
+				<departure id="IR90-BRGEGEAP2-12" departureTime="12:39:00" vehicleRefId="IR90-BRGEGEAP2-12"/>
+			</departures>
+		</transitRoute>
+
+	</transitLine>
+
+	<transitLine id="IR15">
+		<transitRoute id="IR15-GEAPGELZ">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="GE" arrivalOffset="00:00:00" departureOffset="00:03:00" awaitDeparture="true"/>
+				<stop refId="NY" arrivalOffset="00:17:00" departureOffset="00:17:00" awaitDeparture="true"/>
+				<stop refId="MOR" arrivalOffset="00:34:00" departureOffset="00:34:00" awaitDeparture="true"/>
+				<stop refId="LS" arrivalOffset="00:45:00" departureOffset="00:48:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="GE1-GE2"/>
+				<link refId="GE2-COP1"/>
+				<link refId="COP1-COP2"/>
+				<link refId="COP2-NY1"/>
+				<link refId="NY1-NY2"/>
+				<link refId="NY2-GLA1"/>
+				<link refId="GLA1-GLA2"/>
+				<link refId="GLA2-ROL1"/>
+				<link refId="ROL1-ROL2"/>
+				<link refId="ROL2-ALL1"/>
+				<link refId="ALL1-ALL2"/>
+				<link refId="ALL2-MOR1"/>
+				<link refId="MOR1-MOR2"/>
+				<link refId="MOR2_REN1-1"/>
+				<link refId="REN1-REN2"/>
+				<link refId="REN2-LS1"/>
+				<link refId="LS1-LS2"/>
+			</route>
+			<departures>
+				<departure id="IR15-GEAPGELZ-8" departureTime="08:56:00" vehicleRefId="IR15-GEAPGELZ-8"/>
+				<departure id="IR15-GEAPGELZ-9" departureTime="09:56:00" vehicleRefId="IR15-GEAPGELZ-9"/>
+				<departure id="IR15-GEAPGELZ-10" departureTime="10:56:00" vehicleRefId="IR15-GEAPGELZ-10"/>
+				<departure id="IR15-GEAPGELZ-11" departureTime="11:56:00" vehicleRefId="IR15-GEAPGELZ-11"/>
+				<departure id="IR15-GEAPGELZ-12" departureTime="12:56:00" vehicleRefId="IR15-GEAPGELZ-12"/>
+			</departures>
+		</transitRoute>
+
+		<transitRoute id="IR15-LZGEGEAP">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="LS1" arrivalOffset="00:00:00" departureOffset="00:03:00" awaitDeparture="true"/>
+				<stop refId="MOR1" arrivalOffset="00:13:00" departureOffset="00:13:00" awaitDeparture="true"/>
+				<stop refId="NY1" arrivalOffset="00:29:00" departureOffset="00:30:00" awaitDeparture="true"/>
+				<stop refId="GE1" arrivalOffset="00:45:00" departureOffset="00:48:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="LS2-LS1"/>
+				<link refId="LS1-REN2"/>
+				<link refId="REN2-REN1"/>
+				<link refId="REN1-MOR2-2"/>
+				<link refId="MOR2-MOR1"/>
+				<link refId="MOR1-ALL2"/>
+				<link refId="ALL2-ALL1"/>
+				<link refId="ALL1-ROL2"/>
+				<link refId="ROL2-ROL1"/>
+				<link refId="ROL1-GLA2"/>
+				<link refId="GLA2-GLA1"/>
+				<link refId="GLA1-NY2"/>
+				<link refId="NY2-NY1"/>
+				<link refId="NY1-COP2"/>
+				<link refId="COP2-COP1"/>
+				<link refId="COP1-GE2"/>
+				<link refId="GE2-GE1"/>
+			</route>
+			<departures>
+				<departure id="IR15-LZGEGEAP-8" departureTime="08:16:00" vehicleRefId="IR15-LZGEGEAP-8"/>
+				<departure id="IR15-LZGEGEAP-9" departureTime="09:16:00" vehicleRefId="IR15-LZGEGEAP-9"/>
+				<departure id="IR15-LZGEGEAP-10" departureTime="10:16:00" vehicleRefId="IR15-LZGEGEAP-10"/>
+				<departure id="IR15-LZGEGEAP-11" departureTime="11:16:00" vehicleRefId="IR15-LZGEGEAP-11"/>
+				<departure id="IR15-LZGEGEAP-12" departureTime="12:16:00" vehicleRefId="IR15-LZGEGEAP-12"/>
+			</departures>
+		</transitRoute>
+
+	</transitLine>
+
+	<transitLine id="IC1">
+		<transitRoute id="IC1-GEAPZUESG">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="GE" arrivalOffset="00:00:00" departureOffset="00:03:00" awaitDeparture="true"/>
+				<stop refId="LS" arrivalOffset="00:38:00" departureOffset="00:40:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="GE1-GE2"/>
+				<link refId="GE2-COP1"/>
+				<link refId="COP1-COP2"/>
+				<link refId="COP2-NY1"/>
+				<link refId="NY1-NY2"/>
+				<link refId="NY2-GLA1"/>
+				<link refId="GLA1-GLA2"/>
+				<link refId="GLA2-ROL1"/>
+				<link refId="ROL1-ROL2"/>
+				<link refId="ROL2-ALL1"/>
+				<link refId="ALL1-ALL2"/>
+				<link refId="ALL2-MOR1"/>
+				<link refId="MOR1-MOR2"/>
+				<link refId="MOR2_REN1-1"/>
+				<link refId="REN1-REN2"/>
+				<link refId="REN2-LS1"/>
+				<link refId="LS1-LS2"/>
+			</route>
+			<departures>
+				<departure id="IC1-GEAPZUESG-8" departureTime="08:39:00" vehicleRefId="IC1-GEAPZUESG-8"/>
+				<departure id="IC1-GEAPZUESG-9" departureTime="09:39:00" vehicleRefId="IC1-GEAPZUESG-9"/>
+				<departure id="IC1-GEAPZUESG-10" departureTime="10:39:00" vehicleRefId="IC1-GEAPZUESG-10"/>
+				<departure id="IC1-GEAPZUESG-11" departureTime="11:39:00" vehicleRefId="IC1-GEAPZUESG-11"/>
+				<departure id="IC1-GEAPZUESG-12" departureTime="12:39:00" vehicleRefId="IC1-GEAPZUESG-12"/>
+			</departures>
+		</transitRoute>
+
+		<transitRoute id="IC1-SGZUEGEAP">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="LS1" arrivalOffset="00:00:00" departureOffset="00:02:00" awaitDeparture="true"/>
+				<stop refId="GE1" arrivalOffset="00:37:00" departureOffset="00:39:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="LS2-LS1"/>
+				<link refId="LS1-REN2"/>
+				<link refId="REN2-REN1"/>
+				<link refId="REN1-MOR2-2"/>
+				<link refId="MOR2-MOR1"/>
+				<link refId="MOR1-ALL2"/>
+				<link refId="ALL2-ALL1"/>
+				<link refId="ALL1-ROL2"/>
+				<link refId="ROL2-ROL1"/>
+				<link refId="ROL1-GLA2"/>
+				<link refId="GLA2-GLA1"/>
+				<link refId="GLA1-NY2"/>
+				<link refId="NY2-NY1"/>
+				<link refId="NY1-COP2"/>
+				<link refId="COP2-COP1"/>
+				<link refId="COP1-GE2"/>
+				<link refId="GE2-GE1"/>
+			</route>
+			<departures>
+				<departure id="IC1-SGZUEGEAP-8" departureTime="08:41:00" vehicleRefId="IC1-SGZUEGEAP-8"/>
+				<departure id="IC1-SGZUEGEAP-9" departureTime="09:41:00" vehicleRefId="IC1-SGZUEGEAP-9"/>
+				<departure id="IC1-SGZUEGEAP-10" departureTime="10:41:00" vehicleRefId="IC1-SGZUEGEAP-10"/>
+				<departure id="IC1-SGZUEGEAP-11" departureTime="11:41:00" vehicleRefId="IC1-SGZUEGEAP-11"/>
+				<departure id="IC1-SGZUEGEAP-12" departureTime="12:41:00" vehicleRefId="IC1-SGZUEGEAP-12"/>
+			</departures>
+		</transitRoute>
+
+
+	</transitLine>
+
+	<transitLine id="IC5">
+		<transitRoute id="IC5-GEAPZUERS">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="GE" arrivalOffset="00:00:00" departureOffset="00:03:00" awaitDeparture="true"/>
+				<stop refId="MOR" arrivalOffset="00:29:00" departureOffset="00:30:00" awaitDeparture="true"/>
+				<stop refId="REN" arrivalOffset="00:37:00" awaitDeparture="false"/>
+			</routeProfile>
+			<route>
+				<link refId="GE1-GE2"/>
+				<link refId="GE2-COP1"/>
+				<link refId="COP1-COP2"/>
+				<link refId="COP2-NY1"/>
+				<link refId="NY1-NY2"/>
+				<link refId="NY2-GLA1"/>
+				<link refId="GLA1-GLA2"/>
+				<link refId="GLA2-ROL1"/>
+				<link refId="ROL1-ROL2"/>
+				<link refId="ROL2-ALL1"/>
+				<link refId="ALL1-ALL2"/>
+				<link refId="ALL2-MOR1"/>
+				<link refId="MOR1-MOR2"/>
+				<link refId="MOR2_REN1-1"/>
+				<link refId="REN1-REN2"/>
+			</route>
+			<departures>
+				<departure id="IC5-GEAPZUERS-8" departureTime="08:12:00" vehicleRefId="IC5-GEAPZUERS-8"/>
+				<departure id="IC5-GEAPZUERS-9" departureTime="09:12:00" vehicleRefId="IC5-GEAPZUERS-9"/>
+				<departure id="IC5-GEAPZUERS-10" departureTime="10:12:00" vehicleRefId="IC5-GEAPZUERS-10"/>
+				<departure id="IC5-GEAPZUERS-11" departureTime="11:12:00" vehicleRefId="IC5-GEAPZUERS-11"/>
+				<departure id="IC5-GEAPZUERS-12" departureTime="12:12:00" vehicleRefId="IC5-GEAPZUERS-12"/>
+			</departures>
+		</transitRoute>
+
+		<transitRoute id="IC5-RSZUEGEAP">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="REN1" arrivalOffset="00:00:00" departureOffset="00:00:00" awaitDeparture="true"/>
+				<stop refId="MOR1" arrivalOffset="00:07:00" departureOffset="00:09:00" awaitDeparture="true"/>
+				<stop refId="GE1" arrivalOffset="00:38:00" departureOffset="00:40:00" awaitDeparture="true"/>
+			</routeProfile>
+			<route>
+				<link refId="REN2-REN1"/>
+				<link refId="REN1-MOR2-2"/>
+				<link refId="MOR2-MOR1"/>
+				<link refId="MOR1-ALL2"/>
+				<link refId="ALL2-ALL1"/>
+				<link refId="ALL1-ROL2"/>
+				<link refId="ROL2-ROL1"/>
+				<link refId="ROL1-GLA2"/>
+				<link refId="GLA2-GLA1"/>
+				<link refId="GLA1-NY2"/>
+				<link refId="NY2-NY1"/>
+				<link refId="NY1-COP2"/>
+				<link refId="COP2-COP1"/>
+				<link refId="COP1-GE2"/>
+				<link refId="GE2-GE1"/>
+			</route>
+			<departures>
+				<departure id="IC5-RSZUEGEAP-8" departureTime="08:09:00" vehicleRefId="IC5-RSZUEGEAP-8"/>
+				<departure id="IC5-RSZUEGEAP-9" departureTime="09:09:00" vehicleRefId="IC5-RSZUEGEAP-9"/>
+				<departure id="IC5-RSZUEGEAP-10" departureTime="10:09:00" vehicleRefId="IC5-RSZUEGEAP-10"/>
+				<departure id="IC5-RSZUEGEAP-11" departureTime="11:09:00" vehicleRefId="IC5-RSZUEGEAP-11"/>
+				<departure id="IC5-RSZUEGEAP-12" departureTime="12:09:00" vehicleRefId="IC5-RSZUEGEAP-12"/>
+			</departures>
+		</transitRoute>
+
+		<transitRoute id="Construction_REN_LS">
+			<transportMode>rail</transportMode>
+			<routeProfile>
+				<stop refId="MOR" arrivalOffset="00:00:00" departureOffset="00:00:00" awaitDeparture="false"/>
+				<stop refId="Construction_Site_MOR_REN" arrivalOffset="00:00:00" departureOffset="01:00:00" awaitDeparture="true"/>
+				<stop refId="REN" arrivalOffset="01:00:00" departureOffset="01:00:00" awaitDeparture="false"/>
+			</routeProfile>
+			<route>
+				<link refId="MOR1-MOR2"/>
+				<link refId="MOR2_REN1-1"/>
+				<link refId="REN1-REN2"/>
+			</route>
+			<departures>
+				<departure id="Construction_MOR_REN_10" departureTime="10:00:00" vehicleRefId="Construction_Train1"/>
+			</departures>
+		</transitRoute>
+
+
+	</transitLine>
+
+
+</transitSchedule>
diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/transitVehicles.xml
new file mode 100644
index 00000000000..7439829e879
--- /dev/null
+++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoConstructionSiteLsGe/transitVehicles.xml
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<vehicleDefinitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.matsim.org/files/dtd" xsi:schemaLocation="http://www.matsim.org/files/dtd http://www.matsim.org/files/dtd/vehicleDefinitions_v2.0.xsd">
+
+
+	<!-- ====================================================================== -->
+
+	<!-- Four types of vehicles are used for the different traffc types : Regional (RV160), InterRegio (FV200IR), InterCity (FV200IC), Construction (Construction_Train)
+	-->
+
+	<!-- ====================================================================== -->
+
+
+	<vehicleType id="RV160">
+		<attributes>
+			<attribute name="accessTimeInSecondsPerPerson" class="java.lang.Double">5.0</attribute>
+			<attribute name="doorOperationMode" class="org.matsim.vehicles.VehicleType$DoorOperationMode">serial</attribute>
+			<attribute name="egressTimeInSecondsPerPerson" class="java.lang.Double">5.0</attribute>
+			<attribute name="railsimAcceleration" class="java.lang.Double">1</attribute>
+			<attribute name="railsimDeceleration" class="java.lang.Double">1</attribute>
+		</attributes>
+		<capacity seats="200" standingRoomInPersons="0">
+
+		</capacity>
+		<length meter="150"/>
+		<width meter="2.85"/>
+		<maximumVelocity meterPerSecond="44"/>
+		<costInformation>
+
+		</costInformation>
+		<passengerCarEquivalents pce="1.0"/>
+		<networkMode networkMode="rail"/>
+		<flowEfficiencyFactor factor="1.0"/>
+	</vehicleType>
+
+	<vehicleType id="FV200IR">
+		<attributes>
+			<attribute name="accessTimeInSecondsPerPerson" class="java.lang.Double">5.0</attribute>
+			<attribute name="doorOperationMode" class="org.matsim.vehicles.VehicleType$DoorOperationMode">serial</attribute>
+			<attribute name="egressTimeInSecondsPerPerson" class="java.lang.Double">5.0</attribute>
+			<attribute name="railsimAcceleration" class="java.lang.Double">0.8</attribute>
+			<attribute name="railsimDeceleration" class="java.lang.Double">0.8</attribute>
+		</attributes>
+		<capacity seats="400" standingRoomInPersons="0">
+
+		</capacity>
+		<length meter="200"/>
+		<width meter="2.85"/>
+		<maximumVelocity meterPerSecond="55"/>
+		<costInformation>
+
+		</costInformation>
+		<passengerCarEquivalents pce="1.0"/>
+		<networkMode networkMode="rail"/>
+		<flowEfficiencyFactor factor="1.0"/>
+	</vehicleType>
+
+	<vehicleType id="FV200IC">
+		<attributes>
+			<attribute name="accessTimeInSecondsPerPerson" class="java.lang.Double">5.0</attribute>
+			<attribute name="doorOperationMode" class="org.matsim.vehicles.VehicleType$DoorOperationMode">serial</attribute>
+			<attribute name="egressTimeInSecondsPerPerson" class="java.lang.Double">5.0</attribute>
+			<attribute name="railsimAcceleration" class="java.lang.Double">0.8</attribute>
+			<attribute name="railsimDeceleration" class="java.lang.Double">0.8</attribute>
+		</attributes>
+		<capacity seats="400" standingRoomInPersons="0">
+
+		</capacity>
+		<length meter="200"/>
+		<width meter="2.85"/>
+		<maximumVelocity meterPerSecond="55"/>
+		<costInformation>
+
+		</costInformation>
+		<passengerCarEquivalents pce="1.0"/>
+		<networkMode networkMode="rail"/>
+		<flowEfficiencyFactor factor="1.0"/>
+	</vehicleType>
+
+	<vehicleType id="Construction_Train">
+		<attributes>
+			<attribute name="accessTimeInSecondsPerPerson" class="java.lang.Double">5.0</attribute>
+			<attribute name="doorOperationMode" class="org.matsim.vehicles.VehicleType$DoorOperationMode">serial</attribute>
+			<attribute name="egressTimeInSecondsPerPerson" class="java.lang.Double">5.0</attribute>
+			<attribute name="railsimAcceleration" class="java.lang.Double">0.8</attribute>
+			<attribute name="railsimDeceleration" class="java.lang.Double">0.8</attribute>
+		</attributes>
+		<capacity seats="1" standingRoomInPersons="0">
+
+		</capacity>
+		<length meter="200"/>
+		<width meter="2.85"/>
+		<maximumVelocity meterPerSecond="55"/>
+		<costInformation>
+
+		</costInformation>
+		<passengerCarEquivalents pce="1.0"/>
+		<networkMode networkMode="rail"/>
+		<flowEfficiencyFactor factor="1.0"/>
+	</vehicleType>
+
+
+	<vehicle id="RE33-ANNGELS-8" type="RV160"/>
+	<vehicle id="RE33-ANNGELS-9" type="RV160"/>
+	<vehicle id="RE33-ANNGELS-10" type="RV160"/>
+	<vehicle id="RE33-ANNGELS-11" type="RV160"/>
+	<vehicle id="RE33-ANNGELS-12" type="RV160"/>
+
+	<vehicle id="RE33-ANNGESM-8" type="RV160"/>
+	<vehicle id="RE33-ANNGESM-9" type="RV160"/>
+	<vehicle id="RE33-ANNGESM-10" type="RV160"/>
+	<vehicle id="RE33-ANNGESM-11" type="RV160"/>
+	<vehicle id="RE33-ANNGESM-12" type="RV160"/>
+
+	<vehicle id="RE33-VVGEANN-8" type="RV160"/>
+	<vehicle id="RE33-VVGEANN-9" type="RV160"/>
+	<vehicle id="RE33-VVGEANN-10" type="RV160"/>
+	<vehicle id="RE33-VVGEANN-11" type="RV160"/>
+	<vehicle id="RE33-VVGEANN-12" type="RV160"/>
+
+	<vehicle id="RE33-SMGEANN-8" type="RV160"/>
+	<vehicle id="RE33-SMGEANN-9" type="RV160"/>
+	<vehicle id="RE33-SMGEANN-10" type="RV160"/>
+	<vehicle id="RE33-SMGEANN-11" type="RV160"/>
+	<vehicle id="RE33-SMGEANN-12" type="RV160"/>
+
+	<vehicle id="IR90-GEAPGEBR1-8" type="FV200IR"/>
+	<vehicle id="IR90-GEAPGEBR1-9" type="FV200IR"/>
+	<vehicle id="IR90-GEAPGEBR1-10" type="FV200IR"/>
+	<vehicle id="IR90-GEAPGEBR1-11" type="FV200IR"/>
+	<vehicle id="IR90-GEAPGEBR1-12" type="FV200IR"/>
+
+	<vehicle id="IR90-GEAPGEBR2-8" type="FV200IR"/>
+	<vehicle id="IR90-GEAPGEBR2-9" type="FV200IR"/>
+	<vehicle id="IR90-GEAPGEBR2-10" type="FV200IR"/>
+	<vehicle id="IR90-GEAPGEBR2-11" type="FV200IR"/>
+	<vehicle id="IR90-GEAPGEBR2-12" type="FV200IR"/>
+
+	<vehicle id="IR90-BRGEGEAP1-8" type="FV200IR"/>
+	<vehicle id="IR90-BRGEGEAP1-9" type="FV200IR"/>
+	<vehicle id="IR90-BRGEGEAP1-10" type="FV200IR"/>
+	<vehicle id="IR90-BRGEGEAP1-11" type="FV200IR"/>
+	<vehicle id="IR90-BRGEGEAP1-12" type="FV200IR"/>
+
+	<vehicle id="IR90-BRGEGEAP2-8" type="FV200IR"/>
+	<vehicle id="IR90-BRGEGEAP2-9" type="FV200IR"/>
+	<vehicle id="IR90-BRGEGEAP2-10" type="FV200IR"/>
+	<vehicle id="IR90-BRGEGEAP2-11" type="FV200IR"/>
+	<vehicle id="IR90-BRGEGEAP2-12" type="FV200IR"/>
+
+	<vehicle id="IR15-GEAPGELZ-8" type="FV200IR"/>
+	<vehicle id="IR15-GEAPGELZ-9" type="FV200IR"/>
+	<vehicle id="IR15-GEAPGELZ-10" type="FV200IR"/>
+	<vehicle id="IR15-GEAPGELZ-11" type="FV200IR"/>
+	<vehicle id="IR15-GEAPGELZ-12" type="FV200IR"/>
+
+	<vehicle id="IR15-LZGEGEAP-8" type="FV200IR"/>
+	<vehicle id="IR15-LZGEGEAP-9" type="FV200IR"/>
+	<vehicle id="IR15-LZGEGEAP-10" type="FV200IR"/>
+	<vehicle id="IR15-LZGEGEAP-11" type="FV200IR"/>
+	<vehicle id="IR15-LZGEGEAP-12" type="FV200IR"/>
+
+	<vehicle id="IC1-GEAPZUESG-8" type="FV200IC"/>
+	<vehicle id="IC1-GEAPZUESG-9" type="FV200IC"/>
+	<vehicle id="IC1-GEAPZUESG-10" type="FV200IC"/>
+	<vehicle id="IC1-GEAPZUESG-11" type="FV200IC"/>
+	<vehicle id="IC1-GEAPZUESG-12" type="FV200IC"/>
+
+	<vehicle id="IC1-SGZUEGEAP-8" type="FV200IC"/>
+	<vehicle id="IC1-SGZUEGEAP-9" type="FV200IC"/>
+	<vehicle id="IC1-SGZUEGEAP-10" type="FV200IC"/>
+	<vehicle id="IC1-SGZUEGEAP-11" type="FV200IC"/>
+	<vehicle id="IC1-SGZUEGEAP-12" type="FV200IC"/>
+
+	<vehicle id="IC5-GEAPZUERS-8" type="FV200IC"/>
+	<vehicle id="IC5-GEAPZUERS-9" type="FV200IC"/>
+	<vehicle id="IC5-GEAPZUERS-10" type="FV200IC"/>
+	<vehicle id="IC5-GEAPZUERS-11" type="FV200IC"/>
+	<vehicle id="IC5-GEAPZUERS-12" type="FV200IC"/>
+
+	<vehicle id="IC5-RSZUEGEAP-8" type="FV200IC"/>
+	<vehicle id="IC5-RSZUEGEAP-9" type="FV200IC"/>
+	<vehicle id="IC5-RSZUEGEAP-10" type="FV200IC"/>
+	<vehicle id="IC5-RSZUEGEAP-11" type="FV200IC"/>
+	<vehicle id="IC5-RSZUEGEAP-12" type="FV200IC"/>
+
+
+	<vehicle id="Construction_Train1" type="Construction_Train"/>
+
+
+</vehicleDefinitions>