/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.opentripplanner.updater.bike_rental; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; 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 org.opentripplanner.routing.bike_rental.BikeRentalStation; import org.opentripplanner.routing.bike_rental.BikeRentalStationService; import org.opentripplanner.routing.edgetype.RentABikeOffEdge; import org.opentripplanner.routing.edgetype.RentABikeOnEdge; import org.opentripplanner.routing.edgetype.loader.LinkRequest; import org.opentripplanner.routing.edgetype.loader.NetworkLinkerLibrary; import org.opentripplanner.routing.graph.Edge; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.services.GraphService; import org.opentripplanner.routing.vertextype.BikeRentalStationVertex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; public class BikeRentalUpdater implements Runnable { private static final Logger LOG = LoggerFactory.getLogger(BikeRentalUpdater.class); Map<BikeRentalStation, BikeRentalStationVertex> verticesByStation = new HashMap<BikeRentalStation, BikeRentalStationVertex>(); private BikeRentalDataSource source; private Graph graph; private NetworkLinkerLibrary networkLinkerLibrary; private BikeRentalStationService service; private String routerId; private GraphService graphService; private String network = "default"; private boolean setup = false; public void setRouterId(String routerId) { this.routerId = routerId; } public void setNetwork(String network) { this.network = network; } @Autowired public void setBikeRentalDataSource(BikeRentalDataSource source) { this.source = source; } @Autowired public void setGraphService(GraphService graphService) { this.graphService = graphService; } public boolean setup() { graph = graphService.getGraph(routerId); // Handle null routerId. if (graph == null && setup) { // We temporary disable the updater: no graph ready (yet). LOG.error("Can't get graph for router ID {}, disabling updater.", routerId); networkLinkerLibrary = null; service = null; setup = false; } if (graph != null && !setup) { // A graph is available, setting up. LOG.info("Setting up updater for router ID {}.", routerId); networkLinkerLibrary = new NetworkLinkerLibrary(graph, Collections.<Class<?>, Object> emptyMap()); service = graph.getService(BikeRentalStationService.class); if (service == null) { service = new BikeRentalStationService(); graph.putService(BikeRentalStationService.class, service); } setup = true; } return setup; } public List<BikeRentalStation> getStations() { return source.getStations(); } @Override public void run() { if (!setup()) { // Updater has been disabled (no graph available). return; } LOG.debug("Updating bike rental stations from " + source); if (!source.update()) { LOG.debug("No updates"); return; } List<BikeRentalStation> stations = source.getStations(); Set<BikeRentalStation> stationSet = new HashSet<BikeRentalStation>(); Set<String> networks = new HashSet<String>(Arrays.asList(network)); /* add any new stations and update bike counts for existing stations */ for (BikeRentalStation station : stations) { service.addStation(station); stationSet.add(station); BikeRentalStationVertex vertex = verticesByStation.get(station); if (vertex == null) { vertex = new BikeRentalStationVertex(graph, station); LinkRequest request = networkLinkerLibrary.connectVertexToStreets(vertex); for (Edge e : request.getEdgesAdded()) { graph.addTemporaryEdge(e); } verticesByStation.put(station, vertex); new RentABikeOnEdge(vertex, vertex, networks); new RentABikeOffEdge(vertex, vertex, networks); } else { vertex.setBikesAvailable(station.bikesAvailable); vertex.setSpacesAvailable(station.spacesAvailable); } } /* remove existing stations that were not present in the update */ List<BikeRentalStation> toRemove = new ArrayList<BikeRentalStation>(); for (Entry<BikeRentalStation, BikeRentalStationVertex> entry : verticesByStation.entrySet()) { BikeRentalStation station = entry.getKey(); if (stationSet.contains(station)) continue; BikeRentalStationVertex vertex = entry.getValue(); if (graph.containsVertex(vertex)) { graph.removeVertexAndEdges(vertex); } toRemove.add(station); service.removeStation(station); // TODO: need to unsplit any streets that were split } for (BikeRentalStation station : toRemove) { // post-iteration removal to avoid concurrent modification verticesByStation.remove(station); } } }