diff --git a/contribs/application/pom.xml b/contribs/application/pom.xml index 4035f096a83..0ac05d56158 100644 --- a/contribs/application/pom.xml +++ b/contribs/application/pom.xml @@ -148,8 +148,14 @@ xercesImpl 2.12.2 - - + + org.matsim.contrib + dvrp + 2025.0-SNAPSHOT + compile + + + diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/noise/MergeNoiseOutput.java b/contribs/application/src/main/java/org/matsim/application/analysis/noise/MergeNoiseOutput.java index b5b85336c1c..f273ed2b761 100644 --- a/contribs/application/src/main/java/org/matsim/application/analysis/noise/MergeNoiseOutput.java +++ b/contribs/application/src/main/java/org/matsim/application/analysis/noise/MergeNoiseOutput.java @@ -18,6 +18,7 @@ import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Coord; import org.matsim.application.avro.XYTData; +import org.matsim.application.options.CsvOptions; import org.matsim.core.config.Config; import org.matsim.core.utils.io.IOUtils; import org.matsim.core.utils.misc.Time; @@ -92,7 +93,7 @@ public void setMaxTime(int maxTime) { /** * Merges noise data from multiple files into one file. */ - public void run() { + public void run() throws IOException { mergeReceiverPointData(outputDirectory + "/immissions/", "immission"); mergeReceiverPointData(outputDirectory + "/damages_receiverPoint/", "damages_receiverPoint"); mergeLinkData(outputDirectory.toString() + "/emissions/", "emission"); @@ -116,7 +117,7 @@ private void writeAvro(XYTData xytData, File output) { } } - private void mergeLinkData(String pathParameter, String label) { + private void mergeLinkData(String pathParameter, String label) throws IOException { log.info("Merging emissions data for label {}", label); Object2DoubleMap mergedData = new Object2DoubleOpenHashMap<>(); Table csvOutputMerged = Table.create(TextColumn.create("Link Id"), DoubleColumn.create("value")); @@ -126,9 +127,10 @@ private void mergeLinkData(String pathParameter, String label) { // Read the file Table table = Table.read().csv(CsvReadOptions.builder(IOUtils.getBufferedReader(path)) - .columnTypesPartial(Map.of("Link Id", ColumnType.TEXT)) + .columnTypesPartial(Map.of("Link Id", ColumnType.TEXT, + "Noise Emission " + Time.writeTime(time, Time.TIMEFORMAT_HHMMSS), ColumnType.DOUBLE)) .sample(false) - .separator(';').build()); + .separator(CsvOptions.detectDelimiter(path)).build()); for (Row row : table) { String linkId = row.getString("Link Id"); @@ -157,7 +159,7 @@ private void mergeLinkData(String pathParameter, String label) { * @param outputDir path to the receiverPoint data * @param label label for the receiverPoint data (which kind of data) */ - private void mergeReceiverPointData(String outputDir, String label) { + private void mergeReceiverPointData(String outputDir, String label) throws IOException { // data per time step, maps coord to value Int2ObjectMap> data = new Int2ObjectOpenHashMap<>(); @@ -188,7 +190,7 @@ private void mergeReceiverPointData(String outputDir, String label) { "t", ColumnType.DOUBLE, valueHeader, ColumnType.DOUBLE)) .sample(false) - .separator(';').build()); + .separator(CsvOptions.detectDelimiter(timeDataFile)).build()); // Loop over all rows in the data file for (Row row : dataTable) { @@ -265,7 +267,7 @@ private void mergeReceiverPointData(String outputDir, String label) { // Merges the immissions data @Deprecated - private void mergeImmissionsCSV(String pathParameter, String label) { + private void mergeImmissionsCSV(String pathParameter, String label) throws IOException { log.info("Merging immissions data for label {}", label); Object2DoubleMap mergedData = new Object2DoubleOpenHashMap<>(); @@ -284,7 +286,7 @@ private void mergeImmissionsCSV(String pathParameter, String label) { "Receiver Point Id", ColumnType.INTEGER, "t", ColumnType.DOUBLE)) .sample(false) - .separator(';').build()); + .separator(CsvOptions.detectDelimiter(path)).build()); // Loop over all rows in the file for (Row row : table) { diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkParamsOpt.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkParamsOpt.java index e9185fc7a0f..364ad15f036 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkParamsOpt.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/NetworkParamsOpt.java @@ -113,6 +113,7 @@ private static Feature createDefaultFeature(Link link) { String highwayType = NetworkUtils.getHighwayType(link); categories.put("highway_type", highwayType); + ft.put("idx", link.getId().index()); ft.put("speed", NetworkUtils.getAllowedSpeed(link)); ft.put("num_lanes", link.getNumberOfLanes()); ft.put("length", link.getLength()); diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ref/IndividualParams.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ref/IndividualParams.java new file mode 100644 index 00000000000..68ab8207611 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ref/IndividualParams.java @@ -0,0 +1,43 @@ +package org.matsim.application.prepare.network.params.ref; + +import it.unimi.dsi.fastutil.objects.Object2DoubleMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import org.matsim.application.prepare.Predictor; +import org.matsim.application.prepare.network.params.NetworkModel; + +/** + * Reference model that uses one specific speed factor for each link. + */ +public final class IndividualParams implements NetworkModel { + + private static final Predictor INSTANCE = new Model(); + + @Override + public Predictor speedFactor(String junctionType, String highwayType) { + return INSTANCE; + } + + private static final class Model implements Predictor { + + @Override + public double predict(Object2DoubleMap features, Object2ObjectMap categories) { + return predict(features, categories, new double[0]); + } + + @Override + public double predict(Object2DoubleMap features, Object2ObjectMap categories, double[] params) { + if (params.length == 0) + return 1; + + return params[(int) features.getDouble("idx")]; + } + + @Override + public double[] getData(Object2DoubleMap features, Object2ObjectMap categories) { + return new double[]{ + features.getDouble("idx") + }; + } + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/zone_preparation/MaxTravelTimeBasedZoneGenerator.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/zone_preparation/MaxTravelTimeBasedZoneGenerator.java new file mode 100644 index 00000000000..bbbacc6bdb7 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/zone_preparation/MaxTravelTimeBasedZoneGenerator.java @@ -0,0 +1,407 @@ +package org.matsim.application.prepare.network.zone_preparation; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.Node; +import org.matsim.contrib.dvrp.router.TimeAsTravelDisutility; +import org.matsim.contrib.dvrp.trafficmonitoring.QSimFreeSpeedTravelTime; +import org.matsim.contrib.zone.skims.SparseMatrix; +import org.matsim.contrib.zone.skims.TravelTimeMatrices; +import org.matsim.core.router.util.TravelDisutility; +import org.matsim.core.router.util.TravelTime; + +import java.util.*; + +public class MaxTravelTimeBasedZoneGenerator { + private final Network network; + private final double timeRadius; + private final Map, Set>> reachableLinksMap = new HashMap<>(); + private final SparseMatrix freeSpeedTravelTimeSparseMatrix; + private final Set> linksTobeCovered = new HashSet<>(); + private final int zoneGenerationIterations; + private final Map, List> zonalSystemData = new LinkedHashMap<>(); + + private static final Logger log = LogManager.getLogger(MaxTravelTimeBasedZoneGenerator.class); + + public MaxTravelTimeBasedZoneGenerator(Network network, double timeRadius, + SparseMatrix freeSpeedTravelTimeSparseMatrix, int zoneIterations) { + this.network = network; + this.timeRadius = timeRadius; + this.freeSpeedTravelTimeSparseMatrix = freeSpeedTravelTimeSparseMatrix; + this.zoneGenerationIterations = zoneIterations; + linksTobeCovered.addAll(network.getLinks().keySet()); + log.info(linksTobeCovered.size() + " links on the network are to be covered"); + } + + public static class Builder { + private final Network network; + + private double timeRadius = 300; + private double sparseMatrixMaxDistance = 10000; + private TravelTime travelTime = new QSimFreeSpeedTravelTime(1); + private TravelDisutility travelDisutility = new TimeAsTravelDisutility(travelTime); + private int zoneIterations = 0; + + public Builder(Network network) { + this.network = network; + } + + public Builder setTimeRadius(double timeRadius) { + this.timeRadius = timeRadius; + return this; + } + + public Builder setSparseMatrixMaxDistance(double sparseMatrixMaxDistance) { + this.sparseMatrixMaxDistance = sparseMatrixMaxDistance; + return this; + } + + public Builder setTravelTime(TravelTime travelTime) { + this.travelTime = travelTime; + return this; + } + + public Builder setTravelDisutility(TravelDisutility travelDisutility) { + this.travelDisutility = travelDisutility; + return this; + } + + public Builder setZoneIterations(int zoneIterations) { + this.zoneIterations = zoneIterations; + return this; + } + + public MaxTravelTimeBasedZoneGenerator build() { + SparseMatrix freeSpeedTravelTimeSparseMatrix = TravelTimeMatrices.calculateTravelTimeSparseMatrix( + new TravelTimeMatrices.RoutingParams(network, travelTime, travelDisutility, Runtime.getRuntime().availableProcessors()), + sparseMatrixMaxDistance, 0, 0).orElseThrow(); + return new MaxTravelTimeBasedZoneGenerator(network, timeRadius, + freeSpeedTravelTimeSparseMatrix, zoneIterations); + } + } + + public Network compute(){ + analyzeNetwork(); + selectInitialCentroids(); + generateZones(); + writeZonesInfoToAttributes(); + return network; + } + + private void analyzeNetwork() { + // Explore reachable links + log.info("Begin analyzing network. This may take some time..."); + int numOfNodesInNetwork = network.getNodes().size(); + ProgressPrinter networkAnalysisCounter = new ProgressPrinter("Network analysis", numOfNodesInNetwork); + + network.getNodes().keySet().forEach(nodeId -> reachableLinksMap.put(nodeId, new HashSet<>())); + for (Node node1 : network.getNodes().values()) { + // All outgoing links from this node are considered reachable + node1.getOutLinks().values().stream() + .filter(link -> linksTobeCovered.contains(link.getId())) + .forEach(link -> reachableLinksMap.get(node1.getId()).add(link.getId())); + for (Node node2 : network.getNodes().values()) { + // if same node, then skip + if (node1.getId().toString().equals(node2.getId().toString())) { + continue; + } + + double node1ToNode2TravelTime = freeSpeedTravelTimeSparseMatrix.get(node1, node2); + double node2ToNode1TravelTime = freeSpeedTravelTimeSparseMatrix.get(node2, node1); + // if the node 2 is too far away from node 1, then skip + if (node1ToNode2TravelTime == -1 || node2ToNode1TravelTime == -1 || + node1ToNode2TravelTime > timeRadius || node2ToNode1TravelTime > timeRadius) { + // note: -1 means not even recorded in the sparse matrix --> too far away + continue; + } + + // if we reach here, node 2 is reachable from node 1. Then, we check each outgoing links from node 2 + for (Link link : node2.getOutLinks().values()) { + if (linksTobeCovered.contains(link.getId())) { + double linkTravelTime = Math.floor(link.getLength() / link.getFreespeed()) + 1; + if (2 + node1ToNode2TravelTime + linkTravelTime <= timeRadius) { + // above is how VRP travel time calculated + reachableLinksMap.get(node1.getId()).add(link.getId()); + } + } + } + } + // node1 is analyzed, move on to next node + networkAnalysisCounter.countUp(); + } + } + + private void selectInitialCentroids() { + log.info("Begin selecting centroids. This may take some time..."); + int totalLinksToCover = linksTobeCovered.size(); + ProgressPrinter centroidSelectionPrinter = new ProgressPrinter("Initial centroid selection", totalLinksToCover); + + // Copy the reachable links map,including copying the sets in the values of the map + Map, Set>> newlyCoveredLinksMap = createReachableLInksMapCopy(); + + // Initialize uncovered links + Set> uncoveredLinkIds = new HashSet<>(linksTobeCovered); + while (!uncoveredLinkIds.isEmpty()) { + // score the links + Map, Double> nodesScoresMap = scoreTheNodes(newlyCoveredLinksMap); + + // choose the centroid based on score map + Id selectedNodeId = selectBasedOnScoreMap(nodesScoresMap); + + // add that node to the zonal system + zonalSystemData.put(selectedNodeId, new ArrayList<>()); + + // remove all the newly covered links from the uncoveredLinkIds + uncoveredLinkIds.removeAll(reachableLinksMap.get(selectedNodeId)); + + // update the newlyCoveredLinksMap by removing links that are already covered + for (Id nodeId : newlyCoveredLinksMap.keySet()) { + newlyCoveredLinksMap.get(nodeId).removeAll(reachableLinksMap.get(selectedNodeId)); + } + + // Print the progress + int numLinksAlreadyCovered = totalLinksToCover - uncoveredLinkIds.size(); + centroidSelectionPrinter.countTo(numLinksAlreadyCovered); + } + + log.info("Potential centroids identified. There are in total " + zonalSystemData.size() + " potential centroid points"); + // remove redundant centroid + removeRedundantCentroid(); + + } + + private void generateZones() { + log.info("Assigning links to the closest centroid"); + assignLinksToNearestZone(); + + // after the zone is generated, update the location of the centroids (move to a better location) + // this will lead to an updated zonal system --> we may need to run multiple iterations + for (int i = 0; i < zoneGenerationIterations; i++) { + int it = i + 1; + log.info("Improving zones now. Iteration #" + it + " out of " + zoneGenerationIterations); + + List> updatedCentroids = new ArrayList<>(); + for (Id originalZoneCentroidNodeId : zonalSystemData.keySet()) { + Node currentBestCentroidNode = network.getNodes().get(originalZoneCentroidNodeId); + List linksInZone = zonalSystemData.get(originalZoneCentroidNodeId); + Set potentialCentroidNodes = new HashSet<>(); + linksInZone.forEach(link -> potentialCentroidNodes.add(link.getToNode())); + + double bestScore = Double.POSITIVE_INFINITY; + for (Node potentialNewCentroidNode : potentialCentroidNodes) { + double cost = 0; + for (Link link : linksInZone) { + if (!linksTobeCovered.contains(link.getId())) { + // if this link is not relevant -> zero cost + continue; + } + if (!reachableLinksMap.get(potentialNewCentroidNode.getId()).contains(link.getId())) { + // if some link in the original zone is not reachable from here, this node cannot be a centroid + cost = Double.POSITIVE_INFINITY; + break; + } + cost += calculateVrpNodeToLinkTravelTime(potentialNewCentroidNode, link); + } + + if (cost < bestScore) { + bestScore = cost; + currentBestCentroidNode = potentialNewCentroidNode; + } + } + updatedCentroids.add(currentBestCentroidNode.getId()); + } + + // re-generate the zone based on updated centroids + zonalSystemData.clear(); + updatedCentroids.forEach(zoneId -> zonalSystemData.put(zoneId, new ArrayList<>())); + removeRedundantCentroid(); + assignLinksToNearestZone(); + } + } + + private void writeZonesInfoToAttributes() { + // Identify the neighbours for each zone, such that we can color the neighboring zones in different colors + Map> zoneNeighborsMap = new HashMap<>(); + zonalSystemData.keySet().forEach(nodeId -> zoneNeighborsMap.put(nodeId.toString(), new HashSet<>())); + List centroids = new ArrayList<>(zoneNeighborsMap.keySet()); + int numZones = centroids.size(); + for (int i = 0; i < numZones; i++) { + String zoneI = centroids.get(i); + for (int j = i + 1; j < numZones; j++) { + String zoneJ = centroids.get(j); + + Set nodesInZoneI = new HashSet<>(); + zonalSystemData.get(Id.createNodeId(zoneI)).forEach(link -> nodesInZoneI.add(link.getFromNode())); + zonalSystemData.get(Id.createNodeId(zoneI)).forEach(link -> nodesInZoneI.add(link.getToNode())); + + Set nodesInZoneJ = new HashSet<>(); + zonalSystemData.get(Id.createNodeId(zoneJ)).forEach(link -> nodesInZoneJ.add(link.getFromNode())); + zonalSystemData.get(Id.createNodeId(zoneJ)).forEach(link -> nodesInZoneJ.add(link.getToNode())); + + if (!Collections.disjoint(nodesInZoneI, nodesInZoneJ)) { + // If two zones shared any node, then we know they are neighbors + zoneNeighborsMap.get(zoneI).add(zoneJ); + zoneNeighborsMap.get(zoneJ).add(zoneI); + } + } + } + + // Add attribute to the link for visualisation + log.info("Marking links in each zone"); + // Determine the color of each zone (for visualisation) + Map coloringMap = new HashMap<>(); + zoneNeighborsMap.keySet().forEach(zoneId -> coloringMap.put(zoneId, 0)); + for (String zoneId : zoneNeighborsMap.keySet()) { + Set usedColor = new HashSet<>(); + for (String neighboringZone : zoneNeighborsMap.get(zoneId)) { + usedColor.add(coloringMap.get(neighboringZone)); + } + boolean colorFound = false; + int i = 1; + while (!colorFound) { + if (usedColor.contains(i)) { + i++; + } else { + colorFound = true; + } + } + coloringMap.put(zoneId, i); + } + + // Marking the color idx of each link + for (Id centroidNodeId : zonalSystemData.keySet()) { + int color = coloringMap.get(centroidNodeId.toString()); + for (Link link : zonalSystemData.get(centroidNodeId)) { + link.getAttributes().putAttribute("zone_color", color); + link.getAttributes().putAttribute("zone_id", centroidNodeId.toString()); + } + } + + // Marking the relevant links (i.e. links to be covered) + for (Id linkId : network.getLinks().keySet()) { + if (linksTobeCovered.contains(linkId)) { + network.getLinks().get(linkId).getAttributes().putAttribute("relevant", "yes"); + } else { + network.getLinks().get(linkId).getAttributes().putAttribute("relevant", "no"); + } + } + + // Marking centroid nodes + for (Node node : network.getNodes().values()) { + if (zonalSystemData.containsKey(node.getId())) { + node.getAttributes().putAttribute("isCentroid", "yes"); + node.getAttributes().putAttribute("zone_color", coloringMap.get(node.getId().toString())); + } else { + node.getAttributes().putAttribute("isCentroid", "no"); + node.getAttributes().putAttribute("zone_color", Double.NaN); + } + } + } + + private Map, Set>> createReachableLInksMapCopy() { + Map, Set>> reachableLinksMapCopy = new HashMap<>(); + for (Id nodeId : network.getNodes().keySet()) { + reachableLinksMapCopy.put(nodeId, new HashSet<>(reachableLinksMap.get(nodeId))); + } + return reachableLinksMapCopy; + } + + private Map, Double> scoreTheNodes(Map, Set>> newlyCoveredLinksMap) { + // Current implementation: simply count the number of newly covered links + Map, Double> nodeScoresMap = new HashMap<>(); + for (Node node : network.getNodes().values()) { + Set> newlyCoveredLinkIds = newlyCoveredLinksMap.get(node.getId()); + double score = newlyCoveredLinkIds.size(); + nodeScoresMap.put(node.getId(), score); + } + return nodeScoresMap; + } + + private Id selectBasedOnScoreMap(Map, Double> nodesScoresMap) { + // Current implementation: Simply choose the link with best score + Id selectedNodeId = null; + double bestScore = 0; + for (Id nodeId : nodesScoresMap.keySet()) { + if (nodesScoresMap.get(nodeId) > bestScore) { + bestScore = nodesScoresMap.get(nodeId); + selectedNodeId = nodeId; + } + } + return selectedNodeId; + } + + private void removeRedundantCentroid() { + // Find all redundant centroids + log.info("Checking for redundant centroids"); + Set> redundantCentroids = identifyRedundantCentroids(); + log.info("Number of redundant centroids identified = " + redundantCentroids.size()); + + // Remove the redundant centroid that covers the minimum number of links + while (!redundantCentroids.isEmpty()) { + int minReachableLinks = Integer.MAX_VALUE; + Id centroidToRemove = null; + for (Id redundantCentroid : redundantCentroids) { + int numReachableLinks = reachableLinksMap.get(redundantCentroid).size(); + if (numReachableLinks < minReachableLinks) { + minReachableLinks = numReachableLinks; + centroidToRemove = redundantCentroid; + } + } + zonalSystemData.remove(centroidToRemove); + + // update redundant centroids set + redundantCentroids = identifyRedundantCentroids(); + log.info("Removing in progress: " + redundantCentroids.size() + " redundant centroids (i.e., zones) left"); + } + log.info("After removal, there are " + zonalSystemData.size() + " centroids (i.e., zones) remaining"); + } + + protected Set> identifyRedundantCentroids() { + Set> redundantCentroids = new HashSet<>(); + for (Id centroidNodeId : zonalSystemData.keySet()) { + Set> uniqueReachableLinkIds = new HashSet<>(reachableLinksMap.get(centroidNodeId)); + for (Id anotherCentriodNodeId : zonalSystemData.keySet()) { + if (centroidNodeId.toString().equals(anotherCentriodNodeId.toString())) { + // skip itself + continue; + } + uniqueReachableLinkIds.removeAll(reachableLinksMap.get(anotherCentriodNodeId)); + } + if (uniqueReachableLinkIds.isEmpty()) { + // There is no unique links covered by this zone, this zone is redundant + redundantCentroids.add(centroidNodeId); + } + } + return redundantCentroids; + } + + private void assignLinksToNearestZone() { + log.info("Assigning links into nearest zones (i.e., nearest centroid)"); + for (Link linkBeingAssigned : network.getLinks().values()) { + // Find the closest centroid and assign the link to that zone + double minDistance = Double.POSITIVE_INFINITY; + Id closestCentralNodeId = zonalSystemData.keySet().iterator().next(); + // Assign to a random centroid as initialization + for (Id centroidNodeId : zonalSystemData.keySet()) { + Node centroidNode = network.getNodes().get(centroidNodeId); + double distance = calculateVrpNodeToLinkTravelTime(centroidNode, linkBeingAssigned); + if (distance < minDistance) { + minDistance = distance; + closestCentralNodeId = centroidNodeId; + } + } + zonalSystemData.get(closestCentralNodeId).add(linkBeingAssigned); + } + } + + private double calculateVrpNodeToLinkTravelTime(Node fromNode, Link toLink) { + if (freeSpeedTravelTimeSparseMatrix.get(fromNode, toLink.getFromNode()) == -1) { + return Double.POSITIVE_INFINITY; + } + return freeSpeedTravelTimeSparseMatrix.get(fromNode, toLink.getFromNode()) + + Math.ceil(toLink.getLength() / toLink.getFreespeed()) + 2; + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/zone_preparation/PrepareMaxTravelTimeBasedZonalSystem.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/zone_preparation/PrepareMaxTravelTimeBasedZonalSystem.java new file mode 100644 index 00000000000..ab3b543190d --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/zone_preparation/PrepareMaxTravelTimeBasedZonalSystem.java @@ -0,0 +1,110 @@ +package org.matsim.application.prepare.network.zone_preparation; + +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.Point; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.NetworkWriter; +import org.matsim.api.core.v01.network.Node; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.ShpOptions; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.network.algorithms.NetworkCleaner; +import org.matsim.core.network.algorithms.TransportModeNetworkFilter; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.core.utils.geometry.geotools.MGC; +import picocli.CommandLine; + +import java.util.*; + +public class PrepareMaxTravelTimeBasedZonalSystem implements MATSimAppCommand { + @CommandLine.Option(names = "--input", required = true, description = "input network path") + private String inputNetworkPath; + + @CommandLine.Option(names = "--output", required = true, description = "output network path") + private String outputNetworkPath; + + @CommandLine.Option(names = "--max-travel-time", defaultValue = "300", description = "max time distance away from zone centroid [second]") + private double maxTimeDistance; + + @CommandLine.Option(names = "--iterations", defaultValue = "20", description = "number of iterations reduce number of zones") + private int iterations; + + @CommandLine.Option(names = "--network-modes", description = "filter the network based on the modes we are interested in", split = ",", defaultValue = "car") + private List networkModes; + + // Optional shp input for network filtering + @CommandLine.Mixin + private ShpOptions shp = new ShpOptions(); + + @Override + public Integer call() throws Exception { + Network fullNetwork = NetworkUtils.readNetwork(inputNetworkPath); + + // extract the subnetwork from the full network based on allowed modes + Network subNetwork = ScenarioUtils.loadScenario(ConfigUtils.createConfig()).getNetwork(); + new TransportModeNetworkFilter(fullNetwork).filter(subNetwork, new HashSet<>(networkModes)); + + // filter the subnetwork if shp is provided + if (shp.isDefined()){ + filterNetworkWithShp(subNetwork); + } + + // clean the network after the filter process + new NetworkCleaner().run(subNetwork); + + // perform zone-generation on the subnetwork (zone information will be written directly to the attributes of the links in the subnetwork) + MaxTravelTimeBasedZoneGenerator.Builder builder = new MaxTravelTimeBasedZoneGenerator.Builder(subNetwork); + MaxTravelTimeBasedZoneGenerator generator = builder.setTimeRadius(maxTimeDistance).setZoneIterations(iterations).build(); + generator.compute(); + + // add attribute related to zonal information to the full network + for (Id linkId : subNetwork.getLinks().keySet()) { + Link linkInSubNetwork = subNetwork.getLinks().get(linkId); + Link linkInFullNetwork = fullNetwork.getLinks().get(linkId); + for (Map.Entry attribute : linkInSubNetwork.getAttributes().getAsMap().entrySet()) { + // if the newly written attribute (related to zone) does not exist in full network, then add this attribute to the link in full network + if (linkInFullNetwork.getAttributes().getAttribute(attribute.getKey()) == null) { + linkInFullNetwork.getAttributes().putAttribute(attribute.getKey(), attribute.getValue()); + } + } + } + for (Id nodeId : subNetwork.getNodes().keySet()) { + Node nodeInSubNetwork = subNetwork.getNodes().get(nodeId); + Node nodeInFullNetwork = fullNetwork.getNodes().get(nodeId); + for (Map.Entry attribute : nodeInSubNetwork.getAttributes().getAsMap().entrySet()) { + // if the newly written attribute (related to zone) does not exist in full network, then add this attribute to the node in full network + if (nodeInFullNetwork.getAttributes().getAttribute(attribute.getKey()) == null) { + nodeInFullNetwork.getAttributes().putAttribute(attribute.getKey(), attribute.getValue()); + } + } + } + + + // write down the processed full network to the output path + new NetworkWriter(fullNetwork).write(outputNetworkPath); + + return 0; + } + + public static void main(String[] args) { + new PrepareMaxTravelTimeBasedZonalSystem().execute(args); + } + + private void filterNetworkWithShp(Network network){ + Geometry areaToKeep = shp.getGeometry(); + List linksToRemove = new ArrayList<>(); + for (Link link : network.getLinks().values()) { + Point from = MGC.coord2Point(link.getFromNode().getCoord()); + Point to = MGC.coord2Point(link.getToNode().getCoord()); + if (!from.within(areaToKeep) || !to.within(areaToKeep)) { + linksToRemove.add(link); + } + } + for (Link link : linksToRemove) { + network.removeLink(link.getId()); + } + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/zone_preparation/ProgressPrinter.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/zone_preparation/ProgressPrinter.java new file mode 100644 index 00000000000..275a5a462d2 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/zone_preparation/ProgressPrinter.java @@ -0,0 +1,52 @@ +package org.matsim.application.prepare.network.zone_preparation; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ProgressPrinter { + private static final Logger log = LogManager.getLogger(ProgressPrinter.class); + private final String processName; + private final int mileStone; + private final int stepSize; + private int counter = 0; + private int pct = 0; + private boolean valid = true; + + /** + * Progress printer with customizable step size + */ + public ProgressPrinter(String processName, int totalNumToProcess, int stepSize) { + this.processName = processName; + this.stepSize = stepSize; + this.mileStone = totalNumToProcess / (100 / stepSize); + if (mileStone <= 0) { + valid = false; + } + } + + /** + * By default, step size is 10 (percent) + */ + public ProgressPrinter(String processName, int totalNumToProcess) { + this.processName = processName; + this.stepSize = 10; + this.mileStone = totalNumToProcess / this.stepSize; + if (mileStone <= 0) { + valid = false; + } + } + + public void countUp() { + counter++; + if (valid && counter % mileStone == 0) { + pct += stepSize; + log.info(processName + " in progress: " + pct + "% completed"); + } + } + + public void countTo(int currentProgress) { + while (counter < currentProgress) { + countUp(); + } + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfs.java b/contribs/application/src/main/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfs.java index f65bffae57a..5038d1f046c 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfs.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfs.java @@ -7,6 +7,7 @@ import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.NetworkWriter; @@ -199,7 +200,7 @@ private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network Scenario scenario = builder.build(); // add pseudo network for pt - new CreatePseudoNetwork(scenario.getTransitSchedule(), scenario.getNetwork(), "pt_", 0.1, 100000.0).createNetwork(); + new CreatePseudoNetwork(scenario.getTransitSchedule(), scenario.getNetwork(), ptNetworkIdentifier, 0.1, 100000.0).createNetwork(); // create TransitVehicle types // see https://svn.vsp.tu-berlin.de/repos/public-svn/publications/vspwp/2014/14-24/ for veh capacities @@ -219,7 +220,6 @@ private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network VehicleUtils.setEgressTime(reRbVehicleType, 1.0 / 10.0); // 1s per alighting agent, distributed on 10 doors addHbefaMapping(reRbVehicleType, HbefaVehicleCategory.NON_HBEFA_VEHICLE); - scenario.getTransitVehicles().addVehicleType(reRbVehicleType); } VehicleType sBahnVehicleType = vehicleFactory.createVehicleType(Id.create("S-Bahn_veh_type", VehicleType.class)); @@ -230,6 +230,8 @@ private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network VehicleUtils.setDoorOperationMode(sBahnVehicleType, VehicleType.DoorOperationMode.serial); // first finish boarding, then start alighting VehicleUtils.setAccessTime(sBahnVehicleType, 1.0 / 24.0); // 1s per boarding agent, distributed on 8*3 doors VehicleUtils.setEgressTime(sBahnVehicleType, 1.0 / 24.0); // 1s per alighting agent, distributed on 8*3 doors + + addHbefaMapping(sBahnVehicleType, HbefaVehicleCategory.NON_HBEFA_VEHICLE); scenario.getTransitVehicles().addVehicleType(sBahnVehicleType); } VehicleType uBahnVehicleType = vehicleFactory.createVehicleType(Id.create("U-Bahn_veh_type", VehicleType.class)); @@ -240,9 +242,9 @@ private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network VehicleUtils.setDoorOperationMode(uBahnVehicleType, VehicleType.DoorOperationMode.serial); // first finish boarding, then start alighting VehicleUtils.setAccessTime(uBahnVehicleType, 1.0 / 18.0); // 1s per boarding agent, distributed on 6*3 doors VehicleUtils.setEgressTime(uBahnVehicleType, 1.0 / 18.0); // 1s per alighting agent, distributed on 6*3 doors - scenario.getTransitVehicles().addVehicleType(uBahnVehicleType); addHbefaMapping(uBahnVehicleType, HbefaVehicleCategory.NON_HBEFA_VEHICLE); + scenario.getTransitVehicles().addVehicleType(uBahnVehicleType); } VehicleType tramVehicleType = vehicleFactory.createVehicleType(Id.create("Tram_veh_type", VehicleType.class)); @@ -253,6 +255,8 @@ private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network VehicleUtils.setDoorOperationMode(tramVehicleType, VehicleType.DoorOperationMode.serial); // first finish boarding, then start alighting VehicleUtils.setAccessTime(tramVehicleType, 1.0 / 5.0); // 1s per boarding agent, distributed on 5 doors VehicleUtils.setEgressTime(tramVehicleType, 1.0 / 5.0); // 1s per alighting agent, distributed on 5 doors + + addHbefaMapping(tramVehicleType, HbefaVehicleCategory.NON_HBEFA_VEHICLE); scenario.getTransitVehicles().addVehicleType(tramVehicleType); } VehicleType busVehicleType = vehicleFactory.createVehicleType(Id.create("Bus_veh_type", VehicleType.class)); @@ -263,7 +267,10 @@ private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network VehicleUtils.setDoorOperationMode(busVehicleType, VehicleType.DoorOperationMode.serial); // first finish boarding, then start alighting VehicleUtils.setAccessTime(busVehicleType, 1.0 / 3.0); // 1s per boarding agent, distributed on 3 doors VehicleUtils.setEgressTime(busVehicleType, 1.0 / 3.0); // 1s per alighting agent, distributed on 3 doors + + addHbefaMapping(busVehicleType, HbefaVehicleCategory.URBAN_BUS); scenario.getTransitVehicles().addVehicleType(busVehicleType); + } VehicleType ferryVehicleType = vehicleFactory.createVehicleType(Id.create("Ferry_veh_type", VehicleType.class)); { @@ -273,6 +280,8 @@ private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network VehicleUtils.setDoorOperationMode(ferryVehicleType, VehicleType.DoorOperationMode.serial); // first finish boarding, then start alighting VehicleUtils.setAccessTime(ferryVehicleType, 1.0 / 1.0); // 1s per boarding agent, distributed on 1 door VehicleUtils.setEgressTime(ferryVehicleType, 1.0 / 1.0); // 1s per alighting agent, distributed on 1 door + + addHbefaMapping(ferryVehicleType, HbefaVehicleCategory.NON_HBEFA_VEHICLE); scenario.getTransitVehicles().addVehicleType(ferryVehicleType); } @@ -284,6 +293,8 @@ private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network VehicleUtils.setDoorOperationMode(ptVehicleType, VehicleType.DoorOperationMode.serial); // first finish boarding, then start alighting VehicleUtils.setAccessTime(ptVehicleType, 1.0 / 1.0); // 1s per boarding agent, distributed on 1 door VehicleUtils.setEgressTime(ptVehicleType, 1.0 / 1.0); // 1s per alighting agent, distributed on 1 door + + addHbefaMapping(ptVehicleType, HbefaVehicleCategory.NON_HBEFA_VEHICLE); scenario.getTransitVehicles().addVehicleType(ptVehicleType); } @@ -432,6 +443,7 @@ private static void addHbefaMapping(VehicleType vehicleType, HbefaVehicleCategor VehicleUtils.setHbefaTechnology(carEngineInformation, "average"); VehicleUtils.setHbefaSizeClass(carEngineInformation, "average"); VehicleUtils.setHbefaEmissionsConcept(carEngineInformation, "average"); + vehicleType.setNetworkMode(TransportMode.pt); } private static void increaseLinkFreespeedIfLower(Link link, double newFreespeed) { diff --git a/contribs/application/src/test/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfsTest.java b/contribs/application/src/test/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfsTest.java index 5b3732f6347..579f2381f4f 100644 --- a/contribs/application/src/test/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfsTest.java +++ b/contribs/application/src/test/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfsTest.java @@ -30,6 +30,7 @@ void run() { "--network", network.toString(), "--target-crs", "EPSG:4326", "--date", "2019-01-01", + "--validate=true", "--output", output ); diff --git a/contribs/common/pom.xml b/contribs/common/pom.xml index 210daca7aa9..9d59862c307 100644 --- a/contribs/common/pom.xml +++ b/contribs/common/pom.xml @@ -28,5 +28,5 @@ h3 4.1.1 - + diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneSystemUtils.java b/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneSystemUtils.java index d679bc2c2bb..ef27a3ebc4b 100644 --- a/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneSystemUtils.java +++ b/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneSystemUtils.java @@ -20,6 +20,8 @@ import org.matsim.contrib.common.util.DistanceUtils; import org.matsim.contrib.common.zones.io.ZoneShpReader; import org.matsim.contrib.common.zones.io.ZoneXmlReader; +import org.matsim.contrib.common.zones.systems.geom_free_zones.GeometryFreeZoneSystem; +import org.matsim.contrib.common.zones.systems.geom_free_zones.GeometryFreeZoneSystemParams; import org.matsim.contrib.common.zones.systems.grid.GISFileZoneSystemParams; import org.matsim.contrib.common.zones.systems.grid.h3.H3GridZoneSystemParams; import org.matsim.contrib.common.zones.systems.grid.h3.H3ZoneSystem; @@ -81,8 +83,9 @@ public static ZoneSystem createZoneSystem(@Nullable URL context, @Nonnull Networ case H3GridZoneSystemParams.SET_NAME -> { Preconditions.checkNotNull(((H3GridZoneSystemParams) zoneSystemParams).h3Resolution); Preconditions.checkNotNull(crs); - yield new H3ZoneSystem(crs, ((H3GridZoneSystemParams) zoneSystemParams).h3Resolution, network, zoneFilter); + yield new H3ZoneSystem(crs, ((H3GridZoneSystemParams) zoneSystemParams).h3Resolution, network, zoneFilter); } + case GeometryFreeZoneSystemParams.SET_NAME -> new GeometryFreeZoneSystem(network); default -> throw new IllegalStateException("Unexpected value: " + zoneSystemParams.getName()); }; return zoneSystem; diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/zones/systems/geom_free_zones/GeometryFreeZoneSystem.java b/contribs/common/src/main/java/org/matsim/contrib/common/zones/systems/geom_free_zones/GeometryFreeZoneSystem.java new file mode 100644 index 00000000000..2a081dc5d05 --- /dev/null +++ b/contribs/common/src/main/java/org/matsim/contrib/common/zones/systems/geom_free_zones/GeometryFreeZoneSystem.java @@ -0,0 +1,61 @@ +package org.matsim.contrib.common.zones.systems.geom_free_zones; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.Node; +import org.matsim.contrib.common.zones.Zone; +import org.matsim.contrib.common.zones.ZoneImpl; +import org.matsim.contrib.common.zones.ZoneSystem; + +import java.util.*; + +import static org.matsim.contrib.common.zones.systems.geom_free_zones.GeometryFreeZoneSystemParams.SET_NAME; +import static org.matsim.contrib.common.zones.systems.geom_free_zones.GeometryFreeZoneSystemParams.ZONE_ID; + +public class GeometryFreeZoneSystem implements ZoneSystem { + private final Map, Zone> zones = new HashMap<>(); + private final Map, List> linksForZones = new HashMap<>(); + private final Map, Zone> zoneForLinks = new HashMap<>(); + private final Map, Zone> zoneForNodes = new HashMap<>(); + + public GeometryFreeZoneSystem(Network network) { + for (Link link : network.getLinks().values()) { + String zoneIdString = (String) link.getAttributes().getAttribute(ZONE_ID); + Id zoneId = Id.create(zoneIdString, Zone.class); + Zone zone; + if (!zones.containsKey(zoneId)) { + // zone has not yet been created + zone = new ZoneImpl(zoneId, null, SET_NAME); + zones.put(zoneId, zone); + linksForZones.put(zoneId, List.of(link)); + } else { + // zone already created + zone = zones.get(zoneId); + linksForZones.get(zoneId).add(link); + } + zoneForLinks.put(link.getId(), zone); + zoneForNodes.put(link.getToNode().getId(), zone); + } + } + + @Override + public Optional getZoneForLinkId(Id link) { + return zoneForLinks.containsKey(link) ? Optional.of(zoneForNodes.get(link)) : Optional.empty(); + } + + @Override + public Optional getZoneForNodeId(Id nodeId) { + return zoneForNodes.containsKey(nodeId) ? Optional.of(zoneForNodes.get(nodeId)) : Optional.empty(); + } + + @Override + public List getLinksForZoneId(Id zone) { + return linksForZones.get(zone); + } + + @Override + public Map, Zone> getZones() { + return zones; + } +} diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/zones/systems/geom_free_zones/GeometryFreeZoneSystemParams.java b/contribs/common/src/main/java/org/matsim/contrib/common/zones/systems/geom_free_zones/GeometryFreeZoneSystemParams.java new file mode 100644 index 00000000000..5d17c83e8c1 --- /dev/null +++ b/contribs/common/src/main/java/org/matsim/contrib/common/zones/systems/geom_free_zones/GeometryFreeZoneSystemParams.java @@ -0,0 +1,32 @@ +package org.matsim.contrib.common.zones.systems.geom_free_zones; + +import com.google.common.base.Verify; +import jakarta.validation.constraints.Positive; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.network.Network; +import org.matsim.contrib.common.zones.ZoneSystemParams; +import org.matsim.core.config.Config; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.scenario.ScenarioUtils; + +/** + * Generate geometry-free zones based on network directly. Additional attribute in link is required: zone_id + */ +public class GeometryFreeZoneSystemParams extends ZoneSystemParams { + + public static final String SET_NAME = "GeometryFreeZoneSystem"; + public static final String ZONE_ID = "zoneId"; + + public GeometryFreeZoneSystemParams() { + super(SET_NAME); + } + + @Override + protected void checkConsistency(Config config) { + super.checkConsistency(config); + Network network = ScenarioUtils.createScenario(config).getNetwork(); + // Here, we check one arbitrary link from the (sub-)network used for DVRP/DRT vehicles and see if there is a zone ID attribute + Verify.verify(network.getLinks().values().iterator().next().getAttributes().getAttribute(ZONE_ID) != null, + "Zone id attribute not set"); + } +} diff --git a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceModule.java b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceModule.java index abf34cb42f6..c669cc0d237 100644 --- a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceModule.java +++ b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceModule.java @@ -23,6 +23,7 @@ public class DiscreteModeChoiceModule extends AbstractModule { @Override public void install() { addPlanStrategyBinding(STRATEGY_NAME).toProvider(DiscreteModeChoiceStrategyProvider.class); + addControlerListenerBinding().to(UtilitiesWriterHandler.class); if (getConfig().replanning().getPlanSelectorForRemoval().equals(NonSelectedPlanSelector.NAME)) { bindPlanSelectorForRemoval().to(NonSelectedPlanSelector.class); diff --git a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/UtilitiesWriterHandler.java b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/UtilitiesWriterHandler.java new file mode 100644 index 00000000000..ff89cfcdc35 --- /dev/null +++ b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/UtilitiesWriterHandler.java @@ -0,0 +1,52 @@ +package org.matsim.contribs.discrete_mode_choice.modules; + +import com.google.inject.Inject; +import org.matsim.api.core.v01.population.Population; +import org.matsim.contribs.discrete_mode_choice.modules.config.DiscreteModeChoiceConfigGroup; +import org.matsim.contribs.discrete_mode_choice.modules.utils.ExtractPlanUtilities; +import org.matsim.core.config.groups.ControllerConfigGroup; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.controler.events.IterationStartsEvent; +import org.matsim.core.controler.events.ShutdownEvent; +import org.matsim.core.controler.listener.IterationStartsListener; +import org.matsim.core.controler.listener.ShutdownListener; + +import java.io.IOException; + +public class UtilitiesWriterHandler implements ShutdownListener, IterationStartsListener { + private final OutputDirectoryHierarchy outputDirectoryHierarchy; + private final ControllerConfigGroup controllerConfigGroup; + private final Population population; + private final DiscreteModeChoiceConfigGroup discreteModeChoiceConfigGroup; + + @Inject + public UtilitiesWriterHandler(ControllerConfigGroup controllerConfigGroup, DiscreteModeChoiceConfigGroup discreteModeChoiceConfigGroup, Population population, OutputDirectoryHierarchy outputDirectoryHierarchy) { + this.controllerConfigGroup = controllerConfigGroup; + this.discreteModeChoiceConfigGroup = discreteModeChoiceConfigGroup; + this.population = population; + this.outputDirectoryHierarchy = outputDirectoryHierarchy; + } + + @Override + public void notifyShutdown(ShutdownEvent event) { + String filePath = this.outputDirectoryHierarchy.getOutputFilename("dmc_utilities.csv"); + try { + ExtractPlanUtilities.writePlanUtilities(population, filePath); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void notifyIterationStarts(IterationStartsEvent event) { + if(event.getIteration() == controllerConfigGroup.getFirstIteration() || this.discreteModeChoiceConfigGroup.getWriteUtilitiesInterval() % event.getIteration() != 0) { + return; + } + String filePath = this.outputDirectoryHierarchy.getIterationFilename(event.getIteration(), "dmc_utilities.csv"); + try { + ExtractPlanUtilities.writePlanUtilities(population, filePath); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/config/DiscreteModeChoiceConfigGroup.java b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/config/DiscreteModeChoiceConfigGroup.java index c692de9d40b..5a437d97b22 100644 --- a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/config/DiscreteModeChoiceConfigGroup.java +++ b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/config/DiscreteModeChoiceConfigGroup.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.stream.Collectors; +import jakarta.validation.constraints.Positive; import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceModel; import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceModel.FallbackBehaviour; import org.matsim.contribs.discrete_mode_choice.modules.ConstraintModule; @@ -27,7 +28,7 @@ /** * Main config group for the DiscreteModeChoice extension. - * + * * @author sebhoerl */ public class DiscreteModeChoiceConfigGroup extends ReflectiveConfigGroup { @@ -53,7 +54,8 @@ public class DiscreteModeChoiceConfigGroup extends ReflectiveConfigGroup { private Collection tripFilters = new HashSet<>(); private Collection cachedModes = new HashSet<>(); - + @Positive + private int writeUtilitiesInterval = 1; public static final String GROUP_NAME = "DiscreteModeChoice"; public static final String PERFORM_REROUTE = "performReroute"; @@ -110,6 +112,9 @@ public class DiscreteModeChoiceConfigGroup extends ReflectiveConfigGroup { public static final String CACHED_MODES = "cachedModes"; public static final String CACHED_MODES_CMT = "Trips tested with the modes listed here will be cached for each combination of trip and agent during one replanning pass."; + public static final String WRITE_UTILITIES_INTERVAL = "writeUtilitiesInterval"; + public static final String WRITE_UTILITIES_INTERVAL_CMT = "Specifies the interval, in iterations, at which the dmc_utilities.csv file is written. If set to 0, the file is written only at the end of the simulation"; + public DiscreteModeChoiceConfigGroup() { super(GROUP_NAME); } @@ -436,6 +441,22 @@ public String getCachedModesAsString() { return String.join(", ", cachedModes); } + /** + * @param writeUtilitiesInterval -- {@value #WRITE_UTILITIES_INTERVAL_CMT} + */ + @StringSetter(WRITE_UTILITIES_INTERVAL) + public void setWriteUtilitiesInterval(int writeUtilitiesInterval) { + this.writeUtilitiesInterval = writeUtilitiesInterval; + } + + /** + * @return -- {@value #WRITE_UTILITIES_INTERVAL_CMT} + */ + @StringGetter(WRITE_UTILITIES_INTERVAL) + public int getWriteUtilitiesInterval() { + return this.writeUtilitiesInterval; + } + // --- Component configuration --- private final Map, ConfigGroup> componentRegistry = createComponentRegistry( diff --git a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/utils/ExtractPlanUtilities.java b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/utils/ExtractPlanUtilities.java new file mode 100644 index 00000000000..d68b7176728 --- /dev/null +++ b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/utils/ExtractPlanUtilities.java @@ -0,0 +1,51 @@ +package org.matsim.contribs.discrete_mode_choice.modules.utils; + +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.Population; +import org.matsim.core.config.CommandLine; +import org.matsim.core.config.Config; +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; + +public class ExtractPlanUtilities { + public static void writePlanUtilities(Population population, String filePath) throws IOException { + FileWriter writer = new FileWriter(filePath); + writer.write(String.join(CSV_SEPARATOR, CSV_HEADER)); + writer.write("\n"); + + for(Person person: population.getPersons().values()) { + double utility = Double.NaN; + Plan plan = person.getSelectedPlan(); + if(plan != null && plan.getAttributes().getAttribute("utility") != null) { + utility = (double) plan.getAttributes().getAttribute("utility"); + } + writer.write(String.join(CSV_SEPARATOR, new String[]{ + person.getId().toString(), + String.valueOf(utility) + })); + writer.write("\n"); + } + writer.close(); + } + + public static final String CMD_PLANS_PATH = "plans-path"; + public static final String CMD_OUTPUT_PATH = "output-path"; + public static final String CSV_SEPARATOR = ";"; + public static final String[] CSV_HEADER = new String[]{"person_id", "utility"}; + + public static void main(String[] args) throws CommandLine.ConfigurationException, IOException { + CommandLine commandLine = new CommandLine.Builder(args).requireOptions(CMD_PLANS_PATH, CMD_OUTPUT_PATH).build(); + + Config config = ConfigUtils.createConfig(); + Scenario scenario = ScenarioUtils.createScenario(config); + new PopulationReader(scenario).readFile(commandLine.getOptionStrict(CMD_PLANS_PATH)); + + writePlanUtilities(scenario.getPopulation(), commandLine.getOptionStrict(CMD_OUTPUT_PATH)); + } +} diff --git a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/replanning/DiscreteModeChoiceAlgorithm.java b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/replanning/DiscreteModeChoiceAlgorithm.java index 655f3cd94a6..a4c10c02d7a 100644 --- a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/replanning/DiscreteModeChoiceAlgorithm.java +++ b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/replanning/DiscreteModeChoiceAlgorithm.java @@ -1,75 +1,77 @@ -package org.matsim.contribs.discrete_mode_choice.replanning; - -import java.util.Collections; -import java.util.List; -import java.util.Random; - -import org.matsim.api.core.v01.population.Leg; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.api.core.v01.population.PlanElement; -import org.matsim.api.core.v01.population.PopulationFactory; -import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceModel; -import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip; -import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceModel.NoFeasibleChoiceException; -import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.RoutedTripCandidate; -import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate; -import org.matsim.core.population.algorithms.PlanAlgorithm; -import org.matsim.core.router.TripRouter; - -/** - * This replanning algorithm uses a predefined discrete mode choice model to - * perform mode decisions for a given plan. - * - * @author sebhoerl - */ -public class DiscreteModeChoiceAlgorithm implements PlanAlgorithm { - private final Random random; - private final DiscreteModeChoiceModel modeChoiceModel; - private final TripListConverter tripListConverter; - - private final PopulationFactory populationFactory; - - public DiscreteModeChoiceAlgorithm(Random random, DiscreteModeChoiceModel modeChoiceModel, - PopulationFactory populationFactory, TripListConverter tripListConverter) { - this.random = random; - this.modeChoiceModel = modeChoiceModel; - this.populationFactory = populationFactory; - this.tripListConverter = tripListConverter; - } - - @Override - /** - * Performs mode choice on a plan. We assume that TripsToLegs has been called - * before, hence the code is working diretly on legs. - */ - public void run(Plan plan) { - // I) First build a list of DiscreteModeChoiceTrips - List trips = tripListConverter.convert(plan); - - // II) Run mode choice - - try { - // Perform mode choice and retrieve candidates - List chosenCandidates = modeChoiceModel.chooseModes(plan.getPerson(), trips, random); - - for (int i = 0; i < trips.size(); i++) { - DiscreteModeChoiceTrip trip = trips.get(i); - TripCandidate candidate = chosenCandidates.get(i); - - List insertElements; - - if (candidate instanceof RoutedTripCandidate) { - RoutedTripCandidate routedCandidate = (RoutedTripCandidate) candidate; - insertElements = routedCandidate.getRoutedPlanElements(); - } else { - Leg insertLeg = populationFactory.createLeg(candidate.getMode()); - insertElements = Collections.singletonList(insertLeg); - } - - TripRouter.insertTrip(plan, trip.getOriginActivity(), insertElements, trip.getDestinationActivity()); - } - } catch (NoFeasibleChoiceException e) { - throw new IllegalStateException(e); - } - } -} +package org.matsim.contribs.discrete_mode_choice.replanning; + +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.api.core.v01.population.PopulationFactory; +import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceModel; +import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip; +import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceModel.NoFeasibleChoiceException; +import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.RoutedTripCandidate; +import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate; +import org.matsim.contribs.discrete_mode_choice.model.utilities.UtilityCandidate; +import org.matsim.core.population.algorithms.PlanAlgorithm; +import org.matsim.core.router.TripRouter; + +/** + * This replanning algorithm uses a predefined discrete mode choice model to + * perform mode decisions for a given plan. + * + * @author sebhoerl + */ +public class DiscreteModeChoiceAlgorithm implements PlanAlgorithm { + private final Random random; + private final DiscreteModeChoiceModel modeChoiceModel; + private final TripListConverter tripListConverter; + + private final PopulationFactory populationFactory; + + public DiscreteModeChoiceAlgorithm(Random random, DiscreteModeChoiceModel modeChoiceModel, + PopulationFactory populationFactory, TripListConverter tripListConverter) { + this.random = random; + this.modeChoiceModel = modeChoiceModel; + this.populationFactory = populationFactory; + this.tripListConverter = tripListConverter; + } + + @Override + /** + * Performs mode choice on a plan. We assume that TripsToLegs has been called + * before, hence the code is working diretly on legs. + */ + public void run(Plan plan) { + // I) First build a list of DiscreteModeChoiceTrips + List trips = tripListConverter.convert(plan); + + // II) Run mode choice + + try { + // Perform mode choice and retrieve candidates + List chosenCandidates = modeChoiceModel.chooseModes(plan.getPerson(), trips, random); + + for (int i = 0; i < trips.size(); i++) { + DiscreteModeChoiceTrip trip = trips.get(i); + TripCandidate candidate = chosenCandidates.get(i); + + List insertElements; + + if (candidate instanceof RoutedTripCandidate) { + RoutedTripCandidate routedCandidate = (RoutedTripCandidate) candidate; + insertElements = routedCandidate.getRoutedPlanElements(); + } else { + Leg insertLeg = populationFactory.createLeg(candidate.getMode()); + insertElements = Collections.singletonList(insertLeg); + } + + TripRouter.insertTrip(plan, trip.getOriginActivity(), insertElements, trip.getDestinationActivity()); + } + plan.getAttributes().putAttribute("utility", chosenCandidates.stream().mapToDouble(UtilityCandidate::getUtility).sum()); + } catch (NoFeasibleChoiceException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java index 5dd5f1c8bf7..fd0f6672826 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java @@ -174,17 +174,22 @@ public void nextTask(DvrpVehicle vehicle) { } else { nonVisitedPreplannedStops.poll();//remove this stop from queue - var stopTask = taskFactory.createStopTask(vehicle, currentTime, currentTime + stopDuration, currentLink); - if (nextStop.pickup) { - var request = Preconditions.checkNotNull(openRequests.get(nextStop.preplannedRequest.key), - "Request (%s) has not been yet submitted", nextStop.preplannedRequest); - stopTask.addPickupRequest(AcceptedDrtRequest.createFromOriginalRequest(request)); + if(nextStop.preplannedRequest.key.passengerIds.isEmpty() && nonVisitedPreplannedStops.isEmpty()) { + var stayTask = taskFactory.createStayTask(vehicle, currentTime, vehicle.getServiceEndTime(), currentLink); + schedule.addTask(stayTask); } else { - var request = Preconditions.checkNotNull(openRequests.remove(nextStop.preplannedRequest.key), - "Request (%s) has not been yet submitted", nextStop.preplannedRequest); - stopTask.addDropoffRequest(AcceptedDrtRequest.createFromOriginalRequest(request)); + var stopTask = taskFactory.createStopTask(vehicle, currentTime, currentTime + stopDuration, currentLink); + if (nextStop.pickup) { + var request = Preconditions.checkNotNull(openRequests.get(nextStop.preplannedRequest.key), + "Request (%s) has not been yet submitted", nextStop.preplannedRequest); + stopTask.addPickupRequest(AcceptedDrtRequest.createFromOriginalRequest(request)); + } else { + var request = Preconditions.checkNotNull(openRequests.remove(nextStop.preplannedRequest.key), + "Request (%s) has not been yet submitted", nextStop.preplannedRequest); + stopTask.addDropoffRequest(AcceptedDrtRequest.createFromOriginalRequest(request)); + } + schedule.addTask(stopTask); } - schedule.addTask(stopTask); } // switch to the next task and update currentTasks diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZoneSystemParams.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZoneSystemParams.java index 5608a30b944..9e916128860 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZoneSystemParams.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZoneSystemParams.java @@ -24,6 +24,7 @@ import org.matsim.contrib.common.util.ReflectiveConfigGroupWithConfigurableParameterSets; import org.matsim.contrib.common.zones.GridZoneSystem; import org.matsim.contrib.common.zones.ZoneSystemParams; +import org.matsim.contrib.common.zones.systems.geom_free_zones.GeometryFreeZoneSystemParams; import org.matsim.contrib.common.zones.systems.grid.GISFileZoneSystemParams; import org.matsim.contrib.common.zones.systems.grid.h3.H3GridZoneSystemParams; import org.matsim.contrib.common.zones.systems.grid.square.SquareGridZoneSystemParams; @@ -65,6 +66,10 @@ private void initSingletonParameterSets() { addDefinition(H3GridZoneSystemParams.SET_NAME, H3GridZoneSystemParams::new, () -> zoneSystemParams, params -> zoneSystemParams = (H3GridZoneSystemParams)params); + + addDefinition(GeometryFreeZoneSystemParams.SET_NAME, GeometryFreeZoneSystemParams::new, + () -> zoneSystemParams, + params -> zoneSystemParams = (GeometryFreeZoneSystemParams)params); } @Override @@ -85,6 +90,9 @@ public void handleAddUnknownParam(String paramName, String value) { addParameterSet(createParameterSet(H3GridZoneSystemParams.SET_NAME)); break; } + case "GeometryFree":{ + addParameterSet(createParameterSet(GeometryFreeZoneSystemParams.SET_NAME)); + } default: super.handleAddUnknownParam(paramName, value); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DefaultOfferAcceptor.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DefaultOfferAcceptor.java index 9eeed624139..c45e04db6ca 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DefaultOfferAcceptor.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DefaultOfferAcceptor.java @@ -28,7 +28,9 @@ public Optional acceptDrtOffer(DrtRequest request, double de .newBuilder() .request(request) .earliestStartTime(request.getEarliestStartTime()) + .maxRideDuration(request.getMaxRideDuration()) .latestArrivalTime(Math.min(updatedLatestStartTime + request.getMaxRideDuration(), request.getLatestArrivalTime())) - .latestStartTime(updatedLatestStartTime).build()); + .latestStartTime(updatedLatestStartTime) + .build()); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/run/DvrpConfigGroup.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/run/DvrpConfigGroup.java index 25cc051ece8..306b61d62b3 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/run/DvrpConfigGroup.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/run/DvrpConfigGroup.java @@ -19,23 +19,20 @@ package org.matsim.contrib.dvrp.run; -import java.util.Set; - -import javax.annotation.Nullable; - +import jakarta.validation.constraints.DecimalMax; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.PositiveOrZero; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.TransportMode; -import org.matsim.contrib.dynagent.run.DynQSimConfigConsistencyChecker; import org.matsim.contrib.common.util.ReflectiveConfigGroupWithConfigurableParameterSets; +import org.matsim.contrib.dynagent.run.DynQSimConfigConsistencyChecker; import org.matsim.contrib.zone.skims.DvrpTravelTimeMatrixParams; import org.matsim.core.config.Config; -import jakarta.validation.constraints.DecimalMax; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Positive; -import jakarta.validation.constraints.PositiveOrZero; +import javax.annotation.Nullable; +import java.util.Set; public class DvrpConfigGroup extends ReflectiveConfigGroupWithConfigurableParameterSets { private static final Logger log = LogManager.getLogger(DvrpConfigGroup.class); @@ -68,13 +65,14 @@ public static DvrpConfigGroup get(Config config) { @Parameter @Comment("Used for OFFLINE estimation of travel times for VrpOptimizer" + " by means of the exponential moving average." - + " The weighting decrease, alpha, must be in (0,1]." + + " The weighting decrease, alpha, must be in [0,1]." + " We suggest small values of alpha, e.g. 0.05." + " The averaging starts from the initial travel time estimates. If not provided," - + " the free-speed TTs is used as the initial estimates") - @Positive + + " the free-speed TTs is used as the initial estimates. If alpha is set to 0, the initial" + + " travel times stay fixed.") + @PositiveOrZero @DecimalMax("1.0") - public double travelTimeEstimationAlpha = 0.05; // [-], 1 ==> TTs from the last iteration only + public double travelTimeEstimationAlpha = 0.05; // [-], 1 ==> TTs from the last iteration only, 0 ==> initial TTs only @Parameter @Comment("" @@ -146,6 +144,12 @@ protected void checkConsistency(Config config) { if (!config.eventsManager().getSynchronizeOnSimSteps()) { throw new RuntimeException("Synchronization on sim steps is required"); } + if(initialTravelTimesFile == null && travelTimeEstimationAlpha == 0.0) { + throw new RuntimeException("Initial travel times file is required if travel times should not be updated."); + } + if(travelTimeEstimationAlpha == 0.0 && travelTimeEstimationBeta > 0) { + throw new RuntimeException("Online estimation beta should be 0 if travel time should not be updated."); + } } public DvrpTravelTimeMatrixParams getTravelTimeMatrixParams() { diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimeEstimator.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimeEstimator.java index 5d80e161462..efd03ff8c0a 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimeEstimator.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimeEstimator.java @@ -90,7 +90,7 @@ public DvrpOfflineTravelTimeEstimator(TravelTime initialTT, TravelTime observedT this.delimiter = delimiter; alpha = travelTimeEstimationAlpha; - checkArgument(alpha > 0 && alpha <= 1, "travelTimeEstimationAlpha must be in (0,1]"); + checkArgument(alpha >= 0 && alpha <= 1, "travelTimeEstimationAlpha must be in [0,1]"); linkTravelTimes = DvrpOfflineTravelTimes.convertToLinkTravelTimeMatrix(initialTT, network.getLinks().values(), timeDiscretizer); @@ -114,7 +114,9 @@ private int getIdx(double time) { @Override public void notifyMobsimBeforeCleanup(@SuppressWarnings("rawtypes") MobsimBeforeCleanupEvent e) { - updateTTs(observedTT, alpha); + if(alpha > 0) { + updateTTs(observedTT, alpha); + } } @Override diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpTravelTimeModule.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpTravelTimeModule.java index c295c70ac6a..2ac35955346 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpTravelTimeModule.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpTravelTimeModule.java @@ -64,7 +64,7 @@ public void install() { addMobsimListenerBinding().to(DvrpOfflineTravelTimeEstimator.class); addControlerListenerBinding().to(DvrpOfflineTravelTimeEstimator.class); - if (dvrpCfg.travelTimeEstimationBeta > 0) {// online estimation + if (dvrpCfg.travelTimeEstimationBeta > 0 && dvrpCfg.travelTimeEstimationAlpha > 0) {// online estimation bind(DvrpOnlineTravelTimeEstimator.class).asEagerSingleton(); addMobsimListenerBinding().to(DvrpOnlineTravelTimeEstimator.class); bind(DvrpTravelTimeEstimator.class).to(DvrpOnlineTravelTimeEstimator.class); diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/DvrpTravelTimeMatrixParams.java b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/DvrpTravelTimeMatrixParams.java index 77e717cfea7..a12930c5d42 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/DvrpTravelTimeMatrixParams.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/DvrpTravelTimeMatrixParams.java @@ -23,6 +23,7 @@ import jakarta.validation.constraints.PositiveOrZero; import org.matsim.contrib.common.util.ReflectiveConfigGroupWithConfigurableParameterSets; import org.matsim.contrib.common.zones.ZoneSystemParams; +import org.matsim.contrib.common.zones.systems.geom_free_zones.GeometryFreeZoneSystemParams; import org.matsim.contrib.common.zones.systems.grid.GISFileZoneSystemParams; import org.matsim.contrib.common.zones.systems.grid.h3.H3GridZoneSystemParams; import org.matsim.contrib.common.zones.systems.grid.square.SquareGridZoneSystemParams; @@ -77,6 +78,10 @@ private void initSingletonParameterSets() { addDefinition(H3GridZoneSystemParams.SET_NAME, H3GridZoneSystemParams::new, () -> zoneSystemParams, params -> zoneSystemParams = (H3GridZoneSystemParams)params); + + addDefinition(GeometryFreeZoneSystemParams.SET_NAME, GeometryFreeZoneSystemParams::new, + () -> zoneSystemParams, + params -> zoneSystemParams = (GeometryFreeZoneSystemParams)params); } @Override diff --git a/contribs/hybridsim/pom.xml b/contribs/hybridsim/pom.xml index f58a572c939..ac7da45f5b5 100644 --- a/contribs/hybridsim/pom.xml +++ b/contribs/hybridsim/pom.xml @@ -1,77 +1,82 @@ - - org.matsim - contrib - 2025.0-SNAPSHOT - - 4.0.0 - org.matsim.contrib - hybridsim - hybridsim + + org.matsim + contrib + 2025.0-SNAPSHOT + + 4.0.0 + org.matsim.contrib + hybridsim + hybridsim - - 4.27.2 - 1.65.1 - + + 4.28.0 + 1.66.0 + - + - - com.google.protobuf - protobuf-java - ${protobuf.version} - + + com.google.protobuf + protobuf-java + ${protobuf.version} + - - io.grpc - grpc-all - ${grpc.version} - + + io.grpc + grpc-all + ${grpc.version} + - - javax.annotation - javax.annotation-api - 1.3.2 - + + javax.annotation + javax.annotation-api + 1.3.2 + + + io.opentelemetry + opentelemetry-sdk + 1.40.0 + - + - - - - kr.motd.maven - os-maven-plugin - 1.7.1 - - - initialize - - detect - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - 0.6.1 - - com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} - - grpc-java - io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} - - true - - - - - compile - compile-custom - - - - - - + + + + kr.motd.maven + os-maven-plugin + 1.7.1 + + + initialize + + detect + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} + + grpc-java + io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} + + true + + + + + compile + compile-custom + + + + + + diff --git a/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/CarEgressWalkChanger.java b/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/CarEgressWalkChanger.java index ee231f716ed..20c0b113bed 100644 --- a/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/CarEgressWalkChanger.java +++ b/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/CarEgressWalkChanger.java @@ -33,7 +33,7 @@ *

* This class allows to implicitly apply penalties to plans by changing the duration of the egress walk after car interactions. * By construction this only works for plans involving car trips and can not be used if you want to penalize all plans that - * fulfill a certain condition. + * fulfill a certain condition. Note that if you bind this, you should also bind the return value of {@link #getBackChanger()}! *

*

* More precisely, the class will...
@@ -49,13 +49,14 @@ * @author tkohl / Senozon * */ -class CarEgressWalkChanger implements BeforeMobsimListener, AfterMobsimListener { +class CarEgressWalkChanger implements BeforeMobsimListener { public static final String PENALTY_ATTRIBUTE = "parkingPenalty"; private final CarEgressWalkObserver observer; private final AccessEgressFinder egressFinder = new AccessEgressFinder(TransportMode.car); private final Iter0Method iter0Method; + private final CarEgressWalkBackChanger backChanger; /** * Sets the class up with the specified {@linkplain PenaltyGenerator} and {@linkplain PenaltyFunction}. @@ -66,6 +67,21 @@ class CarEgressWalkChanger implements BeforeMobsimListener, AfterMobsimListener public CarEgressWalkChanger(PenaltyGenerator penaltyGenerator, PenaltyFunction penaltyFunction, CarEgressWalkObserver observer, Iter0Method iter0Method) { this.observer = observer; this.iter0Method = iter0Method; + this.backChanger = new CarEgressWalkBackChanger(this); + } + + @Override + public double priority() { //we want this to happen very late, because it should only affect the mobsim, not other listeners + return -1001; + } + + /** + * Returns the class that changes the egress walks bag in AfterMobsim. Note that you need to bind this separately! + * + * @return the {@linkplain CarEgressWalkBackChanger} to this class + */ + public CarEgressWalkBackChanger getBackChanger() { + return this.backChanger; } /** @@ -93,33 +109,6 @@ public void notifyBeforeMobsim(BeforeMobsimEvent event) { this.changeEgressTimesByGridcell(event.getServices().getScenario().getPopulation().getPersons().values(), false); } } - - /** - * resets egress times before scoring / replanning - */ - @Override - public void notifyAfterMobsim(AfterMobsimEvent event) { - // we need to roll back the changes we made before the mobsim, otherwise we can't apply - // a different penalty next iteration. - if (event.getIteration() == 0) { - switch (this.iter0Method) { - case noPenalty: - case hourPenalty: - case estimateFromPlans: - this.changeEgressTimesByGridcell(event.getServices().getScenario().getPopulation().getPersons().values(), true); - break; - case takeFromAttributes: - this.changeEgressTimesByAttribute(event.getServices().getScenario().getPopulation().getPersons().values(), true); - break; - default: - throw new RuntimeException("Unknown iter0 mode"); - } - } else { - this.changeEgressTimesByGridcell(event.getServices().getScenario().getPopulation().getPersons().values(), true); - } - // yyyy this is something we do not like: to just "fake" something and take it back afterwards. Would be good to find some other design - // eventually. Not so obvious, though ... kai, mar'20 - } /** * Changes the egress times of all agents using cars according to the penalty for the corresponding space-time-gridcell and writes the penalty @@ -130,12 +119,13 @@ public void notifyAfterMobsim(AfterMobsimEvent event) { * by that time (calling this method twice, first with {@code false}, then with {@code true} should yield the original plans) */ private void changeEgressTimesByGridcell(Collection population, boolean reverse) { - int sign = reverse ? -1 : 1; for (Person p : population) { for (LegActPair walkActPair : this.egressFinder.findEgressWalks(p.getSelectedPlan())) { - double penalty = sign * this.observer.getPenaltyCalculator().getPenalty(walkActPair.leg.getDepartureTime().seconds(), walkActPair.act.getCoord()); - setTimes(walkActPair, penalty); - if (!reverse) { + double penalty = Math.round(this.observer.getPenaltyCalculator().getPenalty(walkActPair.leg.getDepartureTime().seconds(), walkActPair.act.getCoord())); + if (reverse) { + setTimes(walkActPair, -penalty); + } else { + setTimes(walkActPair, penalty); walkActPair.leg.getAttributes().putAttribute(PENALTY_ATTRIBUTE, penalty); } } @@ -171,4 +161,50 @@ private static void setTimes(LegActPair walkActPair, double penalty) { walkActPair.leg.getRoute().setTravelTime(walkActPair.leg.getRoute().getTravelTime().seconds() + penalty); walkActPair.act.setStartTime(walkActPair.act.getStartTime().seconds() + penalty); } + + /** + * The reverse to {@linkplain CarEgressWalkChanger} + * + * @author tkohl + * + */ + static class CarEgressWalkBackChanger implements AfterMobsimListener { + private final CarEgressWalkChanger forwardChanger; + + CarEgressWalkBackChanger(CarEgressWalkChanger forwardChanger) { + this.forwardChanger = forwardChanger; + } + + @Override + public double priority() { //we want this to happen very early to undo what we did before the mobsim before other listeners can pick it up + return 1001; + } + + /** + * resets egress times before scoring / replanning + */ + @Override + public void notifyAfterMobsim(AfterMobsimEvent event) { + // we need to roll back the changes we made before the mobsim, otherwise we can't apply + // a different penalty next iteration. + if (event.getIteration() == 0) { + switch (forwardChanger.iter0Method) { + case noPenalty: + case hourPenalty: + case estimateFromPlans: + forwardChanger.changeEgressTimesByGridcell(event.getServices().getScenario().getPopulation().getPersons().values(), true); + break; + case takeFromAttributes: + forwardChanger.changeEgressTimesByAttribute(event.getServices().getScenario().getPopulation().getPersons().values(), true); + break; + default: + throw new RuntimeException("Unknown iter0 mode"); + } + } else { + forwardChanger.changeEgressTimesByGridcell(event.getServices().getScenario().getPopulation().getPersons().values(), true); + } + // yyyy this is something we do not like: to just "fake" something and take it back afterwards. Would be good to find some other design + // eventually. Not so obvious, though ... kai, mar'20 + } + } } diff --git a/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/ParkingProxyModule.java b/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/ParkingProxyModule.java index f356f0911e8..41f179d4977 100644 --- a/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/ParkingProxyModule.java +++ b/contribs/parking/src/main/java/org/matsim/contrib/parking/parkingproxy/ParkingProxyModule.java @@ -92,7 +92,9 @@ public void install() { if (parkingConfig.getObserveOnly()) { super.addControlerListenerBinding().toInstance(walkObserver); } else { - super.addControlerListenerBinding().toInstance(new CarEgressWalkChanger(parkingHandler, penaltyFunction, walkObserver, parkingConfig.getIter0Method())); + CarEgressWalkChanger walkChanger = new CarEgressWalkChanger(parkingHandler, penaltyFunction, walkObserver, parkingConfig.getIter0Method()); + super.addControlerListenerBinding().toInstance(walkChanger); + super.addControlerListenerBinding().toInstance(walkChanger.getBackChanger()); } } diff --git a/contribs/protobuf/pom.xml b/contribs/protobuf/pom.xml index a2c8a4ef62b..a08c2d527c8 100644 --- a/contribs/protobuf/pom.xml +++ b/contribs/protobuf/pom.xml @@ -11,7 +11,7 @@ protobuf - 4.27.2 + 4.28.0 diff --git a/matsim/pom.xml b/matsim/pom.xml index 7f768fffe76..e5c689b9261 100644 --- a/matsim/pom.xml +++ b/matsim/pom.xml @@ -222,7 +222,7 @@ com.github.luben zstd-jni - 1.5.6-3 + 1.5.6-5 jakarta.validation diff --git a/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetwork.java b/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetwork.java index e2d6ff5078c..906c97396ee 100644 --- a/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetwork.java +++ b/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetwork.java @@ -41,15 +41,18 @@ import org.matsim.pt.transitSchedule.api.TransitRoute; import org.matsim.pt.transitSchedule.api.TransitRouteStop; import org.matsim.pt.transitSchedule.api.TransitSchedule; +import org.matsim.pt.transitSchedule.api.TransitStopArea; import org.matsim.pt.transitSchedule.api.TransitStopFacility; /** * Builds a network where transit vehicles can drive along and assigns the correct - * links to the transit stop facilities and routes of transit lines. Each stop facility - * is assigned to a loop link, located in a node with the same coordinates as the stop. - * The stop facility ID is used for node and link IDs. + * links to the transit stop facilities and routes of transit lines. As each transit + * stop facility can only be connected to at most one link, the algorithm is forced + * to duplicated transit stop facilities in certain cases to build the network. * - * @author mrieser, davibicudo + * See {@link CreatePseudoNetworkWithLoopLinks} for a version that uses loop links instead of duplicating stop facilities. + * + * @author mrieser */ public class CreatePseudoNetwork { @@ -59,8 +62,13 @@ public class CreatePseudoNetwork { private final double linkFreeSpeed; private final double linkCapacity; - private final Map, Link> links = new HashMap<>(); - private final Map nodes = new HashMap<>(); + + private final Map, Link> links = new HashMap, Link>(); + private final Map, TransitStopFacility> stopFacilities = new HashMap, TransitStopFacility>(); + private final Map nodes = new HashMap(); + private final Map> facilityCopies = new HashMap>(); + + private long linkIdCounter = 0; private final Set transitModes = Collections.singleton(TransportMode.pt); @@ -83,27 +91,24 @@ public CreatePseudoNetwork(final TransitSchedule schedule, final Network network public void createNetwork() { - createStopNodesAndLoopLinks(); + List> toBeRemoved = new LinkedList>(); - List> toBeRemoved = new LinkedList<>(); for (TransitLine tLine : this.schedule.getTransitLines().values()) { for (TransitRoute tRoute : tLine.getRoutes().values()) { - ArrayList> routeLinks = new ArrayList<>(); + ArrayList> routeLinks = new ArrayList>(); TransitRouteStop prevStop = null; for (TransitRouteStop stop : tRoute.getStops()) { - if (prevStop != null) { - Link link = getNetworkLink(prevStop, stop); - routeLinks.add(link.getId()); - } + Link link = getNetworkLink(prevStop, stop); + routeLinks.add(link.getId()); prevStop = stop; } - if (!routeLinks.isEmpty()) { - NetworkRoute route = RouteUtils.createNetworkRoute(routeLinks); + if (routeLinks.size() > 0) { + NetworkRoute route = RouteUtils.createNetworkRoute(routeLinks ); tRoute.setRoute(route); } else { System.err.println("Line " + tLine.getId() + " route " + tRoute.getId() + " has less than two stops. Removing this route from schedule."); - toBeRemoved.add(new Tuple<>(tLine, tRoute)); + toBeRemoved.add(new Tuple(tLine, tRoute)); } } } @@ -113,40 +118,64 @@ public void createNetwork() { } } - private void createStopNodesAndLoopLinks() { - for (TransitStopFacility stop : this.schedule.getFacilities().values()) { - Node node = this.network.getFactory().createNode(Id.createNodeId(this.prefix + stop.getId()), stop.getCoord()); - this.network.addNode(node); - this.nodes.put(stop, node); - - Link loopLink = this.network.getFactory().createLink(Id.createLinkId (this.prefix + stop.getId()), node, node); - stop.setLinkId(loopLink.getId()); - this.network.addLink(loopLink); - Tuple connection = new Tuple<>(node, node); - this.links.put(connection, loopLink); - } - } - private Link getNetworkLink(final TransitRouteStop fromStop, final TransitRouteStop toStop) { - TransitStopFacility fromFacility = fromStop.getStopFacility(); + TransitStopFacility fromFacility = (fromStop == null) ? toStop.getStopFacility() : fromStop.getStopFacility(); TransitStopFacility toFacility = toStop.getStopFacility(); Node fromNode = this.nodes.get(fromFacility); + if (fromNode == null) { + fromNode = this.network.getFactory().createNode(Id.create(this.prefix + toFacility.getId(), Node.class), fromFacility.getCoord()); + this.network.addNode(fromNode); + this.nodes.put(toFacility, fromNode); + } + Node toNode = this.nodes.get(toFacility); + if (toNode == null) { + toNode = this.network.getFactory().createNode(Id.create(this.prefix + toFacility.getId(), Node.class), toFacility.getCoord()); + this.network.addNode(toNode); + this.nodes.put(toFacility, toNode); + } - Tuple connection = new Tuple<>(fromNode, toNode); + Tuple connection = new Tuple(fromNode, toNode); Link link = this.links.get(connection); - return link == null ? createAndAddLink(connection) : link; + if (link == null) { + link = createAndAddLink(fromNode, toNode, connection); + + if (toFacility.getLinkId() == null) { + toFacility.setLinkId(link.getId()); + this.stopFacilities.put(connection, toFacility); + } else { + List copies = this.facilityCopies.get(toFacility); + if (copies == null) { + copies = new ArrayList(); + this.facilityCopies.put(toFacility, copies); + } + Id newId = Id.create(toFacility.getId().toString() + "." + Integer.toString(copies.size() + 1), TransitStopFacility.class); + TransitStopFacility newFacility = this.schedule.getFactory().createTransitStopFacility(newId, toFacility.getCoord(), toFacility.getIsBlockingLane()); + newFacility.setStopAreaId(Id.create(toFacility.getId(), TransitStopArea.class)); + newFacility.setLinkId(link.getId()); + newFacility.setName(toFacility.getName()); + copies.add(newFacility); + this.nodes.put(newFacility, toNode); + this.schedule.addStopFacility(newFacility); + toStop.setStopFacility(newFacility); + this.stopFacilities.put(connection, newFacility); + } + } else { + toStop.setStopFacility(this.stopFacilities.get(connection)); + } + return link; } - private Link createAndAddLink(Tuple connection) { - Node fromNode = connection.getFirst(); - Node toNode = connection.getSecond(); + private Link createAndAddLink(Node fromNode, Node toNode, + Tuple connection) { Link link; - link = this.network.getFactory().createLink(Id.createLinkId(this.prefix + fromNode.getId() + "-" + toNode.getId()), fromNode, - toNode); - link.setLength(CoordUtils.calcEuclideanDistance(fromNode.getCoord(), toNode.getCoord())); - + link = this.network.getFactory().createLink(Id.create(this.prefix + this.linkIdCounter++, Link.class), fromNode, toNode); + if (fromNode == toNode) { + link.setLength(50); + } else { + link.setLength(CoordUtils.calcEuclideanDistance(fromNode.getCoord(), toNode.getCoord())); + } link.setFreespeed(linkFreeSpeed); link.setCapacity(linkCapacity); link.setNumberOfLanes(1); @@ -156,4 +185,11 @@ private Link createAndAddLink(Tuple connection) { return link; } + public Link getLinkBetweenStops(final TransitStopFacility fromStop, final TransitStopFacility toStop) { + Node fromNode = this.nodes.get(fromStop); + Node toNode = this.nodes.get(toStop); + Tuple connection = new Tuple(fromNode, toNode); + return this.links.get(connection); + } + } diff --git a/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinks.java b/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinks.java new file mode 100644 index 00000000000..d0532315137 --- /dev/null +++ b/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinks.java @@ -0,0 +1,153 @@ +/* *********************************************************************** * + * project: org.matsim.* + * CreatePseudoNetwork + * * + * *********************************************************************** * + * * + * copyright : (C) 2009 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.pt.utils; + +import com.google.common.annotations.Beta; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.Node; +import org.matsim.core.population.routes.NetworkRoute; +import org.matsim.core.population.routes.RouteUtils; +import org.matsim.core.utils.collections.Tuple; +import org.matsim.core.utils.geometry.CoordUtils; +import org.matsim.pt.transitSchedule.api.*; + +import java.util.*; + +/** + * Builds a network where transit vehicles can drive along and assigns the correct + * links to the transit stop facilities and routes of transit lines. Each stop facility + * is assigned to a loop link, located in a node with the same coordinates as the stop. + * The stop facility ID is used for node and link IDs. + * + * @author mrieser, davibicudo + * + * @implNote THis functionality might be merged with {@link CreatePseudoNetwork}. + */ +@Beta +public class CreatePseudoNetworkWithLoopLinks { + + private final TransitSchedule schedule; + private final Network network; + private final String prefix; + private final double linkFreeSpeed; + private final double linkCapacity; + + private final Map, Link> links = new HashMap<>(); + private final Map nodes = new HashMap<>(); + + private final Set transitModes = Collections.singleton(TransportMode.pt); + + public CreatePseudoNetworkWithLoopLinks(final TransitSchedule schedule, final Network network, final String networkIdPrefix) { + this.schedule = schedule; + this.network = network; + this.prefix = networkIdPrefix; + this.linkFreeSpeed = 100.0 / 3.6; + this.linkCapacity = 100000.0; + } + + public CreatePseudoNetworkWithLoopLinks(final TransitSchedule schedule, final Network network, final String networkIdPrefix, + final double linkFreeSpeed, final double linkCapacity) { + this.schedule = schedule; + this.network = network; + this.prefix = networkIdPrefix; + this.linkFreeSpeed = linkFreeSpeed; + this.linkCapacity = linkCapacity; + } + + public void createNetwork() { + + createStopNodesAndLoopLinks(); + + List> toBeRemoved = new LinkedList<>(); + for (TransitLine tLine : this.schedule.getTransitLines().values()) { + for (TransitRoute tRoute : tLine.getRoutes().values()) { + ArrayList> routeLinks = new ArrayList<>(); + TransitRouteStop prevStop = null; + for (TransitRouteStop stop : tRoute.getStops()) { + if (prevStop != null) { + Link link = getNetworkLink(prevStop, stop); + routeLinks.add(link.getId()); + } + prevStop = stop; + } + + if (!routeLinks.isEmpty()) { + NetworkRoute route = RouteUtils.createNetworkRoute(routeLinks); + tRoute.setRoute(route); + } else { + System.err.println("Line " + tLine.getId() + " route " + tRoute.getId() + " has less than two stops. Removing this route from schedule."); + toBeRemoved.add(new Tuple<>(tLine, tRoute)); + } + } + } + + for (Tuple remove : toBeRemoved) { + remove.getFirst().removeRoute(remove.getSecond()); + } + } + + private void createStopNodesAndLoopLinks() { + for (TransitStopFacility stop : this.schedule.getFacilities().values()) { + Node node = this.network.getFactory().createNode(Id.createNodeId(this.prefix + stop.getId()), stop.getCoord()); + this.network.addNode(node); + this.nodes.put(stop, node); + + Link loopLink = this.network.getFactory().createLink(Id.createLinkId (this.prefix + stop.getId()), node, node); + stop.setLinkId(loopLink.getId()); + this.network.addLink(loopLink); + Tuple connection = new Tuple<>(node, node); + this.links.put(connection, loopLink); + } + } + + private Link getNetworkLink(final TransitRouteStop fromStop, final TransitRouteStop toStop) { + TransitStopFacility fromFacility = fromStop.getStopFacility(); + TransitStopFacility toFacility = toStop.getStopFacility(); + + Node fromNode = this.nodes.get(fromFacility); + Node toNode = this.nodes.get(toFacility); + + Tuple connection = new Tuple<>(fromNode, toNode); + Link link = this.links.get(connection); + return link == null ? createAndAddLink(connection) : link; + } + + private Link createAndAddLink(Tuple connection) { + Node fromNode = connection.getFirst(); + Node toNode = connection.getSecond(); + Link link; + link = this.network.getFactory().createLink(Id.createLinkId(this.prefix + fromNode.getId() + "-" + toNode.getId()), fromNode, + toNode); + link.setLength(CoordUtils.calcEuclideanDistance(fromNode.getCoord(), toNode.getCoord())); + + link.setFreespeed(linkFreeSpeed); + link.setCapacity(linkCapacity); + link.setNumberOfLanes(1); + this.network.addLink(link); + link.setAllowedModes(this.transitModes); + this.links.put(connection, link); + return link; + } + +} diff --git a/pom.xml b/pom.xml index f7866d67c0a..6dbd5f639cc 100644 --- a/pom.xml +++ b/pom.xml @@ -30,10 +30,10 @@ 21 - 2.23.1 + 2.24.0 31.3 0.49.2 - 1.19.0 + 1.20.0 7.0.0 2.17.2 2.5.0 @@ -103,12 +103,12 @@ com.google.guava guava - 33.2.1-jre + 33.3.0-jre org.apache.commons commons-lang3 - 3.14.0 + 3.17.0 org.apache.commons @@ -118,7 +118,7 @@ org.apache.commons commons-compress - 1.26.2 + 1.27.1 commons-logging @@ -242,7 +242,7 @@ org.slf4j slf4j-api - 2.0.13 + 2.0.16 @@ -425,7 +425,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.3.1 + 3.5.0 org.apache.maven.plugins @@ -445,17 +445,17 @@ org.apache.maven.plugins maven-install-plugin - 3.1.2 + 3.1.3 org.apache.maven.plugins maven-javadoc-plugin - 3.7.0 + 3.10.0 org.codehaus.mojo buildnumber-maven-plugin - 3.2.0 + 3.2.1 validate