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..f370aa77a --- /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/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 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 1eac60548..5a2ba628e 100644 --- a/core/src/test/java/org/eqasim/TestSimulationPipeline.java +++ b/core/src/test/java/org/eqasim/TestSimulationPipeline.java @@ -9,9 +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.ExportNetworkToShapefile; -import org.eqasim.core.tools.ExportTransitLinesToShapefile; -import org.eqasim.core.tools.ExportTransitStopsToShapefile; +import org.eqasim.core.tools.*; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -37,7 +35,7 @@ public class TestSimulationPipeline { public void setUp() throws IOException { URL fixtureUrl = getClass().getClassLoader().getResource("melun"); FileUtils.copyDirectory(new File(fixtureUrl.getPath()), new File("melun_test/input")); - FileUtils.forceMkdir(new File("melun_test/shp")); + FileUtils.forceMkdir(new File("melun_test/exports")); } @After @@ -109,24 +107,59 @@ private void runAnalyses() throws CommandLine.ConfigurationException, IOExceptio assert CRCChecksum.getCRCFromFile("melun_test/output/eqasim_pt.csv") == CRCChecksum.getCRCFromFile("melun_test/output/eqasim_pt_post_sim.csv"); } - private void runShapefileExports() throws Exception { + private void runExports() throws Exception { ExportTransitLinesToShapefile.main(new String[] { "--schedule-path", "melun_test/input/transit_schedule.xml.gz", "--network-path", "melun_test/input/network.xml.gz", "--crs", "EPSG:2154", - "--output-path", "melun_test/shp/lines.shp" + "--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", - "--output-path", "melun_test/shp/stops.shp" + "--output-path", "melun_test/exports/stops.shp" }); ExportNetworkToShapefile.main(new String[] { "--network-path", "melun_test/input/network.xml.gz", "--crs", "EPSG:2154", - "--output-path", "melun_test/shp/network.shp" + "--output-path", "melun_test/exports/network.shp" + }); + + ExportActivitiesToShapefile.main(new String[]{ + "--plans-path", "melun_test/input/population.xml.gz", + "--output-path", "melun_test/exports/activities.shp", + "--crs", "EPSG:2154" + }); + + ExportPopulationToCSV.main(new String[]{ + "--plans-path", "melun_test/input/population.xml.gz", + "--output-path", "melun_test/exports/persons.csv" }); } @@ -134,6 +167,6 @@ private void runShapefileExports() throws Exception { public void testPipeline() throws Exception { runMelunSimulation(); runAnalyses(); - runShapefileExports(); + runExports(); } }