Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Bikeep bicycle parking updater #5798

Merged
merged 3 commits into from
Apr 26, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add Bikeep parking updater
leonardehrenfried committed Apr 17, 2024
commit a1f51daadcbdfca0a5fd654e3888ccc8eb47aa19
8 changes: 4 additions & 4 deletions docs/sandbox/VehicleParking.md
Original file line number Diff line number Diff line change
@@ -61,7 +61,7 @@ This will end up in the API responses as the feed id of of the parking lot.

**Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required`
**Path:** /updaters/[2]
**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub`
**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` | `bikeep`

The source of the vehicle updates.

@@ -131,7 +131,7 @@ This will end up in the API responses as the feed id of of the parking lot.

**Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required`
**Path:** /updaters/[3]
**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub`
**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` | `bikeep`

The source of the vehicle updates.

@@ -216,7 +216,7 @@ This will end up in the API responses as the feed id of of the parking lot.

**Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required`
**Path:** /updaters/[4]
**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub`
**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` | `bikeep`

The source of the vehicle updates.

@@ -281,7 +281,7 @@ This will end up in the API responses as the feed id of of the parking lot.

**Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required`
**Path:** /updaters/[5]
**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub`
**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` | `bikeep`

The source of the vehicle updates.

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.opentripplanner.ext.vehicleparking.bikeep;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.time.Duration;
import org.junit.jupiter.api.Test;
import org.opentripplanner.test.support.ResourceLoader;
import org.opentripplanner.updater.spi.HttpHeaders;

