MATSIM
PHbyModeCalculator.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * PKMbyModeCalculator.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2008 by the members listed in the COPYING, *
8  * LICENSE and WARRANTY file. *
9  * email : info at matsim dot org *
10  * *
11  * *********************************************************************** *
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * See also COPYING, LICENSE and WARRANTY file *
18  * *
19  * *********************************************************************** */
20 
21 package org.matsim.analysis;
22 
23 import java.io.BufferedWriter;
24 import java.io.IOException;
25 import java.nio.file.Files;
26 import java.nio.file.Paths;
27 import java.util.AbstractMap;
28 import java.util.Map;
29 import java.util.TreeMap;
30 import java.util.TreeSet;
31 import java.util.stream.Collectors;
32 import jakarta.inject.Inject;
33 import org.apache.commons.csv.CSVFormat;
34 import org.apache.commons.csv.CSVPrinter;
35 import org.apache.logging.log4j.LogManager;
36 import org.jfree.chart.axis.CategoryLabelPositions;
37 import org.matsim.api.core.v01.IdMap;
38 import org.matsim.api.core.v01.population.*;
44 
49 public class PHbyModeCalculator {
50 
51  private final Map<Integer,Map<String,TravelTimeAndWaitTime>> phtPerIteration = new TreeMap<>();
53  private final String delimiter;
54  private final static String FILENAME = "ph_modestats";
55 
56  private static final String TRAVEL_TIME_SUFFIX = "_travel";
57  private static final String WAIT_TIME_SUFFIX = "_wait";
58  private static final String STAGE_ACTIVITY = "stageActivity";
59 
60  @Inject
62  this.controllerIO = controllerIO;
63  this.delimiter = globalConfig.getDefaultDelimiter();
64  }
65 
66  void addIteration(int iteration, IdMap<Person, Plan> map) {
67  Map<String,TravelTimeAndWaitTime> phtbyMode = map.values()
68  .parallelStream()
69  .flatMap(plan -> plan.getPlanElements().stream())
71  .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, TravelTimeAndWaitTime::sum));
72  phtPerIteration.put(iteration,phtbyMode);
73  }
74 
75  private static AbstractMap.SimpleEntry<String, TravelTimeAndWaitTime> mapPlanElementToEntry(PlanElement pe) {
76  if (pe instanceof Leg leg) {
77  double travelTime = 0.0;
78  double waitTime = 0.0;
79  if (leg.getRoute()!=null) {
80  travelTime = leg.getRoute().getTravelTime().seconds();
81  double enterVehicleTime = Double.NaN;
82  Object attr = leg.getAttributes().getAttribute(EventsToLegs.ENTER_VEHICLE_TIME_ATTRIBUTE_NAME);
83  if (attr != null) {
84  enterVehicleTime = (Double) attr;
85  }
86  waitTime = enterVehicleTime - leg.getDepartureTime().seconds();
87  if (!Double.isFinite(waitTime)) {waitTime = 0.0;}
88  if (waitTime >= 0.0) {
89  travelTime -= waitTime;
90  } else {
91  throw new RuntimeException("negative wait time" + enterVehicleTime + " " + leg.getDepartureTime()
92  .seconds());
93  }
94  }
95  if (Double.isNaN(travelTime)) {travelTime = 0.0; }
96  return new AbstractMap.SimpleEntry<>(leg.getMode(), new TravelTimeAndWaitTime(travelTime, waitTime));
97  }
98 
99  if (pe instanceof Activity act) {
100  if (StageActivityTypeIdentifier.isStageActivity(act.getType())) {
101  double duration = act.getEndTime().orElse(0) - act.getStartTime().orElse(0);
102  return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY, new TravelTimeAndWaitTime(0.0, duration));
103  }
104  }
105 
106  return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY, new TravelTimeAndWaitTime(0.0, 0.0));
107  }
108 
109  void writeOutput(boolean writePng) {
110  writeCsv();
111  if(writePng){
112  new Thread(this::writePng).start();
113  }
114  }
115 
116  private void writeCsv() {
117  TreeSet<String> allModes = getAllModes();
118  try {
119  BufferedWriter writer = Files.newBufferedWriter(Paths.get(controllerIO.getOutputFilename(FILENAME + ".csv")));
120  CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.Builder.create().setDelimiter((this.delimiter.charAt(0))).build());
121  writeHeader(csvPrinter, allModes);
122  writeValues(csvPrinter, allModes);
123  csvPrinter.close();
124  } catch (IOException e) {
125  LogManager.getLogger(getClass()).error("Could not write PH Modestats.");
126  }
127  }
128 
129  private void writeValues(CSVPrinter csvPrinter, TreeSet<String> allModes) throws IOException {
130  for (Map.Entry<Integer,Map<String,TravelTimeAndWaitTime>> e : phtPerIteration.entrySet()){
131  csvPrinter.print(e.getKey());
132  for (String mode : allModes){
133  TravelTimeAndWaitTime travelTimeAndWaitTime = e.getValue().getOrDefault(mode, new TravelTimeAndWaitTime(0.0, 0.0));
134  csvPrinter.print((int) Math.round(travelTimeAndWaitTime.travelTime / 3600.0));
135  csvPrinter.print((int) Math.round(travelTimeAndWaitTime.waitTime / 3600.0));
136  }
137  csvPrinter.println();
138  }
139  }
140 
141  private static void writeHeader(CSVPrinter csvPrinter, TreeSet<String> allModes) throws IOException {
142  csvPrinter.print("Iteration");
143  for (String mode: allModes) {
144  csvPrinter.print(mode + TRAVEL_TIME_SUFFIX);
145  csvPrinter.print(mode + WAIT_TIME_SUFFIX);
146  }
147  csvPrinter.println();
148  }
149 
150  private void writePng(){
151  TreeSet<String> allModes = getAllModes();
152  String[] categories = new String[phtPerIteration.size()];
153  int i = 0;
154  for (Integer it : phtPerIteration.keySet()){
155  categories[i++] = it.toString();
156  }
157 
158  StackedBarChart chart = new StackedBarChart("Passenger hours traveled per Mode","Iteration","person hours",categories);
159  //rotate x-axis by 90degrees
160  chart.getChart().getCategoryPlot().getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.UP_90);
161 
162  for (String mode : allModes){
163  double[] valueTravelTime = phtPerIteration.values().stream()
164  .mapToDouble(k->k.getOrDefault(mode,new TravelTimeAndWaitTime(0.0, 0.0)).travelTime/3600.0)
165  .toArray();
166  chart.addSeries(mode + TRAVEL_TIME_SUFFIX, valueTravelTime);
167  double[] valueWaitTime = phtPerIteration.values().stream()
168  .mapToDouble(k->k.getOrDefault(mode,new TravelTimeAndWaitTime(0.0, 0.0)).waitTime/3600.0)
169  .toArray();
170  chart.addSeries(mode + WAIT_TIME_SUFFIX, valueWaitTime);
171  }
172  chart.addMatsimLogo();
173  chart.saveAsPng(controllerIO.getOutputFilename(FILENAME+ ".png"), 1024, 768);
174  }
175 
176  private TreeSet<String> getAllModes() {
177  return this.phtPerIteration.values()
178  .stream()
179  .flatMap(i -> i.keySet().stream())
180  .collect(Collectors.toCollection(TreeSet::new));
181  }
182 
183  private record TravelTimeAndWaitTime(double travelTime, double waitTime){
184  private static TravelTimeAndWaitTime sum(TravelTimeAndWaitTime object1, TravelTimeAndWaitTime object2) {
185  return new TravelTimeAndWaitTime(object1.travelTime + object2.travelTime, object1.waitTime + object2.waitTime);
186  }
187  }
188 }
189 
static AbstractMap.SimpleEntry< String, TravelTimeAndWaitTime > mapPlanElementToEntry(PlanElement pe)
Collection< V > values()
Definition: IdMap.java:204
static final String ENTER_VEHICLE_TIME_ATTRIBUTE_NAME
final OutputDirectoryHierarchy controllerIO
void addSeries(final String title, final double[] values)
static final boolean isStageActivity(final String activityType)
void saveAsPng(final String filename, final int width, final int height)
Definition: ChartUtil.java:65
void writeValues(CSVPrinter csvPrinter, TreeSet< String > allModes)
record TravelTimeAndWaitTime(double travelTime, double waitTime)
final Map< Integer, Map< String, TravelTimeAndWaitTime > > phtPerIteration
static void writeHeader(CSVPrinter csvPrinter, TreeSet< String > allModes)
V put(Id< T > key, V value)
Definition: IdMap.java:141