Skip to content
Merged
Show file tree
Hide file tree
Changes from 80 commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
41bb836
Skeletal implementation of car hailing
leonardehrenfried Mar 9, 2023
42b7727
Update mapping
leonardehrenfried Mar 9, 2023
da5de34
Add car hailing to API surface
leonardehrenfried Mar 10, 2023
4a238fd
Initial port of OTP1 car hailing code
leonardehrenfried Mar 10, 2023
b212de2
Make prototypical integration with Uber
leonardehrenfried Mar 10, 2023
55ac9e6
Prettify request building
leonardehrenfried Mar 10, 2023
c6ae98e
Refactor OAuth
leonardehrenfried Mar 10, 2023
ce51a43
Move oauth into separate service
leonardehrenfried Mar 12, 2023
6586e95
Add ServicesConfiguration
leonardehrenfried Mar 13, 2023
3d64e0c
Improve car hailing configuration
leonardehrenfried Mar 13, 2023
36f8e05
Rename street mode
leonardehrenfried Mar 13, 2023
3c6e2d0
Improve package structure
leonardehrenfried Mar 13, 2023
371c252
Improve error handling of OAuth http calls
leonardehrenfried Mar 13, 2023
eb78cef
Round coordinates in arrival times cache
leonardehrenfried Mar 13, 2023
a5aad19
Add CarHailingLeg
leonardehrenfried Mar 13, 2023
6da168c
Improve OAuth handling
leonardehrenfried Mar 14, 2023
a60f89e
Add filter for fetching car hailing info
leonardehrenfried Mar 14, 2023
951c9e1
Add car hailing services to filter chain
leonardehrenfried Mar 14, 2023
905b07f
Improve Uber costs
leonardehrenfried Mar 14, 2023
1e68a25
Remove car hailing information from street leg
leonardehrenfried Mar 14, 2023
e0629ee
Add entities to GraphQL schema, rename to ride hailing
leonardehrenfried Mar 15, 2023
966dda3
Complete GraphQL API
leonardehrenfried Mar 15, 2023
11f26a5
More renamings to 'ride hailing'
leonardehrenfried Mar 15, 2023
b7f9e44
Implement ride hailing for access/egress
leonardehrenfried Mar 15, 2023
9972a26
Add estimated arrival time to the API response
leonardehrenfried Mar 16, 2023
12da130
Improve Lyft code
leonardehrenfried Mar 17, 2023
3d6843c
Remove Lyft integration
leonardehrenfried Mar 17, 2023
7e1222a
Update documentation
leonardehrenfried Mar 17, 2023
71a36ec
Remove itineraries that don't have any arrivals
leonardehrenfried Mar 17, 2023
e931ba6
Extract interface for RideHailingService
leonardehrenfried Mar 17, 2023
b82a27f
Remove unused car hailing router
leonardehrenfried Mar 17, 2023
e2618b0
Reorganise code
leonardehrenfried Mar 17, 2023
638f20b
Extract interfaces to make code more testable
leonardehrenfried Mar 17, 2023
e1365ed
Add tests
leonardehrenfried Mar 17, 2023
df739fe
Add documentation
leonardehrenfried Mar 17, 2023
ac3a249
Finishing touches to API
leonardehrenfried Mar 20, 2023
2ea8183
Extract method
leonardehrenfried Mar 20, 2023
4037d1b
Add test for filter
leonardehrenfried Mar 20, 2023
e9e85b4
Add tests and rename config classes
leonardehrenfried Mar 20, 2023
44204a5
Add more tests
leonardehrenfried Mar 20, 2023
efb1407
Merge remote-tracking branch 'upstream/dev-2.x' into car-hailing-uber
leonardehrenfried Mar 21, 2023
cebe339
Rename more identifiers from 'car hailing' to 'ride hailing'
leonardehrenfried Mar 21, 2023
d4998e9
Rename property to rideHailingEstimate
leonardehrenfried Mar 21, 2023
7bd7dec
Apply review feedback
leonardehrenfried Mar 21, 2023
81dba17
Rename rounding method
leonardehrenfried Mar 21, 2023
0365c24
Update graphql schema
leonardehrenfried Mar 22, 2023
688557e
Add example for Uber ride hailing
leonardehrenfried Mar 22, 2023
e1c6c68
Merge remote-tracking branch 'upstream/dev-2.x' into car-hailing-uber
leonardehrenfried Mar 22, 2023
3e09904
Add otp-config.json example for Portland
leonardehrenfried Mar 22, 2023
d0d813b
Shift time to earliest pickup time
leonardehrenfried Mar 24, 2023
b952777
Hook up shifting
leonardehrenfried Mar 24, 2023
5840701
Merge remote-tracking branch 'upstream/dev-2.x' into car-hailing-uber
leonardehrenfried Mar 24, 2023
4962725
Fix formatting
leonardehrenfried Mar 28, 2023
0949f79
Apply review feedback
leonardehrenfried Mar 28, 2023
5d296aa
Apply suggestions from code review
leonardehrenfried Mar 28, 2023
86d7976
Merge remote-tracking branch 'upstream/dev-2.x' into car-hailing-uber
leonardehrenfried Mar 28, 2023
0229ec1
Resolve merge conflicts
leonardehrenfried Mar 28, 2023
154b026
Apply suggestions from code review
leonardehrenfried Mar 29, 2023
ef6ef76
Apply review comments
leonardehrenfried Mar 29, 2023
bce5f76
Remove documentation from main config document
leonardehrenfried Mar 29, 2023
16e312a
Remove superfluous code
leonardehrenfried Mar 29, 2023
de42793
Merge remote-tracking branch 'upstream/dev-2.x' into car-hailing-uber
leonardehrenfried Mar 29, 2023
39cac65
Use correct provider
leonardehrenfried Mar 29, 2023
887377a
Expose the name of the ride, like UberX
leonardehrenfried Mar 30, 2023
83bc182
Incorporate review feedback
leonardehrenfried Mar 30, 2023
72e8aae
Instantiate ride hailing filter in mapper class
leonardehrenfried Mar 30, 2023
80c9def
Wire up request modifier
leonardehrenfried Mar 30, 2023
35c3b06
Add documentation for annotation
leonardehrenfried Mar 30, 2023
f99a397
Merge remote-tracking branch 'upstream/dev-2.x' into car-hailing-uber
leonardehrenfried Mar 31, 2023
52d62c3
Move instatiation into sandbox
leonardehrenfried Apr 9, 2023
0940da4
Move method
leonardehrenfried Apr 9, 2023
977c359
Merge remote-tracking branch 'upstream/dev-2.x' into car-hailing-uber
leonardehrenfried Apr 9, 2023
4d7e207
Apply suggestions from code review
leonardehrenfried Apr 11, 2023
bc7c7ff
Merge remote-tracking branch 'upstream/dev-2.x' into car-hailing-uber
leonardehrenfried Apr 12, 2023
3d23013
Update docs
leonardehrenfried Apr 12, 2023
6dccbb8
Reuse existing scalar for Duration
leonardehrenfried Apr 12, 2023
a54b42f
Extract a separate RideHailingProvider in the API
leonardehrenfried Apr 12, 2023
af5dd5e
Convert class to record
leonardehrenfried Apr 12, 2023
0dc9910
Format comment
leonardehrenfried Apr 12, 2023
1624f39
Add more deeply nested currency type
leonardehrenfried Apr 12, 2023
338655e
Rename to 'value'
leonardehrenfried Apr 12, 2023
f07d153
Rename from name to productName
leonardehrenfried Apr 12, 2023
029389d
Update src/ext/java/org/opentripplanner/ext/ridehailing/RideHailingDe…
leonardehrenfried Apr 12, 2023
84ceac7
Incorporate review feedback
leonardehrenfried Apr 12, 2023
02e09a6
Remove interface for ride hailing service config
leonardehrenfried Apr 12, 2023
88015f2
Update GraphQL schema for Money
leonardehrenfried Apr 13, 2023
60e43dd
Remove timeshifting feature
leonardehrenfried Apr 13, 2023
b534346
Rename cents to amount in Money class
leonardehrenfried Apr 14, 2023
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
17 changes: 17 additions & 0 deletions doc-templates/RideHailing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Ride hailing services

