Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ Feature | Description | Enabled by default | Sandbox
`APIUpdaterStatus` | Enable endpoint for graph updaters status | yes | no
`OptimizeTransfers` | OTP will inspect all itineraries found and optimize where (witch stops) the transfer will happen. Waiting time, priority and guaranteed transfers are taken into account. | yes | no
`GuaranteedTransfers` | Enforce transfers to happen according to the _transfers.txt_(GTFS) and Interchanges(NeTEx). Turing this _off_ will increase the routing performance a little. | yes | no
`ActuatorAPI` | Enpoint for actuators (service health status) | no | yes
`RemoveUnusedStops` | Remove all stops if the stop or its parent station is _not_ in use by any pattern before building the graph. This feature is for debugging and testing. Remove all trips manually witch are not relevant for your test case, and let OTP clean up Stops, Stations, Pathways and Transfers. This feature can improve start-up time and reduce the Raptor stop matrix, witch simplify debugging. NOT RECOMMENDED FOR PRODUCTION. | no | yes
`ActuatorAPI` | Endpoint for actuators (service health status) | no | yes
`GoogleCloudStorage` | Enable Google Cloud Storage integration | no | yes
`SandboxAPITransmodelApi` | Enable Entur Transmodel(NeTEx) GraphQL API | no | yes
`SandboxAPILegacyGraphQLApi` | Enable (GTFS) GraphQL API | no | yes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.opentripplanner.ext.interactivelauncher.client;

import java.time.Duration;
import org.opentripplanner.model.FeedScopedId;
import org.opentripplanner.model.GenericLocation;
import org.opentripplanner.routing.RoutingService;
import org.opentripplanner.standalone.server.Router;

