Skip to content

Commit

Permalink
Merge branch 'master' into shiny-dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
tschlenther authored May 3, 2024
2 parents 62c6f43 + 3b23169 commit 96437b7
Show file tree
Hide file tree
Showing 8 changed files with 565 additions and 68 deletions.
143 changes: 143 additions & 0 deletions src/main/java/org/matsim/analysis/CreateEmissionDashboard.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/* *********************************************************************** *
* project: org.matsim.*
* Controler.java
* *
* *********************************************************************** *
* *
* copyright : (C) 2007 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 org.matsim.analysis;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.matsim.analysis.emissions.KelheimEmissionsDashboard;
import org.matsim.application.ApplicationUtils;
import org.matsim.application.MATSimAppCommand;
import org.matsim.application.options.ShpOptions;
import org.matsim.core.config.Config;
import org.matsim.core.config.ConfigUtils;
import org.matsim.simwrapper.SimWrapper;
import org.matsim.simwrapper.SimWrapperConfigGroup;
import picocli.CommandLine;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;

@CommandLine.Command(
name = "emissions",
description = "Run emission analysis and create SimWrapper dashboard for existing run output."
)
final class CreateEmissionDashboard implements MATSimAppCommand {

private static final Logger log = LogManager.getLogger(CreateEmissionDashboard.class);

@CommandLine.Parameters(arity = "1..*", description = "Path to run output directories for which emission dashboards are to be generated.")
private List<Path> inputPaths;

@CommandLine.Mixin
private final ShpOptions shp = new ShpOptions();

@CommandLine.Option(names = "--base", description = "Optional. " +
"Relative path (from each! output directory provided) to main output folder for the base MATSim run. " +
"Will be used to compare emissions per link.", required = false)
private String baseRun;

private CreateEmissionDashboard(){
}

@Override
public Integer call() throws Exception {

for (Path runDirectory : inputPaths) {
log.info("Running on {}", runDirectory);

//this is to avoid overriding
renameExistingDashboardYAMLs(runDirectory);

Path configPath = ApplicationUtils.matchInput("config.xml", runDirectory);
Config config = ConfigUtils.loadConfig(configPath.toString());
SimWrapper sw = SimWrapper.create(config);

SimWrapperConfigGroup simwrapperCfg = ConfigUtils.addOrGetModule(config, SimWrapperConfigGroup.class);
if (shp.isDefined()){
//not sure if this is the best way to go, might be that the shape file would be automatically read by providing the --shp command line option
simwrapperCfg.defaultParams().shp = shp.getShapeFile().toString();
}
//skip default dashboards
simwrapperCfg.defaultDashboards = SimWrapperConfigGroup.Mode.disabled;
simwrapperCfg.defaultParams().mapCenter = "48.91265,11.89223";

if (baseRun != null){
sw.addDashboard(new KelheimEmissionsDashboard(baseRun));
} else {
sw.addDashboard(new KelheimEmissionsDashboard());
}

try {
sw.generate(runDirectory);
sw.run(runDirectory);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

return 0;
}

public static void main(String[] args) {
new CreateEmissionDashboard().execute(args);

}

private static void renameExistingDashboardYAMLs(Path runDirectory) {
// List of files in the folder
File folder = new File(runDirectory.toString());
File[] files = folder.listFiles();

// Loop through all files in the folder
if (files != null) {
for (File file : files) {
if (file.isFile()) {
// Check if the file name starts with "dashboard-" and ends with ".yaml"
if (file.getName().startsWith("dashboard-") && file.getName().endsWith(".yaml")) {
// Get the current file name
String oldName = file.getName();

// Extract the number from the file name
String numberPart = oldName.substring(oldName.indexOf('-') + 1, oldName.lastIndexOf('.'));

// Increment the number by ten
int number = Integer.parseInt(numberPart) + 10;

// Create the new file name
String newName = "dashboard-" + number + ".yaml";

// Create the new File object with the new file name
File newFile = new File(file.getParent(), newName);

// Rename the file
if (file.renameTo(newFile)) {
log.info("File successfully renamed: " + newName);
} else {
log.info("Error renaming file: " + file.getName());
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.matsim.dashboards;
package org.matsim.analysis;

import org.matsim.analysis.emissions.KelheimEmissionsDashboard;
import org.matsim.core.config.Config;
import org.matsim.core.utils.io.IOUtils;
import org.matsim.simwrapper.Dashboard;
Expand All @@ -24,7 +25,8 @@ public List<Dashboard> getDashboards(Config config, SimWrapper simWrapper) {
trips.setAnalysisArgs("--dist-groups", "0,1000,2000,5000,10000,20000");
return List.of(
trips,
new TravelTimeComparisonDashboard(IOUtils.resolveFileOrResource( "kelheim-v3.0-routes-ref.csv.gz").toString())
new TravelTimeComparisonDashboard(IOUtils.resolveFileOrResource( "kelheim-v3.0-routes-ref.csv.gz").toString()),
new KelheimEmissionsDashboard()
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* *********************************************************************** *
* project: org.matsim.*
* Controler.java
* *
* *********************************************************************** *
* *
* copyright : (C) 2007 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 org.matsim.analysis.emissions;

import org.matsim.application.prepare.network.CreateGeoJsonNetwork;
import org.matsim.simwrapper.Dashboard;
import org.matsim.simwrapper.Header;
import org.matsim.simwrapper.Layout;
import org.matsim.simwrapper.viz.Links;
import org.matsim.simwrapper.viz.Table;
import org.matsim.simwrapper.viz.XYTime;

/**
* this is basically equivalent to the standard emissions dashboard
* but calls the matsim-kelheim-specific emissions analysis class
* {@code KelheimOfflineAirPollutionAnalysisByEngineInformation}
* which has specific network and vehicle type attributes.
*/
public class KelheimEmissionsDashboard implements Dashboard{
private final String pathToCsvBase;

public KelheimEmissionsDashboard() {
this.pathToCsvBase = null;
}

public KelheimEmissionsDashboard(String pathToBaseRun) {
if (!pathToBaseRun.endsWith("/")) {
pathToBaseRun += "/";
}
this.pathToCsvBase = pathToBaseRun + "analysis/emissions/emissions_per_link_per_m.csv";
}

/**
* Produces the dashboard.
*/
public void configure(Header header, Layout layout) {

header.title = "Emissions";
header.description = "Shows the emissions footprint and spatial distribution.";

String linkDescription = "Displays the emissions for each link per meter. Be aware that emission values are provided in the simulation sample size!";
if (pathToCsvBase != null){
linkDescription += String.format("\n Base is %s", pathToCsvBase);
}
String finalLinkDescription = linkDescription;

layout.row("links")
.el(Table.class, (viz, data) -> {
viz.title = "Emissions";
viz.description = "by pollutant. Total values are scaled from the simulation sample size to 100%.";
viz.dataset = data.compute(KelheimOfflineAirPollutionAnalysisByEngineInformation.class, "emissions_total.csv", new String[0]);
viz.enableFilter = false;
viz.showAllRows = true;
viz.width = 1.0;
})
.el(Links.class, (viz, data) -> {
viz.title = "Emissions per Link per Meter";
viz.description = finalLinkDescription;
viz.height = 12.0;
viz.datasets.csvFile = data.compute(KelheimOfflineAirPollutionAnalysisByEngineInformation.class, "emissions_per_link_per_m.csv", new String[0]);
viz.datasets.csvBase = this.pathToCsvBase;
viz.network = data.compute(CreateGeoJsonNetwork.class, "network.geojson", new String[0]);
viz.display.color.columnName = "CO2_TOTAL [g/m]";
viz.display.color.dataset = "csvFile";
//TODO how to set color ramp??
// viz.display.color.setColorRamp(Plotly.ColorScheme.RdBu, 5, true);
viz.display.width.scaleFactor = 100;
viz.display.width.columnName = "CO2_TOTAL [g/m]";
viz.display.width.dataset = "csvFile";
viz.center = data.context().getCenter();
viz.width = 3.0;
});
layout.row("second").el(XYTime.class, (viz, data) -> {
viz.title = "CO₂ Emissions";
viz.description = "per day. Be aware that CO2 values are provided in the simulation sample size!";
viz.height = 12.0;
viz.file = data.compute(KelheimOfflineAirPollutionAnalysisByEngineInformation.class, "emissions_grid_per_day.xyt.csv", new String[0]);
});
layout.row("third")
.el(XYTime.class, (viz, data) -> {
viz.title = "CO₂ Emissions";
viz.description = "per hour. Be aware that CO2 values are provided in the simulation sample size!";
viz.height = 12.;
viz.file = data.compute(KelheimOfflineAirPollutionAnalysisByEngineInformation.class, "emissions_grid_per_hour.csv");
});
}
}
Loading

0 comments on commit 96437b7

Please sign in to comment.