From 0fab39a5d13523d88c1245a8259cbeb9053e7730 Mon Sep 17 00:00:00 2001
From: Paul Heinrich <pheinrich11235@gmail.com>
Date: Thu, 23 Nov 2023 14:10:02 +0100
Subject: [PATCH] refactored PHbyModeCalculator

---
 .../matsim/analysis/PHbyModeCalculator.java   | 205 +++++++++---------
 1 file changed, 100 insertions(+), 105 deletions(-)

diff --git a/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java b/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java
index b4663b50388..41cf719a128 100644
--- a/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java
+++ b/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java
@@ -34,10 +34,7 @@
 import org.apache.logging.log4j.LogManager;
 import org.jfree.chart.axis.CategoryLabelPositions;
 import org.matsim.api.core.v01.IdMap;
-import org.matsim.api.core.v01.population.Activity;
-import org.matsim.api.core.v01.population.Leg;
-import org.matsim.api.core.v01.population.Person;
-import org.matsim.api.core.v01.population.Plan;
+import org.matsim.api.core.v01.population.*;
 import org.matsim.core.config.groups.ControllerConfigGroup;
 import org.matsim.core.config.groups.GlobalConfigGroup;
 import org.matsim.core.controler.OutputDirectoryHierarchy;
@@ -72,118 +69,116 @@ void addIteration(int iteration, IdMap<Person, Plan> map) {
         Map<String,TravelTimeAndWaitTime> phtbyMode = map.values()
                 .parallelStream()
                 .flatMap(plan -> plan.getPlanElements().stream())
-                .map(pe->{
-                	if (pe instanceof Leg) {
-                        Leg leg = (Leg) pe;
-                        double travelTime = 0.0;
-                        double waitTime = 0.0;
-                        if (leg.getRoute()!=null) {
-							travelTime = leg.getRoute().getTravelTime().seconds();
-                            double enterVehicleTime = Double.NaN;
-                            Object attr = leg.getAttributes().getAttribute(EventsToLegs.ENTER_VEHICLE_TIME_ATTRIBUTE_NAME);
-                            if (attr != null) {
-                            	enterVehicleTime = (Double) attr;
-                            }
-							waitTime = enterVehicleTime - leg.getDepartureTime().seconds();
-                            if (!Double.isFinite(waitTime)) {waitTime = 0.0;}
-                            if (waitTime >= 0.0) {
-                            	travelTime -= waitTime;
-                            } else {
-								throw new RuntimeException("negative wait time" + enterVehicleTime + " " + leg.getDepartureTime()
-										.seconds());
-                            }
-                        }
-
-                        if (Double.isNaN(travelTime)) {travelTime = 0.0; }
-
-                        return new AbstractMap.SimpleEntry<>(leg.getMode(),new TravelTimeAndWaitTime(travelTime, waitTime));
-
-                	} else if (pe instanceof Activity) {
-                		Activity act = (Activity) pe;
-                		if (StageActivityTypeIdentifier.isStageActivity(act.getType())) {
-                            double duration = act.getEndTime().orElse(0) - act.getStartTime().orElse(0);
-                            return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY,new TravelTimeAndWaitTime(0.0, duration));
-                		}
-                	}
-                	return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY,new TravelTimeAndWaitTime(0.0, 0.0));
-                })
-                .collect(Collectors.toMap(e->e.getKey(),e->e.getValue(),(a,b)->TravelTimeAndWaitTime.sum(a, b)));
+                .map(PHbyModeCalculator::mapPlanElementToEntry)
+                .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, TravelTimeAndWaitTime::sum));
         phtPerIteration.put(iteration,phtbyMode);
     }
 