This sandbox feature allows you to use ride hailing services like Uber.

## Contact Info

- Leonard Ehrenfried, [[email protected]](mailto:[email protected])

## Configuration

In order enable this feature, add a new section `rideHailingServices` in `router-config.json`.

The supported ride-hailing providers are listed below.

### Uber

<!-- INSERT: uber-car-hailing -->
4 changes: 2 additions & 2 deletions docs/RouteRequest.md
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ The unit is cost unit per second of time difference.

**Since version:** `2.1` ∙ **Type:** `enum map of duration` ∙ **Cardinality:** `Optional`
**Path:** /routingDefaults
**Enum keys:** `not-set` | `walk` | `bike` | `bike-to-park` | `bike-rental` | `scooter-rental` | `car` | `car-to-park` | `car-pickup` | `car-rental` | `flexible`
**Enum keys:** `not-set` | `walk` | `bike` | `bike-to-park` | `bike-rental` | `scooter-rental` | `car` | `car-to-park` | `car-pickup` | `car-rental` | `car-hailing` | `flexible`

Limit access/egress per street mode.

Expand All @@ -599,7 +599,7 @@ done because some street modes searches are much more resource intensive than ot

**Since version:** `2.2` ∙ **Type:** `enum map of duration` ∙ **Cardinality:** `Optional`
**Path:** /routingDefaults
**Enum keys:** `not-set` | `walk` | `bike` | `bike-to-park` | `bike-rental` | `scooter-rental` | `car` | `car-to-park` | `car-pickup` | `car-rental` | `flexible`
**Enum keys:** `not-set` | `walk` | `bike` | `bike-to-park` | `bike-rental` | `scooter-rental` | `car` | `car-to-park` | `car-pickup` | `car-rental` | `car-hailing` | `flexible`

