org.opentripplanner.updater.bike_rental.GbfsBikeRentalDataSource Maven / Gradle / Ivy
package org.opentripplanner.updater.bike_rental;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Sets;
import org.opentripplanner.routing.bike_rental.BikeRentalStation;
import org.opentripplanner.updater.UpdaterDataSourceParameters;
import org.opentripplanner.util.NonLocalizedString;
import java.util.*;
/**
* Created by demory on 2017-03-14.
*/
public class GbfsBikeRentalDataSource implements BikeRentalDataSource {
private final GbfsStationDataSource stationInformationSource; // station_information.json required by GBFS spec
private final GbfsStationStatusDataSource stationStatusSource; // station_status.json required by GBFS spec
private final GbfsFloatingBikeDataSource floatingBikeSource; // free_bike_status.json declared OPTIONAL by GBFS spec
private final String networkName;
/** Some car rental systems and flex transit systems work exactly like bike rental, but with cars. */
private final boolean routeAsCar;
public GbfsBikeRentalDataSource (Parameters parameters, String networkName) {
routeAsCar = parameters.routeAsCar();
stationInformationSource = new GbfsStationDataSource(parameters);
stationStatusSource = new GbfsStationStatusDataSource(parameters);
floatingBikeSource = new GbfsFloatingBikeDataSource(parameters);
setBaseUrl(parameters.getUrl());
if (networkName != null && !networkName.isEmpty()) {
this.networkName = networkName;
} else {
this.networkName = "GBFS";
}
}
// TODO This should be updated to fetch the endpoints defined in gbfs.json
private void setBaseUrl (String url) {
String baseUrl = url;
if (!baseUrl.endsWith("/")) baseUrl += "/";
stationInformationSource.setUrl(baseUrl + "station_information.json");
stationStatusSource.setUrl(baseUrl + "station_status.json");
floatingBikeSource.setUrl(baseUrl + "free_bike_status.json");
}
@Override
public boolean update() {
// These first two GBFS files are required.
boolean updatesFound = stationInformationSource.update();
updatesFound |= stationStatusSource.update();
// This floating-bikes file is optional, and does not appear in all GBFS feeds.
updatesFound |= floatingBikeSource.update();
// Return true if ANY of the sub-updaters found any updates.
return updatesFound;
}
@Override
public List getStations() {
// Index all the station status entries on their station ID.
Map statusLookup = new HashMap<>();
for (BikeRentalStation station : stationStatusSource.getStations()) {
statusLookup.put(station.id, station);
}
// Iterate over all known stations, and if we have any status information add it to those station objects.
for (BikeRentalStation station : stationInformationSource.getStations()) {
if (!statusLookup.containsKey(station.id)) continue;
BikeRentalStation status = statusLookup.get(station.id);
station.bikesAvailable = status.bikesAvailable;
station.spacesAvailable = status.spacesAvailable;
}
// Copy the full list of station objects (with status updates) into a List, appending the floating bike stations.
List stations = new LinkedList<>(stationInformationSource.getStations());
stations.addAll(floatingBikeSource.getStations());
// Set identical network ID on all stations
Set networkIdSet = Sets.newHashSet(this.networkName);
for (BikeRentalStation station : stations) station.networks = networkIdSet;
return stations;
}
class GbfsStationDataSource extends GenericJsonBikeRentalDataSource {
public GbfsStationDataSource (Parameters config) {
super(config, "data/stations");
}
@Override
public BikeRentalStation makeStation(JsonNode stationNode) {
BikeRentalStation brstation = new BikeRentalStation();
brstation.id = stationNode.path("station_id").asText();
brstation.x = stationNode.path("lon").asDouble();
brstation.y = stationNode.path("lat").asDouble();
brstation.name = new NonLocalizedString(stationNode.path("name").asText());
brstation.isCarStation = routeAsCar;
return brstation;
}
}
class GbfsStationStatusDataSource extends GenericJsonBikeRentalDataSource {
public GbfsStationStatusDataSource (Parameters config) {
super(config, "data/stations");
}
@Override
public BikeRentalStation makeStation(JsonNode stationNode) {
BikeRentalStation brstation = new BikeRentalStation();
brstation.id = stationNode.path("station_id").asText();
brstation.bikesAvailable = stationNode.path("num_bikes_available").asInt();
brstation.spacesAvailable = stationNode.path("num_docks_available").asInt();
brstation.isCarStation = routeAsCar;
return brstation;
}
}
class GbfsFloatingBikeDataSource extends GenericJsonBikeRentalDataSource {
public GbfsFloatingBikeDataSource (Parameters config) {
super(config, "data/bikes");
}
@Override
public BikeRentalStation makeStation(JsonNode stationNode) {
BikeRentalStation brstation = new BikeRentalStation();
brstation.id = stationNode.path("bike_id").asText();
brstation.name = new NonLocalizedString(stationNode.path("name").asText());
brstation.x = stationNode.path("lon").asDouble();
brstation.y = stationNode.path("lat").asDouble();
brstation.bikesAvailable = 1;
brstation.spacesAvailable = 0;
brstation.allowDropoff = false;
brstation.isFloatingBike = true;
brstation.isCarStation = routeAsCar;
return brstation;
}
}
public interface Parameters extends UpdaterDataSourceParameters {
boolean routeAsCar();
}
}