+	private static AbstractMap.SimpleEntry<String, TravelTimeAndWaitTime> mapPlanElementToEntry(PlanElement pe) {
+		if (pe instanceof Leg leg) {
+			double travelTime = 0.0;
+			double waitTime = 0.0;
+			if (leg.getRoute()!=null) {
+				travelTime = leg.getRoute().getTravelTime().seconds();
+				double enterVehicleTime = Double.NaN;
+				Object attr = leg.getAttributes().getAttribute(EventsToLegs.ENTER_VEHICLE_TIME_ATTRIBUTE_NAME);
+				if (attr != null) {
+					enterVehicleTime = (Double) attr;
+				}
+				waitTime = enterVehicleTime - leg.getDepartureTime().seconds();
+				if (!Double.isFinite(waitTime)) {waitTime = 0.0;}
+				if (waitTime >= 0.0) {
+					travelTime -= waitTime;
+				} else {
+					throw new RuntimeException("negative wait time" + enterVehicleTime + " " + leg.getDepartureTime()
+							.seconds());
+				}
+			}
+			if (Double.isNaN(travelTime)) {travelTime = 0.0; }
+			return new AbstractMap.SimpleEntry<>(leg.getMode(), new TravelTimeAndWaitTime(travelTime, waitTime));
+		} else if (pe instanceof Activity act) {
+			if (StageActivityTypeIdentifier.isStageActivity(act.getType())) {
+				double duration = act.getEndTime().orElse(0) - act.getStartTime().orElse(0);
+				return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY, new TravelTimeAndWaitTime(0.0, duration));
+			}
+		}
+		return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY, new TravelTimeAndWaitTime(0.0, 0.0));
+	}
 
     void writeOutput() {
-        writePHTText();
-
+		writeCsv();
+		if(writePng){
+			writePng();
+		}
     }
 
-    private void writePHTText() {
-        TreeSet<String> allModes = new TreeSet<>();
-        allModes.addAll(this.phtPerIteration.values()
-                .stream()
-                .flatMap(i->i.keySet().stream())
-                .collect(Collectors.toSet()));
-
-        try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(Paths.get(controllerIO.getOutputFilename( FILENAME+ ".csv"))), CSVFormat.DEFAULT.withDelimiter(this.delimiter.charAt(0)))) {
-            csvPrinter.print("Iteration");
-            for (String mode: allModes) {
-                csvPrinter.print(mode + TRAVEL_TIME_SUFFIX);
-                csvPrinter.print(mode + WAIT_TIME_SUFFIX);
-            }
-            csvPrinter.println();
-
-            for (Map.Entry<Integer,Map<String,TravelTimeAndWaitTime>> e : phtPerIteration.entrySet()){
-                csvPrinter.print(e.getKey());
-                for (String mode : allModes){
-					TravelTimeAndWaitTime travelTimeAndWaitTime = e.getValue().getOrDefault(mode, new TravelTimeAndWaitTime(0.0, 0.0));
-					csvPrinter.print((int) Math.round(travelTimeAndWaitTime.travelTime / 3600.0));
-					csvPrinter.print((int) Math.round(travelTimeAndWaitTime.waitTime / 3600.0));
-				}
-                csvPrinter.println();
-            }
-
-
-        } catch (IOException e) {
+	private void writeCsv() {
+		TreeSet<String> allModes = getAllModes();
+		try {
+			CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(Paths.get(controllerIO.getOutputFilename( FILENAME+ ".csv"))), CSVFormat.DEFAULT.withDelimiter(this.delimiter.charAt(0)));
+			writeHeader(csvPrinter, allModes);
+			writeValues(csvPrinter, allModes);
+		} catch (IOException e) {
 			LogManager.getLogger(getClass()).error("Could not write PH Modestats.");
 		}
-        if (writePng){
-            String[] categories = new String[phtPerIteration.size()];
-            int i = 0;
-            for (Integer it : phtPerIteration.keySet()){
-                categories[i++] = it.toString();
-            }
-
-            StackedBarChart chart = new StackedBarChart("Passenger hours traveled per Mode","Iteration","person hours",categories);
-            //rotate x-axis by 90degrees
-            chart.getChart().getCategoryPlot().getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.UP_90);
-
-            for (String mode : allModes){
-                double[] valueTravelTime =  phtPerIteration.values().stream()
-                        .mapToDouble(k->k.getOrDefault(mode,new TravelTimeAndWaitTime(0.0, 0.0)).travelTime/3600.0)
-                        .toArray();
-                chart.addSeries(mode + TRAVEL_TIME_SUFFIX, valueTravelTime);
-                double[] valueWaitTime =  phtPerIteration.values().stream()
-                        .mapToDouble(k->k.getOrDefault(mode,new TravelTimeAndWaitTime(0.0, 0.0)).waitTime/3600.0)
-                        .toArray();
-                chart.addSeries(mode + WAIT_TIME_SUFFIX, valueWaitTime);
-            }
-            chart.addMatsimLogo();
-            chart.saveAsPng(controllerIO.getOutputFilename(FILENAME+ ".png"), 1024, 768);
-
-        }
-
-    }
-
-    private static class TravelTimeAndWaitTime {
-    	private double travelTime;
-    	private double waitTime;
+	}
+
+	private void writeValues(CSVPrinter csvPrinter, TreeSet<String> allModes) throws IOException {
+		for (Map.Entry<Integer,Map<String,TravelTimeAndWaitTime>> e : phtPerIteration.entrySet()){
+			csvPrinter.print(e.getKey());
+			for (String mode : allModes){
+				TravelTimeAndWaitTime travelTimeAndWaitTime = e.getValue().getOrDefault(mode, new TravelTimeAndWaitTime(0.0, 0.0));
+				csvPrinter.print((int) Math.round(travelTimeAndWaitTime.travelTime / 3600.0));
+				csvPrinter.print((int) Math.round(travelTimeAndWaitTime.waitTime / 3600.0));
+			}
+			csvPrinter.println();
+		}
+	}
 
