diff --git a/.gitignore b/.gitignore
index e915921..ca2ad2b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,9 @@
# Add own ignores on top, don't edit the template
original_data/gtfs/*.zip
+original_data/gtfs/
original_data/gtfs/*
+
original_data/osm/switzerland_railways.osm
# Common files to ignore when working with MATSim
diff --git a/original_data/shp/switzerland/switzerland.cpg b/original_data/shp/switzerland/switzerland.cpg
new file mode 100644
index 0000000..3ad133c
--- /dev/null
+++ b/original_data/shp/switzerland/switzerland.cpg
@@ -0,0 +1 @@
+UTF-8
\ No newline at end of file
diff --git a/original_data/shp/switzerland/switzerland.ctf b/original_data/shp/switzerland/switzerland.ctf
new file mode 100644
index 0000000..014cb42
--- /dev/null
+++ b/original_data/shp/switzerland/switzerland.ctf
@@ -0,0 +1,65 @@
+Name;Alias
+NO;NO
+NAME;NAME
+AGGLO_ID;AGGLO_ID
+AMR_ID;AMR_ID
+MUN_ID;MUN_ID
+KT_ID;KT_ID
+MSR_ID;MSR_ID
+SL3_ID;SL3_ID
+AGGLO_NAME;AGGLO_NAME
+AMR_NAME;AMR_NAME
+MUN_NAME;MUN_NAME
+KT_NAME;KT_NAME
+MSR_NAME;MSR_NAME
+SL3_NAME;SL3_NAME
+AMGR_ID;AMGR_ID
+AMGR_NAME;AMGR_NAME
+AREA_LAND;AREA_LAND
+MOTORISATION_LEVEL;MOTORISA~1
+ZONE_INDEX;ZONE_INDEX
+VISIT_L;VISIT_L
+VISIT_S;VISIT_S
+VISIT_S_ST;VISIT_S_ST
+VISIT_S_LT;VISIT_S_LT
+SCHL_ENR_1;SCHL_ENR_1
+SCHL_ENR_2;SCHL_ENR_2
+SCHL_APPR;SCHL_APPR
+SCHL_ENR_3;SCHL_ENR_3
+AREA_LAND;AREA_LAND
+DENSITY;DENSITY
+POP_TOTAL;POP_TOTAL
+POP_EMPL;POP_EMPL
+POP_PUP_1;POP_PUP_1
+POP_PUP_2;POP_PUP_2
+POP_APPR;POP_APPR
+POP_STUD_3;POP_STUD_3
+POP_0017;POP_0017
+POP_1824;POP_1824
+POP_2544;POP_2544
+POP_4564;POP_4564
+POP_6574;POP_6574
+POP_75XX;POP_75XX
+POP_DL;POP_DL
+POP_CARS;POP_CARS
+POP_CARAVL;POP_CARAVL
+POP_HT;POP_HT
+POP_GA;POP_GA
+POP_VA;POP_VA
+POP_VA_HT;POP_VA_HT
+JOBS_TOTAL;JOBS_TOTAL
+JOBS_ENDO;JOBS_ENDO
+FTE_TOTAL;FTE_TOTAL
+FTE_ENDO;FTE_ENDO
+AT_CAR;AT_CAR
+AT_RIDE;AT_RIDE
+PC_CAR;PC_CAR
+PC_RIDE;PC_RIDE
+ACCSIB_MUL;ACCSIB_MUL
+ACCSIB_CAR;ACCSIB_CAR
+ACCSIB_PT;ACCSIB_PT
+ACCSIB_JOB_45MIN_PT;ACCSIB_J~2
+CAR_TTIMES;CAR_TTIMES
+PT_TTIMES;PT_TTIMES
+PT_TRSFERS;PT_TRSFERS
+PT_FREQNCY;PT_FREQNCY
diff --git a/original_data/shp/switzerland/switzerland.dbf b/original_data/shp/switzerland/switzerland.dbf
new file mode 100644
index 0000000..44627d4
Binary files /dev/null and b/original_data/shp/switzerland/switzerland.dbf differ
diff --git a/original_data/shp/switzerland/switzerland.prj b/original_data/shp/switzerland/switzerland.prj
new file mode 100644
index 0000000..a63c61d
--- /dev/null
+++ b/original_data/shp/switzerland/switzerland.prj
@@ -0,0 +1 @@
+PROJCS["CH1903+_LV95_TOWGS",GEOGCS["GCS_CH1903+_LV95_TOWGS",DATUM["D_CH1903+",SPHEROID["Bessel_1841",6377397.155,299.1528128],TOWGS84[674.4,15.1,405.3,0,0,0,0]],PRIMEM["Greenwich",0],UNIT["Degree",0.0174532925199432955]],PROJECTION["Hotine_Oblique_Mercator_Azimuth_Center"],PARAMETER["False_Easting",2600000],PARAMETER["False_Northing",1200000],PARAMETER["Scale_Factor",1],PARAMETER["Azimuth",90],PARAMETER["Longitude_Of_Center",7.439583333333333],PARAMETER["Latitude_Of_Center",46.95240555555556],UNIT["Meter",1]]
\ No newline at end of file
diff --git a/original_data/shp/switzerland/switzerland.shp b/original_data/shp/switzerland/switzerland.shp
new file mode 100644
index 0000000..f98074a
Binary files /dev/null and b/original_data/shp/switzerland/switzerland.shp differ
diff --git a/original_data/shp/switzerland/switzerland.shx b/original_data/shp/switzerland/switzerland.shx
new file mode 100644
index 0000000..a4efed7
Binary files /dev/null and b/original_data/shp/switzerland/switzerland.shx differ
diff --git a/pom.xml b/pom.xml
index 638cc64..de50bdf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,7 +71,13 @@
assertj-core
3.24.2
-
+
+
+ org.matsim
+ pt2matsim
+ 24.4
+
+
ch.sbb.matsim.contrib
railsim
diff --git a/src/main/java/ch/sbb/prepare/GenerateRailsimInput.java b/src/main/java/ch/sbb/prepare/GenerateRailsimInput.java
new file mode 100644
index 0000000..2c5065e
--- /dev/null
+++ b/src/main/java/ch/sbb/prepare/GenerateRailsimInput.java
@@ -0,0 +1,303 @@
+/* *********************************************************************** *
+ * project: org.matsim.*
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2016 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * *********************************************************************** */
+
+package ch.sbb.prepare;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.locationtech.jts.geom.prep.PreparedGeometry;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ConfigUtils;
+import org.matsim.core.config.ConfigWriter;
+import org.matsim.core.utils.collections.CollectionUtils;
+import org.matsim.pt.transitSchedule.api.TransitLine;
+import org.matsim.pt.transitSchedule.api.TransitRoute;
+import org.matsim.pt.transitSchedule.api.TransitRouteStop;
+import org.matsim.pt.transitSchedule.api.TransitSchedule;
+import org.matsim.pt2matsim.config.OsmConverterConfigGroup;
+import org.matsim.pt2matsim.config.PublicTransitMappingConfigGroup;
+import org.matsim.pt2matsim.run.CreateDefaultOsmConfig;
+import org.matsim.pt2matsim.run.CreateDefaultPTMapperConfig;
+import org.matsim.pt2matsim.run.Gtfs2TransitSchedule;
+import org.matsim.pt2matsim.run.Osm2MultimodalNetwork;
+import org.matsim.pt2matsim.run.PublicTransitMapper;
+import org.matsim.pt2matsim.tools.NetworkTools;
+import org.matsim.pt2matsim.tools.ScheduleTools;
+import org.matsim.pt2matsim.tools.debug.ScheduleCleaner;
+import org.matsim.utils.gis.shp2matsim.ShpGeometryUtils;
+import org.matsim.vehicles.Vehicles;
+
+public final class GenerateRailsimInput {
+ private static final Logger log = LogManager.getLogger(GenerateRailsimInput.class);
+
+ // input osm and gtfs file
+ private static final String INPUT_OSM_FILE = "original_data/osm/switzerland_railways.osm";
+ private static final String INPUT_GTFS_FILE = "original_data/gtfs/gtfs_fp2024_2024-03-20_04-15.zip";
+
+ // optional: trim the schedule
+ // private static final String areaShpFileForTrimming = null;
+ private static final String areaShpFileForTrimming = "original_data/shp/olten/olten.shp";
+
+ private static final String removeLinesOutsideThisArea = "original_data/shp/switzerland/switzerland.shp";
+
+ // optional: filter by line name prefix
+ private static final Set transitLineNamePrefixesToKeep = CollectionUtils.stringToSet("IC,IR,EC,RE");
+ // private static final Set transitLineNamePrefixesToKeep = null;
+
+ private static final String EPSG2056 = "EPSG:2056";
+
+ // matsim input files to write
+ private static final String MATSIM_INPUT = "matsim_input/";
+ private static final String VEHICLES_FINAL = MATSIM_INPUT + "transitVehicles.xml.gz";
+ private static final String NETWORK_FINAL = MATSIM_INPUT + "transitNetwork.xml.gz";
+ private static final String SCHEDULE_FINAL = MATSIM_INPUT + "transitSchedule.xml.gz";
+
+ // intermediate files
+
+ private static final String MATSIM_INPUT_TMP = "matsim_input/tmp/";
+
+ private static final String PT2MATSIM_OSM_CONVERTER_CONFIG_DEFAULT = MATSIM_INPUT_TMP + "pt2matsim_osm_converter_config_default.xml";
+ private static final String PT2MATSIM_OSM_CONVERTER_CONFIG = MATSIM_INPUT_TMP + "pt2matsim_osm_converter_config.xml";
+ private static final String PT2MATSIM_MAPPER_CONFIG_DEFAULT = MATSIM_INPUT_TMP + "pt2matsim_mapper_config_default.xml";
+ private static final String PT2MATSIM_MAPPER_CONFIG = MATSIM_INPUT_TMP + "pt2matsim_mapper_config_adjusted.xml";
+
+ private static final String SCHEDULE_GTFS = MATSIM_INPUT_TMP + "schedule_gtfs.xml.gz";
+ private static final String SCHEDULE_GTFS_FILTERED = MATSIM_INPUT_TMP + "schedule_gtfs_filtered.xml.gz";
+ private static final String SCHEDULE_GTFS_FILTERED_MAPPED = MATSIM_INPUT_TMP + "schedule_gtfs_filtered_mapped.xml.gz";
+ private static final String SCHEDULE_GTFS_FILTERED_MAPPED_TRIMMED = MATSIM_INPUT_TMP + "schedule_gtfs_filtered_mapped_trimmed.xml.gz";
+
+ private static final String NETWORK_OSM = MATSIM_INPUT_TMP + "network_osm.xml.gz";
+ private static final String NETWORK_OSM_MAPPED = MATSIM_INPUT_TMP + "network_osm_mapped.xml.gz";
+
+ private static final String VEHICLES_GTFS = MATSIM_INPUT_TMP + "vehicles_gtfs.xml.gz";
+ private static final String VEHICLES_GTFS_TRIMMED = MATSIM_INPUT_TMP + "vehicles_gtfs_trimmed.xml.gz";
+
+ public static void main(String[] args) throws MalformedURLException {
+
+ new File(MATSIM_INPUT).mkdirs();
+ new File(MATSIM_INPUT_TMP).mkdirs();
+
+ // 1. Convert a gtfs schedule to an unmapped transit schedule
+ gtfsToSchedule();
+ filterSchedule();
+
+ // 2. Convert an osm map to a MATSim network
+ createOsmConfigFile( PT2MATSIM_OSM_CONVERTER_CONFIG );
+ Osm2MultimodalNetwork.main(new String[]{ PT2MATSIM_OSM_CONVERTER_CONFIG });
+
+ // 3. Map the schedule onto the network
+ createMapperConfigFile(PT2MATSIM_MAPPER_CONFIG);
+ PublicTransitMapper.main(new String[]{PT2MATSIM_MAPPER_CONFIG});
+
+ trimSchedule();
+ writeFinalFiles();
+ }
+
+ private static void writeFinalFiles() {
+ TransitSchedule schedule = ScheduleTools.readTransitSchedule(SCHEDULE_GTFS_FILTERED_MAPPED_TRIMMED);
+ Network network = NetworkTools.readNetwork(NETWORK_OSM_MAPPED);
+ Vehicles vehicles = ScheduleTools.readVehicles(VEHICLES_GTFS_TRIMMED);
+
+ ScheduleTools.writeTransitSchedule(schedule, SCHEDULE_FINAL);
+ NetworkTools.writeNetwork(network, NETWORK_FINAL);
+ ScheduleTools.writeVehicles(vehicles, VEHICLES_FINAL);
+ }
+
+ private static void trimSchedule() throws MalformedURLException {
+ TransitSchedule schedule = ScheduleTools.readTransitSchedule(SCHEDULE_GTFS_FILTERED_MAPPED);
+ Network network = NetworkTools.readNetwork(NETWORK_OSM_MAPPED);
+ Vehicles vehicles = ScheduleTools.readVehicles(VEHICLES_GTFS);
+
+ if (areaShpFileForTrimming != null) {
+ List geometries = ShpGeometryUtils.loadPreparedGeometries(new File(areaShpFileForTrimming).toURI().toURL());
+
+ for (TransitLine transitLine : new HashSet<>(schedule.getTransitLines().values())) {
+ for(TransitRoute transitRoute : new HashSet<>(transitLine.getRoutes().values())) {
+ if (routeHasLinkInArea(transitRoute, network, geometries)) {
+ // keep
+ } else {
+ // remove
+ transitLine.removeRoute(transitRoute);
+ log.info("Route " + transitRoute + " removed.");
+ }
+ }
+
+ }
+ }
+
+ // remove transit lines without routes
+ for (TransitLine transitLine : new HashSet<>(schedule.getTransitLines().values())) {
+ if (transitLine.getRoutes().size() == 0) {
+ schedule.removeTransitLine(transitLine);
+ log.info("Remove line " + transitLine.getId());
+ }
+ }
+
+ ScheduleCleaner.cleanVehicles(schedule, vehicles);
+ ScheduleCleaner.removeNotUsedStopFacilities(schedule);
+ ScheduleCleaner.removeNotUsedMinimalTransferTimes(schedule);
+ ScheduleTools.writeTransitSchedule(schedule, SCHEDULE_GTFS_FILTERED_MAPPED_TRIMMED);
+ ScheduleTools.writeVehicles(vehicles, VEHICLES_GTFS_TRIMMED);
+ }
+
+ private static boolean routeHasLinkInArea(TransitRoute route, Network network, List geometries) {
+ for (Id id : route.getRoute().getLinkIds()) {
+ Link link = network.getLinks().get(id);
+ if (ShpGeometryUtils.isCoordInPreparedGeometries(link.getCoord(), geometries)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean routeHasStopInArea(TransitRoute route, List geometries) {
+ for (TransitRouteStop stop : route.getStops()) {
+ if (ShpGeometryUtils.isCoordInPreparedGeometries(stop.getStopFacility().getCoord(), geometries)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void filterSchedule() throws MalformedURLException {
+ TransitSchedule schedule = ScheduleTools.readTransitSchedule(SCHEDULE_GTFS);
+
+ // remove non-rail transit lines, e.g. buses, light-rail, ...
+ for(TransitLine transitLine : new HashSet<>(schedule.getTransitLines().values())) {
+ for(TransitRoute transitRoute : new HashSet<>(transitLine.getRoutes().values())) {
+ if(!transitRoute.getTransportMode().equals("rail")) {
+ transitLine.removeRoute(transitRoute);
+ }
+ }
+ }
+
+ // remove lines outside the provided area, e.g. Switzerland
+ if (removeLinesOutsideThisArea != null) {
+ List geometries = ShpGeometryUtils.loadPreparedGeometries(new File(removeLinesOutsideThisArea).toURI().toURL());
+
+ for (TransitLine transitLine : new HashSet<>(schedule.getTransitLines().values())) {
+ for(TransitRoute transitRoute : new HashSet<>(transitLine.getRoutes().values())) {
+ if (routeHasStopInArea(transitRoute, geometries)) {
+ // keep
+ } else {
+ // remove
+ transitLine.removeRoute(transitRoute);
+ }
+ }
+ }
+ }
+
+ if (transitLineNamePrefixesToKeep == null || transitLineNamePrefixesToKeep.isEmpty()) {
+ // do not filter by line name prefix
+
+ } else {
+ for(TransitLine transitLine : new HashSet<>(schedule.getTransitLines().values())) {
+ boolean lineToKeep = false;
+ for (String prefix : transitLineNamePrefixesToKeep) {
+ if (transitLine.getName().startsWith(prefix)) {
+ lineToKeep = true;
+ break;
+ }
+ }
+
+ if(lineToKeep) {
+ // keep
+ } else {
+ // remove
+ for(TransitRoute transitRoute : new HashSet<>(transitLine.getRoutes().values())) {
+ transitLine.removeRoute(transitRoute);
+ }
+ }
+ }
+ }
+
+ ScheduleCleaner.removeNotUsedStopFacilities(schedule);
+ ScheduleTools.writeTransitSchedule(schedule, SCHEDULE_GTFS_FILTERED);
+ }
+
+ /**
+ * 1. A GTFS or HAFAS Schedule or a OSM map with information on public transport
+ * has to be converted to an unmapped MATSim Transit Schedule.
+ *
+ * Here as a first example, the GTFS-schedule of GrandRiverTransit, Waterloo-Area, Canada, is converted.
+ */
+ public static void gtfsToSchedule() {
+ String[] gtfsConverterArgs = new String[]{
+ // [0] gtfs zip file
+ INPUT_GTFS_FILE,
+ // [1] which service ids should be used. One of the following:
+ // dayWithMostTrips, date in the format yyyymmdd, , dayWithMostServices, all
+ "dayWithMostTrips",
+ // [2] the output coordinate system. Use WGS84 for no transformation.
+ EPSG2056,
+ // [3] output transit schedule file
+ SCHEDULE_GTFS,
+ // [4] output default vehicles file (optional)
+ VEHICLES_GTFS,
+ };
+ Gtfs2TransitSchedule.main(gtfsConverterArgs);
+ }
+
+ /**
+ * 2. A MATSim network of the area is required. If no such network is already available,
+ * the PT2MATSim package provides the possibility to use OSM-maps as data-input.
+ *
+ */
+ public static void createOsmConfigFile(String configFile) {
+ CreateDefaultOsmConfig.main(new String[]{PT2MATSIM_OSM_CONVERTER_CONFIG_DEFAULT});
+ Config osmConverterConfig = ConfigUtils.loadConfig(
+ PT2MATSIM_OSM_CONVERTER_CONFIG_DEFAULT,
+ new OsmConverterConfigGroup());
+ OsmConverterConfigGroup osmConfig = ConfigUtils.addOrGetModule(osmConverterConfig, OsmConverterConfigGroup.class);
+ osmConfig.setOsmFile(INPUT_OSM_FILE);
+ osmConfig.setOutputCoordinateSystem(EPSG2056);
+ osmConfig.setOutputNetworkFile(NETWORK_OSM);
+ osmConfig.setKeepPaths(true);
+ new ConfigWriter(osmConverterConfig).write(configFile);
+ }
+
+ /**
+ * 3. The core of the PT2MATSim-package is the mapping process of the schedule to the network.
+ */
+ public static void createMapperConfigFile(String configFile) {
+ CreateDefaultPTMapperConfig.main(new String[]{ PT2MATSIM_MAPPER_CONFIG_DEFAULT});
+ Config config = ConfigUtils.loadConfig(
+ PT2MATSIM_MAPPER_CONFIG_DEFAULT,
+ PublicTransitMappingConfigGroup.createDefaultConfig());
+ PublicTransitMappingConfigGroup ptmConfig = ConfigUtils.addOrGetModule(config, PublicTransitMappingConfigGroup.class);
+
+ ptmConfig.setInputNetworkFile(NETWORK_OSM);
+ ptmConfig.setOutputNetworkFile(NETWORK_OSM_MAPPED);
+ ptmConfig.setOutputScheduleFile(SCHEDULE_GTFS_FILTERED_MAPPED);
+ ptmConfig.setInputScheduleFile(SCHEDULE_GTFS_FILTERED);
+ ptmConfig.setModesToKeepOnCleanUp(CollectionUtils.stringToSet("rail"));
+ ptmConfig.setScheduleFreespeedModes(CollectionUtils.stringToSet("rail"));
+ new ConfigWriter(config).write(configFile);
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/ch/sbb/run/info.md b/src/main/java/ch/sbb/run/info.md
new file mode 100644
index 0000000..c7d9dab
--- /dev/null
+++ b/src/main/java/ch/sbb/run/info.md
@@ -0,0 +1 @@
+see 'main' branch
\ No newline at end of file