From e9347099d2e1f5ea3ff35d7839339eb017abf33b Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 8 Apr 2024 17:55:47 +0200 Subject: [PATCH] adding todos for network param improvements --- .../network/params/ApplyNetworkParams.java | 41 ++++++++++++------- .../network/params/EvalFreespeedParams.java | 2 +- .../network/params/FreespeedOptServer.java | 2 +- .../network/params/NetworkParamsOpt.java | 15 +++++-- .../network/params/hbs/HBSNetworkParams.java | 22 ++++++++++ .../network/params/hbs/HBSRoadCapacity.java | 14 +++++++ .../params/ApplyNetworkParamsTest.java | 1 - .../sumo/SumoNetworkFeatureExtractor.java | 35 +++++++++++++++- .../sumo/SumoNetworkFeatureExtractorTest.java | 21 ++++++++++ 9 files changed, 130 insertions(+), 23 deletions(-) create mode 100644 contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java create mode 100644 contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java create mode 100644 contribs/sumo/src/test/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractorTest.java diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ApplyNetworkParams.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ApplyNetworkParams.java index deef01dfe5e..1cbfb9d24fe 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ApplyNetworkParams.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/ApplyNetworkParams.java @@ -40,6 +40,11 @@ public class ApplyNetworkParams implements MATSimAppCommand { @CommandLine.Mixin private final OutputOptions output = OutputOptions.ofCommand(ApplyNetworkParams.class); + // TODO: + // Filter road types, filter junction types + // adjustment factors + // TODO: capacity threshold handling + @CommandLine.Parameters(arity = "1..*", description = "Type of parameters to apply. Available: ${COMPLETION-CANDIDATES}") private Set params; @@ -94,13 +99,15 @@ public Integer call() throws Exception { } } - Map, Feature> features = NetworkParamsOpt.readFeatures(input.getPath("features.csv"), network.getLinks().size()); + Map, Feature> features = NetworkParamsOpt.readFeatures(input.getPath("features.csv"), network.getLinks()); for (Link link : network.getLinks().values()) { Feature ft = features.get(link.getId()); try { - applyChanges(link, ft.junctionType(), ft.features()); + applyChanges(link, ft); + } catch (UnsupportedOperationException u) { + // This will be ignored silently and should only be thrown if certain changes should not be applied } catch (IllegalArgumentException e) { warn++; log.warn("Error processing link {}", link.getId(), e); @@ -117,32 +124,34 @@ public Integer call() throws Exception { /** * Apply speed and capacity models and apply changes. */ - private void applyChanges(Link link, String junctionType, Object2DoubleMap features) { - - String type = NetworkUtils.getHighwayType(link); + private void applyChanges(Link link, Feature ft) { boolean modified = false; if (params.contains(NetworkAttribute.capacity)) { - FeatureRegressor capacity = model.capacity(junctionType); + FeatureRegressor capacity = model.capacity(ft.junctionType()); + if (capacity == null) { + throw new UnsupportedOperationException("Capacity model not available for " + ft.junctionType()); + } - double perLane = capacity.predict(features); + double perLane = capacity.predict(ft.features()); - double cap = capacityEstimate(features.getDouble("speed")); + double cap = capacityEstimate(ft.features().getDouble("speed")); // Minimum thresholds - double threshold = switch (junctionType) { + double threshold = switch (ft.junctionType()) { // traffic light can reduce capacity at least to 50% (with equal green split) case "traffic_light" -> 0.4; case "right_before_left" -> 0.6; // Motorways are kept at their max theoretical capacity - case "priority" -> type.startsWith("motorway") ? 1 : 0.8; + case "priority" -> ft.highwayType().startsWith("motorway") ? 1 : 0.8; default -> 0; }; if (perLane < cap * threshold) { - log.warn("Increasing capacity per lane on {} ({}, {}) from {} to {}", link.getId(), type, junctionType, perLane, cap * threshold); + log.warn("Increasing capacity per lane on {} ({}, {}) from {} to {}", + link.getId(), ft.highwayType(), ft.junctionType(), perLane, cap * threshold); perLane = cap * threshold; modified = true; } @@ -154,11 +163,15 @@ private void applyChanges(Link link, String junctionType, Object2DoubleMap speedFactorBounds[1]) { log.warn("Reducing speed factor on {} from {} to {}", link.getId(), speedFactor, speedFactorBounds[1]); diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/EvalFreespeedParams.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/EvalFreespeedParams.java index 014d2615549..e6f8d8e8393 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/EvalFreespeedParams.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/EvalFreespeedParams.java @@ -151,7 +151,7 @@ public Integer call() throws Exception { mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT); validationSet = readValidation(validationFiles, refHours); - features = readFeatures(input.getPath("features.csv"), network.getLinks().size()); + features = readFeatures(input.getPath("features.csv"), network.getLinks()); CSVPrinter csv; Path out = output.getPath("eval.csv"); diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FreespeedOptServer.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FreespeedOptServer.java index 387824839f4..fef19850305 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FreespeedOptServer.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/FreespeedOptServer.java @@ -102,7 +102,7 @@ public Integer call() throws Exception { } validationSet = NetworkParamsOpt.readValidation(validationFiles, refHours); - features = NetworkParamsOpt.readFeatures(input.getPath("features.csv"), network.getLinks().size()); + features = NetworkParamsOpt.readFeatures(input.getPath("features.csv"), network.getLinks()); log.info("Initial score:"); applyAndEvaluateParams(null, "init"); 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 2c69e7c6498..5c67d808e9d 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 @@ -15,6 +15,7 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.Node; import org.matsim.application.analysis.traffic.traveltime.SampleValidationRoutes; +import org.matsim.core.network.NetworkUtils; import org.matsim.core.router.DijkstraFactory; import org.matsim.core.router.costcalculators.OnlyTimeDependentTravelDisutility; import org.matsim.core.router.util.LeastCostPathCalculator; @@ -55,9 +56,11 @@ static NetworkModel load(Class modelClazz) { /** * Read network edge features from csv. */ - static Map, Feature> readFeatures(String input, int expectedLinks) throws IOException { + static Map, Feature> readFeatures(String input, Map, ? extends Link> links) throws IOException { - Map, Feature> features = new IdMap<>(Link.class, expectedLinks); + // TODO: read features from link attributes as well, if not present as input + + Map, Feature> features = new IdMap<>(Link.class, links.size()); try (CSVParser reader = new CSVParser(IOUtils.getBufferedReader(input), CSVFormat.DEFAULT.builder().setHeader().setSkipHeaderRecord(true).build())) { @@ -67,6 +70,7 @@ static Map, Feature> readFeatures(String input, int expectedLinks) thro for (CSVRecord row : reader) { Id id = Id.createLinkId(row.get("linkId")); + Link link = links.get(id); Object2DoubleOpenHashMap ft = new Object2DoubleOpenHashMap<>(); ft.defaultReturnValue(Double.NaN); @@ -81,7 +85,10 @@ static Map, Feature> readFeatures(String input, int expectedLinks) thro } } - features.put(id, new Feature(row.get("junction_type"), ft)); + String highwayType = header.contains(NetworkUtils.TYPE) ? row.get(NetworkUtils.TYPE) : + (link != null ? NetworkUtils.getHighwayType(link) : null); + + features.put(id, new Feature(row.get("junction_type"), highwayType, ft)); } } @@ -170,7 +177,7 @@ static Result evaluate(Network network, Object2DoubleMap features) { + record Feature(String junctionType, String highwayType, Object2DoubleMap features) { } record Result(double rmse, double mae, Map> data) { diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java new file mode 100644 index 00000000000..b83282fab31 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSNetworkParams.java @@ -0,0 +1,22 @@ +package org.matsim.application.prepare.network.params.hbs; + +import org.matsim.application.prepare.network.params.FeatureRegressor; +import org.matsim.application.prepare.network.params.NetworkModel; + +/** + * Capacity params calculated according to + * "Handbuch für die Bemessung von Straßenverkehrsanlagen“ (HBS) + */ +public class HBSNetworkParams implements NetworkModel { + + @Override + public FeatureRegressor capacity(String junctionType) { + // TODO: should depend on junction type and or road type + return new HBSRoadCapacity(); + } + + @Override + public FeatureRegressor speedFactor(String junctionType) { + throw new UnsupportedOperationException("Not implemented"); + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java new file mode 100644 index 00000000000..3cbc16f005f --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/params/hbs/HBSRoadCapacity.java @@ -0,0 +1,14 @@ +package org.matsim.application.prepare.network.params.hbs; + +import it.unimi.dsi.fastutil.objects.Object2DoubleMap; +import org.matsim.application.prepare.network.params.FeatureRegressor; + +/** + * See {@link HBSNetworkParams}. + */ +class HBSRoadCapacity implements FeatureRegressor { + @Override + public double predict(Object2DoubleMap ft) { + return 0; + } +} diff --git a/contribs/application/src/test/java/org/matsim/application/prepare/network/params/ApplyNetworkParamsTest.java b/contribs/application/src/test/java/org/matsim/application/prepare/network/params/ApplyNetworkParamsTest.java index b25737d6c40..e9f723d6db8 100644 --- a/contribs/application/src/test/java/org/matsim/application/prepare/network/params/ApplyNetworkParamsTest.java +++ b/contribs/application/src/test/java/org/matsim/application/prepare/network/params/ApplyNetworkParamsTest.java @@ -40,6 +40,5 @@ void apply() throws Exception { ); assertThat(output.resolve("network-opt.xml")).exists(); - } } diff --git a/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractor.java b/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractor.java index 7bf1154bf2d..1ecc144610e 100644 --- a/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractor.java +++ b/contribs/sumo/src/main/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractor.java @@ -77,6 +77,32 @@ private static Set directionSet(SumoNetworkHandler.Connection c) { return set; } + /** + * Calculate the curvature of an edge. + * + * @return KU in gon/km + */ + static double calcCurvature(SumoNetworkHandler.Edge edge) { + + double gon = 0; + List coords = edge.shape; + for (int i = 0; i < coords.size() - 2; i++) { + + double[] a = coords.get(i); + double[] b = coords.get(i + 1); + double[] c = coords.get(i + 2); + + double ab = Math.sqrt(Math.pow(b[0] - a[0], 2) + Math.pow(b[1] - a[1], 2)); + double bc = Math.sqrt(Math.pow(c[0] - b[0], 2) + Math.pow(c[1] - b[1], 2)); + double ac = Math.sqrt(Math.pow(c[0] - a[0], 2) + Math.pow(c[1] - a[1], 2)); + + double angle = Math.acos((ac * ac - ab * ab - bc * bc) / (-2 * ab * bc)); + gon += Math.abs(angle) * 200 / Math.PI; + } + + return gon / (edge.getLength() * 1000); + } + /** * Get priority. Higher is more important. */ @@ -86,9 +112,9 @@ private int getPrio(SumoNetworkHandler.Edge edge) { public List getHeader() { return List.of("linkId", "highway_type", "speed", "length", "num_lanes", "change_num_lanes", "change_speed", "num_to_links", "num_conns", - "num_response", "num_foes", "dir_multiple_s", "dir_l", "dir_r", "dir_s", "dir_exclusive", + "num_response", "num_foes", "dir_multiple_s", "dir_l", "dir_r", "dir_s", "dir_exclusive", "curvature", "junction_type", "junction_inc_lanes", "priority_higher", "priority_equal", "priority_lower", - "is_secondary_or_higher", "is_primary_or_higher", "is_motorway", "is_link"); + "is_secondary_or_higher", "is_primary_or_higher", "is_motorway", "is_link", "is_merging"); } public void print(CSVPrinter out) { @@ -152,6 +178,9 @@ public void print(CSVPrinter out, String linkId, SumoNetworkHandler.Edge edge) t .mapToInt(e -> e.lanes.size()) .sum(); + boolean merging = incomingEdges.get(junction.id).stream() + .anyMatch(e -> e.type.contains("link")); + boolean geq_secondary = switch (highwayType) { case "secondary", "primary", "trunk", "motorway" -> true; default -> false; @@ -178,6 +207,7 @@ public void print(CSVPrinter out, String linkId, SumoNetworkHandler.Edge edge) t out.print(bool(dirs.contains('r'))); out.print(bool(dirs.contains('s'))); out.print(bool(exclusiveDirs)); + out.print(calcCurvature(edge)); out.print(junction.type); out.print(Math.min(12, incomingLanes)); out.print(bool("higher".equals(prio))); @@ -187,6 +217,7 @@ public void print(CSVPrinter out, String linkId, SumoNetworkHandler.Edge edge) t out.print(bool(geq_primary)); out.print(bool("motorway".equals(highwayType))); out.print(bool(highwayType.contains("link"))); + out.print(bool(merging)); out.println(); } diff --git a/contribs/sumo/src/test/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractorTest.java b/contribs/sumo/src/test/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractorTest.java new file mode 100644 index 00000000000..c42ad51db5c --- /dev/null +++ b/contribs/sumo/src/test/java/org/matsim/contrib/sumo/SumoNetworkFeatureExtractorTest.java @@ -0,0 +1,21 @@ +package org.matsim.contrib.sumo; + +import org.junit.jupiter.api.Test; + +class SumoNetworkFeatureExtractorTest { + + @Test + void curvature() throws Exception { + + String[] coords = {"0,0", "0,100", "0,0"}; + SumoNetworkHandler.Edge edge = new SumoNetworkHandler.Edge("1", "2", "3", "highway", 0, "name", coords); + edge.lanes.add(new SumoNetworkHandler.Lane("1", 0, 200, 50/3.6, null, null)); + + double ku = SumoNetworkFeatureExtractor.calcCurvature(edge); + + //TODO should be around 400 for 200m road + + System.out.println(ku); + + } +}