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

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

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.function.Function;
import java.util.stream.Collectors;
import org.opentripplanner.graph_builder.linking.DisposableEdgeCollection;
import org.opentripplanner.graph_builder.linking.LinkingDirection;
import org.opentripplanner.graph_builder.linking.VertexLinker;
import org.opentripplanner.model.FeedScopedId;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.core.TraverseModeSet;
import org.opentripplanner.routing.edgetype.StreetVehicleParkingLink;
import org.opentripplanner.routing.edgetype.VehicleParkingEdge;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.vehicle_parking.VehicleParking;
import org.opentripplanner.routing.vehicle_parking.VehicleParkingHelper;
import org.opentripplanner.routing.vehicle_parking.VehicleParkingService;
import org.opentripplanner.routing.vehicle_parking.VehicleParkingState;
import org.opentripplanner.routing.vertextype.VehicleParkingEntranceVertex;
import org.opentripplanner.updater.DataSource;
import org.opentripplanner.updater.GraphWriterRunnable;
import org.opentripplanner.updater.PollingGraphUpdater;
import org.opentripplanner.updater.WriteToGraphCallback;
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 WriteToGraphCallback saveResultOnGraph;

    private final Map> verticesByPark = new HashMap<>();

    private final Map> tempEdgesByPark = new HashMap<>();

    private final DataSource source;

    private final List oldVehicleParkings = new ArrayList<>();

    private VertexLinker linker;

    private VehicleParkingService vehicleParkingService;

    public VehicleParkingUpdater(VehicleParkingUpdaterParameters parameters, DataSource source) {
        super(parameters);
        this.source = source;

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

    @Override
    public void setGraphUpdaterManager(WriteToGraphCallback saveResultOnGraph) {
        this.saveResultOnGraph = saveResultOnGraph;
    }

    @Override
    public void setup(Graph graph) {
        // Creation of network linker library will not modify the graph
        linker = graph.getLinker();
        // Adding a vehicle parking station service needs a graph writer runnable
        vehicleParkingService = graph.getService(VehicleParkingService.class, true);
    }

    @Override
    protected void runPolling() throws Exception {
        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);
        saveResultOnGraph.execute(graphWriterRunnable);
    }

    @Override
    public void teardown() {
    }

    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(Graph graph) {
            // 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<>();

            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;
                }

                vehicleParkingService.removeVehicleParking(oldVehicleParking);

                if (verticesByPark.containsKey(oldVehicleParking)) {
                    tempEdgesByPark.get(oldVehicleParking)
                            .forEach(DisposableEdgeCollection::disposeEdges);
                    verticesByPark.get(oldVehicleParking)
                            .forEach(v -> removeVehicleParkingEdgesFromGraph(v, 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(graph, updatedVehicleParking);
                var disposableEdgeCollectionsForVertex = linkVehicleParkingVertexToStreets(vehicleParkingVertices);

                VehicleParkingHelper.linkVehicleParkingEntrances(vehicleParkingVertices);

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

            for (final VehicleParking vehicleParking : toAdd) {
                vehicleParkingService.addVehicleParking(vehicleParking);
            }

            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.BOTH_WAYS,
                    (vertex, streetVertex) -> List.of(
                        new StreetVehicleParkingLink((VehicleParkingEntranceVertex) vertex, streetVertex),
                        new StreetVehicleParkingLink(streetVertex, (VehicleParkingEntranceVertex) vertex)
                    ));
                disposableEdgeCollections.add(disposableWalkEdges);
            }

            if (vehicleParkingEntranceVertex.isCarAccessible()) {
                var disposableCarEdges = linker.linkVertexForRealTime(
                    vehicleParkingEntranceVertex,
                    new TraverseModeSet(TraverseMode.CAR),
                    LinkingDirection.BOTH_WAYS,
                    (vertex, streetVertex) -> List.of(
                        new StreetVehicleParkingLink((VehicleParkingEntranceVertex) vertex, streetVertex),
                        new StreetVehicleParkingLink(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);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy