diff --git a/src/main/java/org/opentripplanner/updater/bike_rental/BikeRentalUpdater.java b/src/main/java/org/opentripplanner/updater/bike_rental/BikeRentalUpdater.java index f2fad57241a..9c4f174f1f1 100644 --- a/src/main/java/org/opentripplanner/updater/bike_rental/BikeRentalUpdater.java +++ b/src/main/java/org/opentripplanner/updater/bike_rental/BikeRentalUpdater.java @@ -97,6 +97,8 @@ protected void configurePolling (Graph graph, JsonNode config) throws Exception source = new SmooveBikeRentalDataSource(networkName); } else if (sourceType.equals("bicimad")) { source = new BicimadBikeRentalDataSource(); + } else if (sourceType.equals("samocat")) { + source = new SamocatScooterRentalDataSource(networkName); } } diff --git a/src/main/java/org/opentripplanner/updater/bike_rental/SamocatScooterRentalDataSource.java b/src/main/java/org/opentripplanner/updater/bike_rental/SamocatScooterRentalDataSource.java new file mode 100644 index 00000000000..795c7d981d7 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/bike_rental/SamocatScooterRentalDataSource.java @@ -0,0 +1,90 @@ +package org.opentripplanner.updater.bike_rental; + +import org.opentripplanner.routing.bike_rental.BikeRentalStation; +import org.opentripplanner.util.NonLocalizedString; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashSet; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Implementation of a BikeRentalDataSource for the Samocat scooter rental service used in Helsinki. + * Since scooters rental works similarly as bike rental, there is no point to create own data structures + * for them. + * @see BikeRentalDataSource + */ +public class SamocatScooterRentalDataSource extends GenericJsonBikeRentalDataSource { + + private static final Logger log = LoggerFactory.getLogger(SamocatScooterRentalDataSource.class); + + private String networkName; + + public SamocatScooterRentalDataSource(String networkName) { + super("results"); + this.networkName = defaultIfEmpty(networkName, "samocat"); + } + + private String defaultIfEmpty(String value, String defaultValue) { + if (value == null || value.isEmpty()) + return defaultValue; + + return value; + } + + /** + *
+     * {
+     * "count": 10,
+     * "next": null,
+     * "previous": null,
+     * "results": [
+     *     {
+     *       "type": "Feature",
+     *       "geometry": {
+     *           "type": "Point",
+     *           "coordinates": [
+     *             60.167913,
+     *             24.952269
+     *           ]
+     *        },
+     *       "properties": {
+     *           "station_id": "0309",
+     *           "city": "Helsinki",
+     *           "country": "Finland",
+     *           "address": "some address",
+     *           "rack_sum": 12,
+     *           "free_racks": 2,
+     *           "available_devices": 1
+     *       }
+     *     }
+     *   ]
+     * }
+     * 
+ */ + public BikeRentalStation makeStation(JsonNode node) { + BikeRentalStation station = new BikeRentalStation(); + JsonNode properties = node.path("properties"); + JsonNode coordinates = node.path("geometry").path("coordinates"); + station.id = properties.path("station_id").asText(); + station.name = new NonLocalizedString(properties.path("address").asText()); + station.state = "Station on"; + station.networks = new HashSet(); + station.networks.add(this.networkName); + try { + if (coordinates.get(0).isNull() || coordinates.isNull()) { + return null; + } + station.x = coordinates.get(0).asDouble(); + station.y = coordinates.get(1).asDouble(); + station.bikesAvailable = properties.path("available_devices").asInt(); + station.spacesAvailable = properties.path("free_racks").asInt(); + return station; + } catch (NumberFormatException e) { + // E.g. coordinates is empty + log.info("Error parsing bike rental station " + station.id, e); + return null; + } + } +} \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index db6325c60ba..1f0517813ed 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -54,6 +54,7 @@ + diff --git a/src/test/java/org/opentripplanner/updater/bike_rental/TestBikeRentalStationSource.java b/src/test/java/org/opentripplanner/updater/bike_rental/TestBikeRentalStationSource.java index 3fef663d5b2..e22dd8134d6 100644 --- a/src/test/java/org/opentripplanner/updater/bike_rental/TestBikeRentalStationSource.java +++ b/src/test/java/org/opentripplanner/updater/bike_rental/TestBikeRentalStationSource.java @@ -75,4 +75,53 @@ public void testSmoove() { BikeRentalStation hamnWithCustomNetwork = rentalStationsWithCustomNetwork.get(0); assertEquals("[Helsinki]", hamnWithCustomNetwork.networks.toString()); } + + public void testSamocat() { + SamocatScooterRentalDataSource source = new SamocatScooterRentalDataSource(null); + source.setUrl("file:src/test/resources/bike/samocat.json"); + assertTrue(source.update()); + List rentalStations = source.getStations(); + + // Invalid station without coordinates should be ignored, so only 3 + assertEquals(3, rentalStations.size()); + for (BikeRentalStation rentalStation : rentalStations) { + System.out.println(rentalStation); + } + + BikeRentalStation testikuja = rentalStations.get(0); + assertEquals("Testikuja 3", testikuja.name.toString()); + assertEquals("0451", testikuja.id); + // Ignore whitespace in coordinates string + assertEquals(24.9355143, testikuja.x); + assertEquals(60.1637284, testikuja.y); + assertEquals(0, testikuja.spacesAvailable); + assertEquals(0, testikuja.bikesAvailable); + assertEquals("Station on", testikuja.state); + assertEquals("[samocat]", testikuja.networks.toString()); + + BikeRentalStation footie = rentalStations.get(1); + assertEquals("Footie 3", footie.name.toString()); + assertEquals("0450", footie.id); + assertEquals(3, footie.spacesAvailable); + assertEquals(4, footie.bikesAvailable); + assertEquals(24.958877, footie.x); + assertEquals(60.194449, footie.y); + + BikeRentalStation bartie = rentalStations.get(2); + assertEquals("Bartie 10", bartie.name.toString()); + assertEquals("3451", bartie.id); + assertEquals(24.9537278, bartie.x); + assertEquals(60.2177349, bartie.y); + assertEquals(5, bartie.spacesAvailable); + assertEquals(1, bartie.bikesAvailable); + // Ignores mismatch with total_slots + + // Test giving network name to data source + SamocatScooterRentalDataSource sourceWithCustomNetwork = new SamocatScooterRentalDataSource("vuosaari"); + sourceWithCustomNetwork.setUrl("file:src/test/resources/bike/samocat.json"); + assertTrue(sourceWithCustomNetwork.update()); + List rentalStationsWithCustomNetwork = sourceWithCustomNetwork.getStations(); + BikeRentalStation testitieWithCustomNetwork = rentalStationsWithCustomNetwork.get(0); + assertEquals("[vuosaari]", testitieWithCustomNetwork.networks.toString()); + } } diff --git a/src/test/resources/bike/samocat.json b/src/test/resources/bike/samocat.json new file mode 100644 index 00000000000..c337c8eb939 --- /dev/null +++ b/src/test/resources/bike/samocat.json @@ -0,0 +1,83 @@ +{ + "count": 3, + "next": null, + "previous": null, + "results": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 24.9355143, + 60.1637284 + ] + }, + "properties": { + "station_id": "0451", + "city": "Helsinki", + "country": "FI", + "address": "Testikuja 3", + "rack_sum": 12, + "free_racks": 0, + "available_devices": 0 + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 24.958877, + 60.194449 + ] + }, + "properties": { + "station_id": "0450", + "city": "Helsinki", + "country": "FI", + "address": "Footie 3", + "rack_sum": 12, + "free_racks": 3, + "available_devices": 4 + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 24.9537278, + 60.2177349 + ] + }, + "properties": { + "station_id": "3451", + "city": "Helsinki", + "country": "FI", + "address": "Bartie 10", + "rack_sum": 12, + "free_racks": 5, + "available_devices": 1 + } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + null, + null + ] + }, + "properties": { + "station_id": "3451", + "city": "Helsinki", + "country": "FI", + "address": "Nulltie 1", + "rack_sum": 12, + "free_racks": 5, + "available_devices": 1 + } + } + ] +} \ No newline at end of file