Skip to content

Commit

Permalink
Merge branch 'master' into kmt_configWarning
Browse files Browse the repository at this point in the history
  • Loading branch information
kt86 authored Oct 10, 2024
2 parents 8cb4c2b + 5f085ef commit 3eaf372
Show file tree
Hide file tree
Showing 119 changed files with 41,815 additions and 687 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,13 @@ public static Path matchInput(String name, Path dir) {
if (path.isPresent())
return path.get();

// Match more general pattern at last
path = matchPattern(".+\\.[a-zA-Z0-9]*_" + name + "\\..+", dir);
// Match more general pattern
path = matchPattern(".+\\.[a-zA-Z0-9\\-]*_" + name + "\\..+", dir);
if (path.isPresent())
return path.get();

// Even more permissive pattern
path = matchPattern(".+\\.[a-zA-Z0-9_.\\-]*(_|\\.)" + name + "\\..+", dir);
if (path.isPresent())
return path.get();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ private Config prepareConfig() {
config.transit().setTransitScheduleFile(null);
config.transit().setVehiclesFile(null);
config.plans().setInputFile(ApplicationUtils.matchInput("plans", input.getRunDirectory()).toAbsolutePath().toString());
config.facilities().setInputFile(null);
config.facilities().setInputFile(ApplicationUtils.matchInput("facilities", input.getRunDirectory()).toAbsolutePath().toString());
config.eventsManager().setNumberOfThreads(null);
config.eventsManager().setEstimatedNumberOfEvents(null);
//ts, aug '24: not sure if and why we need to set 1 thread
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.matsim.application.ApplicationUtils;
import org.matsim.application.CommandSpec;
import org.matsim.application.MATSimAppCommand;
import org.matsim.application.options.CsvOptions;
Expand All @@ -34,6 +35,7 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.IntStream;

Expand All @@ -45,15 +47,13 @@
produces = {
"mode_share.csv", "mode_share_per_dist.csv", "mode_users.csv", "trip_stats.csv",
"mode_share_per_%s.csv", "population_trip_stats.csv", "trip_purposes_by_hour.csv",
"mode_share_distance_distribution.csv",
"mode_share_distance_distribution.csv", "mode_shift.csv",
"mode_choices.csv", "mode_choice_evaluation.csv", "mode_choice_evaluation_per_mode.csv",
"mode_confusion_matrix.csv", "mode_prediction_error.csv"
}
)
public class TripAnalysis implements MATSimAppCommand {

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

/**
* Attributes which relates this person to a reference person.
*/
Expand All @@ -66,31 +66,24 @@ public class TripAnalysis implements MATSimAppCommand {
* Person attribute containing its weight for analysis purposes.
*/
public static final String ATTR_REF_WEIGHT = "ref_weight";

private static final Logger log = LogManager.getLogger(TripAnalysis.class);
@CommandLine.Option(names = "--person-filter", description = "Define which persons should be included into trip analysis. Map like: Attribute name (key), attribute value (value). " +
"The attribute needs to be contained by output_persons.csv. Persons who do not match all filters are filtered out.", split = ",")
private final Map<String, String> personFilters = new HashMap<>();
@CommandLine.Mixin
private InputOptions input = InputOptions.ofCommand(TripAnalysis.class);
@CommandLine.Mixin
private OutputOptions output = OutputOptions.ofCommand(TripAnalysis.class);

@CommandLine.Option(names = "--input-ref-data", description = "Optional path to reference data", required = false)
private String refData;

@CommandLine.Option(names = "--match-id", description = "Pattern to filter agents by id")
private String matchId;

@CommandLine.Option(names = "--dist-groups", split = ",", description = "List of distances for binning", defaultValue = "0,1000,2000,5000,10000,20000")
private List<Long> distGroups;

@CommandLine.Option(names = "--modes", split = ",", description = "List of considered modes, if not set all will be used")
private List<String> modeOrder;

@CommandLine.Option(names = "--shp-filter", description = "Define how the shp file filtering should work", defaultValue = "home")
private LocationFilter filter;

@CommandLine.Option(names = "--person-filter", description = "Define which persons should be included into trip analysis. Map like: Attribute name (key), attribute value (value). " +
"The attribute needs to be contained by output_persons.csv. Persons who do not match all filters are filtered out.", split = ",")
private final Map<String, String> personFilters = new HashMap<>();

@CommandLine.Mixin
private ShpOptions shp;

Expand Down Expand Up @@ -137,6 +130,20 @@ private static double[] calcHistogram(double[] data, double[] bins) {
return hist;
}

private static Map<String, ColumnType> getColumnTypes() {
Map<String, ColumnType> columnTypes = new HashMap<>(Map.of("person", ColumnType.TEXT,
"trav_time", ColumnType.STRING, "wait_time", ColumnType.STRING, "dep_time", ColumnType.STRING,
"longest_distance_mode", ColumnType.STRING, "main_mode", ColumnType.STRING,
"start_activity_type", ColumnType.TEXT, "end_activity_type", ColumnType.TEXT,
"first_pt_boarding_stop", ColumnType.TEXT, "last_pt_egress_stop", ColumnType.TEXT));

// Map.of only has 10 argument max
columnTypes.put("traveled_distance", ColumnType.LONG);
columnTypes.put("euclidean_distance", ColumnType.LONG);

return columnTypes;
}

@Override
public Integer call() throws Exception {

Expand Down Expand Up @@ -209,18 +216,8 @@ public Integer call() throws Exception {

log.info("Filtered {} out of {} persons", persons.rowCount(), total);

Map<String, ColumnType> columnTypes = new HashMap<>(Map.of("person", ColumnType.TEXT,
"trav_time", ColumnType.STRING, "wait_time", ColumnType.STRING, "dep_time", ColumnType.STRING,
"longest_distance_mode", ColumnType.STRING, "main_mode", ColumnType.STRING,
"start_activity_type", ColumnType.TEXT, "end_activity_type", ColumnType.TEXT,
"first_pt_boarding_stop", ColumnType.TEXT, "last_pt_egress_stop", ColumnType.TEXT));

// Map.of only has 10 argument max
columnTypes.put("traveled_distance", ColumnType.LONG);
columnTypes.put("euclidean_distance", ColumnType.LONG);

Table trips = Table.read().csv(CsvReadOptions.builder(IOUtils.getBufferedReader(input.getPath("trips.csv")))
.columnTypesPartial(columnTypes)
.columnTypesPartial(getColumnTypes())
.sample(false)
.separator(CsvOptions.detectDelimiter(input.getPath("trips.csv"))).build());

Expand Down Expand Up @@ -314,6 +311,8 @@ public Integer call() throws Exception {

writeTripDistribution(joined);

writeModeShift(joined);

return 0;
}

Expand Down Expand Up @@ -583,6 +582,34 @@ private void writeTripDistribution(Table trips) throws IOException {
}
}

private void writeModeShift(Table trips) throws IOException {
Path path;
try {
Path dir = Path.of(input.getPath("trips.csv")).getParent().resolve("ITERS").resolve("it.0");
path = ApplicationUtils.matchInput("trips.csv", dir);
} catch (Exception e) {
log.error("Could not find trips from 0th iteration.", e);
return;
}

Table originalTrips = Table.read().csv(CsvReadOptions.builder(IOUtils.getBufferedReader(path.toString()))
.columnTypesPartial(getColumnTypes())
.sample(false)
.separator(CsvOptions.detectDelimiter(path.toString())).build());

// Use longest_distance_mode where main_mode is not present
originalTrips.stringColumn("main_mode")
.set(originalTrips.stringColumn("main_mode").isMissing(),
originalTrips.stringColumn("longest_distance_mode"));

originalTrips.column("main_mode").setName("original_mode");

Table joined = new DataFrameJoiner(trips, "trip_id").inner(true, originalTrips);
Table aggr = joined.summarize("trip_id", count).by("original_mode", "main_mode");

aggr.write().csv(output.getPath("mode_shift.csv").toFile());
}

/**
* How shape file filtering should be applied.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package org.matsim.application.avro;

import it.unimi.dsi.fastutil.objects.Object2FloatAVLTreeMap;
import it.unimi.dsi.fastutil.objects.Object2FloatSortedMap;
import org.apache.avro.file.CodecFactory;
import org.apache.avro.file.DataFileWriter;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.specific.SpecificDatumWriter;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.matsim.core.utils.io.IOUtils;

import java.io.IOException;
import java.nio.file.Path;
import java.util.*;

public class CSVToAvroConverter {

public static void main(String[] args) throws IOException {
String projection = args.length > 2 ? args[2] : null;
String name = args.length > 3 ? args[3] : "Emissions";

XYTData avroData = readCSV(args[0], projection, name);
writeAvroFile(avroData, Path.of(args[1]));
}

/**
* Reads a CSV file, processes its data, and returns the corresponding Avro object.
*
* @param csvFilePath the path to the CSV file
* @param projection the projection (CRS)
* @param name the name for the data series (defaults is "Emissions")
* @throws IOException if an error occurs during reading the file
*/
public static XYTData readCSV(String csvFilePath, String projection, String name) throws IOException {
List<CSVEntries> entries = new ArrayList<>();
List<Float> xCoords = new ArrayList<>();
List<Float> yCoords = new ArrayList<>();
List<Integer> timestamps = new ArrayList<>();
Object2FloatSortedMap<XYT> valuesMap = new Object2FloatAVLTreeMap<>(Comparator.comparing((XYT e) -> e.t)
.thenComparing(e -> e.x)
.thenComparing(e -> e.y));

try (CSVParser csvReader = new CSVParser(IOUtils.getBufferedReader(csvFilePath), CSVFormat.DEFAULT.builder()
.setCommentMarker('#').setSkipHeaderRecord(true).setHeader().build())) {

String comment = csvReader.getHeaderComment();

if (comment != null && (projection == null || projection.isEmpty())) {
projection = comment;
} else if (projection == null) {
projection = "";
}

for (CSVRecord record : csvReader) {
try {
int time = (int) Double.parseDouble(record.get(0));
float x = Float.parseFloat(record.get(1));
float y = Float.parseFloat(record.get(2));
float value = Float.parseFloat(record.get(3));

entries.add(new CSVEntries(time, x, y, value));

} catch (NumberFormatException e) {
System.out.println("Skipping invalid line: " + String.join(",", record));
}
}
}

// Sort entries by time -> x -> y
entries.sort(Comparator.comparing((CSVEntries e) -> e.time)
.thenComparing(e -> e.x)
.thenComparing(e -> e.y));

for (CSVEntries entry : entries) {
if (!xCoords.contains(entry.x)) {
xCoords.add(entry.x);
}
if (!yCoords.contains(entry.y)) {
yCoords.add(entry.y);
}
if (!timestamps.contains(entry.time)) {
timestamps.add(entry.time);
}

valuesMap.put(new XYT(entry.x, entry.y, entry.time), entry.value);
}

// Check if all combinations of x, y, and time exist
for (int time : timestamps) {
for (float x : xCoords) {
for (float y : yCoords) {
XYT key = new XYT(x, y, time);
if (!valuesMap.containsKey(key)) {
valuesMap.put(key, 0f);
}
}
}
}

// Create Avro data object
XYTData avroData = new XYTData();
avroData.setCrs(projection);
avroData.setXCoords(xCoords);
avroData.setYCoords(yCoords);
avroData.setTimestamps(timestamps);

List<Float> valuesList = new ArrayList<>(valuesMap.values());
Map<CharSequence, List<Float>> result = new HashMap<>();
result.put(name != null && !name.isEmpty() ? name : "Emissions", valuesList);

avroData.setData(result);

return avroData;
}

/**
* Writes the Avro data
*
* @param avroData the Avro data
* @param avroFile the path to the output Avro file
* @throws IOException if an error occurs during writing the file
*/
public static void writeAvroFile(XYTData avroData, Path avroFile) throws IOException {
DatumWriter<XYTData> datumWriter = new SpecificDatumWriter<>(XYTData.class);
try (DataFileWriter<XYTData> dataFileWriter = new DataFileWriter<>(datumWriter)) {
dataFileWriter.setCodec(CodecFactory.deflateCodec(9));
dataFileWriter.create(XYTData.getClassSchema(), avroFile.toFile());
dataFileWriter.append(avroData);
}
}

private record CSVEntries(int time, float x, float y, float value) {
}

private record XYT(float x, float y, float t) {
}
}
Loading

0 comments on commit 3eaf372

Please sign in to comment.