All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdater Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.updater.vehicle_parking;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.linking.DisposableEdgeCollection;
import org.opentripplanner.routing.linking.VertexLinker;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.service.vehicleparking.model.VehicleParking;
import org.opentripplanner.service.vehicleparking.model.VehicleParkingHelper;
import org.opentripplanner.service.vehicleparking.model.VehicleParkingState;
import org.opentripplanner.street.model.edge.LinkingDirection;
import org.opentripplanner.street.model.edge.StreetVehicleParkingLink;
import org.opentripplanner.street.model.edge.VehicleParkingEdge;
import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex;
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.DataSource;
import org.opentripplanner.updater.spi.PollingGraphUpdater;
import org.opentripplanner.utils.tostring.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Graph updater that dynamically sets availability information on vehicle parking lots. This
 * updater fetches data from a single {@link DataSource}.
 */
public class VehicleParkingUpdater extends PollingGraphUpdater {

  private static final Logger LOG = LoggerFactory.getLogger(VehicleParkingUpdater.class);
  private final Map> verticesByPark =
    new HashMap<>();
  private final Map> tempEdgesByPark =
    new HashMap<>();
  private final DataSource source;
  private final List oldVehicleParkings = new ArrayList<>();
  private final VertexLinker linker;

  private final VehicleParkingRepository parkingRepository;

  public VehicleParkingUpdater(
    VehicleParkingUpdaterParameters parameters,
    DataSource source,
    VertexLinker vertexLinker,
    VehicleParkingRepository parkingRepository
  ) {
    super(parameters);
    this.source = source;
    // Creation of network linker library will not modify the graph
    this.linker = vertexLinker;
    // Adding a vehicle parking station service needs a graph writer runnable
    this.parkingRepository = parkingRepository;

    LOG.info("Creating vehicle-parking updater running every {}: {}", pollingPeriod(), source);
  }

  @Override
  protected void runPolling() throws InterruptedException, ExecutionException {
    LOG.debug("Updating vehicle parkings from {}", source);
    if (!source.update()) {
      LOG.debug("No updates");
      return;
    }
    List vehicleParkings = source.getUpdates();

    // Create graph writer runnable to apply these stations to the graph
    VehicleParkingGraphWriterRunnable graphWriterRunnable = new VehicleParkingGraphWriterRunnable(
      vehicleParkings
    );
    updateGraph(graphWriterRunnable);
  }

  private class VehicleParkingGraphWriterRunnable implements GraphWriterRunnable {

    private final Map oldVehicleParkingsById;
    private final Set updatedVehicleParkings;

    private VehicleParkingGraphWriterRunnable(List updatedVehicleParkings) {
      this.oldVehicleParkingsById = oldVehicleParkings
        .stream()
        .collect(Collectors.toMap(VehicleParking::getId, Function.identity()));
      this.updatedVehicleParkings = new HashSet<>(updatedVehicleParkings);
    }

    @Override
    public void run(RealTimeUpdateContext context) {
      // Apply stations to graph
      /* Add any new park and update space available for existing parks */
      Set toAdd = new HashSet<>();
      Set toLink = new HashSet<>();
      Set toRemove = new HashSet<>();

      var vehicleParkingHelper = new VehicleParkingHelper(context.graph());

      for (VehicleParking updatedVehicleParking : updatedVehicleParkings) {
        var operational = updatedVehicleParking.getState().equals(VehicleParkingState.OPERATIONAL);
        var alreadyExists = oldVehicleParkings.contains(updatedVehicleParking);

        if (alreadyExists) {
          oldVehicleParkingsById
            .get(updatedVehicleParking.getId())
            .updateAvailability(updatedVehicleParking.getAvailability());
        } else {
          toAdd.add(updatedVehicleParking);
          if (operational) {
            toLink.add(updatedVehicleParking);
          }
        }
      }

      /* Remove existing parks that were not present in the update */
      for (var oldVehicleParking : oldVehicleParkings) {
        if (updatedVehicleParkings.contains(oldVehicleParking)) {
          continue;
        }

        if (verticesByPark.containsKey(oldVehicleParking)) {
          tempEdgesByPark.get(oldVehicleParking).forEach(DisposableEdgeCollection::disposeEdges);
          verticesByPark
            .get(oldVehicleParking)
            .forEach(v -> removeVehicleParkingEdgesFromGraph(v, context.graph()));
          verticesByPark.remove(oldVehicleParking);
        }

        toRemove.add(oldVehicleParking);
      }

      /* Add new parks, after removing, so that there are no duplicate vertices for removed and re-added parks.*/
      for (final VehicleParking updatedVehicleParking : toLink) {
        var vehicleParkingVertices = vehicleParkingHelper.createVehicleParkingVertices(
          updatedVehicleParking
        );
        var disposableEdgeCollectionsForVertex = linkVehicleParkingVertexToStreets(
          vehicleParkingVertices
        );

        VehicleParkingHelper.linkVehicleParkingEntrances(vehicleParkingVertices);

        verticesByPark.put(updatedVehicleParking, vehicleParkingVertices);
        tempEdgesByPark.put(updatedVehicleParking, disposableEdgeCollectionsForVertex);
      }

      parkingRepository.updateVehicleParking(toAdd, toRemove);

      oldVehicleParkings.removeAll(toRemove);
      oldVehicleParkings.addAll(toAdd);
    }

