From 200dda6e85fa2c822d66b52cdeff8c2a6129fd73 Mon Sep 17 00:00:00 2001 From: Tarek Chouaki Date: Sun, 17 Dec 2023 20:53:35 +0100 Subject: [PATCH 1/4] feat: ExportActivitiesToShapeFile --- .../tools/ExportActivitiesToShapeFile.java | 92 +++++++++++++++++++ .../org/eqasim/TestSimulationPipeline.java | 7 ++ 2 files changed, 99 insertions(+) create mode 100644 core/src/main/java/org/eqasim/core/tools/ExportActivitiesToShapeFile.java diff --git a/core/src/main/java/org/eqasim/core/tools/ExportActivitiesToShapeFile.java b/core/src/main/java/org/eqasim/core/tools/ExportActivitiesToShapeFile.java new file mode 100644 index 000000000..9f534ca7b --- /dev/null +++ b/core/src/main/java/org/eqasim/core/tools/ExportActivitiesToShapeFile.java @@ -0,0 +1,92 @@ +package org.eqasim.core.tools; + +import org.locationtech.jts.geom.Coordinate; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.population.Activity; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.api.core.v01.population.Population; +import org.matsim.core.config.CommandLine; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.population.io.PopulationReader; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.core.utils.gis.PointFeatureFactory; +import org.matsim.core.utils.gis.ShapeFileWriter; +import org.opengis.feature.simple.SimpleFeature; +import org.opengis.referencing.crs.CoordinateReferenceSystem; + +import java.util.*; +import java.util.stream.Collectors; + +public class ExportActivitiesToShapeFile { + + public static void exportActivitiesToShapeFile(Population population, String crsString, Set ignoredActivityTypesSet, String outputPath) { + + CoordinateReferenceSystem crs = MGC.getCRS(crsString); + + PointFeatureFactory pointFactory = new PointFeatureFactory.Builder() // + .setCrs(crs).setName("id") // + .addAttribute("personId", String.class) + .addAttribute("activityIndex", Integer.class) + .addAttribute("type", String.class)// + .addAttribute("linkId", String.class) + .addAttribute("facilityId", String.class) + .addAttribute("startTime", Double.class) + .addAttribute("endTime", Double.class)// + .create(); + + Collection features = new LinkedList<>(); + + for(Person person: population.getPersons().values()) { + if(person.getSelectedPlan() == null) { + continue; + } + int activityIndex = -1; + for(PlanElement planElement: person.getSelectedPlan().getPlanElements()) { + if (!(planElement instanceof Activity)) { + continue; + } + Activity a = (Activity) planElement; + activityIndex++; + if(ignoredActivityTypesSet.contains(a.getType())) { + continue; + } + Coordinate coordinate = new Coordinate(a.getCoord().getX(), a.getCoord().getY()); + SimpleFeature feature = pointFactory.createPoint(coordinate, + new Object[] { + person.getId().toString(), + activityIndex, + a.getType(), + a.getLinkId().toString(), + a.getFacilityId() == null ? null : a.getFacilityId().toString(), + a.getStartTime().orElse(Double.NaN), + a.getEndTime().orElse(Double.NaN) + }, + null); + features.add(feature); + } + } + ShapeFileWriter.writeGeometries(features, outputPath); + } + + + public static void main(String[] args) throws CommandLine.ConfigurationException { + CommandLine commandLine = new CommandLine.Builder(args).requireOptions("plans-path", "output-path", "crs") + .allowOptions("ignored-activity-types").build(); + + String plansPath = commandLine.getOptionStrict("plans-path"); + String outputPath = commandLine.getOptionStrict("output-path"); + String crs = commandLine.getOptionStrict("crs"); + Set ignoredActivityTypes = Arrays.stream(commandLine.getOption("ignored-activity-types").orElse("").split(",")) + .map(String::trim) + .filter(s -> s.length()>0) + .collect(Collectors.toSet()); + + Scenario scenario = ScenarioUtils.createScenario(ConfigUtils.createConfig()); + PopulationReader populationReader = new PopulationReader(scenario); + populationReader.readFile(plansPath); + + exportActivitiesToShapeFile(scenario.getPopulation(), crs, ignoredActivityTypes, outputPath); + } +} diff --git a/core/src/test/java/org/eqasim/TestSimulationPipeline.java b/core/src/test/java/org/eqasim/TestSimulationPipeline.java index 1eac60548..1ce32a530 100644 --- a/core/src/test/java/org/eqasim/TestSimulationPipeline.java +++ b/core/src/test/java/org/eqasim/TestSimulationPipeline.java @@ -9,6 +9,7 @@ import org.eqasim.core.simulation.mode_choice.AbstractEqasimExtension; import org.eqasim.core.simulation.mode_choice.EqasimModeChoiceModule; import org.eqasim.core.simulation.mode_choice.parameters.ModeParameters; +import org.eqasim.core.tools.ExportActivitiesToShapeFile; import org.eqasim.core.tools.ExportNetworkToShapefile; import org.eqasim.core.tools.ExportTransitLinesToShapefile; import org.eqasim.core.tools.ExportTransitStopsToShapefile; @@ -128,6 +129,12 @@ private void runShapefileExports() throws Exception { "--crs", "EPSG:2154", "--output-path", "melun_test/shp/network.shp" }); + + ExportActivitiesToShapeFile.main(new String[]{ + "--plans-path", "melun_test/input/population.xml.gz", + "--output-path", "melun_test/shp/activities.shp", + "--crs", "EPSG:2154" + }); } @Test From f204fc801ec475d93c19c1a0d7c34fbf3a4941d9 Mon Sep 17 00:00:00 2001 From: Tarek Chouaki Date: Sun, 17 Dec 2023 21:06:27 +0100 Subject: [PATCH 2/4] feat: ExportPopulationToCSV --- .../core/tools/ExportPopulationToCSV.java | 64 +++++++++++++++++++ .../org/eqasim/TestSimulationPipeline.java | 24 +++---- 2 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 core/src/main/java/org/eqasim/core/tools/ExportPopulationToCSV.java diff --git a/core/src/main/java/org/eqasim/core/tools/ExportPopulationToCSV.java b/core/src/main/java/org/eqasim/core/tools/ExportPopulationToCSV.java new file mode 100644 index 000000000..9496ef642 --- /dev/null +++ b/core/src/main/java/org/eqasim/core/tools/ExportPopulationToCSV.java @@ -0,0 +1,64 @@ +package org.eqasim.core.tools; + +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Population; +import org.matsim.core.config.CommandLine; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.population.io.PopulationReader; +import org.matsim.core.scenario.ScenarioUtils; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +public class ExportPopulationToCSV { + + public static final String[] IGNORED_ATTRIBUTES = new String[]{"vehicles"}; + + public static void exportPopulationToCSV(Population population, String filePath) { + + List ignoredAttributes = List.of(IGNORED_ATTRIBUTES); + + List attributes = population.getPersons().values().stream() + .flatMap(p -> p.getAttributes().getAsMap().keySet().stream()) + .distinct() + .filter(attribute -> !ignoredAttributes.contains(attribute)) + .collect(Collectors.toList()); + + String[] header = new String[attributes.size()+1]; + header[0] = "person_id"; + for(int i=0; i Date: Sun, 17 Dec 2023 22:21:14 +0100 Subject: [PATCH 3/4] feat: extending ExportTransitLinesToShapefile Two features: - It is now possible to specify which items are exported by one of transit line id, transit route id or mode. - The line name is now included in the exported shapefile --- .../tools/ExportTransitLinesToShapefile.java | 44 ++++++++++++++++--- .../org/eqasim/TestSimulationPipeline.java | 24 ++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/eqasim/core/tools/ExportTransitLinesToShapefile.java b/core/src/main/java/org/eqasim/core/tools/ExportTransitLinesToShapefile.java index fad411f69..35a8ecae5 100644 --- a/core/src/main/java/org/eqasim/core/tools/ExportTransitLinesToShapefile.java +++ b/core/src/main/java/org/eqasim/core/tools/ExportTransitLinesToShapefile.java @@ -1,11 +1,12 @@ package org.eqasim.core.tools; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; +import org.apache.commons.lang3.BooleanUtils; import org.locationtech.jts.geom.Coordinate; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.IdSet; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; @@ -27,11 +28,19 @@ public class ExportTransitLinesToShapefile { public static void main(String[] args) throws Exception { CommandLine cmd = new CommandLine.Builder(args) // - .requireOptions("schedule-path", "network-path", "output-path", "crs") // + .requireOptions("schedule-path", "network-path", "output-path", "crs") + .allowOptions("modes", "transit-lines", "transit-routes") .build(); String schedulePath = cmd.getOptionStrict("schedule-path"); String networkPath = cmd.getOptionStrict("network-path"); + Optional modesOption = cmd.getOption("modes"); + Optional transitLinesOption = cmd.getOption("transit-lines"); + Optional transitRoutesOption = cmd.getOption("transit-routes"); + + if(BooleanUtils.toInteger(modesOption.isPresent()) + BooleanUtils.toInteger(transitLinesOption.isPresent()) + BooleanUtils.toInteger(transitRoutesOption.isPresent()) > 1) { + throw new IllegalStateException("Only one of the options 'modes', 'transit-lines' and 'transit-routes' can be used"); + } Config config = ConfigUtils.createConfig(); Scenario scenario = ScenarioUtils.createScenario(config); @@ -45,14 +54,34 @@ public static void main(String[] args) throws Exception { PolylineFeatureFactory linkFactory = new PolylineFeatureFactory.Builder() // .setCrs(crs).setName("line") // .addAttribute("line_id", String.class) // + .addAttribute("line_name", String.class)// .addAttribute("route_id", String.class) // .addAttribute("mode", String.class) // .create(); Network network = scenario.getNetwork(); + Set modes = new HashSet<>(); + IdSet transitLineIdSet = new IdSet<>(TransitLine.class); + IdSet transitRouteIdSet = new IdSet<>(TransitRoute.class); + + transitLinesOption.ifPresent(value -> Arrays.stream(value.split(",")).map(String::trim).map(s -> Id.create(s, TransitLine.class)).forEach(transitLineIdSet::add)); + transitRoutesOption.ifPresent(value -> Arrays.stream(value.split(",")).map(String::trim).map(s -> Id.create(s, TransitRoute.class)).forEach(transitRouteIdSet::add)); + if(modesOption.isPresent()) { + modes = Arrays.stream(modesOption.get().split(",")).map(String::trim).collect(Collectors.toSet()); + } + for (TransitLine transitLine : scenario.getTransitSchedule().getTransitLines().values()) { + if(transitLineIdSet.size() > 0 && !transitLineIdSet.contains(transitLine.getId())) { + continue; + } for (TransitRoute transitRoute : transitLine.getRoutes().values()) { + if(transitRouteIdSet.size() > 0 && !transitRouteIdSet.contains(transitRoute.getId())) { + continue; + } + if(modes.size() > 0 && !modes.contains(transitRoute.getTransportMode())) { + continue; + } NetworkRoute networkRoute = transitRoute.getRoute(); List links = new ArrayList<>(networkRoute.getLinkIds().size() + 2); links.add(network.getLinks().get(networkRoute.getStartLinkId())); @@ -76,9 +105,10 @@ public static void main(String[] args) throws Exception { SimpleFeature feature = linkFactory.createPolyline( // coordinates, // new Object[] { // - transitLine.getId().toString(), // + transitLine.getId().toString(), + transitLine.getName(),// transitRoute.getId().toString(), // - transitRoute.getTransportMode() // + transitRoute.getTransportMode(),// }, null); features.add(feature); diff --git a/core/src/test/java/org/eqasim/TestSimulationPipeline.java b/core/src/test/java/org/eqasim/TestSimulationPipeline.java index 3b5db0c07..1c72f6f63 100644 --- a/core/src/test/java/org/eqasim/TestSimulationPipeline.java +++ b/core/src/test/java/org/eqasim/TestSimulationPipeline.java @@ -115,6 +115,30 @@ private void runExports() throws Exception { "--output-path", "melun_test/exports/lines.shp" }); + ExportTransitLinesToShapefile.main(new String[] { + "--schedule-path", "melun_test/input/transit_schedule.xml.gz", + "--network-path", "melun_test/input/network.xml.gz", + "--crs", "EPSG:2154", + "--modes", "rail", + "--output-path", "melun_test/exports/lines_rail.shp" + }); + + ExportTransitLinesToShapefile.main(new String[] { + "--schedule-path", "melun_test/input/transit_schedule.xml.gz", + "--network-path", "melun_test/input/network.xml.gz", + "--crs", "EPSG:2154", + "--transit-lines", "IDFM:C02364,IDFM:C00879", + "--output-path", "melun_test/exports/lines_line_ids.shp" + }); + + ExportTransitLinesToShapefile.main(new String[] { + "--schedule-path", "melun_test/input/transit_schedule.xml.gz", + "--network-path", "melun_test/input/network.xml.gz", + "--crs", "EPSG:2154", + "--transit-routes", "IDFM:TRANSDEV_AMV:27719-C00637-14017001,IDFM:SNCF:42048-C01728-9e8c577f-7ff9-4fe7-93e7-3c3854aa5ecf", + "--output-path", "melun_test/exports/lines_route_ids.shp" + }); + ExportTransitStopsToShapefile.main(new String[] { "--schedule-path", "melun_test/input/transit_schedule.xml.gz", "--crs", "EPSG:2154", From f522911c20cf35a76f8a5402a74535070142eef6 Mon Sep 17 00:00:00 2001 From: Tarek Chouaki Date: Sun, 17 Dec 2023 22:29:18 +0100 Subject: [PATCH 4/4] fix: renamed ExportActivitiesToShapefile to have consistent naming of tools --- ...ivitiesToShapeFile.java => ExportActivitiesToShapefile.java} | 2 +- core/src/test/java/org/eqasim/TestSimulationPipeline.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename core/src/main/java/org/eqasim/core/tools/{ExportActivitiesToShapeFile.java => ExportActivitiesToShapefile.java} (98%) diff --git a/core/src/main/java/org/eqasim/core/tools/ExportActivitiesToShapeFile.java b/core/src/main/java/org/eqasim/core/tools/ExportActivitiesToShapefile.java similarity index 98% rename from core/src/main/java/org/eqasim/core/tools/ExportActivitiesToShapeFile.java rename to core/src/main/java/org/eqasim/core/tools/ExportActivitiesToShapefile.java index 9f534ca7b..f370aa77a 100644 --- a/core/src/main/java/org/eqasim/core/tools/ExportActivitiesToShapeFile.java +++ b/core/src/main/java/org/eqasim/core/tools/ExportActivitiesToShapefile.java @@ -19,7 +19,7 @@ import java.util.*; import java.util.stream.Collectors; -public class ExportActivitiesToShapeFile { +public class ExportActivitiesToShapefile { public static void exportActivitiesToShapeFile(Population population, String crsString, Set ignoredActivityTypesSet, String outputPath) { diff --git a/core/src/test/java/org/eqasim/TestSimulationPipeline.java b/core/src/test/java/org/eqasim/TestSimulationPipeline.java index 1c72f6f63..5a2ba628e 100644 --- a/core/src/test/java/org/eqasim/TestSimulationPipeline.java +++ b/core/src/test/java/org/eqasim/TestSimulationPipeline.java @@ -151,7 +151,7 @@ private void runExports() throws Exception { "--output-path", "melun_test/exports/network.shp" }); - ExportActivitiesToShapeFile.main(new String[]{ + ExportActivitiesToShapefile.main(new String[]{ "--plans-path", "melun_test/input/population.xml.gz", "--output-path", "melun_test/exports/activities.shp", "--crs", "EPSG:2154"