Limit direct route duration per street mode.

Expand Down
15 changes: 12 additions & 3 deletions docs/RouterConfiguration.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ A full list of them can be found in the [RouteRequest](RouteRequest.md).
|-------------------------------------------------------------------------------------------|:---------------------:|---------------------------------------------------------------------------------------------------|:----------:|---------------|:-----:|
| [configVersion](#configVersion) | `string` | Deployment version of the *router-config.json*. | *Optional* | | 2.1 |
| [requestLogFile](#requestLogFile) | `string` | The path of the log file for the requests. | *Optional* | | 2.0 |
| [streetRoutingTimeout](#streetRoutingTimeout) | `duration` | The maximum time a street routing request is allowed to take before returning a timeout. | *Optional* | `"PT5S"` | na |
| [streetRoutingTimeout](#streetRoutingTimeout) | `duration` | The maximum time a street routing request is allowed to take before returning a timeout. | *Optional* | `"PT5S"` | 2.2 |
| [flex](sandbox/Flex.md) | `object` | Configuration for flex routing. | *Optional* | | 2.1 |
| [rideHailingServices](sandbox/RideHailing.md) | `object[]` | Configuration for interfaces to external ride hailing services like Uber. | *Optional* | | 2.3 |
| [routingDefaults](RouteRequest.md) | `object` | The default parameters for the routing query. | *Optional* | | 2.0 |
| timetableUpdates | `object` | Global configuration for timetable updaters. | *Optional* | | 2.2 |
| [transit](#transit) | `object` | Configuration for transit searches with RAPTOR. | *Optional* | | na |
Expand All @@ -54,7 +55,7 @@ A full list of them can be found in the [RouteRequest](RouteRequest.md).
|    [pagingSearchWindowAdjustments](#transit_pagingSearchWindowAdjustments) | `duration[]` | The provided array of durations is used to increase the search-window for the next/previous page. | *Optional* | | na |
|    [stopTransferCost](#transit_stopTransferCost) | `enum map of integer` | Use this to set a stop transfer cost for the given transfer priority | *Optional* | | 2.0 |
|    [transferCacheRequests](#transit_transferCacheRequests) | `object[]` | Routing requests to use for pre-filling the stop-to-stop transfer cache. | *Optional* | | 2.3 |
| transmodelApi | `object` | Configuration for the Transmodel GraphQL API. | *Optional* | | na |
| transmodelApi | `object` | Configuration for the Transmodel GraphQL API. | *Optional* | | 2.1 |
|    [hideFeedId](#transmodelApi_hideFeedId) | `boolean` | Hide the FeedId in all API output, and add it to input. | *Optional* | `false` | na |
|    [tracingHeaderTags](#transmodelApi_tracingHeaderTags) | `string[]` | Used to group requests when monitoring OTP. | *Optional* | | na |
| [updaters](UpdaterConfig.md) | `object[]` | Configuration for the updaters that import various types of data into OTP. | *Optional* | | 1.5 |
Expand Down Expand Up @@ -127,7 +128,7 @@ number of transit vehicles used in that itinerary.

<h3 id="streetRoutingTimeout">streetRoutingTimeout</h3>

**Since version:** `na` ∙ **Type:** `duration` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"PT5S"`
**Since version:** `2.2` ∙ **Type:** `duration` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"PT5S"`
**Path:** /

The maximum time a street routing request is allowed to take before returning a timeout.
Expand Down Expand Up @@ -703,6 +704,14 @@ HTTP headers to add to the request. Any header key, value can be inserted.
"timeout" : 300000
}
}
],
"rideHailingServices" : [
{
"type" : "uber-car-hailing",
"clientId" : "secret-id",
"clientSecret" : "very-secret",
"wheelchairAccessibleRideType" : "car"
}
]
}
```
Expand Down
1 change: 1 addition & 0 deletions docs/SandboxExtension.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ nearby stops generated by routing via OSM data.
- [Travel Time (Isochrone & Surface) API](sandbox/TravelTime.md) - Travel Time API
- [IBI accessibility score](sandbox/IBIAccessibilityScore.md) - IBI accessibility score
- [Fares](sandbox/Fares.md) - Fare calculation
- [Ride hailing](sandbox/RideHailing.md) - Ride hailing services like Uber


## Terminology
Expand Down
6 changes: 6 additions & 0 deletions docs/examples/ibi/portland/otp-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"otpFeatures" : {
"SandboxAPILegacyGraphQLApi": true,
"VehicleToStopHeuristics": true
}
}
10 changes: 9 additions & 1 deletion docs/examples/ibi/portland/router-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"geoidElevation": true
},
"transit": {
"maxNumberOfTransfers" : 4
"maxNumberOfTransfers": 4
},
"updaters": [
{
Expand Down Expand Up @@ -56,5 +56,13 @@
"sourceType": "gbfs",
"url": "https://gbfs.spin.pm/api/gbfs/v2/portland"
}
],
"rideHailingServices": [
{
"type": "uber-car-hailing",
"clientId": "${UBER_CLIENT_ID}",
"clientSecret": "${UBER_CLIENT_SECRET}",
"wheelchairAccessibleRideType": "970ed376-bd33-4ad1-81c5-d2928a347a06"
}
]
}
44 changes: 44 additions & 0 deletions docs/sandbox/RideHailing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Ride hailing services

This sandbox feature allows you to use ride hailing services like Uber.

## Contact Info

- Leonard Ehrenfried, [[email protected]](mailto:[email protected])

## Configuration

In order enable this feature, add a new section `rideHailingServices` in `router-config.json`.

The supported ride-hailing providers are listed below.

### Uber

<!-- uber-car-hailing BEGIN -->
<!-- NOTE! This section is auto-generated. Do not change, change doc in code instead. -->

| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since |
|------------------------------|:--------:|----------------------------------------------------------|:----------:|---------------|:-----:|
| type = "uber-car-hailing" | `enum` | The type of the service. | *Required* | | 2.3 |
| clientId | `string` | OAuth client id to access the API. | *Required* | | 2.3 |
| clientSecret | `string` | OAuth client secret to access the API. | *Required* | | 2.3 |
| wheelchairAccessibleRideType | `string` | The id of the requested wheelchair accessible ride type. | *Required* | | 2.3 |


##### Example configuration

```JSON
// router-config.json
{
"rideHailingServices" : [
{
"type" : "uber-car-hailing",
"clientId" : "secret-id",
"clientSecret" : "very-secret",
"wheelchairAccessibleRideType" : "car"
}
]
}
```

<!-- uber-car-hailing END -->
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,4 @@ nav:
- Travel Time Isochrones: 'sandbox/TravelTime.md'
- IBI Accessibility Score: 'sandbox/IBIAccessibilityScore.md'
- Fares: 'sandbox/Fares.md'
- Ride Hailing: 'sandbox/RideHailing.md'
1 change: 0 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,6 @@
</argLine>
<!-- Jenkins needs XML test reports to determine whether the build is stable. -->
<disableXmlReport>true</disableXmlReport>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
<!-- code coverage report -->
Expand Down
3 changes: 2 additions & 1 deletion src/client/js/otp/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,5 +350,6 @@ otp.config.modes = {
"FLEX_ACCESS,FLEX_EGRESS,TRANSIT" : _tr('Transit with flex access and egress'),
//TRANSLATORS: Travel by: mode of transport (Used in selection in Travel Options widgets)
"FLEX_DIRECT" : _tr('Direct flex search'),
"CARPOOL,WALK" : _tr("Carpool")
"CARPOOL,WALK" : _tr("Carpool"),
"CAR_HAIL,TRANSIT,WALK" : _tr("Car hailing and transit")
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import static org.opentripplanner.model.plan.PlanTestConstants.B;
import static org.opentripplanner.model.plan.PlanTestConstants.C;
import static org.opentripplanner.model.plan.PlanTestConstants.D;
import static org.opentripplanner.model.plan.PlanTestConstants.D10m;
import static org.opentripplanner.model.plan.PlanTestConstants.E;
import static org.opentripplanner.model.plan.PlanTestConstants.T11_00;
import static org.opentripplanner.model.plan.PlanTestConstants.T11_01;
import static org.opentripplanner.model.plan.PlanTestConstants.T11_15;
Expand Down Expand Up @@ -109,6 +111,7 @@ class GraphQLIntegrationTest {
.walk(20, B)
.bus(122, T11_01, T11_15, C)
.rail(439, T11_30, T11_50, D)
.carHail(D10m, E)
.build();

var railLeg = i1.getTransitLeg(2);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.opentripplanner.ext.legacygraphqlapi;

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

import com.fasterxml.jackson.core.JsonProcessingException;
import graphql.schema.CoercingSerializeException;
import java.time.Duration;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.opentripplanner.framework.json.ObjectMappers;

class LegacyGraphQLScalarsTest {

@Test
void duration() {
var string = LegacyGraphQLScalars.durationScalar
.getCoercing()
.serialize(Duration.ofMinutes(30));
assertEquals("PT30M", string);
}

@Test
void nonDuration() {
Assertions.assertThrows(
CoercingSerializeException.class,
() -> LegacyGraphQLScalars.durationScalar.getCoercing().serialize(new Object())
);
}

@Test
void geoJson() throws JsonProcessingException {
var gm = new GeometryFactory();
var polygon = gm.createPolygon(
new Coordinate[] {
new Coordinate(0, 0),
new Coordinate(1, 1),
new Coordinate(2, 2),
new Coordinate(0, 0),
}
);
var geoJson = LegacyGraphQLScalars.geoJsonScalar.getCoercing().serialize(polygon);

var jsonNode = ObjectMappers
.ignoringExtraFields()
.readTree(
"{\"type\":\"Polygon\",\"coordinates\":[[[0.0,0.0],[1.0,1.0],[2.0,2.0],[0.0,0.0]]]}"
);
assertEquals(jsonNode, geoJson);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.opentripplanner.ext.ridehailing;

import static graphql.Assert.assertTrue;
import static java.time.Duration.ofMinutes;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.opentripplanner._support.time.ZoneIds;
import org.opentripplanner.model.GenericLocation;
import org.opentripplanner.routing.api.request.RequestModes;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.test.support.VariableSource;

class RideHailingDepartureTimeShifterTest {

static Instant instant = OffsetDateTime.parse("2023-03-23T17:13:46+01:00").toInstant();

RideHailingService service = new TestRideHailingService(
TestRideHailingService.DEFAULT_ARRIVAL_TIMES,
List.of()
);

static Stream<Arguments> testCases = Stream.of(
// leave now, so shift by 10 minutes
Arguments.of(instant, instant.plus(TestRideHailingService.DEFAULT_ARRIVAL_DURATION)),
Arguments.of(instant.plus(ofMinutes(15)), instant.plus(ofMinutes(15))),
// no shifting because it's in the future
Arguments.of(instant.plus(ofMinutes(30)), instant.plus(ofMinutes(30))),
Arguments.of(instant.plus(ofMinutes(40)), instant.plus(ofMinutes(40))),
// only shift by 9 minutes because we are wanting to leave in one minute
Arguments.of(instant.plus(ofMinutes(1)), instant.plus(ofMinutes(10)))
);

@ParameterizedTest
@VariableSource("testCases")
void shift(Instant searchTime, Instant expectedTimeAfterShifting) {
var req = new RouteRequest();
req.setTo(new GenericLocation(0d, 0d));
req.setFrom(new GenericLocation(0d, 0d));
req.setDateTime(searchTime);
req.journey().setModes(RequestModes.of().withAccessMode(StreetMode.CAR_HAILING).build());

var result = RideHailingDepartureTimeShifter.shiftDepartureTime(req, List.of(service), instant);

assertTrue(result.isSuccess());
var shifted = result.successValue();

var time = OffsetDateTime.ofInstant(shifted.dateTime(), ZoneIds.BERLIN);

var expectedTime = OffsetDateTime.ofInstant(expectedTimeAfterShifting, ZoneIds.BERLIN);

// start time should be shifted by 10 minutes
assertEquals(expectedTime.toString(), time.toString());
}
}
Loading