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
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,11 @@
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
<dependency>
<groupId>io.leonard</groupId>
<artifactId>opening-hours-evaluator</artifactId>
<version>1.2.1</version>
</dependency>
Comment on lines +693 to +697
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if we should have this dependency - I know it is @leonardehrenfried and @flaktack who maintain it, but I am skeptical to use a library that is just 2 classes and a thin wrapper around another lib - it is poetically creating problems in the future, while maintaining the needed logic inside OTP seem like a better option. Also the ch.poole.openinghoursparser lib is used in this PR directly, so we should include the dependency to it as well, and not relay on a transitive dependency. Another problem is the license - am not an expert, but we at least need to include it if we use the lib.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will keep using this as a depedency but we will add ch.poole.openinghoursparser to pom as an separate depedency as well.

<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ private ApiVehicleParkingWithEntrance mapVehicleParking(VehicleParkingWithEntran
.hasWheelchairAccessibleCarPlaces(vp.hasWheelchairAccessibleCarPlaces())
.availability(mapVehicleParkingSpaces(vp.getAvailability()))
.capacity(mapVehicleParkingSpaces(vp.getCapacity()))
.closesSoon(vehicleParkingWithEntrance.isClosesSoon())
.realtime(vehicleParkingWithEntrance.isRealtime())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ public class ApiVehicleParkingWithEntrance {
*/
public final ApiVehicleParkingSpaces availability;

/**
* True if the difference of visiting time for a {@link org.opentripplanner.routing.vehicle_parking.VehicleParking
* VehicleParking} and the closing time is inside the request's {@link
* org.opentripplanner.routing.api.request.RoutingRequest#vehicleParkingClosesSoonSeconds
* RoutingRequest#vehicleParkingClosesSoonSeconds} interval.
*/
public final boolean closesSoon;

/**
* True if realtime information is used for checking availability.
*/
Expand All @@ -98,6 +106,7 @@ public class ApiVehicleParkingWithEntrance {
boolean hasWheelchairAccessibleCarPlaces,
ApiVehicleParkingSpaces capacity,
ApiVehicleParkingSpaces availability,
boolean closesSoon,
boolean realtime
) {
this.id = id;
Expand All @@ -114,6 +123,7 @@ public class ApiVehicleParkingWithEntrance {
this.hasWheelchairAccessibleCarPlaces = hasWheelchairAccessibleCarPlaces;
this.capacity = capacity;
this.availability = availability;
this.closesSoon = closesSoon;
this.realtime = realtime;
}

Expand All @@ -137,6 +147,7 @@ public static class ApiVehicleParkingWithEntranceBuilder {
private boolean hasWheelchairAccessibleCarPlaces;
private ApiVehicleParkingSpaces capacity;
private ApiVehicleParkingSpaces availability;
private boolean closesSoon;
private boolean realtime;

ApiVehicleParkingWithEntranceBuilder() {}
Expand Down Expand Up @@ -221,6 +232,12 @@ public ApiVehicleParkingWithEntranceBuilder availability(
return this;
}

public ApiVehicleParkingWithEntranceBuilder closesSoon(
boolean closesSoon
) {
this.closesSoon = closesSoon;
return this;
}

public ApiVehicleParkingWithEntranceBuilder realtime(
boolean realtime
Expand All @@ -233,7 +250,7 @@ public ApiVehicleParkingWithEntrance build() {
return new ApiVehicleParkingWithEntrance(
id, name, entranceId, entranceName, detailsUrl,
imageUrl, note, tags, hasBicyclePlaces, hasAnyCarPlaces, hasCarPlaces,
hasWheelchairAccessibleCarPlaces, capacity, availability, realtime
hasWheelchairAccessibleCarPlaces, capacity, availability, closesSoon, realtime
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public TripPlannerResponse plan(@Context UriInfo uriInfo, @Context Request grizz

response.debugOutput = res.getDebugTimingAggregator().finishedRendering();
}
catch (Exception e) {
catch (Throwable e) {
LOG.error("System error", e);
PlannerError error = new PlannerError(Message.SYSTEM_ERROR);
response.setError(error);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.opentripplanner.graph_builder.issues;

import org.opentripplanner.graph_builder.DataImportIssue;
import org.opentripplanner.openstreetmap.model.OSMWithTags;

public class ParkAndRideOpeningHoursUnparsed implements DataImportIssue {

public static final String FMT = "Park and ride '%s' (%s) has an invalid opening_hours value, it will always be open: %s";
public static final String HTMLFMT = "Park and ride <a href='%s'>'%s' (%s)</a> has an invalid opening_hours value, it will always be open: <code>%s</code>\"";

private final String name;
private final OSMWithTags entity;
private final String openingHours;

public ParkAndRideOpeningHoursUnparsed(String name, OSMWithTags entity, String openingHours){
this.name = name;
this.entity = entity;
this.openingHours = openingHours;
}

@Override
public String getMessage() {
return String.format(FMT, name, entity, openingHours);
}

@Override
public String getHTMLMessage() {
return String.format(HTMLFMT, entity.getOpenStreetMapLink(), name, entity, openingHours);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.opentripplanner.graph_builder.module.osm;

import ch.poole.openinghoursparser.OpeningHoursParseException;
import com.google.common.collect.Iterables;
import gnu.trove.iterator.TLongIterator;
import gnu.trove.list.TLongList;
Expand Down Expand Up @@ -27,6 +28,7 @@
import org.opentripplanner.graph_builder.DataImportIssueStore;
import org.opentripplanner.graph_builder.issues.Graphwide;
import org.opentripplanner.graph_builder.issues.InvalidVehicleParkingCapacity;
import org.opentripplanner.graph_builder.issues.ParkAndRideOpeningHoursUnparsed;
import org.opentripplanner.graph_builder.issues.ParkAndRideUnlinked;
import org.opentripplanner.graph_builder.issues.StreetCarSpeedZero;
import org.opentripplanner.graph_builder.issues.TurnRestrictionBad;
Expand All @@ -43,6 +45,8 @@
import org.opentripplanner.openstreetmap.model.OSMWay;
import org.opentripplanner.openstreetmap.model.OSMWithTags;
import org.opentripplanner.routing.api.request.RoutingRequest;
import org.opentripplanner.routing.core.OsmOpeningHours;
import org.opentripplanner.routing.core.TimeRestriction;
import org.opentripplanner.routing.core.TraversalRequirements;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.edgetype.AreaEdge;
Expand Down Expand Up @@ -544,6 +548,8 @@ private VehicleParking createVehicleParkingObjectFromOsmEntity(
) || carCapacity.orElse(0) > 0;
var wheelchairAccessibleCarPlaces = wheelchairAccessibleCarCapacity.orElse(0) > 0;

var openingHours = parseVehicleParkingOpeningHours(entity, creativeName);

var id = new FeedScopedId(
VEHICLE_PARKING_OSM_FEED_ID,
String.format("%s/%d", entity.getClass().getSimpleName(), entity.getId())
Expand Down Expand Up @@ -573,6 +579,7 @@ private VehicleParking createVehicleParkingObjectFromOsmEntity(
.y(lat)
.tags(tags)
.detailsUrl(entity.getTag("website"))
.openingHours(openingHours)
.bicyclePlaces(bicyclePlaces)
.carPlaces(carPlaces)
.wheelchairAccessibleCarPlaces(wheelchairAccessibleCarPlaces)
Expand All @@ -581,6 +588,20 @@ private VehicleParking createVehicleParkingObjectFromOsmEntity(
.build();
}

private TimeRestriction parseVehicleParkingOpeningHours(OSMWithTags entity, I18NString creativeName) {
final var openingHoursTag = entity.getTag("opening_hours");
if (openingHoursTag != null) {
try {
return OsmOpeningHours.parseFromOsm(openingHoursTag);
} catch (OpeningHoursParseException e) {
issueStore.add(new ParkAndRideOpeningHoursUnparsed(
creativeName.toString(), entity, openingHoursTag
));
}
}
return null;
}

private I18NString nameParkAndRideEntity(OSMWithTags osmWithTags) {
// If there is an explicit name user that. The explicit name is used so that tag-based
// translations are used, which are not handled by "CreativeNamer"s.
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/org/opentripplanner/model/plan/Place.java
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,12 @@ public static Place forVehicleRentalPlace(VehicleRentalStationVertex vertex, Str
);
}

public static Place forVehicleParkingEntrance(VehicleParkingEntranceVertex vertex, String name, RoutingRequest request) {
public static Place forVehicleParkingEntrance(
VehicleParkingEntranceVertex vertex,
String name,
boolean closesSoon,
RoutingRequest request
) {
TraverseMode traverseMode = null;
if (request.streetSubRequestModes.getCar()) {
traverseMode = TraverseMode.CAR;
Expand All @@ -228,6 +233,7 @@ public static Place forVehicleParkingEntrance(VehicleParkingEntranceVertex verte
VehicleParkingWithEntrance.builder()
.vehicleParking(vertex.getVehicleParking())
.entrance(vertex.getParkingEntrance())
.closesSoon(closesSoon)
.realtime(realTime)
.build()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ public class VehicleParkingWithEntrance {

private final VehicleParkingEntrance entrance;

/**
* True if the difference of visiting time for a {@link org.opentripplanner.routing.vehicle_parking.VehicleParking
* VehicleParking} and the closing time is inside the request's {@link
* org.opentripplanner.routing.api.request.RoutingRequest#vehicleParkingClosesSoonSeconds
* RoutingRequest#vehicleParkingClosesSoonSeconds} interval.
*/
public final boolean closesSoon;

/**
* Was realtime data used when parking at this VehicleParking.
*/
Expand All @@ -17,10 +25,12 @@ public class VehicleParkingWithEntrance {
VehicleParkingWithEntrance(
VehicleParking vehicleParking,
VehicleParkingEntrance entrance,
boolean closesSoon,
boolean realtime
) {
this.vehicleParking = vehicleParking;
this.entrance = entrance;
this.closesSoon = closesSoon;
this.realtime = realtime;
}

Expand All @@ -32,6 +42,10 @@ public VehicleParkingEntrance getEntrance() {
return this.entrance;
}

public boolean isClosesSoon() {
return closesSoon;
}

public boolean isRealtime() {
return realtime;
}
Expand All @@ -44,6 +58,7 @@ public static class VehicleParkingWithEntranceBuilder {

private VehicleParking vehicleParking;
private VehicleParkingEntrance entrance;
private boolean closesSoon;
private boolean realtime;

VehicleParkingWithEntranceBuilder() {}
Expand All @@ -62,6 +77,13 @@ public VehicleParkingWithEntranceBuilder entrance(
return this;
}

public VehicleParkingWithEntranceBuilder closesSoon(
boolean closesSoon
) {
this.closesSoon = closesSoon;
return this;
}

public VehicleParkingWithEntranceBuilder realtime(
boolean realtime
) {
Expand All @@ -70,7 +92,7 @@ public VehicleParkingWithEntranceBuilder realtime(
}

public VehicleParkingWithEntrance build() {
return new VehicleParkingWithEntrance(vehicleParking, entrance, realtime);
return new VehicleParkingWithEntrance(vehicleParking, entrance, closesSoon, realtime);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,17 @@ private static Place makePlace(State state, Locale requestedLocale) {
} else if(vertex instanceof VehicleRentalStationVertex) {
return Place.forVehicleRentalPlace((VehicleRentalStationVertex) vertex, name);
} else if (vertex instanceof VehicleParkingEntranceVertex) {
return Place.forVehicleParkingEntrance((VehicleParkingEntranceVertex) vertex, name, state.getOptions());
var vehicleParking = ((VehicleParkingEntranceVertex) vertex).getVehicleParking();
var limit = state.getTimeAsZonedDateTime()
.plusSeconds(state.getOptions().vehicleParkingClosesSoonSeconds);
var closesSoon = false;
if (vehicleParking.getOpeningHours() != null) {
// This ignores the parking being closed for less than vehicleParkingClosesSoonSeconds
closesSoon = !vehicleParking.getOpeningHours()
.isTraverseableAt(limit.toLocalDateTime());
}
return Place.forVehicleParkingEntrance(
(VehicleParkingEntranceVertex) vertex, name, closesSoon, state.getOptions());
} else {
return Place.normal(vertex, name);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private TransitRouterResult route() {

debugTimingAggregator.finishedPatternFiltering();

var accessEgresses = getAccessEgresses(transitLayer);
var accessEgresses = getAccessEgresses(transitLayer, searchStartTime);

debugTimingAggregator.finishedAccessEgress(
accessEgresses.getAccesses().size(),
Expand Down Expand Up @@ -149,21 +149,22 @@ private TransitRouterResult route() {
}

private AccessEgresses getAccessEgresses(
TransitLayer transitLayer
TransitLayer transitLayer,
ZonedDateTime startOfTime
) {
var accessEgressMapper = new AccessEgressMapper(transitLayer.getStopIndex());
var accessList = new ArrayList<AccessEgress>();
var egressList = new ArrayList<AccessEgress>();

var accessCalculator = (Runnable) () -> {
debugTimingAggregator.startedAccessCalculating();
accessList.addAll(getAccessEgresses(accessEgressMapper, false));
accessList.addAll(getAccessEgresses(accessEgressMapper, startOfTime, false));
debugTimingAggregator.finishedAccessCalculating();
};

var egressCalculator = (Runnable) () -> {
debugTimingAggregator.startedEgressCalculating();
egressList.addAll(getAccessEgresses(accessEgressMapper, true));
egressList.addAll(getAccessEgresses(accessEgressMapper, startOfTime, true));
debugTimingAggregator.finishedEgressCalculating();
};

Expand Down Expand Up @@ -194,7 +195,7 @@ private AccessEgresses getAccessEgresses(

private Collection<AccessEgress> getAccessEgresses(
AccessEgressMapper accessEgressMapper,
boolean isEgress
ZonedDateTime startOfTime, boolean isEgress
) {
var results = new ArrayList<AccessEgress>();
var mode = isEgress ? request.modes.egressMode : request.modes.accessMode;
Expand All @@ -213,7 +214,7 @@ private Collection<AccessEgress> getAccessEgresses(
isEgress
);

results.addAll(accessEgressMapper.mapNearbyStops(nearbyStops, isEgress));
results.addAll(accessEgressMapper.mapNearbyStops(nearbyStops, startOfTime, isEgress));

// Special handling of flex accesses
if (OTPFeature.FlexRouting.isOn() && mode == StreetMode.FLEXIBLE) {
Expand All @@ -223,7 +224,7 @@ private Collection<AccessEgress> getAccessEgresses(
isEgress
);

results.addAll(accessEgressMapper.mapFlexAccessEgresses(flexAccessList, isEgress));
results.addAll(accessEgressMapper.mapFlexAccessEgresses(flexAccessList, startOfTime, isEgress));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public static Collection<NearbyStop> streetSearch(
// the routingContext (rctx), which results in the created temporary edges being removed prematurely.
// findNearbyStopsViaStreets() will call cleanup() on the created routing request.
RoutingRequest nearbyRequest = rr.getStreetSearchRequest(streetMode);
nearbyRequest.ignoreAndCollectTimeRestrictions = true;

NearbyStopFinder nearbyStopFinder = new NearbyStopFinder(
rr.rctx.graph,
Expand Down
Loading