Skip to content

Commit f37713d

Browse files
authored
feat(server): optional transit and freespeed calibration functionality (#264)
* freeflow calibration wip * freespeed routing works * wip freeflow * consider parallel links * update freeflow calibration * reactivate transit * make transit optional * update flag
1 parent 2f5e3c3 commit f37713d

File tree

6 files changed

+275
-33
lines changed

6 files changed

+275
-33
lines changed

server/src/main/java/org/eqasim/server/RunServer.java

+30-16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.io.File;
44
import java.io.IOException;
5+
import java.util.Collections;
56
import java.util.concurrent.ExecutorService;
67
import java.util.concurrent.Executors;
78

@@ -16,11 +17,15 @@
1617
import org.eqasim.server.services.router.road.RoadRouterService;
1718
import org.eqasim.server.services.router.transit.TransitRouterService;
1819
import org.matsim.api.core.v01.Scenario;
20+
import org.matsim.api.core.v01.network.Network;
1921
import org.matsim.core.config.CommandLine;
2022
import org.matsim.core.config.CommandLine.ConfigurationException;
2123
import org.matsim.core.config.Config;
2224
import org.matsim.core.config.ConfigGroup;
2325
import org.matsim.core.config.ConfigUtils;
26+
import org.matsim.core.network.NetworkUtils;
27+
import org.matsim.core.network.algorithms.NetworkCleaner;
28+
import org.matsim.core.network.algorithms.TransportModeNetworkFilter;
2429
import org.matsim.core.network.io.MatsimNetworkReader;
2530
import org.matsim.core.scenario.ScenarioUtils;
2631
import org.matsim.pt.transitSchedule.api.TransitScheduleReader;
@@ -36,7 +41,7 @@ public static void main(String[] args)
3641
throws ConfigurationException, JsonParseException, JsonMappingException, IOException {
3742
CommandLine cmd = new CommandLine.Builder(args) //
3843
.requireOptions("config-path", "port") //
39-
.allowOptions("threads", "configuration-path") //
44+
.allowOptions("threads", "configuration-path", "use-transit") //
4045
.build();
4146

4247
int threads = cmd.getOption("threads").map(Integer::parseInt)
@@ -65,31 +70,40 @@ public static void main(String[] args)
6570
new MatsimNetworkReader(scenario.getNetwork())
6671
.readURL(ConfigGroup.getInputFileURL(config.getContext(), config.network().getInputFile()));
6772

68-
new TransitScheduleReader(scenario)
69-
.readURL(ConfigGroup.getInputFileURL(config.getContext(), config.transit().getTransitScheduleFile()));
73+
boolean useTransit = cmd.getOption("use-transit").map(Boolean::parseBoolean).orElse(true);
74+
if (useTransit) {
75+
new TransitScheduleReader(scenario).readURL(
76+
ConfigGroup.getInputFileURL(config.getContext(), config.transit().getTransitScheduleFile()));
77+
}
7078

7179
ExecutorService executor = Executors.newFixedThreadPool(threads);
7280

73-
RoadRouterService roadRouterService = RoadRouterService.create(config, scenario.getNetwork(),
74-
configuration.walk, threads);
81+
Network roadNetwork = NetworkUtils.createNetwork();
82+
new TransportModeNetworkFilter(scenario.getNetwork()).filter(roadNetwork, Collections.singleton("car"));
83+
new NetworkCleaner().run(roadNetwork);
84+
85+
RoadRouterService roadRouterService = RoadRouterService.create(config, roadNetwork, configuration.walk,
86+
threads);
7587
RoadRouterEndpoint roadRouterEndpoint = new RoadRouterEndpoint(executor, roadRouterService);
7688
app.post("/router/road", roadRouterEndpoint::post);
7789

78-
RoadIsochroneService roadIsochroneService = RoadIsochroneService.create(config, scenario.getNetwork(),
90+
RoadIsochroneService roadIsochroneService = RoadIsochroneService.create(config, roadNetwork,
7991
configuration.walk);
8092
RoadIsochroneEndpoint roadIsochroneEndpoint = new RoadIsochroneEndpoint(executor, roadIsochroneService);
8193
app.post("/isochrone/road", roadIsochroneEndpoint::post);
8294

83-
TransitRouterService transitRouterService = TransitRouterService.create(config, scenario.getNetwork(),
84-
scenario.getTransitSchedule(), configuration.transit, configuration.walk);
85-
TransitRouterEndpoint transitRouterEndpoint = new TransitRouterEndpoint(executor, transitRouterService);
86-
app.post("/router/transit", transitRouterEndpoint::post);
87-
88-
TransitIsochroneService transitIsochroneService = TransitIsochroneService.create(config,
89-
scenario.getTransitSchedule(), configuration.transit, configuration.walk);
90-
TransitIsochroneEndpoint transitIsochroneEndpoint = new TransitIsochroneEndpoint(executor,
91-
transitIsochroneService);
92-
app.post("/isochrone/transit", transitIsochroneEndpoint::post);
95+
if (useTransit) {
96+
TransitRouterService transitRouterService = TransitRouterService.create(config, scenario.getNetwork(),
97+
scenario.getTransitSchedule(), configuration.transit, configuration.walk);
98+
TransitRouterEndpoint transitRouterEndpoint = new TransitRouterEndpoint(executor, transitRouterService);
99+
app.post("/router/transit", transitRouterEndpoint::post);
100+
101+
TransitIsochroneService transitIsochroneService = TransitIsochroneService.create(config,
102+
scenario.getTransitSchedule(), configuration.transit, configuration.walk);
103+
TransitIsochroneEndpoint transitIsochroneEndpoint = new TransitIsochroneEndpoint(executor,
104+
transitIsochroneService);
105+
app.post("/isochrone/transit", transitIsochroneEndpoint::post);
106+
}
93107

94108
// Run API
95109
int port = Integer.parseInt(cmd.getOptionStrict("port"));

server/src/main/java/org/eqasim/server/api/RoadRouterEndpoint.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.concurrent.ExecutionException;
99
import java.util.concurrent.ExecutorService;
1010

11+
import org.eqasim.server.services.router.road.FreespeedSettings;
1112
import org.eqasim.server.services.router.road.RoadRouterRequest;
1213
import org.eqasim.server.services.router.road.RoadRouterResponse;
1314
import org.eqasim.server.services.router.road.RoadRouterService;
@@ -25,11 +26,11 @@ public RoadRouterEndpoint(ExecutorService executor, RoadRouterService service) {
2526
this.service = service;
2627
}
2728

28-
private Collection<RoadRouterResponse> process(List<RoadRouterRequest> requests)
29+
private Collection<RoadRouterResponse> process(List<RoadRouterRequest> requests, FreespeedSettings freespeed)
2930
throws InterruptedException, ExecutionException {
3031
List<Callable<RoadRouterResponse>> tasks = new LinkedList<>();
3132
for (RoadRouterRequest request : requests) {
32-
tasks.add(() -> service.processRequest(request));
33+
tasks.add(() -> service.processRequest(request, freespeed));
3334
}
3435

3536
List<RoadRouterResponse> response = new LinkedList<>();
@@ -44,14 +45,16 @@ public void post(Context ctx) throws JsonProcessingException, InterruptedExcepti
4445
Request request = readRequest(ctx, Request.class);
4546

4647
if (request.request != null) {
47-
writeResponse(ctx, process(Collections.singletonList(request.request)).iterator().next());
48+
writeResponse(ctx,
49+
process(Collections.singletonList(request.request), request.freespeed).iterator().next());
4850
} else {
49-
writeResponse(ctx, process(request.batch));
51+
writeResponse(ctx, process(request.batch, request.freespeed));
5052
}
5153
}
5254

5355
static public class Request {
5456
public RoadRouterRequest request = null;
5557
public List<RoadRouterRequest> batch = new LinkedList<>();
58+
public FreespeedSettings freespeed = null;
5659
}
5760
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.eqasim.server.services.router.road;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
5+
public class FreespeedSettings {
6+
@JsonProperty("major_factor")
7+
public double majorFactor = 1.0;
8+
9+
@JsonProperty("intermediate_factor")
10+
public double intermediateFactor = 1.0;
11+
12+
@JsonProperty("minor_factor")
13+
public double minorFactor = 1.0;
14+
15+
@JsonProperty("major_crossing_penalty_s")
16+
public double majorCrossingPenalty_s = 1.0;
17+
18+
@JsonProperty("minor_crossing_penalty_s")
19+
public double minorCrossingPenalty_s = 1.0;
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package org.eqasim.server.services.router.road;
2+
3+
import org.matsim.api.core.v01.IdMap;
4+
import org.matsim.api.core.v01.network.Link;
5+
import org.matsim.api.core.v01.network.Network;
6+
import org.matsim.api.core.v01.population.Person;
7+
import org.matsim.core.router.util.TravelTime;
8+
import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime;
9+
import org.matsim.vehicles.Vehicle;
10+
11+
public class ModifiedFreeSpeedTravelTime {
12+
private final TravelTime delegate = new FreeSpeedTravelTime();
13+
private final IdMap<Link, LinkRecord> links;
14+
15+
private ModifiedFreeSpeedTravelTime(IdMap<Link, LinkRecord> links) {
16+
this.links = links;
17+
}
18+
19+
public double getLinkTravelTime(FreespeedSettings settings, Link link, double time, Person person,
20+
Vehicle vehicle) {
21+
LinkRecord linkRecord = links.get(link.getId());
22+
23+
double roadFactor = switch (linkRecord.linkType) {
24+
case major -> settings.majorFactor;
25+
case intermediate -> settings.intermediateFactor;
26+
case minor -> settings.minorFactor;
27+
default -> 1.0;
28+
};
29+
30+
double crossingPenalty = switch (linkRecord.crossingType) {
31+
case major -> settings.majorCrossingPenalty_s;
32+
case minor -> settings.minorCrossingPenalty_s;
33+
default -> 0.0;
34+
};
35+
36+
return delegate.getLinkTravelTime(link, time, person, vehicle) * roadFactor + crossingPenalty;
37+
}
38+
39+
public enum LinkType {
40+
major, intermediate, minor
41+
}
42+
43+
public enum CrossingType {
44+
major, minor, none
45+
}
46+
47+
public record LinkRecord(LinkType linkType, CrossingType crossingType) {
48+
}
49+
50+
static public ModifiedFreeSpeedTravelTime create(Network network) {
51+
IdMap<Link, LinkRecord> links = new IdMap<>(Link.class);
52+
53+
for (Link link : network.getLinks().values()) {
54+
links.put(link.getId(), new LinkRecord(decideLinkType(link), decideCrossingType(link)));
55+
}
56+
57+
int crossingNone = 0;
58+
int crossingMinor = 0;
59+
int crossingMajor = 0;
60+
61+
int linkMajor = 0;
62+
int linkIntermediate = 0;
63+
int linkMinor = 0;
64+
65+
for (LinkRecord record : links.values()) {
66+
switch (record.crossingType) {
67+
case major:
68+
crossingMajor += 1;
69+
break;
70+
case minor:
71+
crossingMinor += 1;
72+
break;
73+
case none:
74+
crossingNone += 1;
75+
break;
76+
default:
77+
break;
78+
}
79+
80+
switch (record.linkType) {
81+
case intermediate:
82+
linkIntermediate += 1;
83+
break;
84+
case major:
85+
linkMajor += 1;
86+
break;
87+
case minor:
88+
linkMinor += 1;
89+
break;
90+
default:
91+
break;
92+
}
93+
}
94+
95+
System.out.println("Crossing none: " + crossingNone);
96+
System.out.println("Crossing minor: " + crossingMinor);
97+
System.out.println("Crossing major: " + crossingMajor);
98+
99+
System.out.println("Link minor: " + linkMinor);
100+
System.out.println("Link intermediate: " + linkIntermediate);
101+
System.out.println("Link major: " + linkMajor);
102+
103+
// System.exit(1);
104+
105+
return new ModifiedFreeSpeedTravelTime(links);
106+
}
107+
108+
private static LinkType decideLinkType(Link link) {
109+
String osm = (String) link.getAttributes().getAttribute("osm:way:highway");
110+
111+
if (osm != null) {
112+
if (osm.contains("motorway") || osm.contains("trunk") || osm.contains("primary")) {
113+
return LinkType.major;
114+
} else if (osm.contains("secondary") || osm.contains("tertiary")) {
115+
return LinkType.intermediate;
116+
}
117+
}
118+
119+
return LinkType.minor;
120+
}
121+
122+
private static CrossingType decideCrossingType(Link link) {
123+
if (link.getToNode().getInLinks().size() == 1) { // straight road or diverge
124+
return CrossingType.none;
125+
} else {
126+
double maximumCapacity = Double.NEGATIVE_INFINITY;
127+
int maximumCount = 0;
128+
boolean foundLower = false;
129+
130+
for (Link inlink : link.getToNode().getInLinks().values()) {
131+
if (inlink.getCapacity() > maximumCapacity) {
132+
maximumCapacity = inlink.getCapacity();
133+
maximumCount = 1;
134+
} else if (inlink.getCapacity() == maximumCapacity) {
135+
maximumCount++;
136+
}
137+
138+
foundLower |= inlink.getCapacity() < link.getCapacity();
139+
}
140+
141+
boolean isMajor = link.getCapacity() == maximumCapacity && foundLower; //maximumCount == 2;
142+
if (isMajor) {
143+
return CrossingType.major;
144+
} else {
145+
return CrossingType.minor;
146+
}
147+
}
148+
}
149+
}

server/src/main/java/org/eqasim/server/services/router/road/RoadRouterRequest.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ public class RoadRouterRequest {
2626

2727
@JsonProperty("provide_geometry")
2828
public boolean provideGeometry = false;
29-
29+
3030
@JsonProperty("access_egress_radius_km")
3131
public Double accessEgressRadius_km = null;
32+
33+
@JsonProperty("consider_parallel_links")
34+
public boolean considerParallelLinks = false;
35+
36+
@JsonProperty("freespeed")
37+
public FreespeedSettings freespeed = null;
3238
}

0 commit comments

Comments
 (0)