public class TestClient implements Runnable {
private final Router router;
private final RoutingService routingService;

public TestClient(Router router) {
this.router = router;
this.routingService = new RoutingService(router.graph);
}

@Override
public void run() {
if (sleepOnStartup()) { return; }
System.out.println("\nTestClient.run ....ooooooo000000000000000!\n");


var graph = router.graph;




var req = router.defaultRoutingRequest.clone();

req.from = new GenericLocation("", new FeedScopedId("EN", "NSR:StopPlace:58947"), null, null);
req.to = new GenericLocation("", new FeedScopedId("EN", "NSR:StopPlace:58952"), null, null);
req.dateTime = 1630825200;
req.searchWindow = Duration.ofMinutes(121);


var result = routingService.route(req, router);

System.out.println("RES: " + result);
}

private boolean sleepOnStartup() {
try {
Thread.sleep(3000);
}
catch (InterruptedException e) {
e.printStackTrace();
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -58,6 +59,10 @@ public Collection<StopCollection> getChildStations() {
return this.childStations;
}

public void removeChildStationIf(Predicate<? super StopCollection> filter) {
this.childStations.removeIf(filter);
}

public void addChildStation(StopCollection station) {
this.childStations.add(station);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Predicate;
import java.util.stream.Collectors;


Expand All @@ -15,7 +16,7 @@
public class MultiModalStation extends TransitEntity implements StopCollection {
private static final long serialVersionUID = 1L;

private final Collection<Station> childStations;
private Collection<Station> childStations;

private String name;

Expand Down Expand Up @@ -98,6 +99,12 @@ public Collection<Station> getChildStations() {
return this.childStations;
}

public void removeChildStationIf(Predicate<Station> filter) {
childStations = Collections.unmodifiableCollection(childStations.stream()
.filter(Predicate.not(filter))
.collect(Collectors.toList()));
}

@Override
public String toString() {
return "<MultiModal station " + getId() + ">";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.model.Agency;
import org.opentripplanner.model.BoardingArea;
Expand All @@ -29,7 +31,9 @@
import org.opentripplanner.model.Route;
import org.opentripplanner.model.ShapePoint;
import org.opentripplanner.model.Station;
import org.opentripplanner.model.StationElement;
import org.opentripplanner.model.Stop;
import org.opentripplanner.model.StopCollection;
import org.opentripplanner.model.StopPattern;
import org.opentripplanner.model.TransitEntity;
import org.opentripplanner.model.Trip;
Expand All @@ -42,6 +46,7 @@
import org.opentripplanner.model.calendar.impl.CalendarServiceDataFactoryImpl;
import org.opentripplanner.model.transfer.Transfer;
import org.opentripplanner.model.transfer.TransferPoint;
import org.opentripplanner.util.OTPFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -294,6 +299,9 @@ private void removeEntitiesWithInvalidReferences() {
removeStopTimesForNoneExistingTrips();
fixOrRemovePatternsWhichReferenceNoneExistingTrips();
removeTransfersForNoneExistingTrips();
if(OTPFeature.RemoveUnusedStops.isOn()) {
removeStopsUnusedByTripPatterns();
}
}

/** Remove all trips witch reference none existing service ids */
Expand Down Expand Up @@ -336,6 +344,124 @@ private void removeTransfersForNoneExistingTrips() {
logRemove("Trip", orgSize, transfers.size(), "Transfer to/from trip does not exist.");
}

private void removeStopsUnusedByTripPatterns() {
HashSet<Stop> keepStops = new HashSet<>();
HashSet<StopCollection> keepStopCollections = new HashSet<>();

for (TripPattern p : tripPatterns.values()) {
keepStops.addAll(p.getStops());
for (Stop stop : p.getStops()) {
if(stop.getParentStation() != null) {
keepStopCollections.add(stop.getParentStation());
}
}
}

// Remove Stops
int orgSize = stopsById.size();
stopsById.removeIf(s -> !keepStops.contains(s) && !keepStopCollections.contains(s.getParentStation()));
logRemove("Stop", orgSize, stopsById.size(), "Stop/parent station not in use by any patterns.");

// Remove Stations
orgSize = stationsById.size();
stationsById.removeIf(s -> !keepStopCollections.contains(s));
logRemove("Station", orgSize, stationsById.size(), "Station not in use by any patterns.");

// Update/remove Multi-Modal-Stations
cleanAndRemoveMultiModalStationsWithReferencesToNoneExistingStations(keepStopCollections);
keepStopCollections.addAll(multiModalStationsById.values());

// Update/remove Group of stations
cleanAndRemoveGroupOfStationsWitchReferenceNoneExistingChildren(keepStopCollections);

// Remove Pathways and pathway nodes
removePathwaysAndStationElementsWithMissingReferences();

// Remove Transfers
orgSize = transfers.size();
transfers.removeIf(it -> !keepStops.contains(it.getFrom().getStop()) || !keepStops.contains(it.getTo().getStop()));
logRemove("Transfer", orgSize, transfers.size(), "Transfer to none existing stop.");
}


private void cleanAndRemoveMultiModalStationsWithReferencesToNoneExistingStations(
Set<? super Station> keepStations
) {
Set<StopCollection> keepMMStations = new HashSet<>();

for (MultiModalStation mms : multiModalStationsById.values()) {
mms.removeChildStationIf(s -> !keepStations.contains(s));
if(!mms.getChildStations().isEmpty()) { keepMMStations.add(mms); }
}

int orgSize = multiModalStationsById.size();
multiModalStationsById.removeIf(it -> !keepMMStations.contains(it));
logRemove("MultiModalStation", orgSize, multiModalStationsById.size(), "MultiModalStation without existing stations.");
}

private void cleanAndRemoveGroupOfStationsWitchReferenceNoneExistingChildren(
Set<StopCollection> keepChildren
) {
Set<StopCollection> keepGOStations = new HashSet<>();

// First remove all none existing stations and multi-modal stations
for (GroupOfStations gos : groupsOfStationsById.values()) {
gos.removeChildStationIf(it -> !(it instanceof GroupOfStations) && !keepChildren.contains(it));
}

// Take care of nested group-of-station(GOS) by removing all GOSs without stops in them
for (GroupOfStations gos : groupsOfStationsById.values()) {
gos.removeChildStationIf(it -> it.getChildStops().isEmpty());
if(!gos.getChildStops().isEmpty()) {
keepGOStations.add(gos);
}
}

// Finally update the index
int orgSize = groupsOfStationsById.size();
groupsOfStationsById.removeIf(it -> !keepGOStations.contains(it));
logRemove("GroupOfStations", orgSize, groupsOfStationsById.size(), "GroupOfStations without existing stations.");
}

private void removePathwaysAndStationElementsWithMissingReferences() {
int orgSizePathways = pathways.size();
int orgSizeEntrances = entrancesById.size();
int orgSizePathwayNodes = pathwayNodesById.size();
int orgSizeBoardingAreas = boardingAreasById.size();

Set<StationElement> keepStationElement = new HashSet<>(stopsById.values());
keepStationElement.addAll(entrancesById.values());
keepStationElement.addAll(pathwayNodesById.values());
keepStationElement.addAll(boardingAreasById.values());

boolean changed = true;

// Iteratively remove Pathway elements until no more elements can be removed
while (changed) {
changed = pathways.removeIf(p ->
!keepStationElement.contains(p.getFromStop()) ||
!keepStationElement.contains(p.getToStop())
);

// Find all elements used in the pathways
keepStationElement.clear();
keepStationElement.addAll(pathways.stream()
.flatMap(p -> Stream.of(p.getFromStop(), p.getToStop()))
.collect(Collectors.toList())
);
}

// Clean indexes
entrancesById.removeIf(e -> !keepStationElement.contains(e));
pathwayNodesById.removeIf(e -> !keepStationElement.contains(e));
boardingAreasById.removeIf(e -> !keepStationElement.contains(e));

logRemove("Pathway", orgSizePathways, pathways.size(), "Pathway without existing station element.");
logRemove("Entrance", orgSizeEntrances, entrancesById.size(), "Entrance not part of Pathway.");
logRemove("PathwayNode", orgSizePathwayNodes, pathwayNodesById.size(), "PathwayNode not part of Pathway.");
logRemove("BoardingArea", orgSizeBoardingAreas, boardingAreasById.size(), "BoardingArea not part of Pathway.");
}

/** Return {@code true} if the from/to trip reference is none null, but do not exist. */
private boolean transferTripsDoesNotExist(Transfer t) {
return transferTripPointDoesNotExist(t.getFrom())
Expand All @@ -349,6 +475,9 @@ private boolean transferTripPointDoesNotExist(TransferPoint p) {

private static void logRemove(String type, int orgSize, int newSize, String reason) {
if(orgSize == newSize) { return; }
LOG.info("{} of {} {}(s) removed. Reason: {}", orgSize - newSize, orgSize, type, reason);
LOG.info(
"{} of {} {}(s) removed, {} left. Reason: {}",
orgSize - newSize, orgSize, type, newSize, reason
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public RoutingWorker(RaptorConfig<TripSchedule> config, RoutingRequest request)
this.raptorService = new RaptorService<>(config);
this.request = request;
this.emptyDirectModeHandler = new FilterTransitWhenDirectModeIsEmpty(request.modes);
System.out.println(request);
}

public RoutingResponse route(Router router) {
Expand Down
9 changes: 7 additions & 2 deletions src/main/java/org/opentripplanner/standalone/OTPMain.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.opentripplanner.standalone;

import static org.opentripplanner.model.projectinfo.OtpProjectInfo.projectInfo;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;
import org.opentripplanner.datastore.DataSource;
import org.opentripplanner.ext.interactivelauncher.client.TestClient;
import org.opentripplanner.graph_builder.GraphBuilder;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.SerializedGraphObject;
Expand All @@ -17,8 +20,6 @@
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;

import static org.opentripplanner.model.projectinfo.OtpProjectInfo.projectInfo;

/**
* This is the main entry point to OpenTripPlanner. It allows both building graphs and starting up
* an OTP server depending on command line options. OTPMain is a concrete class making it possible
Expand Down Expand Up @@ -172,6 +173,10 @@ private static void startOTPServer(CommandLineParameters params) {
router.graphVisualizer.run();
}

new Thread(new TestClient(router)).start();



/* Start web server if requested. */
// We could start the server first so it can report build/load progress to a load balancer.
// This would also avoid the awkward call to set the router on the appConstruction after it's constructed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public RaptorSearchWindowCalculator withSearchParams(SearchParams params) {

public RaptorSearchWindowCalculator calculate() {
if(heuristicMinTransitTime == NOT_SET) {
throw new IllegalArgumentException("The minTravelTime is not set.");
throw new IllegalArgumentException("The heuristicMinTransitTime is not set.");
}

if (!params.isSearchWindowSet()) {
Expand All @@ -101,7 +101,7 @@ int roundUpToNearestMinute(int minTravelTimeInSeconds) {
);
}
// See the UnitTest for verification of this:
// We want: 0 -> 0, 1 -> 60, 59 -> 60 ...
// We want: 0 -> 0, 1 -> 60, 60 -> 60, 61 -> 120 ...
return ((minTravelTimeInSeconds + 59) / 60) * 60;
}

Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/opentripplanner/util/OTPFeature.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public enum OTPFeature {
APIUpdaterStatus(true),
OptimizeTransfers(true),
GuaranteedTransfers(true),
RemoveUnusedStops(false),

// Sandbox extension features - Must be turned OFF by default
ActuatorAPI(false),
Expand Down