-    	private TravelTimeAndWaitTime(double travelTime, double waitTime) {
-    		this.travelTime = travelTime;
-    		this.waitTime = waitTime;
-    	}
+	private static void writeHeader(CSVPrinter csvPrinter, TreeSet<String> allModes) throws IOException {
+		csvPrinter.print("Iteration");
+		for (String mode: allModes) {
+			csvPrinter.print(mode + TRAVEL_TIME_SUFFIX);
+			csvPrinter.print(mode + WAIT_TIME_SUFFIX);
+		}
+		csvPrinter.println();
+	}
+
+	private void writePng(){
+		TreeSet<String> allModes = getAllModes();
+		String[] categories = new String[phtPerIteration.size()];
+		int i = 0;
+		for (Integer it : phtPerIteration.keySet()){
+			categories[i++] = it.toString();
+		}
 
-    	private static TravelTimeAndWaitTime sum(TravelTimeAndWaitTime object1, TravelTimeAndWaitTime object2) {
+		StackedBarChart chart = new StackedBarChart("Passenger hours traveled per Mode","Iteration","person hours",categories);
+		//rotate x-axis by 90degrees
+		chart.getChart().getCategoryPlot().getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.UP_90);
+
+		for (String mode : allModes){
+			double[] valueTravelTime =  phtPerIteration.values().stream()
+													   .mapToDouble(k->k.getOrDefault(mode,new TravelTimeAndWaitTime(0.0, 0.0)).travelTime/3600.0)
+													   .toArray();
+			chart.addSeries(mode + TRAVEL_TIME_SUFFIX, valueTravelTime);
+			double[] valueWaitTime =  phtPerIteration.values().stream()
+													 .mapToDouble(k->k.getOrDefault(mode,new TravelTimeAndWaitTime(0.0, 0.0)).waitTime/3600.0)
+													 .toArray();
+			chart.addSeries(mode + WAIT_TIME_SUFFIX, valueWaitTime);
+		}
+		chart.addMatsimLogo();
+		chart.saveAsPng(controllerIO.getOutputFilename(FILENAME+ ".png"), 1024, 768);
+	}
+
+	private TreeSet<String> getAllModes() {
+		return this.phtPerIteration.values()
+								   .stream()
+								   .flatMap(i -> i.keySet().stream())
+								   .collect(Collectors.toCollection(TreeSet::new));
+	}
+
+	private record TravelTimeAndWaitTime(double travelTime, double waitTime){
+		private static TravelTimeAndWaitTime sum(TravelTimeAndWaitTime object1, TravelTimeAndWaitTime object2) {
     		return new TravelTimeAndWaitTime(object1.travelTime + object2.travelTime, object1.waitTime + object2.waitTime);
     	}
     }