
org.opentripplanner.updater.vehicle_rental.VehicleRentalUpdater Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
The newest version!
package org.opentripplanner.updater.vehicle_rental;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.opentripplanner.framework.lang.ObjectUtils;
import org.opentripplanner.framework.logging.Throttle;
import org.opentripplanner.framework.time.DurationUtils;
import org.opentripplanner.framework.time.TimeUtils;
import org.opentripplanner.framework.tostring.ToStringBuilder;
import org.opentripplanner.routing.linking.DisposableEdgeCollection;
import org.opentripplanner.routing.linking.LinkingDirection;
import org.opentripplanner.routing.linking.VertexLinker;
import org.opentripplanner.service.vehiclerental.VehicleRentalRepository;
import org.opentripplanner.service.vehiclerental.model.GeofencingZone;
import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace;
import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink;
import org.opentripplanner.service.vehiclerental.street.VehicleRentalEdge;
import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex;
import org.opentripplanner.street.model.RentalFormFactor;
import org.opentripplanner.street.model.RentalRestrictionExtension;
import org.opentripplanner.street.model.edge.StreetEdge;
import org.opentripplanner.street.model.vertex.VertexFactory;
import org.opentripplanner.street.search.TraverseMode;
import org.opentripplanner.street.search.TraverseModeSet;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.updater.GraphWriterRunnable;
import org.opentripplanner.updater.RealTimeUpdateContext;
import org.opentripplanner.updater.spi.PollingGraphUpdater;
import org.opentripplanner.updater.spi.UpdaterConstructionException;
import org.opentripplanner.updater.spi.WriteToGraphCallback;
import org.opentripplanner.updater.vehicle_rental.datasources.VehicleRentalDatasource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Dynamic vehicle-rental station updater which updates the Graph with vehicle rental stations from
* one VehicleRentalDataSource.
*/
public class VehicleRentalUpdater extends PollingGraphUpdater {
private static final Logger LOG = LoggerFactory.getLogger(VehicleRentalUpdater.class);
private final Throttle unlinkedPlaceThrottle;
private final VehicleRentalDatasource source;
private final String nameForLogging;
private WriteToGraphCallback saveResultOnGraph;
private Map latestModifiedEdges = Map.of();
private Set latestAppliedGeofencingZones = Set.of();
private final Map verticesByStation = new HashMap<>();
private final Map tempEdgesByStation = new HashMap<>();
private final VertexLinker linker;
private final VehicleRentalRepository service;
public VehicleRentalUpdater(
VehicleRentalUpdaterParameters parameters,
VehicleRentalDatasource source,
VertexLinker vertexLinker,
VehicleRentalRepository repository
) throws IllegalArgumentException {
super(parameters);
// Configure updater
LOG.info("Setting up vehicle rental updater for {}.", source);
this.source = source;
this.nameForLogging =
ObjectUtils.ifNotNull(
parameters.sourceParameters().network(),
parameters.sourceParameters().url()
);
this.unlinkedPlaceThrottle = Throttle.ofOneSecond();
// Creation of network linker library will not modify the graph
this.linker = vertexLinker;
// Adding a vehicle rental station service needs a graph writer runnable
this.service = repository;
try {
// Do any setup if needed
source.setup();
} catch (UpdaterConstructionException e) {
LOG.warn("Unable to setup updater: {}", nameForLogging, e);
}
if (runOnlyOnce()) {
LOG.info(
"Creating vehicle-rental updater running once only (non-polling): {}",
nameForLogging
);
} else {
LOG.info(
"Creating vehicle-rental updater running every {}: {}",
DurationUtils.durationToStr(pollingPeriod()),
nameForLogging
);
}
}
@Override
public void setup(WriteToGraphCallback writeToGraphCallback) {
this.saveResultOnGraph = writeToGraphCallback;
}
@Override
public String toString() {
return ToStringBuilder.of(VehicleRentalUpdater.class).addObj("source", source).toString();
}
@Override
public String getConfigRef() {
return toString();
}
@Override
protected void runPolling() {
LOG.debug("Updating vehicle rental stations from {}", nameForLogging);
if (!source.update()) {
LOG.debug("No updates from {}", nameForLogging);
return;
}
List stations = source.getUpdates();
var geofencingZones = source.getGeofencingZones();
// Create graph writer runnable to apply these stations to the graph
VehicleRentalGraphWriterRunnable graphWriterRunnable = new VehicleRentalGraphWriterRunnable(
stations,
geofencingZones
);
saveResultOnGraph.execute(graphWriterRunnable);
}
private class VehicleRentalGraphWriterRunnable implements GraphWriterRunnable {
private final List stations;
private final Set geofencingZones;
public VehicleRentalGraphWriterRunnable(
List stations,
List geofencingZones
) {
this.stations = stations;
this.geofencingZones = Set.copyOf(geofencingZones);
}
@Override
public void run(RealTimeUpdateContext context) {
// Apply stations to graph
Set stationSet = new HashSet<>();
var vertexFactory = new VertexFactory(context.graph());
/* add any new stations and update vehicle counts for existing stations */
for (VehicleRentalPlace station : stations) {
service.addVehicleRentalStation(station);
stationSet.add(station.getId());
VehicleRentalPlaceVertex vehicleRentalVertex = verticesByStation.get(station.getId());
if (vehicleRentalVertex == null) {
vehicleRentalVertex = vertexFactory.vehicleRentalPlace(station);
DisposableEdgeCollection tempEdges = linker.linkVertexForRealTime(
vehicleRentalVertex,
new TraverseModeSet(TraverseMode.WALK),
LinkingDirection.BOTH_WAYS,
(vertex, streetVertex) ->
List.of(
StreetVehicleRentalLink.createStreetVehicleRentalLink(
(VehicleRentalPlaceVertex) vertex,
streetVertex
),
StreetVehicleRentalLink.createStreetVehicleRentalLink(
streetVertex,
(VehicleRentalPlaceVertex) vertex
)
)
);
if (vehicleRentalVertex.getOutgoing().isEmpty()) {
// Copy reference to pass into lambda
var vrv = vehicleRentalVertex;
unlinkedPlaceThrottle.throttle(() ->
// the toString includes the text "Bike rental station"
LOG.warn(
"VehicleRentalPlace is unlinked for {}: {} {}",
nameForLogging,
vrv,
unlinkedPlaceThrottle.setupInfo()
)
);
}
Set formFactors = Stream
.concat(
station.getAvailablePickupFormFactors(false).stream(),
station.getAvailableDropoffFormFactors(false).stream()
)
.collect(Collectors.toSet());
for (RentalFormFactor formFactor : formFactors) {
tempEdges.addEdge(
VehicleRentalEdge.createVehicleRentalEdge(vehicleRentalVertex, formFactor)
);
}
verticesByStation.put(station.getId(), vehicleRentalVertex);
tempEdgesByStation.put(station.getId(), tempEdges);
} else {
vehicleRentalVertex.setStation(station);
}
}
/* remove existing stations that were not present in the update */
List toRemove = new ArrayList<>();
for (Entry entry : verticesByStation.entrySet()) {
FeedScopedId station = entry.getKey();
if (stationSet.contains(station)) continue;
toRemove.add(station);
service.removeVehicleRentalStation(station);
}
for (FeedScopedId station : toRemove) {
// post-iteration removal to avoid concurrent modification
verticesByStation.remove(station);
tempEdgesByStation.get(station).disposeEdges();
tempEdgesByStation.remove(station);
}
// this check relies on the generated equals for the record which also recursively checks that
// the JTS geometries are equal
if (!geofencingZones.isEmpty() && !geofencingZones.equals(latestAppliedGeofencingZones)) {
LOG.info("Computing geofencing zones for {}", nameForLogging);
var start = System.currentTimeMillis();
latestModifiedEdges.forEach(StreetEdge::removeRentalExtension);
var updater = new GeofencingVertexUpdater(
context.graph().getStreetIndex()::getEdgesForEnvelope
);
latestModifiedEdges = updater.applyGeofencingZones(geofencingZones);
latestAppliedGeofencingZones = geofencingZones;
var end = System.currentTimeMillis();
var millis = Duration.ofMillis(end - start);
LOG.info(
"Geofencing zones computation took {}. Added extension to {} edges. For {}",
TimeUtils.durationToStrCompact(millis),
latestModifiedEdges.size(),
nameForLogging
);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy