Skip to content

Commit

Permalink
adding todos for network param improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
rakow committed Apr 8, 2024
1 parent a898855 commit e934709
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<NetworkAttribute> params;

Expand Down Expand Up @@ -94,13 +99,15 @@ public Integer call() throws Exception {
}
}

Map<Id<Link>, Feature> features = NetworkParamsOpt.readFeatures(input.getPath("features.csv"), network.getLinks().size());
Map<Id<Link>, 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);
Expand All @@ -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<String> 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;
}
Expand All @@ -154,11 +163,15 @@ private void applyChanges(Link link, String junctionType, Object2DoubleMap<Strin
if (params.contains(NetworkAttribute.freespeed)) {

double speedFactor = 1.0;
FeatureRegressor speedModel = model.speedFactor(junctionType);
FeatureRegressor speedModel = model.speedFactor(ft.junctionType());

if (speedModel == null) {
throw new UnsupportedOperationException("Speed model not available for " + ft.junctionType());
}

speedFactor = paramsOpt != null ?
speedModel.predict(features, paramsOpt.getParams(junctionType)) :
speedModel.predict(features);
speedModel.predict(ft.features(), paramsOpt.getParams(ft.junctionType())) :
speedModel.predict(ft.features());

if (speedFactor > speedFactorBounds[1]) {
log.warn("Reducing speed factor on {} from {} to {}", link.getId(), speedFactor, speedFactorBounds[1]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -55,9 +56,11 @@ static NetworkModel load(Class<? extends NetworkModel> modelClazz) {
/**
* Read network edge features from csv.
*/
static Map<Id<Link>, Feature> readFeatures(String input, int expectedLinks) throws IOException {
static Map<Id<Link>, Feature> readFeatures(String input, Map<Id<Link>, ? extends Link> links) throws IOException {

Map<Id<Link>, Feature> features = new IdMap<>(Link.class, expectedLinks);
// TODO: read features from link attributes as well, if not present as input

Map<Id<Link>, Feature> features = new IdMap<>(Link.class, links.size());

try (CSVParser reader = new CSVParser(IOUtils.getBufferedReader(input),
CSVFormat.DEFAULT.builder().setHeader().setSkipHeaderRecord(true).build())) {
Expand All @@ -67,6 +70,7 @@ static Map<Id<Link>, Feature> readFeatures(String input, int expectedLinks) thro
for (CSVRecord row : reader) {

Id<Link> id = Id.createLinkId(row.get("linkId"));
Link link = links.get(id);

Object2DoubleOpenHashMap<String> ft = new Object2DoubleOpenHashMap<>();
ft.defaultReturnValue(Double.NaN);
Expand All @@ -81,7 +85,10 @@ static Map<Id<Link>, 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));
}
}

Expand Down Expand Up @@ -170,7 +177,7 @@ static Result evaluate(Network network, Object2DoubleMap<SampleValidationRoutes.
return new Result(rmse.getMean(), mse.getMean(), data);
}

record Feature(String junctionType, Object2DoubleMap<String> features) {
record Feature(String junctionType, String highwayType, Object2DoubleMap<String> features) {
}

record Result(double rmse, double mae, Map<String, List<Data>> data) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
}
}
Original file line number Diff line number Diff line change
@@ -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<String> ft) {
return 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,5 @@ void apply() throws Exception {
);

assertThat(output.resolve("network-opt.xml")).exists();

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,32 @@ private static Set<Character> 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<double[]> 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.
*/
Expand All @@ -86,9 +112,9 @@ private int getPrio(SumoNetworkHandler.Edge edge) {

public List<String> 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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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)));
Expand All @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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);

}
}

0 comments on commit e934709

Please sign in to comment.