class BikeepUpdaterTest {

@Test
void parse() {
var uri = ResourceLoader.of(this).uri("bikeep.json");
var parameters = new BikeepUpdaterParameters(
"bikeep",
uri,
"bikeep",
Duration.ofSeconds(30),
HttpHeaders.empty()
);
var updater = new BikeepUpdater(parameters);
updater.update();
var lots = updater.getUpdates();

assertEquals(9, lots.size());

lots.forEach(l -> assertNotNull(l.getName()));

var first = lots.getFirst();
assertEquals("bikeep:224121", first.getId().toString());
assertEquals("(60.40593, 4.99634)", first.getCoordinate().toString());
assertEquals("Ågotnes Terminal", first.getName().toString());
assertEquals(10, first.getAvailability().getBicycleSpaces());
assertEquals(10, first.getCapacity().getBicycleSpaces());

var last = lots.getLast();
assertEquals("bikeep:224111", last.getId().toString());
assertEquals("(59.88741, 10.5205)", last.getCoordinate().toString());
assertEquals("Sandvika Storsenter Nytorget", last.getName().toString());
assertEquals(13, last.getAvailability().getBicycleSpaces());
assertEquals(15, last.getCapacity().getBicycleSpaces());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
4.996344,
60.405932
]
},
"properties": {
"code": "224121",
"label": "Ågotnes Terminal",
"name": "#224121 Ågotnes Terminal",
"address": "Ågotnes",
"tags": [
"FREE",
"BIKE",
"PRIVATE",
"BOOKABLE"
],
"icon": {
"png": "",
"png2x": "",
"svg": ""
},
"parking": {
"available": 10,
"online": 10,
"total": 10
},
"renting": null
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
10.666802,
59.436443
]
},
"properties": {
"code": "226261",
"label": "Gågata Østre",
"name": "#226261 Gågata Østre",
"address": "Dronningens gate, Moss",
"tags": [
"FREE",
"PRIVATE",
"BOOKABLE",
"BIKE"
],
"icon": {
"png": "",
"png2x": "",
"svg": ""
},
"parking": {
"available": 7,
"online": 10,
"total": 10
},
"renting": null
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
10.661444,
59.435401
]
},
"properties": {
"code": "226259",
"label": "Gågata Vestre",
"name": "#226259 Gågata Vestre",
"address": "Dronningens gate, Moss",
"tags": [
"BIKE",
"FREE",
"PRIVATE",
"BOOKABLE"
],
"icon": {
"png": "",
"png2x": "",
"svg": ""
},
"parking": {
"available": 5,
"online": 5,
"total": 5
},
"renting": null
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
10.774958,
59.946535
]
},
"properties": {
"code": "223443",
"label": "Storo Storsenter",
"name": "#223443 Storo Storsenter",
"address": "Norway",
"tags": [
"BIKE",
"PRIVATE",
"BOOKABLE",
"FREE"
],
"icon": {
"png": "https://assets.bikeep.com/locations/icons/bikeep.png",
"png2x": "https://assets.bikeep.com/locations/icons/bikeep@2x.png",
"svg": ""
},
"parking": {
"available": 17,
"online": 20,
"total": 20
},
"renting": null
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
10.501222,
59.914578
]
},
"properties": {
"code": "224519",
"label": "Kolsås Sykkelhotell",
"name": "#224519 Kolsås Sykkelhotell",
"address": "Norway",
"tags": [
"PRIVATE",
"FREE",
"BOOKABLE",
"BIKE_HOUSE",
"BIKE"
],
"icon": {
"png": "https://assets.bikeep.com/locations/icons/bikeep.png",
"png2x": "https://assets.bikeep.com/locations/icons/bikeep@2x.png",
"svg": ""
},
"parking": {
"available": 13,
"online": 22,
"total": 22
},
"renting": null
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
10.663716,
59.435539
]
},
"properties": {
"code": "226260",
"label": "Gågata Midtre",
"name": "#226260 Gågata Midtre",
"address": "Dronningens gate, Moss",
"tags": [
"FREE",
"BOOKABLE",
"PRIVATE",
"BIKE"
],
"icon": {
"png": "",
"png2x": "",
"svg": ""
},
"parking": {
"available": 5,
"online": 5,
"total": 5
},
"renting": null
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
5.320344,
60.463246
]
},
"properties": {
"code": "226266",
"label": "Åsane Sykkelhus",
"name": "#226266 Åsane Sykkelhus",
"address": "Åsane terminal",
"tags": [
"BOOKABLE",
"BIKE",
"FREE",
"PRIVATE"
],
"icon": {
"png": "",
"png2x": "",
"svg": ""
},
"parking": {
"available": 11,
"online": 12,
"total": 12
},
"renting": null
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
10.521137,
59.889181
]
},
"properties": {
"code": "224112",
"label": "Sandvika Storsenter Kjørbokollen",
"name": "#224112 Sandvika Storsenter Kjørbokollen",
"address": "Brodtkorbsgate 7, Sandvika",
"tags": [
"PRIVATE",
"FREE",
"BIKE",
"BOOKABLE"
],
"icon": {
"png": "",
"png2x": "",
"svg": ""
},
"parking": {
"available": 5,
"online": 5,
"total": 5
},
"renting": null
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
10.520496,
59.887412
]
},
"properties": {
"code": "224111",
"label": "Sandvika Storsenter Nytorget",
"name": "#224111 Sandvika Storsenter Nytorget",
"address": "Sandviksveien 176, Sandvika",
"tags": [
"BIKE",
"BOOKABLE",
"PRIVATE",
"FREE"
],
"icon": {
"png": "",
"png2x": "",
"svg": ""
},
"parking": {
"available": 13,
"online": 15,
"total": 15
},
"renting": null
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.opentripplanner.ext.vehicleparking.bikeep;

import com.fasterxml.jackson.databind.JsonNode;
import org.opentripplanner.framework.geometry.WgsCoordinate;
import org.opentripplanner.framework.i18n.NonLocalizedString;
import org.opentripplanner.framework.tostring.ToStringBuilder;
import org.opentripplanner.routing.vehicle_parking.VehicleParking;
import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces;
import org.opentripplanner.routing.vehicle_parking.VehicleParkingState;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.updater.spi.GenericJsonDataSource;

/**
* Vehicle parking updater for Bikeep's API.
*/
public class BikeepUpdater extends GenericJsonDataSource<VehicleParking> {

private static final String JSON_PARSE_PATH = "features";
private final BikeepUpdaterParameters params;

public BikeepUpdater(BikeepUpdaterParameters parameters) {
super(parameters.url().toString(), JSON_PARSE_PATH, parameters.httpHeaders());
this.params = parameters;
}

@Override
protected VehicleParking parseElement(JsonNode jsonNode) {
var coords = jsonNode.path("geometry").path("coordinates");
var coordinate = new WgsCoordinate(coords.get(1).asDouble(), coords.get(0).asDouble());

var props = jsonNode.path("properties");
var vehicleParkId = new FeedScopedId(params.feedId(), props.path("code").asText());
var name = new NonLocalizedString(props.path("label").asText());
var parking = props.path("parking");

var availability = VehicleParkingSpaces
.builder()
.bicycleSpaces(parking.get("available").asInt())
.build();
var capacity = VehicleParkingSpaces
.builder()
.bicycleSpaces(parking.get("total").asInt())
.build();

VehicleParking.VehicleParkingEntranceCreator entrance = builder ->
builder
.entranceId(new FeedScopedId(params.feedId(), vehicleParkId.getId() + "/entrance"))
.coordinate(coordinate)
.walkAccessible(true)
.carAccessible(true);

return VehicleParking
.builder()
.id(vehicleParkId)
.name(name)
.state(VehicleParkingState.OPERATIONAL)
.coordinate(coordinate)
.bicyclePlaces(true)
.availability(availability)
.capacity(capacity)
.entrance(entrance)
.build();
}

@Override
public String toString() {
return ToStringBuilder
.of(this.getClass())
.addStr("feedId", this.params.feedId())
.addStr("url", this.params.url().toString())
.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.opentripplanner.ext.vehicleparking.bikeep;

import java.net.URI;
import java.time.Duration;
import org.opentripplanner.updater.spi.HttpHeaders;
import org.opentripplanner.updater.vehicle_parking.VehicleParkingSourceType;
import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters;

/**
* Class that extends {@link VehicleParkingUpdaterParameters} with parameters required by {@link
* BikeepUpdater}.
*/
public record BikeepUpdaterParameters(
String configRef,
URI url,
String feedId,
Duration frequency,
HttpHeaders httpHeaders
)
implements VehicleParkingUpdaterParameters {
@Override
public VehicleParkingSourceType sourceType() {
return VehicleParkingSourceType.BIKEEP;
}
}
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@
import org.opentripplanner.framework.i18n.I18NString;
import org.opentripplanner.framework.i18n.NonLocalizedString;
import org.opentripplanner.framework.i18n.TranslatedString;
import org.opentripplanner.framework.tostring.ToStringBuilder;
import org.opentripplanner.model.calendar.openinghours.OHCalendar;
import org.opentripplanner.model.calendar.openinghours.OpeningHoursCalendarService;
import org.opentripplanner.openstreetmap.OSMOpeningHoursParser;
@@ -36,6 +37,7 @@ abstract class ParkAPIUpdater extends GenericJsonDataSource<VehicleParking> {
private final Collection<String> staticTags;

private final OSMOpeningHoursParser osmOpeningHoursParser;
private final String url;

public ParkAPIUpdater(
ParkAPIUpdaterParameters parameters,
@@ -46,6 +48,7 @@ public ParkAPIUpdater(
this.staticTags = parameters.tags();
this.osmOpeningHoursParser =
new OSMOpeningHoursParser(openingHoursCalendarService, parameters.timeZone());
this.url = parameters.url();
}

@Override
@@ -196,4 +199,9 @@ private List<String> parseTags(JsonNode node, String... tagNames) {
}
return tagList;
}

@Override
public String toString() {
return ToStringBuilder.of(getClass()).addStr("feedId", feedId).addObj("url", url).toString();
}
}
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Set;
import org.opentripplanner.ext.vehicleparking.bikeep.BikeepUpdaterParameters;
import org.opentripplanner.ext.vehicleparking.bikely.BikelyUpdaterParameters;
import org.opentripplanner.ext.vehicleparking.hslpark.HslParkUpdaterParameters;
import org.opentripplanner.ext.vehicleparking.noi.NoiUpdaterParameters;
@@ -88,6 +89,17 @@ public static VehicleParkingUpdaterParameters create(String updaterRef, NodeAdap
.asDuration(Duration.ofMinutes(1)),
HttpHeadersConfig.headers(c, V2_6)
);
case BIKEEP -> new BikeepUpdaterParameters(
updaterRef,
c.of("url").since(V2_6).summary("URL of the locations endpoint.").asUri(),
feedId,
c
.of("frequency")
.since(V2_6)
.summary("How often to update the source.")
.asDuration(Duration.ofMinutes(1)),
HttpHeadersConfig.headers(c, V2_6)
);
};
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.opentripplanner.updater.vehicle_parking;

import org.opentripplanner.ext.vehicleparking.bikeep.BikeepUpdater;
import org.opentripplanner.ext.vehicleparking.bikeep.BikeepUpdaterParameters;
import org.opentripplanner.ext.vehicleparking.bikely.BikelyUpdater;
import org.opentripplanner.ext.vehicleparking.bikely.BikelyUpdaterParameters;
import org.opentripplanner.ext.vehicleparking.hslpark.HslParkUpdater;
@@ -39,6 +41,7 @@ public static DataSource<VehicleParking> create(
);
case BIKELY -> new BikelyUpdater((BikelyUpdaterParameters) parameters);
case NOI_OPEN_DATA_HUB -> new NoiUpdater((NoiUpdaterParameters) parameters);
case BIKEEP -> new BikeepUpdater((BikeepUpdaterParameters) parameters);
};
}
}
Original file line number Diff line number Diff line change
@@ -6,4 +6,5 @@ public enum VehicleParkingSourceType {
HSL_PARK,
BIKELY,
NOI_OPEN_DATA_HUB,
BIKEEP,
}