Skip to content

Commit 24e9d71

Browse files
authored
feat: routing and isochrone server (#217)
* wip * wip * updating server * add isochrone functionality * add isochrone server * add segment length * update transfer distance * update * fix transit and fix typo for road * update server to latest develop branch * finished rewrite * cleanup routing services * revert core changes
1 parent f1af717 commit 24e9d71

24 files changed

Lines changed: 2239 additions & 0 deletions

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<module>san_francisco</module>
1818
<module>los_angeles</module>
1919
<module>vdf</module>
20+
<module>server</module>
2021
</modules>
2122

2223
<properties>

server/pom.xml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<modelVersion>4.0.0</modelVersion>
3+
<artifactId>server</artifactId>
4+
5+
<parent>
6+
<groupId>org.eqasim</groupId>
7+
<artifactId>eqasim</artifactId>
8+
<version>1.5.0</version>
9+
<relativePath>../pom.xml</relativePath>
10+
</parent>
11+
12+
<dependencies>
13+
<dependency>
14+
<groupId>org.geotools</groupId>
15+
<artifactId>gt-geopkg</artifactId>
16+
<version>24.2</version>
17+
</dependency>
18+
<dependency>
19+
<groupId>org.slf4j</groupId>
20+
<artifactId>slf4j-simple</artifactId>
21+
<version>2.0.7</version>
22+
</dependency>
23+
<dependency>
24+
<groupId>io.javalin</groupId>
25+
<artifactId>javalin</artifactId>
26+
<version>5.6.1</version>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.eqasim</groupId>
30+
<artifactId>core</artifactId>
31+
<version>1.5.0</version>
32+
</dependency>
33+
</dependencies>
34+
35+
<profiles>
36+
<profile>
37+
<id>standalone</id>
38+
<build>
39+
<plugins>
40+
<plugin>
41+
<groupId>org.apache.maven.plugins</groupId>
42+
<artifactId>maven-shade-plugin</artifactId>
43+
<version>3.2.0</version>
44+
<executions>
45+
<execution>
46+
<phase>package</phase>
47+
<goals>
48+
<goal>shade</goal>
49+
</goals>
50+
<configuration>
51+
<transformers>
52+
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
53+
</transformers>
54+
<filters>
55+
<filter>
56+
<artifact>*:*</artifact>
57+
<excludes>
58+
<exclude>META-INF/*.SF</exclude>
59+
<exclude>META-INF/*.DSA</exclude>
60+
<exclude>META-INF/*.RSA</exclude>
61+
</excludes>
62+
</filter>
63+
</filters>
64+
</configuration>
65+
</execution>
66+
</executions>
67+
</plugin>
68+
</plugins>
69+
</build>
70+
</profile>
71+
</profiles>
72+
</project>
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package org.eqasim.server;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
import java.util.concurrent.ExecutorService;
6+
import java.util.concurrent.Executors;
7+
8+
import org.eqasim.server.api.RoadIsochroneEndpoint;
9+
import org.eqasim.server.api.RoadRouterEndpoint;
10+
import org.eqasim.server.api.TransitIsochroneEndpoint;
11+
import org.eqasim.server.api.TransitRouterEndpoint;
12+
import org.eqasim.server.services.ServiceConfiguration;
13+
import org.eqasim.server.services.isochrone.road.RoadIsochroneService;
14+
import org.eqasim.server.services.isochrone.transit.TransitIsochroneService;
15+
import org.eqasim.server.services.router.road.RoadRouterService;
16+
import org.eqasim.server.services.router.transit.TransitRouterService;
17+
import org.matsim.api.core.v01.Scenario;
18+
import org.matsim.core.config.CommandLine;
19+
import org.matsim.core.config.CommandLine.ConfigurationException;
20+
import org.matsim.core.config.Config;
21+
import org.matsim.core.config.ConfigGroup;
22+
import org.matsim.core.config.ConfigUtils;
23+
import org.matsim.core.network.io.MatsimNetworkReader;
24+
import org.matsim.core.scenario.ScenarioUtils;
25+
import org.matsim.pt.transitSchedule.api.TransitScheduleReader;
26+
27+
import com.fasterxml.jackson.core.JsonParseException;
28+
import com.fasterxml.jackson.databind.JsonMappingException;
29+
import com.fasterxml.jackson.databind.ObjectMapper;
30+
31+
import io.javalin.Javalin;
32+
33+
public class RunServer {
34+
public static void main(String[] args)
35+
throws ConfigurationException, JsonParseException, JsonMappingException, IOException {
36+
CommandLine cmd = new CommandLine.Builder(args) //
37+
.requireOptions("config-path", "port") //
38+
.allowOptions("threads", "configuration-path") //
39+
.build();
40+
41+
int threads = cmd.getOption("threads").map(Integer::parseInt)
42+
.orElse(Runtime.getRuntime().availableProcessors());
43+
44+
ServiceConfiguration configuration = new ServiceConfiguration();
45+
46+
if (cmd.hasOption("configuration-path")) {
47+
ObjectMapper objectMapper = new ObjectMapper();
48+
configuration = objectMapper.readValue(new File(cmd.getOptionStrict("configuration-path")),
49+
ServiceConfiguration.class);
50+
}
51+
52+
// Create Javalin application and enable CORS
53+
Javalin app = Javalin.create(config -> {
54+
config.plugins.enableCors(cors -> {
55+
cors.add(it -> {
56+
it.anyHost();
57+
});
58+
});
59+
});
60+
61+
Config config = ConfigUtils.loadConfig(cmd.getOptionStrict("config-path"));
62+
Scenario scenario = ScenarioUtils.createScenario(config);
63+
64+
new MatsimNetworkReader(scenario.getNetwork())
65+
.readURL(ConfigGroup.getInputFileURL(config.getContext(), config.network().getInputFile()));
66+
67+
new TransitScheduleReader(scenario)
68+
.readURL(ConfigGroup.getInputFileURL(config.getContext(), config.transit().getTransitScheduleFile()));
69+
70+
ExecutorService executor = Executors.newFixedThreadPool(threads);
71+
72+
RoadRouterService roadRouterService = RoadRouterService.create(config, scenario.getNetwork(),
73+
configuration.walk, threads);
74+
RoadRouterEndpoint roadRouterEndpoint = new RoadRouterEndpoint(executor, roadRouterService);
75+
app.post("/router/road", roadRouterEndpoint::post);
76+
77+
RoadIsochroneService roadIsochroneService = RoadIsochroneService.create(config, scenario.getNetwork(),
78+
configuration.walk);
79+
RoadIsochroneEndpoint roadIsochroneEndpoint = new RoadIsochroneEndpoint(executor, roadIsochroneService);
80+
app.post("/isochrone/road", roadIsochroneEndpoint::post);
81+
82+
TransitRouterService transitRouterService = TransitRouterService.create(config, scenario.getNetwork(),
83+
scenario.getTransitSchedule(), configuration.transit, configuration.walk);
84+
TransitRouterEndpoint transitRouterEndpoint = new TransitRouterEndpoint(executor, transitRouterService);
85+
app.post("/router/transit", transitRouterEndpoint::post);
86+
87+
TransitIsochroneService transitIsochroneService = TransitIsochroneService.create(config,
88+
scenario.getTransitSchedule(), configuration.transit, configuration.walk);
89+
TransitIsochroneEndpoint transitIsochroneEndpoint = new TransitIsochroneEndpoint(executor,
90+
transitIsochroneService);
91+
app.post("/isochrone/transit", transitIsochroneEndpoint::post);
92+
93+
// Run API
94+
int port = Integer.parseInt(cmd.getOptionStrict("port"));
95+
app.start(port);
96+
}
97+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.eqasim.server.api;
2+
3+
import java.util.List;
4+
5+
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.core.type.TypeReference;
7+
import com.fasterxml.jackson.databind.JsonMappingException;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
10+
import io.javalin.http.ContentType;
11+
import io.javalin.http.Context;
12+
13+
public abstract class AbstractEndpoint {
14+
private final ObjectMapper objectMapper = new ObjectMapper();
15+
16+
protected <T> T readRequest(Context ctx, Class<T> requestType)
17+
throws JsonMappingException, JsonProcessingException {
18+
return objectMapper.readValue(ctx.body(), requestType);
19+
}
20+
21+
protected <T> List<T> readRequests(Context ctx, Class<T> requestType)
22+
throws JsonMappingException, JsonProcessingException {
23+
return objectMapper.readValue(ctx.body(), new TypeReference<List<T>>() {
24+
});
25+
}
26+
27+
protected <T> void writeResponse(Context ctx, T response) throws JsonProcessingException {
28+
ctx.contentType(ContentType.JSON);
29+
ctx.result(objectMapper.writeValueAsString(response));
30+
}
31+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.eqasim.server.api;
2+
3+
import java.util.Collection;
4+
import java.util.Collections;
5+
import java.util.LinkedList;
6+
import java.util.List;
7+
import java.util.concurrent.Callable;
8+
import java.util.concurrent.ExecutionException;
9+
import java.util.concurrent.ExecutorService;
10+
11+
import org.eqasim.server.services.isochrone.road.RoadIsochroneRequest;
12+
import org.eqasim.server.services.isochrone.road.RoadIsochroneResponse;
13+
import org.eqasim.server.services.isochrone.road.RoadIsochroneService;
14+
15+
import com.fasterxml.jackson.core.JsonProcessingException;
16+
17+
import io.javalin.http.Context;
18+
19+
public class RoadIsochroneEndpoint extends AbstractEndpoint {
20+
private final ExecutorService executor;
21+
private final RoadIsochroneService service;
22+
23+
public RoadIsochroneEndpoint(ExecutorService executor, RoadIsochroneService service) {
24+
this.executor = executor;
25+
this.service = service;
26+
}
27+
28+
private Collection<RoadIsochroneResponse> process(List<RoadIsochroneRequest> requests)
29+
throws InterruptedException, ExecutionException {
30+
List<Callable<RoadIsochroneResponse>> tasks = new LinkedList<>();
31+
for (RoadIsochroneRequest request : requests) {
32+
tasks.add(() -> service.processRequest(request));
33+
}
34+
35+
List<RoadIsochroneResponse> response = new LinkedList<>();
36+
for (var task : executor.invokeAll(tasks)) {
37+
response.add(task.get());
38+
}
39+
40+
return response;
41+
}
42+
43+
public void post(Context ctx) throws JsonProcessingException, InterruptedException, ExecutionException {
44+
Request request = readRequest(ctx, Request.class);
45+
46+
if (request.request != null) {
47+
writeResponse(ctx, process(Collections.singletonList(request.request)).iterator().next());
48+
} else {
49+
writeResponse(ctx, process(request.batch));
50+
}
51+
}
52+
53+
static public class Request {
54+
public RoadIsochroneRequest request = null;
55+
public List<RoadIsochroneRequest> batch = new LinkedList<>();
56+
}
57+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.eqasim.server.api;
2+
3+
import java.util.Collection;
4+
import java.util.Collections;
5+
import java.util.LinkedList;
6+
import java.util.List;
7+
import java.util.concurrent.Callable;
8+
import java.util.concurrent.ExecutionException;
9+
import java.util.concurrent.ExecutorService;
10+
11+
import org.eqasim.server.services.router.road.RoadRouterRequest;
12+
import org.eqasim.server.services.router.road.RoadRouterResponse;
13+
import org.eqasim.server.services.router.road.RoadRouterService;
14+
15+
import com.fasterxml.jackson.core.JsonProcessingException;
16+
17+
import io.javalin.http.Context;
18+
19+
public class RoadRouterEndpoint extends AbstractEndpoint {
20+
private final ExecutorService executor;
21+
private final RoadRouterService service;
22+
23+
public RoadRouterEndpoint(ExecutorService executor, RoadRouterService service) {
24+
this.executor = executor;
25+
this.service = service;
26+
}
27+
28+
private Collection<RoadRouterResponse> process(List<RoadRouterRequest> requests)
29+
throws InterruptedException, ExecutionException {
30+
List<Callable<RoadRouterResponse>> tasks = new LinkedList<>();
31+
for (RoadRouterRequest request : requests) {
32+
tasks.add(() -> service.processRequest(request));
33+
}
34+
35+
List<RoadRouterResponse> response = new LinkedList<>();
36+
for (var task : executor.invokeAll(tasks)) {
37+
response.add(task.get());
38+
}
39+
40+
return response;
41+
}
42+
43+
public void post(Context ctx) throws JsonProcessingException, InterruptedException, ExecutionException {
44+
Request request = readRequest(ctx, Request.class);
45+
46+
if (request.request != null) {
47+
writeResponse(ctx, process(Collections.singletonList(request.request)).iterator().next());
48+
} else {
49+
writeResponse(ctx, process(request.batch));
50+
}
51+
}
52+
53+
static public class Request {
54+
public RoadRouterRequest request = null;
55+
public List<RoadRouterRequest> batch = new LinkedList<>();
56+
}
57+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.eqasim.server.api;
2+
3+
import java.util.Collection;
4+
import java.util.Collections;
5+
import java.util.LinkedList;
6+
import java.util.List;
7+
import java.util.concurrent.Callable;
8+
import java.util.concurrent.ExecutionException;
9+
import java.util.concurrent.ExecutorService;
10+
11+
import org.eqasim.server.services.isochrone.transit.TransitIsochroneRequest;
12+
import org.eqasim.server.services.isochrone.transit.TransitIsochroneResponse;
13+
import org.eqasim.server.services.isochrone.transit.TransitIsochroneService;
14+
15+
import com.fasterxml.jackson.core.JsonProcessingException;
16+
17+
import io.javalin.http.Context;
18+
19+
public class TransitIsochroneEndpoint extends AbstractEndpoint {
20+
private final ExecutorService executor;
21+
private final TransitIsochroneService service;
22+
23+
public TransitIsochroneEndpoint(ExecutorService executor, TransitIsochroneService service) {
24+
this.executor = executor;
25+
this.service = service;
26+
}
27+
28+
private Collection<TransitIsochroneResponse> process(List<TransitIsochroneRequest> requests)
29+
throws InterruptedException, ExecutionException {
30+
List<Callable<TransitIsochroneResponse>> tasks = new LinkedList<>();
31+
for (TransitIsochroneRequest request : requests) {
32+
tasks.add(() -> service.processRequest(request));
33+
}
34+
35+
List<TransitIsochroneResponse> response = new LinkedList<>();
36+
for (var task : executor.invokeAll(tasks)) {
37+
response.add(task.get());
38+
}
39+
40+
return response;
41+
}
42+
43+
public void post(Context ctx) throws JsonProcessingException, InterruptedException, ExecutionException {
44+
Request request = readRequest(ctx, Request.class);
45+
46+
if (request.request != null) {
47+
writeResponse(ctx, process(Collections.singletonList(request.request)).iterator().next());
48+
} else {
49+
writeResponse(ctx, process(request.batch));
50+
}
51+
}
52+
53+
static public class Request {
54+
public TransitIsochroneRequest request = null;
55+
public List<TransitIsochroneRequest> batch = new LinkedList<>();
56+
}
57+
}

0 commit comments

Comments
 (0)