    private List linkVehicleParkingVertexToStreets(
      List vehicleParkingVertices
    ) {
      List disposableEdgeCollectionsForVertex = new ArrayList<>();
      for (var vehicleParkingVertex : vehicleParkingVertices) {
        var disposableEdges = linkVehicleParkingForRealtime(vehicleParkingVertex);
        disposableEdgeCollectionsForVertex.addAll(disposableEdges);

        if (vehicleParkingVertex.getOutgoing().isEmpty()) {
          LOG.info("Vehicle parking {} unlinked", vehicleParkingVertex);
        }
      }
      return disposableEdgeCollectionsForVertex;
    }

    private List linkVehicleParkingForRealtime(
      VehicleParkingEntranceVertex vehicleParkingEntranceVertex
    ) {
      List disposableEdgeCollections = new ArrayList<>();
      if (vehicleParkingEntranceVertex.isWalkAccessible()) {
        var disposableWalkEdges = linker.linkVertexForRealTime(
          vehicleParkingEntranceVertex,
          new TraverseModeSet(TraverseMode.WALK),
          LinkingDirection.BIDIRECTIONAL,
          (vertex, streetVertex) ->
            List.of(
              StreetVehicleParkingLink.createStreetVehicleParkingLink(
                (VehicleParkingEntranceVertex) vertex,
                streetVertex
              ),
              StreetVehicleParkingLink.createStreetVehicleParkingLink(
                streetVertex,
                (VehicleParkingEntranceVertex) vertex
              )
            )
        );
        disposableEdgeCollections.add(disposableWalkEdges);
      }

      if (vehicleParkingEntranceVertex.isCarAccessible()) {
        var disposableCarEdges = linker.linkVertexForRealTime(
          vehicleParkingEntranceVertex,
          new TraverseModeSet(TraverseMode.CAR),
          LinkingDirection.BIDIRECTIONAL,
          (vertex, streetVertex) ->
            List.of(
              StreetVehicleParkingLink.createStreetVehicleParkingLink(
                (VehicleParkingEntranceVertex) vertex,
                streetVertex
              ),
              StreetVehicleParkingLink.createStreetVehicleParkingLink(
                streetVertex,
                (VehicleParkingEntranceVertex) vertex
              )
            )
        );
        disposableEdgeCollections.add(disposableCarEdges);
      }

      return disposableEdgeCollections;
    }

    private void removeVehicleParkingEdgesFromGraph(
      VehicleParkingEntranceVertex entranceVertex,
      Graph graph
    ) {
      entranceVertex
        .getIncoming()
        .stream()
        .filter(VehicleParkingEdge.class::isInstance)
        .forEach(graph::removeEdge);
      entranceVertex
        .getOutgoing()
        .stream()
        .filter(VehicleParkingEdge.class::isInstance)
        .forEach(graph::removeEdge);
      graph.remove(entranceVertex);
    }
  }

  @Override
  public String toString() {
    return ToStringBuilder.of(this.getClass()).addObj("source", source).toString();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy