Skip to content

Commit

Permalink
split landuseAnalysis in a separate class und add commercial faciliti…
Browse files Browse the repository at this point in the history
…es as output for this analysis
  • Loading branch information
rewertvsp committed Mar 26, 2024
1 parent d93c98f commit 47af8f5
Show file tree
Hide file tree
Showing 12 changed files with 495 additions and 274 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
* *********************************************************************** */
package org.matsim.smallScaleCommercialTrafficGeneration;

import com.google.common.base.Joiner;
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
import com.graphhopper.jsprit.core.problem.job.Job;
import com.graphhopper.jsprit.core.problem.solution.SolutionCostCalculator;
Expand All @@ -28,6 +27,7 @@
import com.graphhopper.jsprit.core.problem.solution.route.activity.BreakActivity;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
Expand All @@ -40,24 +40,23 @@
import org.matsim.api.core.v01.population.*;
import org.matsim.application.options.ShpOptions;
import org.matsim.application.options.ShpOptions.Index;
import org.matsim.core.gbl.MatsimRandom;
import org.matsim.core.network.NetworkUtils;
import org.matsim.core.population.PopulationUtils;
import org.matsim.core.utils.io.IOUtils;
import org.matsim.freight.carriers.*;
import org.matsim.freight.carriers.CarrierCapabilities.FleetSize;
import org.matsim.freight.carriers.Tour.Pickup;
import org.matsim.freight.carriers.Tour.ServiceActivity;
import org.matsim.freight.carriers.Tour.TourElement;
import org.matsim.freight.carriers.jsprit.MatsimJspritFactory;
import org.matsim.core.gbl.MatsimRandom;
import org.matsim.core.network.NetworkUtils;
import org.matsim.core.population.PopulationUtils;
import org.matsim.core.utils.io.IOUtils;
import org.matsim.vehicles.Vehicle;
import org.matsim.vehicles.VehicleType;
import org.matsim.vehicles.VehicleUtils;
import org.matsim.vehicles.Vehicles;

