diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index e322411a..11507255 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -94,4 +94,4 @@ jobs: path: staging env: - MAVEN_OPTS: -Xmx10G \ No newline at end of file + MAVEN_OPTS: -Xmx512m \ No newline at end of file diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 4ef94b1d..55ba46d1 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -17,4 +17,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} env: - MAVEN_OPTS: -Xmx10G \ No newline at end of file + MAVEN_OPTS: -Xmx512m \ No newline at end of file diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 00000000..e24476fd --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,38 @@ +# This CITATION.cff file was generated with cffinit. +# Visit https://bit.ly/cffinit to generate yours today! + +cff-version: 1.2.0 +title: The MATSim Open Kelheim Scenario +message: >- + If you use this software, please cite it using the + metadata from this file. +type: software +authors: + - given-names: Tilmann + family-names: Schlenther + email: schlenther@vsp.tu-berlin.de + affiliation: Technische Universität Berlin + orcid: 'https://orcid.org/0000-0001-6781-6918' + - given-names: Chengqi + family-names: Lu + email: lu@vsp.tu-berlin.de + affiliation: Technische Universität Berlin + - given-names: Christian + family-names: Rakow + email: rakow@vsp.tu-berlin.de + affiliation: Technische Universität Berlin + - given-names: Simon + family-names: Meinhardt + email: meinhardt@vsp.tu-berlin.de + affiliation: Technische Universität Berlin + - given-names: Kai + family-names: Nagel + email: nagel@vsp.tu-berlin.de + affiliation: Technische Universität Berlin + orcid: 'https://orcid.org/0000-0003-2775-6898' +url: "https://github.com/matsim-scenarios/matsim-kelheim" +doi: 10.5281/zenodo.8322240 +date-released: 2023-09-06 +year: 2023 +version: 3.0 +license: AGPL-3.0 diff --git a/README.md b/README.md index bc944805..5c1eff26 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,19 @@ [![Build Status](https://github.com/matsim-scenarios/matsim-kelheim/actions/workflows/build.yaml/badge.svg)](https://github.com/matsim-scenarios/matsim-kelheim/actions/workflows/build.yaml) ![license](https://img.shields.io/github/license/matsim-scenarios/matsim-kelheim.svg) +[![DOI (v3.0)](https://zenodo.org/badge/360167859.svg)](https://zenodo.org/badge/latestdoi/360167859) ![JDK](https://img.shields.io/badge/JDK-17+-green.svg) ![Kelheim MATSim network and agents](visualization-kelheim.png "Kelheim MATSim network and agents") + + + + ### About this project -This repository provides an open MATSim transport model for Kelheim, provided by the [Transport Systems Planning and Transport Telematics group](https://www.vsp.tu-berlin.de) of [Technische Universität Berlin](http://www.tu-berlin.de). +This repository provides an open MATSim transport model for Kelheim, provided by the [Transport Systems Planning and Transport Telematics group](https://www.tu.berlin/vsp) of [Technische Universität Berlin](http://www.tu-berlin.de). @@ -24,9 +29,11 @@ The **MATSim input files, output files, analysis data and visualizations** are l **Other data files**, in particular in `original-input-data`, have their own individual licenses that need to be individually clarified with the copyright holders. -### Note +The input plans (person transport demand) for this project were generated based on data provided by [Senozon AG](https://senozon.com/). + +### Note (where to find input and output) -Handling of large files within git is not without problems (git lfs files are not included in the zip download; we have to pay; ...). In consequence, large files, both on the input and on the output side, reside at https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/kelheim . +Handling of large files within git is not without problems (git lfs files are not included in the zip download; we have to pay; ...). In consequence, large files, both on the input and on the output side, reside at [the public matsim-kelheim data repo](https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/kelheim). ---- ### Run the MATSim Kelheim scenario @@ -42,7 +49,8 @@ It can be used by using either of these methods: 1. Set up the project in your IDE. 1. Make sure the project is configured as maven project. -1. Run the JAVA class `src/main/java/org/matsim/run/RunKelheimScenario.java` with the following program argument `run`. Add '--1pct' for test runs with a smaller sample size. +1. Run the JAVA class `src/main/java/org/matsim/run/RunKelheimScenario.java` with the following program argument `run`. + 1. Add '--1pct' for test runs with a smaller sample size. Be aware that the model is calibrated with 25 pct, and outputs for 1 pct might be a little off. 1. "Open" the output directory. You can drag files into VIA as was already done above. 1. Edit the config file or adjust the run class. Re-run MATSim. @@ -51,8 +59,26 @@ It can be used by using either of these methods: 1. Open the cmd and go to your project directory 2. Build the scenario using `mvnw package`. Add the option `-Dskiptests=true` in order to skip tests and speed up the process. -3. There should be a file directly in the `matsim-kelheim` directory with name approximately as `matsim-kelheim-2.x.jar`. -4. Run this file from the command line using `java -jar matsim-kelheim-2.x.jar --help` to see all possible options. +3. There should be a file directly in the `matsim-kelheim` directory with name approximately as `matsim-kelheim-3.x-SNAPSHOT-.jar`. +4. Run this file from the command line using `java -jar matsim-kelheim-3.x-SNAPSHOT-.jar --help` to see all possible options. 1. For example, one can disable lanes or run smaller sample sizes using the available options -5. Start this scenario using the default config by running `java -jar matsim-kelheim-2.x.jar`. -6. "Open" the output directory. You can drag files into VIA as was already done above. +5. Start this scenario using the default config by running `java -jar matsim-kelheim-3.x-SNAPSHOT-.jar`. + 1. If you want to run the scenario somewhere else, e.g. on a computation cluster, make sure to not only copy the jar but also the 'input' directory and put it right next to the jar. +6. "Open" the output directory. + 1. You can drag files into VIA as was already done above. + 2. You can also browse the output directory on vsp.berlin/simwrapper and analyze some of your results with interactive dashboards. + +---- +### Results and analysis + +Here are the most common ways to analyse and visualize the results (and inputs): + +1. [Simunto VIA](https://www.simunto.com/via/) +2. [SimWrapper](https://www.vsp.berlin/simwrapper) + 1. (use Google for the best experience) + 2. Browse your local output directory or [the public matsim-kelheim data repo](https://vsp.berlin/simwrapper/public/de/kelheim) + 2. Explore and create many interactive visualisations and dashboards +3. Analysis the output .csv tables using the R language and [the matsim-r package](https://github.com/matsim-vsp/matsim-r) + +If you have questions, feel free to contact us [(VSP)](https://www.tu.berlin/vsp) any time :) + diff --git a/input/test.config.xml b/input/test.config.xml index 17261b83..a9f7e502 100644 --- a/input/test.config.xml +++ b/input/test.config.xml @@ -67,8 +67,8 @@ - - + + @@ -87,56 +87,56 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - + @@ -145,11 +145,12 @@ - + + - - + + @@ -158,46 +159,48 @@ + + + + + - - - + - - + - + - + - - + + - + - + - + @@ -206,7 +209,7 @@ - + @@ -215,10 +218,11 @@ - + + diff --git a/input/test.with-drt.config.xml b/input/test.with-drt.config.xml index e5d76695..d32dc581 100644 --- a/input/test.with-drt.config.xml +++ b/input/test.with-drt.config.xml @@ -22,10 +22,10 @@ - - - - + + + + @@ -33,12 +33,12 @@ - + - - + + @@ -72,72 +72,70 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + - - - - - - + + + + - - - - - - + + + + - - - - - - + + + + + - - - - - - + + + + - - - + + + + + + + + + + @@ -146,11 +144,12 @@ - + + - - + + @@ -159,55 +158,62 @@ + + + + - - + + - - + + - + + + - + + - + - + - - + + - + - + - + @@ -216,7 +222,7 @@ - + @@ -225,10 +231,11 @@ - + + @@ -240,44 +247,62 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -305,17 +330,16 @@ - - - - - - - - - - - + + + + + + + + + + diff --git a/input/v3.0/kelheim-v3.0-25pct.kexi.config.xml b/input/v3.0/kelheim-v3.0-25pct.kexi.config.xml new file mode 100644 index 00000000..823714fe --- /dev/null +++ b/input/v3.0/kelheim-v3.0-25pct.kexi.config.xml @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/input/v3.0/kelheim-v3.0-config.xml b/input/v3.0/kelheim-v3.0-config.xml index 649c572f..4b7ba234 100644 --- a/input/v3.0/kelheim-v3.0-config.xml +++ b/input/v3.0/kelheim-v3.0-config.xml @@ -163,66 +163,63 @@ + + - - - - - - + + + + + + + + - - - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + - - - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + diff --git a/pom.xml b/pom.xml index 06c4a421..132d2965 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ matsim-all - 16.0-PR2680 + 16.0-PR2750 @@ -208,7 +208,7 @@ 1 false - @{argLine} -Xmx9500m -Djava.awt.headless=true -Dmatsim.preferLocalDtds=true + @{argLine} -Xmx6500m -Djava.awt.headless=true -Dmatsim.preferLocalDtds=true diff --git a/src/main/R/drtAnalysis/kpi_averaging.R b/src/main/R/drtAnalysis/kpi_averaging.R new file mode 100644 index 00000000..f4997782 --- /dev/null +++ b/src/main/R/drtAnalysis/kpi_averaging.R @@ -0,0 +1,111 @@ +#####libraries#### +library(stringr) +library(tidyverse) + +#####global variables#### +path_to_data <- "path/to/data (folder of a specific case, with different seeds)" +stats = c("mean","median","sd" ,"max", "min") + +##### Collect all folder names#### +folders_list<-list.files(path_to_data,full.names = TRUE) +folders_seeded = list() + +for(i in 1:length(folders_list)){ + if (endsWith(folders_list[i],".tsv")){ + next + } + + case_name = tail(str_split(folders_list[i],"-")[[1]],n = 1) + + if(!case_name %in% names(folders_seeded)){ + folders_seeded[[case_name]] = folders_list[i] + }else{ + folders_seeded[[case_name]] = append(folders_seeded[[case_name]],folders_list[i]) + } +} + +######################################### +#####Reading and averaging drt tables#### +folders_drt_averaged_table = list() + +for(case_name in names(folders_seeded)){ + for(folder in folders_seeded[[case_name]]){ + files_list<- list.files(paste0(folder,"/analysis-drt-service-quality"),full.names = TRUE) + drt_KPI_file <- files_list[grepl(pattern = "drt_KPI.tsv",files_list)] + cat("processing ",drt_KPI_file," \r\n") + drt_KPI_table <- read.delim(drt_KPI_file) + + if(!case_name %in% names(folders_drt_averaged_table)){ + folders_drt_averaged_table[[case_name]] = drt_KPI_table + }else{ + folders_drt_averaged_table[[case_name]] = rbind(folders_drt_averaged_table[[case_name]],drt_KPI_table) + } + + } + + tbl_colnames = c("stat",colnames(folders_drt_averaged_table[[case_name]])) + result_tibble = tbl_colnames %>% purrr::map_dfc(setNames, object = list(numeric())) + for(stat in stats){ + func = get(stat) + new_row = c(stat) + for(column in colnames(folders_drt_averaged_table[[case_name]])){ + new_row = append(new_row,func(folders_drt_averaged_table[[case_name]][[column]])) + } + + result_tibble = rbind(result_tibble,new_row) + } + colnames(result_tibble) = tbl_colnames + + folders_drt_averaged_table[[case_name]] = result_tibble + +} + +print(folders_drt_averaged_table) + +#Write averaged drt tables#### +for(case_name in names(folders_drt_averaged_table)){ + write.table(folders_drt_averaged_table[[case_name]],paste0(path_to_data, "/kpi_summary_drt_", case_name, ".tsv"),quote = FALSE,row.names = FALSE) +} + + + +######################################### +#####Reading and averaging av tables##### +folders_av_averaged_table = list() +for(case_name in names(folders_seeded)){ + for(folder in folders_seeded[[case_name]]){ + files_list<- list.files(paste0(folder,"/analysis-drt-service-quality"),full.names = TRUE) + av_KPI_file <- files_list[grepl(pattern = "av_KPI.tsv",files_list)] + cat("processing ",av_KPI_file," \r\n") + av_KPI_table <- read.delim(av_KPI_file) + + if(!case_name %in% names(folders_av_averaged_table)){ + folders_av_averaged_table[[case_name]] = av_KPI_table + }else{ + folders_av_averaged_table[[case_name]] = rbind(folders_av_averaged_table[[case_name]],av_KPI_table) + } + } + + tbl_colnames = c("stat",colnames(folders_av_averaged_table[[case_name]])) + result_tibble = tbl_colnames %>% purrr::map_dfc(setNames, object = list(numeric())) + for(stat in stats){ + func = get(stat) + new_row = c(stat) + for(column in colnames(folders_av_averaged_table[[case_name]])){ + new_row = append(new_row,func(folders_av_averaged_table[[case_name]][[column]])) + } + result_tibble = rbind(result_tibble,new_row) + } + colnames(result_tibble) = tbl_colnames + folders_av_averaged_table[[case_name]] = result_tibble +} + +print(folders_av_averaged_table) + +#Write averaged av tables#### +for(case_name in names(folders_av_averaged_table)){ + write.table(folders_av_averaged_table[[case_name]],paste0(path_to_data, "/kpi_summary_av_",case_name,".tsv"),quote = FALSE,row.names = FALSE) +} + + + diff --git a/src/main/R/mid.csv b/src/main/R/mid.csv deleted file mode 100644 index 12275451..00000000 --- a/src/main/R/mid.csv +++ /dev/null @@ -1,31 +0,0 @@ -dist_group,mode,share -0 - 1000,car,0.0557 -1000 - 2000,car,0.0758 -2000 - 5000,car,0.1507 -5000 - 10000,car,0.1139 -10000 - 20000,car,0.098 -20000+,car,0.0935 -0 - 1000,walk,0.0804 -1000 - 2000,walk,0.0318 -2000 - 5000,walk,0.0165 -5000 - 10000,walk,0.0044 -10000 - 20000,walk,0.0009 -20000+,walk,0.0006 -0 - 1000,pt,0.001 -1000 - 2000,pt,0.0012 -2000 - 5000,pt,0.0086 -5000 - 10000,pt,0.007 -10000 - 20000,pt,0.0061 -20000+,pt,0.0101 -0 - 1000,bike,0.0247 -1000 - 2000,bike,0.0242 -2000 - 5000,bike,0.0235 -5000 - 10000,bike,0.0063 -10000 - 20000,bike,0.0025 -20000+,bike,0.0019 -0 - 1000,ride,0.0144 -1000 - 2000,ride,0.0212 -2000 - 5000,ride,0.0447 -5000 - 10000,ride,0.0316 -10000 - 20000,ride,0.0257 -20000+,ride,0.0265 diff --git a/src/main/R/mid_adj.csv b/src/main/R/mid_adj.csv deleted file mode 100644 index ddfca9f3..00000000 --- a/src/main/R/mid_adj.csv +++ /dev/null @@ -1,31 +0,0 @@ -dist_group,mode,share -0 - 1000,car,0.04917652958739915 -1000 - 2000,car,0.04956785176611809 -2000 - 5000,car,0.10849424221801696 -5000 - 10000,car,0.15076819523354637 -10000 - 20000,car,0.14211152124057438 -20000+,car,0.11194215806986973 -0 - 1000,walk,0.07098371595739482 -1000 - 2000,walk,0.0207949562818279 -2000 - 5000,walk,0.011878931629709887 -5000 - 10000,walk,0.005824232300505742 -10000 - 20000,walk,0.0013051058073113972 -20000+,walk,0.0007183453993788432 -0 - 1000,pt,0.00088288203927108 -1000 - 2000,pt,0.000784715331389732 -2000 - 5000,pt,0.00619144315245485 -5000 - 10000,pt,0.009265824114440954 -10000 - 20000,pt,0.008845717138443916 -20000+,pt,0.012092147556210528 -0 - 1000,bike,0.021807186369995676 -1000 - 2000,bike,0.0158250925163596 -2000 - 5000,bike,0.01691847838170802 -5000 - 10000,bike,0.00833924170299686 -10000 - 20000,bike,0.0036252939091983255 -20000+,bike,0.002274760431366337 -0 - 1000,ride,0.012713501365503551 -1000 - 2000,ride,0.013863304187885268 -2000 - 5000,ride,0.03218110568775951 -5000 - 10000,ride,0.04182857743090488 -10000 - 20000,ride,0.03726802138655879 -20000+,ride,0.03172692180589891 diff --git a/src/main/R/srv.R b/src/main/R/srv.R index ec9f4c19..9ff94757 100644 --- a/src/main/R/srv.R +++ b/src/main/R/srv.R @@ -8,7 +8,7 @@ library(sf) source("https://raw.githubusercontent.com/matsim-scenarios/matsim-duesseldorf/master/src/main/R/theme.R") -setwd("PLEASE ADJUST TO YOUR LOCAL DIRECTORY FOR matsim-kelheim/src/main/R") +setwd("D:/git/matsim-kelheim/src/main/R") theme_set(theme_Publication(18)) @@ -23,7 +23,12 @@ shape <- st_read("../../../input/shp/dilutionArea.shp", crs=25832) ######### sim_scale <- 4 # set to 4 for 25pct, 10 for 10pct, 100 for 1pct, ... -f <- "../../../output/output-kelheim-25pct/" # set to run output directory +#f <- "../../../output/output-kelheim-25pct/" # set to run output directory + + +f <- "//sshfs.r/schlenther@cluster.math.tu-berlin.de/net/ils/matsim-kelheim/calibration-v3.0-noMgnUtl/runs/014-cnt/" # set to run output directory +f <- "//sshfs.r/schlenther@cluster.math.tu-berlin.de/net/ils/matsim-kelheim/calibration-ride12/calibration-bike-3-mc/runs/009/" # set to run output directory + homes <- read_csv("https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/kelheim/kelheim-v3.0/input/kelheim-v3.0-homes.csv", @@ -65,8 +70,7 @@ sim <- trips %>% # Read survey data ######## -srv <- read_csv("mid_adj.csv") %>% - mutate(main_mode=mode) %>% +srv <- read_csv("../resources/kelheim_mode_share.csv") %>% mutate(scaled_trips=122258 * 3.2 * share) %>% mutate(source = "mid") %>% mutate(dist_group=fct_relevel(dist_group, levels)) %>% @@ -77,9 +81,9 @@ srv <- read_csv("mid_adj.csv") %>% ####### srv_aggr <- srv %>% - group_by(mode) %>% + group_by(main_mode) %>% summarise(share=sum(share)) %>% # assume shares sum to 1 - mutate(mode=fct_relevel(mode, "walk", "bike", "pt", "ride", "car")) + mutate(mode=fct_relevel(main_mode, "walk", "bike", "pt", "ride", "car")) aggr <- sim %>% group_by(mode) %>% @@ -109,7 +113,7 @@ combined + plot_layout(guides = "collect") g <- arrangeGrob(p1_aggr, p2_aggr, ncol = 2) g -out <- file.path(f, "analysis-mode-choice") +out <- file.path(f, "R-analysis-mode-choice") if(!file.exists(out)){ print("creating analysis sub-directory") dir.create(out) @@ -122,6 +126,9 @@ ggsave(filename = "modal-split.png", path = out, g, # Combined plot by distance ########## +srv <- srv %>% + mutate(mode = main_mode) + total <- bind_rows(srv, sim) %>% mutate(mode=fct_relevel(mode, "walk", "bike", "pt", "ride", "car")) @@ -160,16 +167,15 @@ numTrips <- (shortDistance - sim_sum * tripShare) / (tripShare - 1) # Distance distributions based on RegioStar data ########################## -levels <- c("0 - 500", "500 - 1000", "1000 - 2000", "2000 - 5000", "5000 - 10000", - "10000 - 20000", "20000 - 50000", "50000 - 100000", "100000+") - -breaks <-c (0, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, Inf) +levels <- c("0 - 1000", "1000 - 2000", "2000 - 5000", "5000 - 10000", "10000 - 20000", "20000+") +breaks <- c(0, 1000, 2000, 5000, 10000, 20000, Inf) trips2 <- trips %>% mutate(dist_group = cut(traveled_distance, breaks=breaks, labels=levels, right = F)) %>% mutate(mode = fct_relevel(main_mode, "walk", "bike", "pt", "ride", "car")) -rs <- read_csv("tidied-mode-share-per-distance.csv") %>% +rs <- read_csv("../resources/kelheim_mode_share_per_dist.csv") %>% + mutate(mode = main_mode) %>% mutate(source="rs") sim <- trips2 %>% @@ -182,7 +188,7 @@ sim <- mutate(sim, share=trips/sum(sim$trips)) total_distance_dist <- bind_rows(filter(rs, mode=="total_distance_distribution"), sim) dist_order <- factor(total_distance_dist$dist_group, level = levels) -dist_order <- fct_explicit_na(dist_order, "100000+") +dist_order <- fct_explicit_na(dist_order, "20000+") g <- ggplot(total_distance_dist, aes(y=share, x=source, fill=source)) + @@ -209,7 +215,7 @@ by_distance <- bind_rows(filter(rs, mode!="total_distance_distribution"), sim) % mutate(mode=fct_relevel(mode, "walk", "bike", "pt", "ride", "car")) dist_order <- factor(by_distance$dist_group, level = levels) -dist_order <- fct_explicit_na(dist_order, "100000+") +dist_order <- fct_explicit_na(dist_order, "20000+") g <- ggplot(by_distance, aes(y=share, x=source, fill=mode)) + labs(subtitle = paste("Kelheim scenario", substring(f, 52)), x="distance [m]", y="share") + @@ -222,3 +228,4 @@ g ggsave(filename = "modal-distance-distribution-relative.png", path = out, g, width = 12, height = 10, device='png', dpi=300) + diff --git a/src/main/java/org/matsim/analysis/postAnalysis/drt/DrtServiceQualityAnalysis.java b/src/main/java/org/matsim/analysis/postAnalysis/drt/DrtServiceQualityAnalysis.java index 58d953f1..db49ee7f 100644 --- a/src/main/java/org/matsim/analysis/postAnalysis/drt/DrtServiceQualityAnalysis.java +++ b/src/main/java/org/matsim/analysis/postAnalysis/drt/DrtServiceQualityAnalysis.java @@ -31,7 +31,6 @@ import org.matsim.core.utils.gis.ShapeFileReader; import org.matsim.core.utils.gis.ShapeFileWriter; import org.matsim.core.utils.io.IOUtils; -import org.matsim.run.RunKelheimScenario; import org.matsim.utils.gis.shp2matsim.ShpGeometryUtils; import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleType; @@ -80,7 +79,7 @@ public Integer call() throws Exception { Files.createDirectory(outputFolder); } - Config config = ConfigUtils.loadConfig(configPath.toString()); + Config config = ConfigUtils.loadConfig(configPath.toString(), new MultiModeDrtConfigGroup(DrtWithExtensionsConfigGroup::new)); int lastIteration = config.controler().getLastIteration(); String runId = config.controler().getRunId(); Path folderOfLastIteration = Path.of(directory.toString() + "/ITERS/it." + lastIteration); @@ -88,11 +87,6 @@ public Integer call() throws Exception { List modes = new ArrayList<>(); for (DrtConfigGroup drtCfg : multiModeDrtConfigGroup.getModalElements()) { modes.add(drtCfg.getMode()); - //only the KEXI (conventionally driven drt) should get companions - if (drtCfg.getMode().equals(TransportMode.drt)) { - DrtWithExtensionsConfigGroup drtWithExtensionsConfigGroup = (DrtWithExtensionsConfigGroup) drtCfg; - RunKelheimScenario.addDrtCompanionParameters(drtWithExtensionsConfigGroup); - } } VehicleType vehicleTypeAv = VehicleUtils.createVehicleType(Id.create("av_type_for_route_calculation", VehicleType.class)); diff --git a/src/main/java/org/matsim/dashboards/KelheimDashboardProvider.java b/src/main/java/org/matsim/dashboards/KelheimDashboardProvider.java index e98819f5..40a8de49 100644 --- a/src/main/java/org/matsim/dashboards/KelheimDashboardProvider.java +++ b/src/main/java/org/matsim/dashboards/KelheimDashboardProvider.java @@ -1,9 +1,12 @@ package org.matsim.dashboards; import org.matsim.core.config.Config; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.run.RunKelheimScenario; import org.matsim.simwrapper.Dashboard; import org.matsim.simwrapper.DashboardProvider; import org.matsim.simwrapper.SimWrapper; +import org.matsim.simwrapper.dashboard.TravelTimeComparisonDashboard; import org.matsim.simwrapper.dashboard.TripDashboard; import java.util.List; @@ -20,8 +23,10 @@ public List getDashboards(Config config, SimWrapper simWrapper) { TripDashboard trips = new TripDashboard("kelheim_mode_share.csv", "kelheim_mode_share_per_dist.csv", null); trips.setAnalysisArgs("--dist-groups", "0,1000,2000,5000,10000,20000"); - - return List.of(trips); + return List.of( + trips, + new TravelTimeComparisonDashboard(IOUtils.resolveFileOrResource( "kelheim-v" + RunKelheimScenario.VERSION + "-routes-ref.csv.gz").toString()) + ); } } diff --git a/src/main/java/org/matsim/run/RunKelheimScenario.java b/src/main/java/org/matsim/run/RunKelheimScenario.java index 76a3f6b5..b4995552 100644 --- a/src/main/java/org/matsim/run/RunKelheimScenario.java +++ b/src/main/java/org/matsim/run/RunKelheimScenario.java @@ -54,7 +54,6 @@ import org.matsim.extensions.pt.routing.ptRoutingModes.PtIntermodalRoutingModesConfigGroup; import org.matsim.run.prepare.PrepareNetwork; import org.matsim.run.prepare.PreparePopulation; -import org.matsim.run.utils.KelheimCaseStudyTool; import org.matsim.simwrapper.SimWrapperConfigGroup; import org.matsim.simwrapper.SimWrapperModule; import org.matsim.vehicles.VehicleType; @@ -79,7 +78,7 @@ }) public class RunKelheimScenario extends MATSimApplication { - static final String VERSION = "3.0"; + public static final String VERSION = "3.0"; private static final double WEIGHT_1_PASSENGER = 16517.; private static final double WEIGHT_2_PASSENGER = 2084.; private static final double WEIGHT_3_PASSENGER = 532.; @@ -102,19 +101,13 @@ public class RunKelheimScenario extends MATSimApplication { @CommandLine.Option(names = "--av-fare", defaultValue = "0.0", description = "AV fare (euro per trip)") private double avFare; - /** - * this command line option allows to circumvent setting the drt service area per config. Not my preferred option, but its used to reduce nr. of configs (ts 07/23) - */ - @CommandLine.Option(names = "--case-study", defaultValue = "NULL", description = "Case study for the av scenario") - private KelheimCaseStudyTool.AvServiceArea avServiceArea; - @CommandLine.Option(names = "--bike-rnd", defaultValue = "false", description = "enable randomness in ASC of bike") private boolean bikeRnd; @CommandLine.Option(names = "--random-seed", defaultValue = "4711", description = "setting random seed for the simulation") private long randomSeed; - @CommandLine.Option(names = "--intermodal", defaultValue = "false", description = "enable DRT service") + @CommandLine.Option(names = "--intermodal", defaultValue = "false", description = "enable intermodality for DRT service") private boolean intermodal; @CommandLine.Option(names = "--plans", defaultValue = "", description = "Use different input plans") @@ -345,9 +338,6 @@ public void handleEvent(PersonDepartureEvent event) { for (DrtConfigGroup drtCfg : multiModeDrtConfig.getModalElements()) { controler.addOverridingModule(new KelheimDrtFareModule(drtCfg, network, avFare)); - if (drtCfg.getMode().equals("av")) { - KelheimCaseStudyTool.setConfigFile(config, drtCfg, avServiceArea); - } } //controler.addOverridingModule(new DrtEstimatorModule()); diff --git a/src/main/java/org/matsim/run/prepare/DrtStopsWriter.java b/src/main/java/org/matsim/run/prepare/DrtStopsWriter.java index 799c7ddf..6b59ba95 100644 --- a/src/main/java/org/matsim/run/prepare/DrtStopsWriter.java +++ b/src/main/java/org/matsim/run/prepare/DrtStopsWriter.java @@ -1,23 +1,28 @@ package org.matsim.run.prepare; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.locationtech.jts.geom.Geometry; import org.matsim.api.core.v01.Coord; +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.application.options.ShpOptions; +import org.matsim.core.network.NetworkUtils; import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.geometry.CoordUtils; import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.core.utils.io.IOUtils; import org.matsim.core.utils.io.MatsimXmlWriter; import org.matsim.core.utils.io.UncheckedIOException; +import org.matsim.run.RunKelheimScenario; import org.opengis.feature.simple.SimpleFeature; -import java.io.BufferedReader; import java.io.FileWriter; import java.io.IOException; -import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.HashSet; @@ -66,13 +71,19 @@ public void write() throws UncheckedIOException, IOException { this.writeDoctype("transitSchedule", "http://www.matsim.org/files/dtd/transitSchedule_v1.dtd"); this.writeStartTag("transitSchedule", null); this.writeStartTag("transitStops", null); - this.writeTransitStops(network); + this.writeTransitStopsAndVizFiles(network); this.writeEndTag("transitStops"); this.writeEndTag("transitSchedule"); this.close(); } - private void writeTransitStops(Network network) throws IOException { + /** + * additionally to writing the stops xml file, also writes a csv file that contains the same information as well as a network file that contains only + * the links assigned to stops (for visualisation). + * @param network to retrieve link id's from + * @throws IOException if some file can't be opened or written + */ + private void writeTransitStopsAndVizFiles(Network network) throws IOException { // Write csv file for adjusted stop location FileWriter csvWriter = new FileWriter(outputFolder + "/" + mode + "-stops-locations.csv"); @@ -89,39 +100,64 @@ private void writeTransitStops(Network network) throws IOException { log.info("Start processing the network. This may take some time..."); URL data = new URL("https://svn.vsp.tu-berlin.de/" + "repos/public-svn/matsim/scenarios/countries/de/kelheim/original-data/" + - "KEXI_Haltestellen_Liste_Kelheim_utm32n.csv"); - - BufferedReader csvReader = new BufferedReader(new InputStreamReader(data.openStream())); - csvReader.readLine(); - String stopEntry = csvReader.readLine(); - while (stopEntry != null) { - - String[] stopData = stopEntry.split(";"); - // write stop - Coord coord = new Coord(Double.parseDouble(stopData[2]), Double.parseDouble(stopData[3])); - - if (serviceArea == null || MGC.coord2Point(coord).within(serviceArea)) { - List> attributes = new ArrayList>(5); - attributes.add(createTuple("id", stopData[0])); - attributes.add(createTuple("x", stopData[2])); - attributes.add(createTuple("y", stopData[3])); - Link link = getStopLink(coord, network); - attributes.add(createTuple("linkRefId", link.getId().toString())); - this.writeStartTag("stopFacility", attributes, true); - - csvWriter.append(stopData[0]); - csvWriter.append(","); - csvWriter.append(link.getId().toString()); - csvWriter.append(","); - csvWriter.append(Double.toString(link.getToNode().getCoord().getX())); - csvWriter.append(","); - csvWriter.append(Double.toString(link.getToNode().getCoord().getY())); - csvWriter.append("\n"); + "KEXI_Haltestellen_Liste_Kelheim_utm32n_withLinkIds.csv"); + Set> allLinks = new HashSet<>(); + + try (CSVParser parser = new CSVParser(IOUtils.getBufferedReader(data), + CSVFormat.DEFAULT.withDelimiter(';').withFirstRecordAsHeader())) { + for (CSVRecord row : parser) { + Coord coord = new Coord(Double.parseDouble(row.get("x")), Double.parseDouble(row.get("y"))); + if (serviceArea == null || MGC.coord2Point(coord).within(serviceArea)) { + List> attributes = new ArrayList<>(5); + attributes.add(createTuple("id", row.get("Haltestellen-Nr."))); + attributes.add(createTuple("x", row.get("x"))); + attributes.add(createTuple("y", row.get("y"))); + Link link = null; + // If link is already determined by hand in the raw data, then use that link directly. + if (row.get("linkId_v" + RunKelheimScenario.VERSION)!=null){ + link = network.getLinks().get(Id.createLinkId(row.get("linkId_v" + RunKelheimScenario.VERSION))); + } else { + link = getStopLink(coord, network); + } + allLinks.add(link.getId()); + attributes.add(createTuple("linkRefId", link.getId().toString())); + + //write into stops xml file + this.writeStartTag("stopFacility", attributes, true); + + //write into csv file for viz + csvWriter.append(row.get("Haltestellen-Nr.")); + csvWriter.append(","); + csvWriter.append(link.getId().toString()); + csvWriter.append(","); + csvWriter.append(Double.toString(link.getToNode().getCoord().getX())); + csvWriter.append(","); + csvWriter.append(Double.toString(link.getToNode().getCoord().getY())); + csvWriter.append("\n"); + } } - - stopEntry = csvReader.readLine(); } + csvWriter.close(); + + //write filtered network file (for viz) + writeFilteredNetwork(network, allLinks); + } + + private void writeFilteredNetwork(Network network, Set> allLinks) { + //remove all links but the ones in the set + network.getLinks().keySet() + .forEach(linkId -> { + if (!allLinks.contains(linkId)) { + network.removeLink(linkId); + } + }); + //remove 'empty' nodes + network.getNodes().values().stream() + .filter(node -> node.getInLinks().size() == 0 && node.getOutLinks().size() == 0) + .forEach(node -> network.removeNode(node.getId())); + + NetworkUtils.writeNetwork(network, outputFolder + "/" + mode + "-stops-links.xml.gz"); } private Link getStopLink(Coord coord, Network network) { diff --git a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java index 1ca9dbe8..3bafb9f9 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java +++ b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java @@ -159,14 +159,18 @@ private void prepareNetworkDrt(Network network) { } } + Set allowedModes = new HashSet<>(link.getAllowedModes()); if (isAvAllowed) { - Set allowedModes = new HashSet<>(link.getAllowedModes()); - if (!allowedModes.contains("av")) { allowedModes.add("av"); link.setAllowedModes(allowedModes); linkCount[1] = linkCount[1] + 1; } + } else { + if (allowedModes.contains("av")) { + allowedModes.remove("av"); + link.setAllowedModes(allowedModes); + } } } diff --git a/src/main/java/org/matsim/run/utils/KelheimCaseStudyTool.java b/src/main/java/org/matsim/run/utils/KelheimCaseStudyTool.java deleted file mode 100644 index 928e9e33..00000000 --- a/src/main/java/org/matsim/run/utils/KelheimCaseStudyTool.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.matsim.run.utils; - -import org.matsim.contrib.drt.run.DrtConfigGroup; -import org.matsim.core.config.Config; - -/** - * Helper class to setup config file for case studies. - */ -public final class KelheimCaseStudyTool { - - private KelheimCaseStudyTool() { - } - - /** - * Different possible services areas for the AV. - */ - public enum AvServiceArea {NULL, CORE, CORE_WITH_SHOP, HOHENPFAHL, BAUERNSIEDLUNG} - // NULL: do not change anything; CORE: Donaupark + Altstadt; HOHENPFAHL: CORE + Hohenpfahl area; BAUERNSIEDLUNG: CORE + Bauernsiedlung area - - public static void setConfigFile(Config config, DrtConfigGroup drtConfig, AvServiceArea avServiceAreas) { - // Set drt related things (vehicle file, stops file) - if (avServiceAreas == AvServiceArea.CORE) { - drtConfig.transitStopFile = "av-stops-DP-AS.xml"; - } - - if (avServiceAreas == AvServiceArea.CORE_WITH_SHOP) { - drtConfig.transitStopFile = "av-stops-DP-AS-shops.xml"; - } - - if (avServiceAreas == AvServiceArea.HOHENPFAHL) { - drtConfig.transitStopFile = "av-stops-Hohenpfahl-DP-AS.xml"; - } - - if (avServiceAreas == AvServiceArea.BAUERNSIEDLUNG) { - drtConfig.transitStopFile = "av-stops-Bauernsiedlung-DP-AS.xml"; - } - - // Update output directory - if (avServiceAreas != AvServiceArea.NULL) { - String outputPath = config.controler().getOutputDirectory() + "-" + avServiceAreas.toString(); - config.controler().setOutputDirectory(outputPath); - } - } -} diff --git a/src/main/resources/kelheim-v3.0-routes-ref.csv.gz b/src/main/resources/kelheim-v3.0-routes-ref.csv.gz new file mode 100644 index 00000000..7f0eaaee Binary files /dev/null and b/src/main/resources/kelheim-v3.0-routes-ref.csv.gz differ