Skip to content

Commit

Permalink
feat(routing): use graphhopper to provide GTFS based public transport…
Browse files Browse the repository at this point in the history
… routing (#441)

* refactor(routing): use opencsv instead of javacsv for CSV parsing from GTFS
* clean(routing): exclude unused transient libraries
* clean(routing): resolve several checkstyle problems
* clean(routing): define default navigationConfiguration
* fix(routing): fix handling of empty lines
* clean(routing): use SpeedUtils for kmh conversion
* rename Routing to VehicleRouting
* clean(routing): added note to not forget redesigning MultiModalLeg
  • Loading branch information
kschrab authored Jan 9, 2025
1 parent 8b8c6c7 commit 2cf33c8
Show file tree
Hide file tree
Showing 30 changed files with 17,977 additions and 81 deletions.
32 changes: 32 additions & 0 deletions NOTICE-THIRD-PARTY.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ GraphHopper Core (8.0)
* Source: https://github.com/graphhopper/graphhopper/graphhopper-core


GraphHopper Reader for Gtfs Data (8.0)

* License: Apache-2.0
* Maven artifact: `com.graphhopper:graphhopper-reader-gtfs:8.0`
* Project: https://www.graphhopper.com/graphhopper-reader-gtfs
* Source: https://github.com/graphhopper/graphhopper/graphhopper-reader-gtfs


GraphHopper Web API (8.0)

* License: Apache-2.0
Expand All @@ -117,6 +125,14 @@ Gson (2.10.1)
* Source: https://github.com/google/gson/gson/


gtfs-realtime-bindings (0.0.5)

* License: Apache-2.0
* Maven artifact: `io.mobilitydata.transit:gtfs-realtime-bindings:0.0.5`
* Project: https://github.com/MobilityData/gtfs-realtime-bindings
* Source: https://github.com/MobilityData/gtfs-realtime-bindings


Guava: Google Core Libraries for Java (32.1.1-jre)

* License: Apache-2.0
Expand Down Expand Up @@ -221,6 +237,22 @@ Logback Core Module (1.5.0)
* Source: https://github.com/qos-ch/logback/logback-core


mapdb (1.0.8)

* License: Apache-2.0
* Maven artifact: `org.mapdb:mapdb:1.0.8`
* Project: http://www.mapdb.org
* Source: https://github.com/jankotek/MapDB


opencsv (5.9)

* License: Apache-2.0
* Maven artifact: `com.opencsv:opencsv:5.9`
* Project: http://opencsv.sf.net
* Source: https://sourceforge.net/p/opencsv/source/ci/master/tree/


org.locationtech.jts:jts-core (1.19.0)

* License: BSD-3-Clause (Eclipse Distribution License), Eclipse Public License, Version 2.0
Expand Down
7 changes: 7 additions & 0 deletions bundle/src/assembly/mosaic-bundle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@
<include>org.locationtech.jts:jts-core</include>
<!-- [END] GraphHopper dependencies -->

<!-- [START] GraphHopper PT routing dependencies -->
<include>com.graphhopper:graphhopper-reader-gtfs</include>
<include>io.mobilitydata.transit:gtfs-realtime-bindings</include>
<include>org.mapdb:mapdb</include>
<include>com.opencsv:opencsv</include>
<!-- [END] GraphHopper PT routing dependencies -->

<include>com.github.mwiede:jsch</include>
<include>com.google.protobuf:protobuf-java</include>
<include>org.xerial:sqlite-jdbc</include>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ public ApplicationAmbassador(AmbassadorParameter ambassadorParameter) {
// set the CNC (central navigation component)
CentralNavigationComponent cnc = new CentralNavigationComponent(
ambassadorParameter,
ambassadorConfig.navigationConfiguration
Validate.notNull(ambassadorConfig.navigationConfiguration, "Field navigationConfiguration must not be null."),
Validate.notNull(ambassadorConfig.publicTransportConfiguration, "Field publicTransportConfiguration must not be null.")
);
SimulationKernel.SimulationKernel.setCentralNavigationComponent(cnc);
}
Expand Down Expand Up @@ -228,6 +229,7 @@ public void initialize(final long startTime, final long endTime) throws Internal

private void shutdownSimulationUnits(Event event) {
SimulationKernel.SimulationKernel.setCurrentSimulationTime(event.getTime());
SimulationKernel.SimulationKernel.getCentralNavigationComponent().close();

log.debug("remaining events: {}", eventScheduler.getAllEvents());
UnitSimulator.UnitSimulator.removeAllSimulationUnits();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,18 @@
import org.eclipse.mosaic.lib.objects.vehicle.VehicleRoute;
import org.eclipse.mosaic.lib.routing.CandidateRoute;
import org.eclipse.mosaic.lib.routing.IllegalRouteException;
import org.eclipse.mosaic.lib.routing.Routing;
import org.eclipse.mosaic.lib.routing.RoutingCostFunction;
import org.eclipse.mosaic.lib.routing.RoutingParameters;
import org.eclipse.mosaic.lib.routing.RoutingPosition;
import org.eclipse.mosaic.lib.routing.RoutingRequest;
import org.eclipse.mosaic.lib.routing.RoutingResponse;
import org.eclipse.mosaic.lib.routing.VehicleRouting;
import org.eclipse.mosaic.lib.routing.config.CPublicTransportRouting;
import org.eclipse.mosaic.lib.routing.database.DatabaseRouting;
import org.eclipse.mosaic.lib.routing.norouting.NoRouting;
import org.eclipse.mosaic.lib.routing.pt.PtRouting;
import org.eclipse.mosaic.lib.routing.pt.PtRoutingRequest;
import org.eclipse.mosaic.lib.routing.pt.PtRoutingResponse;
import org.eclipse.mosaic.rti.api.IllegalValueException;
import org.eclipse.mosaic.rti.api.Interaction;
import org.eclipse.mosaic.rti.api.InternalFederateException;
Expand All @@ -55,6 +59,7 @@
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import javax.annotation.Nonnull;

/**
* The {@link CentralNavigationComponent} unites functionality concerned with
Expand All @@ -81,13 +86,24 @@ public class CentralNavigationComponent {
/**
* The IRoutingApi.
*/
private Routing routing;
private VehicleRouting vehicleRouting;


/**
* Access to Public Transport routing.
*/
private PtRouting ptRouting;

/**
* The configuration for routingAPI.
*/
private CApplicationAmbassador.CRoutingByType configuration;

/**
* The configuration for public transport routing.
*/
private CPublicTransportRouting ptConfiguration;

/**
* Constructor for the CentralNavigationComponent.
* Sets the logger and the configuration for navigation.
Expand All @@ -99,20 +115,22 @@ public class CentralNavigationComponent {
*/
public CentralNavigationComponent(
final AmbassadorParameter ambassadorParameter,
CApplicationAmbassador.CRoutingByType navigationConfiguration
@Nonnull CApplicationAmbassador.CRoutingByType navigationConfiguration,
@Nonnull CPublicTransportRouting publicTransportConfiguration
) {
this.applicationAmbassadorParameter = ambassadorParameter;
this.configuration = navigationConfiguration;
this.ptConfiguration = publicTransportConfiguration;
}

/**
* This method initializes the {@link CentralNavigationComponent}. It is called
* by the {@link ApplicationAmbassador}.
* The {@link #routing} will be created and initialized and other simulators will be informed
* The {@link #vehicleRouting} will be created and initialized and other simulators will be informed
* using a {@link VehicleRoutesInitialization} interaction.
*
* @param rtiAmbassador the ambassador of the run time infrastructure
* @throws InternalFederateException if the {@link #routing} couldn't be initialized or the
* @throws InternalFederateException if the {@link #vehicleRouting} couldn't be initialized or the
* {@link VehicleRoutesInitialization} interaction couldn't be send to the rti
*/
public void initialize(RtiAmbassador rtiAmbassador) throws InternalFederateException {
Expand All @@ -121,14 +139,17 @@ public void initialize(RtiAmbassador rtiAmbassador) throws InternalFederateExcep
try {
this.log.info("Initializing CNC-Navigation");

routing = createFromType(this.configuration != null ? this.configuration.type : null);
routing.initialize(configuration, applicationAmbassadorParameter.configuration.getParentFile());
vehicleRouting = createFromType(configuration.type);
vehicleRouting.initialize(configuration, applicationAmbassadorParameter.configuration.getParentFile());

ptRouting = new PtRouting();
ptRouting.initialize(ptConfiguration, applicationAmbassadorParameter.configuration.getParentFile());

this.log.info("CNC - Navigation-System initialized");

try {
final Map<String, VehicleRoute> routeMap = routing.getRoutesFromDatabaseForMessage();
for (var routeEntry: routeMap.entrySet()) {
final Map<String, VehicleRoute> routeMap = vehicleRouting.getRoutesFromDatabaseForMessage();
for (var routeEntry : routeMap.entrySet()) {
SimulationKernel.SimulationKernel.registerRoute(routeEntry.getKey(), routeEntry.getValue());
}

Expand All @@ -147,6 +168,12 @@ public void initialize(RtiAmbassador rtiAmbassador) throws InternalFederateExcep
}
}

public void close() {
if (ptRouting != null) {
ptRouting.close();
}
}

/**
* Returns an unmodifiable view of all routes known to the {@link SimulationKernel}.
*
Expand All @@ -156,15 +183,15 @@ public Map<String, VehicleRoute> getAllRoutes() {
return SimulationKernel.SimulationKernel.getRoutes();
}

Routing createFromType(String type) throws InternalFederateException {
VehicleRouting createFromType(String type) throws InternalFederateException {
if (type == null || "database".equalsIgnoreCase(type) || "graphhopper".equalsIgnoreCase(type)) {
return new DatabaseRouting();
} else if ("no-routing".equalsIgnoreCase(type)) {
return new NoRouting();
} else {
try {
Class<?> routingImplClass = Class.forName(type);
return (Routing) routingImplClass.getConstructor().newInstance();
return (VehicleRouting) routingImplClass.getConstructor().newInstance();
} catch (Exception e) {
String msg = "Could not create Routing instance from type '" + type + "'.";
InternalFederateException ex = new InternalFederateException(msg, e);
Expand All @@ -183,7 +210,7 @@ Routing createFromType(String type) throws InternalFederateException {
* for.
*/
RoutingResponse findRoutes(RoutingRequest routingRequest) {
return routing.findRoutes(routingRequest);
return vehicleRouting.findRoutes(routingRequest);
}

/**
Expand Down Expand Up @@ -220,7 +247,7 @@ public VehicleRoute switchRoute(VehicleData vehicleData, CandidateRoute rawRoute
return requestStaticRouteChange(vehicleData, knownRoute, currentRoute, time);
} else {
// generate a new route
VehicleRoute route = routing.createRouteForRTI(rawRoute);
VehicleRoute route = vehicleRouting.createRouteForRTI(rawRoute);

// propagate the new route
try {
Expand Down Expand Up @@ -268,7 +295,7 @@ private VehicleRoute requestStaticRouteChange(VehicleData vehicleData, VehicleRo
GeoPoint getTargetPositionOfRoute(String routeId) {
if (getAllRoutes().containsKey(routeId)) {
String lastConnectionId = Iterables.getLast(getAllRoutes().get(routeId).getConnectionIds(), null);
return routing.getConnection(lastConnectionId).getEndNode().getPosition();
return vehicleRouting.getConnection(lastConnectionId).getEndNode().getPosition();
} else {
return null;
}
Expand All @@ -283,19 +310,30 @@ GeoPoint getTargetPositionOfRoute(String routeId) {
public GeoPoint getSourcePositionOfRoute(String routeId) {
if (getAllRoutes().containsKey(routeId)) {
String firstConnectionId = Iterables.getFirst(getAllRoutes().get(routeId).getConnectionIds(), null);
return routing.getConnection(firstConnectionId).getStartNode().getPosition();
return vehicleRouting.getConnection(firstConnectionId).getStartNode().getPosition();
} else {
return null;
}
}

/**
* Find a public transport route from a provided position to a provided target position at a specific request time.
*
* @param routingRequest A {@link PtRoutingRequest} that contains
* the origin, the end, the request time, and additional
* routing parameters to calculate the public transport route.
*/
PtRoutingResponse findPtRoute(PtRoutingRequest routingRequest) {
return ptRouting.findPtRoute(routingRequest);
}

/**
* Provides the current routing API implementation.
*
* @return The {@link Routing}.
* @return The {@link VehicleRouting}.
*/
public Routing getRouting() {
return routing;
public VehicleRouting getRouting() {
return vehicleRouting;
}

/**
Expand All @@ -305,7 +343,7 @@ public Routing getRouting() {
* @return A {@link GeoPoint} representing the position of the node.
*/
private GeoPoint getPositionOfNode(String nodeId) {
return routing.getNode(nodeId).getPosition();
return vehicleRouting.getNode(nodeId).getPosition();
}

/**
Expand All @@ -315,7 +353,7 @@ private GeoPoint getPositionOfNode(String nodeId) {
* @return the length of the connection in [m]
*/
double getLengthOfConnection(String connectionId) {
return routing.getConnection(connectionId).getLength();
return vehicleRouting.getConnection(connectionId).getLength();
}

/**
Expand Down Expand Up @@ -386,7 +424,7 @@ public VehicleDeparture createRouteForOdInfo(long time, OriginDestinationPair od
final RoutingRequest request = new RoutingRequest(new RoutingPosition(sourcePoint), new RoutingPosition(targetPoint), params);

// find route
final RoutingResponse response = routing.findRoutes(request);
final RoutingResponse response = vehicleRouting.findRoutes(request);
// check if best route, matches one of the existing routes and if so choose that existing route
if (response.getBestRoute() != null) {
VehicleRoute route = null;
Expand All @@ -398,7 +436,7 @@ public VehicleDeparture createRouteForOdInfo(long time, OriginDestinationPair od
}
if (route == null) {
try {
route = routing.createRouteForRTI(response.getBestRoute());
route = vehicleRouting.createRouteForRTI(response.getBestRoute());
propagateRoute(route, time);
} catch (IllegalRouteException e) {
log.error("[CNC.createRouteForODInfo]: Could not create route.", e);
Expand Down Expand Up @@ -428,7 +466,7 @@ private GeoPoint chooseGeoPointInCircle(GeoCircle origin) {

/**
* This method refines the road position depending on the
* implementation of the {@link Routing} interface this can have
* implementation of the {@link VehicleRouting} interface this can have
* different levels of complexity.
*
* @param roadPosition the {@link IRoadPosition} to be refined
Expand All @@ -438,7 +476,7 @@ public IRoadPosition refineRoadPosition(IRoadPosition roadPosition) {
if (roadPosition == null) {
return null;
}
return routing.refineRoadPosition(roadPosition);
return vehicleRouting.refineRoadPosition(roadPosition);
}

/**
Expand Down Expand Up @@ -500,7 +538,7 @@ INode getNextNodeOnRoute(String routeId, IRoadPosition roadPosition, Predicate<I

List<String> nodeList = currentRoute.getNodeIds().subList(indexOfUpcomingNode, currentRoute.getNodeIds().size());
for (String nodeId : nodeList) {
INode node = routing.getNode(nodeId);
INode node = vehicleRouting.getNode(nodeId);
if (nodeCondition.test(node)) {
return node;
}
Expand All @@ -520,7 +558,7 @@ INode getNextNodeOnRoute(String routeId, IRoadPosition roadPosition, Predicate<I
public Collection<CandidateRoute> approximateCosts(Collection<CandidateRoute> candidateRoutes, String lastNodeId) {
List<CandidateRoute> routesWithCosts = new ArrayList<>();
for (CandidateRoute candidateRoute : candidateRoutes) {
routesWithCosts.add(routing.approximateCostsForCandidateRoute(candidateRoute, lastNodeId));
routesWithCosts.add(vehicleRouting.approximateCostsForCandidateRoute(candidateRoute, lastNodeId));
}
return routesWithCosts;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import org.eclipse.mosaic.lib.geo.CartesianRectangle;
import org.eclipse.mosaic.lib.objects.trafficlight.TrafficLightGroup;
import org.eclipse.mosaic.lib.objects.vehicle.VehicleType;
import org.eclipse.mosaic.lib.routing.Routing;
import org.eclipse.mosaic.lib.routing.VehicleRouting;
import org.eclipse.mosaic.lib.routing.database.DatabaseRouting;
import org.eclipse.mosaic.rti.api.InternalFederateException;

Expand Down Expand Up @@ -83,7 +83,7 @@ public CentralPerceptionComponent(CPerception perceptionConfiguration) {
*/
public void initialize() throws InternalFederateException {
try {
Routing routing = SimulationKernel.SimulationKernel.getCentralNavigationComponent().getRouting();
VehicleRouting routing = SimulationKernel.SimulationKernel.getCentralNavigationComponent().getRouting();
// evaluate bounding box for perception
scenarioBounds = configuration.perceptionArea == null
? routing.getScenarioBounds() : configuration.perceptionArea.toCartesian();
Expand Down
Loading

0 comments on commit 2cf33c8

Please sign in to comment.