import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -72,7 +71,6 @@
public class SmallScaleCommercialTrafficUtils {

private static final Logger log = LogManager.getLogger(SmallScaleCommercialTrafficUtils.class);
private static final Joiner JOIN = Joiner.on("\t");

/**
* Creates and return the Index of the zone shape.
Expand All @@ -82,7 +80,7 @@ public class SmallScaleCommercialTrafficUtils {
* @param shapeFileZoneNameColumn Column name of the zone in the shape file
* @return indexZones
*/
static Index getIndexZones(Path shapeFileZonePath, String shapeCRS, String shapeFileZoneNameColumn) {
public static Index getIndexZones(Path shapeFileZonePath, String shapeCRS, String shapeFileZoneNameColumn) {

ShpOptions shpZones = new ShpOptions(shapeFileZonePath, shapeCRS, StandardCharsets.UTF_8);
if (shpZones.readFeatures().iterator().next().getAttribute(shapeFileZoneNameColumn) == null)
Expand All @@ -98,7 +96,7 @@ static Index getIndexZones(Path shapeFileZonePath, String shapeCRS, String shape
* @param shapeFileLanduseTypeColumn Column name of the landuse in the shape file
* @return indexLanduse
*/
static Index getIndexLanduse(Path shapeFileLandusePath, String shapeCRS, String shapeFileLanduseTypeColumn) {
public static Index getIndexLanduse(Path shapeFileLandusePath, String shapeCRS, String shapeFileLanduseTypeColumn) {
ShpOptions shpLanduse = new ShpOptions(shapeFileLandusePath, shapeCRS, StandardCharsets.UTF_8);
if (shpLanduse.readFeatures().iterator().next().getAttribute(shapeFileLanduseTypeColumn) == null)
throw new NullPointerException("The column '" + shapeFileLanduseTypeColumn + "' does not exist in the landuse shape file. Please check the input.");
Expand All @@ -113,7 +111,7 @@ static Index getIndexLanduse(Path shapeFileLandusePath, String shapeCRS, String
* @param shapeFileBuildingTypeColumn Column name of the building in the shape file
* @return indexBuildings
*/
static Index getIndexBuildings(Path shapeFileBuildingsPath, String shapeCRS, String shapeFileBuildingTypeColumn) {
public static Index getIndexBuildings(Path shapeFileBuildingsPath, String shapeCRS, String shapeFileBuildingTypeColumn) {
ShpOptions shpBuildings = new ShpOptions(shapeFileBuildingsPath, shapeCRS, StandardCharsets.UTF_8);
if (shpBuildings.readFeatures().iterator().next().getAttribute(shapeFileBuildingTypeColumn) == null)
throw new NullPointerException("The column '" + shapeFileBuildingTypeColumn + "' does not exist in the building shape file. Please check the input.");
Expand All @@ -136,17 +134,6 @@ public static Index getIndexRegions(Path shapeFileRegionsPath, String shapeCRS,
return shpRegions.createIndex(shapeCRS, regionsShapeRegionColumn);
}

/**
* Writes a csv file with the result of the distribution per zone of the input data.
*/
static void writeResultOfDataDistribution(Map<String, Object2DoubleMap<String>> resultingDataPerZone,
Path outputFileInOutputFolder, Map<String, String> zoneIdRegionConnection)
throws IOException {

writeCSVWithCategoryHeader(resultingDataPerZone, outputFileInOutputFolder, zoneIdRegionConnection);
log.info("The data distribution is finished and written to: " + outputFileInOutputFolder);
}

/** Finds the nearest possible link for the building polygon.
* @param zone
* @param noPossibleLinks
Expand Down Expand Up @@ -191,39 +178,6 @@ static Id<Link> findNearestPossibleLink(String zone, List<String> noPossibleLink
return newLink;
}

/**
* Writer of data distribution data.
*/
private static void writeCSVWithCategoryHeader(Map<String, Object2DoubleMap<String>> resultingDataPerZone,
Path outputFileInInputFolder,
Map<String, String> zoneIdRegionConnection) throws MalformedURLException {
BufferedWriter writer = IOUtils.getBufferedWriter(outputFileInInputFolder.toUri().toURL(),
StandardCharsets.UTF_8, true);
try {
String[] header = new String[]{"zoneID", "region", "Inhabitants", "Employee", "Employee Primary Sector",
"Employee Construction", "Employee Secondary Sector Rest", "Employee Retail",
"Employee Traffic/Parcels", "Employee Tertiary Sector Rest"};
JOIN.appendTo(writer, header);
writer.write("\n");
for (String zone : resultingDataPerZone.keySet()) {
List<String> row = new ArrayList<>();
row.add(zone);
row.add(zoneIdRegionConnection.get(zone));
for (String category : header) {
if (!category.equals("zoneID") && !category.equals("region"))
row.add(String.valueOf((int) Math.round(resultingDataPerZone.get(zone).getDouble(category))));
}
JOIN.appendTo(writer, row);
writer.write("\n");
}

writer.close();

} catch (IOException e) {
log.error("Could not write the csv file with the data distribution data.", e);
}
}

/**
* Creates a population including the plans in preparation for the MATSim run. If a different name of the population is set, different plan variants per person are created
*/
Expand Down Expand Up @@ -571,6 +525,37 @@ static String findZoneOfLink(Id<Link> linkId, Map<String, Map<Id<Link>, Link>> l
return null;
}


/** TODO
* @param pathToDataDistributionToZones
* @return
* @throws IOException
*/
static Map<String, Object2DoubleMap<String>> readDataDistribution(Path pathToDataDistributionToZones) throws IOException {
if (!Files.exists(pathToDataDistributionToZones)) {
log.error("Required data per zone file {} not found", pathToDataDistributionToZones);
}

Map<String, Object2DoubleMap<String>> resultingDataPerZone = new HashMap<>();
try (BufferedReader reader = IOUtils.getBufferedReader(pathToDataDistributionToZones.toString())) {
CSVParser parse = CSVFormat.Builder.create(CSVFormat.DEFAULT).setDelimiter('\t').setHeader()
.setSkipHeaderRecord(true).build().parse(reader);

for (CSVRecord record : parse) {
String zoneID = record.get("zoneID");
resultingDataPerZone.put(zoneID, new Object2DoubleOpenHashMap<>());
for (int n = 2; n < parse.getHeaderMap().size(); n++) {
resultingDataPerZone.get(zoneID).mergeDouble(parse.getHeaderNames().get(n),
Double.parseDouble(record.get(n)), Double::sum);
}
}
}
log.info("Data distribution for " + resultingDataPerZone.size() + " zones was read from " +
pathToDataDistributionToZones);
return resultingDataPerZone;

}

/**
* Creates a cost calculator.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package org.matsim.smallScaleCommercialTrafficGeneration.prepare;

import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.locationtech.jts.geom.Geometry;
import org.matsim.api.core.v01.Coord;
import org.matsim.api.core.v01.Id;
import org.matsim.application.MATSimAppCommand;
import org.matsim.application.options.ShpOptions;
import org.matsim.core.utils.geometry.geotools.MGC;
import org.matsim.facilities.*;
import org.matsim.smallScaleCommercialTrafficGeneration.SmallScaleCommercialTrafficUtils;
import org.opengis.feature.simple.SimpleFeature;
import picocli.CommandLine;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.matsim.smallScaleCommercialTrafficGeneration.prepare.LanduseBuildingAnalysis.*;

@CommandLine.Command(name = "create-data-distribution-of-structure-data", description = "Create data distribution as preperation for the generation of small scale commercial traffic", showDefaultValues = true)
public class CreateDataDistributionOfStructureData implements MATSimAppCommand {

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

private enum LanduseConfiguration {
useOnlyOSMLanduse, useOSMBuildingsAndLanduse
}

@CommandLine.Option(names = "--pathOutput", description = "Path for the output", defaultValue = "output/TestDistributionClass")
private Path output;

@CommandLine.Option(names = "--landuseConfiguration", description = "Set option of used OSM data. Options: useOnlyOSMLanduse, useOSMBuildingsAndLanduse, useExistingDataDistribution", defaultValue = "useOSMBuildingsAndLanduse")
private LanduseConfiguration usedLanduseConfiguration;

@CommandLine.Option(names = "--regionsShapeFileName", description = "Path of the region shape file.", defaultValue = "contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testRegions.shp")
private Path shapeFileRegionsPath;

@CommandLine.Option(names = "--regionsShapeRegionColumn", description = "Name of the region column in the region shape file.", defaultValue = "region")
private String regionsShapeRegionColumn;

@CommandLine.Option(names = "--zoneShapeFileName", description = "Path of the zone shape file.", defaultValue = "contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testZones.shp")
private Path shapeFileZonePath;

@CommandLine.Option(names = "--zoneShapeFileNameColumn", description = "Name of the unique column of the name/Id of each zone in the zones shape file.", defaultValue = "name")
private String shapeFileZoneNameColumn;

@CommandLine.Option(names = "--buildingsShapeFileName", description = "Path of the buildings shape file", defaultValue = "contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testBuildings.shp")
private Path shapeFileBuildingsPath;

@CommandLine.Option(names = "--shapeFileBuildingTypeColumn", description = "Name of the unique column of the building type in the buildings shape file.", defaultValue = "type")
private String shapeFileBuildingTypeColumn;

@CommandLine.Option(names = "--landuseShapeFileName", description = "Path of the landuse shape file", defaultValue = "contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/shp/testLanduse.shp")
private Path shapeFileLandusePath;

@CommandLine.Option(names = "--shapeFileLanduseTypeColumn", description = "Name of the unique column of the landuse type in the landuse shape file.", defaultValue = "fclass")
private String shapeFileLanduseTypeColumn;

@CommandLine.Option(names = "--shapeCRS", description = "CRS of the three input shape files (zones, landuse, buildings", defaultValue = "EPSG:4326")
private String shapeCRS;

@CommandLine.Option(names = "--pathToInvestigationAreaData", description = "Path to the investigation area data", defaultValue = "contribs/small-scale-traffic-generation/test/input/org/matsim/smallScaleCommercialTrafficGeneration/investigationAreaData.csv")
private Path pathToInvestigationAreaData;

private final Map<String, List<String>> landuseCategoriesAndDataConnection = new HashMap<>();
private final Map<String, Map<String, List<SimpleFeature>>> buildingsPerZone = new HashMap<>();

private ShpOptions.Index indexZones;
private ShpOptions.Index indexBuildings;
private ShpOptions.Index indexLanduse;
private ShpOptions.Index indexInvestigationAreaRegions;

public static void main(String[] args) {
System.exit(new CommandLine(new CreateDataDistributionOfStructureData()).execute(args));
}

@Override
public Integer call() throws Exception {
log.info("Create data distribution of structure data");

if (!Files.exists(shapeFileLandusePath)) {
throw new Exception("Required landuse shape file not found:" + shapeFileLandusePath.toString());
}
if (!Files.exists(shapeFileBuildingsPath)) {
throw new Exception(
"Required OSM buildings shape file {} not found" + shapeFileBuildingsPath.toString());
}
if (!Files.exists(shapeFileZonePath)) {
throw new Exception("Required districts shape file {} not found" + shapeFileZonePath.toString());
}
if (!Files.exists(shapeFileRegionsPath)) {
throw new Exception("Required regions shape file {} not found" + shapeFileRegionsPath.toString());
}

indexZones = SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, shapeCRS, shapeFileZoneNameColumn);
indexBuildings = SmallScaleCommercialTrafficUtils.getIndexBuildings(shapeFileBuildingsPath, shapeCRS, shapeFileBuildingTypeColumn);
indexLanduse = SmallScaleCommercialTrafficUtils.getIndexLanduse(shapeFileLandusePath, shapeCRS, shapeFileLanduseTypeColumn);
indexInvestigationAreaRegions = SmallScaleCommercialTrafficUtils.getIndexRegions(shapeFileRegionsPath, shapeCRS, regionsShapeRegionColumn);

if(Files.notExists(output))
new File(output.toString()).mkdir();

createDefaultDataConnectionForOSM(landuseCategoriesAndDataConnection); //TODO: find way to import this connection

Map<String, Object2DoubleMap<String>> resultingDataPerZone = LanduseBuildingAnalysis
.createInputDataDistribution(output, landuseCategoriesAndDataConnection,
usedLanduseConfiguration.toString(), indexLanduse, indexZones,
indexBuildings, indexInvestigationAreaRegions, shapeFileZoneNameColumn, buildingsPerZone, pathToInvestigationAreaData);

ActivityFacilities facilities = FacilitiesUtils.createActivityFacilities();

ActivityFacilitiesFactory f = facilities.getFactory();

for (String zone : buildingsPerZone.keySet()) {
for (String assignedDataType : buildingsPerZone.get(zone).keySet()) {
buildingsPerZone.get(zone).get(assignedDataType).forEach(singleBuilding -> {
Id<ActivityFacility> id = Id.create(singleBuilding.getID(), ActivityFacility.class);
if (facilities.getFacilities().containsKey(id)) {
facilities.getFacilities().get(id).addActivityOption(f.createActivityOption(assignedDataType));
} else {
Coord coord = MGC.point2Coord(((Geometry) singleBuilding.getDefaultGeometry()).getCentroid());
ActivityFacility facility = f.createActivityFacility(id, coord);
facility.addActivityOption(f.createActivityOption(assignedDataType));
String[] buildingTypes = ((String) singleBuilding.getAttribute("type")).split(";");
int calculatedAreaPerBuildingCategory = calculateAreaPerBuildingCategory(singleBuilding, buildingTypes);
facility.getAttributes().putAttribute("shareOfZone_" + assignedDataType,
getShareOfTheBuildingAreaOfTheRelatedAreaOfTheZone(zone, calculatedAreaPerBuildingCategory,
assignedDataType)); //TODO: check if this is the right
//TODO Employee hat momentan noch infinity als Wert
facility.getAttributes().putAttribute("zone", zone);
facilities.addActivityFacility(facility);
}
});

}
}
Path facilityOutput = output.resolve("commercialFacilities.xml.gz");
log.info("Created {} facilities, writing to {}", facilities.getFacilities().size(), facilityOutput);

FacilitiesWriter writer = new FacilitiesWriter(facilities);
writer.write(facilityOutput.toString());

return 0;
}
}
Loading

0 comments on commit 47af8f5

Please sign in to comment.