Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/dev-2.x' into upstream-merge-2…
Browse files Browse the repository at this point in the history
…023-11-14
  • Loading branch information
leonardehrenfried committed Nov 22, 2023
2 parents 8ca02e5 + 8efb874 commit e5652eb
Show file tree
Hide file tree
Showing 139 changed files with 17,884 additions and 15,149 deletions.
56 changes: 56 additions & 0 deletions doc-templates/StopConsolidation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!--
NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc.
- Template directory is: /doc-templates
- Generated directory is: /docs
-->
# Stop consolidation

## Contact Info

- [Jon Campbell](mailto:[email protected]), Arcadis, USA

## Feature explanation

This sandbox feature allows you to "combine" equivalent stops from across several feeds into a single,
consolidated one.

It is achieved by defining a "primary" stop and one or more "secondary" stops. During the graph
build all trip patterns are modified so that the secondary ones are swapped out for their
primary equivalent.

## Effects

This has the following consequences

- When you query the departures for a primary stop you see a consolidated view of all the equivalent departures.
- When transferring at a consolidated stop you no longer get instructions like "walk 5 meters to stop X"

!!! warning "Downsides"

However, this feature has also severe downsides:

- It makes realtime trip updates referencing a stop id much more complicated and in many cases
impossible to resolve.
You can only reference a stop by its sequence, which only works in GTFS-RT, not Siri.
- Fare calculation and transfers are unlikely to work as expected.


## Configuration

To enable this feature you need to add a file to OTP's working directory and configure
its name like this:

<!-- INSERT: config -->

The additional config file must look like the following:

<!-- INSERT: file -->

The column names mean the following:

- `stop_group_id`: id to group several rows in the file together
- `feed_id`: feed id of the stop
- `stop_id`: id of the stop
- `is_primary`: whether the row represents a primary stop, `1` means yes and `0` means no

2 changes: 2 additions & 0 deletions docs/BuildConfiguration.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Sections follow that describe particular settings in more depth.
| [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 |
| staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 |
| staticParkAndRide | `boolean` | Whether we should create car P+R stations from OSM data. | *Optional* | `true` | 1.5 |
| stopConsolidationFile | `string` | Name of the CSV-formatted file in the build directory which contains the configuration for stop consolidation. | *Optional* | | 2.5 |
| [streetGraph](#streetGraph) | `uri` | URI to the street graph object file for reading and writing. | *Optional* | | 2.0 |
| [subwayAccessTime](#subwayAccessTime) | `double` | Minutes necessary to reach stops served by trips on routes of route_type=1 (subway) from the street. | *Optional* | `2.0` | 1.5 |
| [transitModelTimeZone](#transitModelTimeZone) | `time-zone` | Time zone for the graph. | *Optional* | | 2.2 |
Expand Down Expand Up @@ -1165,6 +1166,7 @@ case where this is not the case.
}
}
],
"stopConsolidationFile" : "consolidated-stops.csv",
"emissions" : {
"carAvgCo2PerKm" : 170,
"carAvgOccupancy" : 1.3
Expand Down
5 changes: 5 additions & 0 deletions docs/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle
- Fix serialization of `AtomicInteger` [#5508](https://github.com/opentripplanner/OpenTripPlanner/pull/5508)
- Improve linking of fixed stops used by flex trips [#5503](https://github.com/opentripplanner/OpenTripPlanner/pull/5503)
- Keep min transfer filter is not local to group-by-filters [#5436](https://github.com/opentripplanner/OpenTripPlanner/pull/5436)
- Add paging deduplication when cropping. [#5458](https://github.com/opentripplanner/OpenTripPlanner/pull/5458)
- Consolidate equivalent stops from several feeds [#5429](https://github.com/opentripplanner/OpenTripPlanner/pull/5429)
- Check transport mode when mapping GroupStops [#5518](https://github.com/opentripplanner/OpenTripPlanner/pull/5518)
- Cleanup trip times - Part A [#5437](https://github.com/opentripplanner/OpenTripPlanner/pull/5437)
- Transfer cost limit [#5516](https://github.com/opentripplanner/OpenTripPlanner/pull/5516)
[](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE)

## 2.4.0 (2023-09-13)
Expand Down
5 changes: 5 additions & 0 deletions docs/examples/ibi/seattle/build-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"transitModelTimeZone": "America/Los_Angeles",
"fares": "orca",
"stopConsolidationFile": "consolidated-stops.csv"
}
78 changes: 78 additions & 0 deletions docs/sandbox/StopConsolidation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<!--
NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc.
- Template directory is: /doc-templates
- Generated directory is: /docs
-->
# Stop consolidation

## Contact Info

- [Jon Campbell](mailto:[email protected]), Arcadis, USA

## Feature explanation

This sandbox feature allows you to "combine" equivalent stops from across several feeds into a single,
consolidated one.

It is achieved by defining a "primary" stop and one or more "secondary" stops. During the graph
build all trip patterns are modified so that the secondary ones are swapped out for their
primary equivalent.

## Effects

This has the following consequences

- When you query the departures for a primary stop you see a consolidated view of all the equivalent departures.
- When transferring at a consolidated stop you no longer get instructions like "walk 5 meters to stop X"

!!! warning "Downsides"

However, this feature has also severe downsides:

- It makes realtime trip updates referencing a stop id much more complicated and in many cases
impossible to resolve.
You can only reference a stop by its sequence, which only works in GTFS-RT, not Siri.
- Fare calculation and transfers are unlikely to work as expected.


## Configuration

To enable this feature you need to add a file to OTP's working directory and configure
its name like this:

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

```JSON
// build-config.json
{
"stopConsolidationFile" : "consolidated-stops.csv"
}
```

<!-- config END -->

The additional config file must look like the following:

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

```
stop_group_id,feed_id,stop_id,is_primary
1,pierce,1705867009,0
1,kcm,10225,1
2,pierce,1569,0
2,commtrans,473,0
2,kcm,1040,1
```

<!-- file END -->

The column names mean the following:

- `stop_group_id`: id to group several rows in the file together
- `feed_id`: feed id of the stop
- `stop_id`: id of the stop
- `is_primary`: whether the row represents a primary stop, `1` means yes and `0` means no

2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ markdown_extensions:
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
- admonition

# MkDocs will automatically discover pages if you don't list them here.
# In that case subdirectories can be used to organize pages.
Expand Down Expand Up @@ -112,3 +113,4 @@ nav:
- Fares: 'sandbox/Fares.md'
- Ride Hailing: 'sandbox/RideHailing.md'
- Emissions: 'sandbox/Emissions.md'
- Stop Consolidation: 'sandbox/StopConsolidation.md'
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@


<properties>
<otp.serialization.version.id>128</otp.serialization.version.id>
<otp.serialization.version.id>130</otp.serialization.version.id>
<!-- Lib versions - keep list sorted on property name -->
<geotools.version>30.0</geotools.version>
<google.dagger.version>2.48.1</google.dagger.version>
<jackson.version>2.15.3</jackson.version>
<jackson.version>2.16.0</jackson.version>
<jersey.version>3.1.3</jersey.version>
<junit.version>5.10.1</junit.version>
<micrometer.version>1.11.5</micrometer.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.network.Route;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.TripTimes;
import org.opentripplanner.transit.model.timetable.TripTimesFactory;

class EmissionsTest {

Expand Down Expand Up @@ -130,7 +130,7 @@ private ScheduledTransitLeg createTransitLeg(Route route) {
.withRoute(route)
.build();
return new ScheduledTransitLegBuilder<>()
.withTripTimes(new TripTimes(trip, stopTimes, new Deduplicator()))
.withTripTimes(TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()))
.withTripPattern(pattern)
.withBoardStopIndexInPattern(0)
.withAlightStopIndexInPattern(2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.model.site.StopLocation;

public class UnscheduledTripTest {
class UnscheduledTripTest {

private static final int STOP_A = 0;
private static final int STOP_B = 1;
Expand Down Expand Up @@ -620,8 +620,8 @@ void accessTemplatesNoAlighting() {
.of(0, 1)
.forEach(index -> {
var template = templates.get(index);
assertEquals(template.fromStopIndex, 0);
assertEquals(template.toStopIndex, index + 2);
assertEquals(0, template.fromStopIndex);
assertEquals(index + 2, template.toStopIndex);
});
}

Expand All @@ -633,8 +633,8 @@ void egressTemplates() {

assertEquals(4, templates.size());
var template = templates.get(0);
assertEquals(template.fromStopIndex, 3);
assertEquals(template.toStopIndex, 0);
assertEquals(0, template.fromStopIndex);
assertEquals(3, template.toStopIndex);
}

@Nonnull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ private static TripPattern delay(TripPattern pattern1, int seconds) {
}

private static TripTimes delay(TripTimes tt, int seconds) {
var delayed = new TripTimes(tt);
var delayed = tt.copyOfScheduledTimes();
IntStream
.range(0, delayed.getNumStops())
.forEach(i -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.opentripplanner.transit.model.timetable.RealTimeState;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.TripTimes;
import org.opentripplanner.transit.model.timetable.TripTimesFactory;
import org.opentripplanner.transit.service.DefaultTransitService;
import org.opentripplanner.transit.service.StopModel;
import org.opentripplanner.transit.service.TransitModel;
Expand Down Expand Up @@ -111,7 +112,7 @@ class ModifiedTripBuilderTest {
STOP_TIME_C_1.setStopSequence(1);
}

private static final TripTimes TRIP_TIMES = new TripTimes(
private static final TripTimes TRIP_TIMES = TripTimesFactory.tripTimes(
TRIP,
List.of(STOP_TIME_A_1, STOP_TIME_B_1, STOP_TIME_C_1),
DEDUPLICATOR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.opentripplanner.transit.model.timetable.OccupancyStatus;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.TripTimes;
import org.opentripplanner.transit.model.timetable.TripTimesFactory;
import org.opentripplanner.transit.service.StopModel;
import uk.org.siri.siri20.OccupancyEnumeration;

Expand Down Expand Up @@ -74,7 +75,7 @@ public void setUp() {
.build();

Trip trip = Trip.of(new FeedScopedId(FEED_ID, "TRIP_ID")).withRoute(route).build();
tripTimes = new TripTimes(trip, List.of(stopTime), new Deduplicator());
tripTimes = TripTimesFactory.tripTimes(trip, List.of(stopTime), new Deduplicator());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.opentripplanner.ext.stopconsolidation;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.opentripplanner.ext.stopconsolidation.TestStopConsolidationModel.STOP_C;
import static org.opentripplanner.ext.stopconsolidation.TestStopConsolidationModel.STOP_D;
import static org.opentripplanner.model.plan.PlanTestConstants.T11_05;
import static org.opentripplanner.model.plan.PlanTestConstants.T11_12;

import java.util.List;
import org.junit.jupiter.api.Test;
import org.opentripplanner.ext.stopconsolidation.internal.DefaultStopConsolidationRepository;
import org.opentripplanner.ext.stopconsolidation.internal.DefaultStopConsolidationService;
import org.opentripplanner.ext.stopconsolidation.model.ConsolidatedStopGroup;
import org.opentripplanner.model.plan.Place;
import org.opentripplanner.model.plan.PlanTestConstants;
import org.opentripplanner.model.plan.TestItineraryBuilder;

class ConsolidatedStopNameFilterTest {

@Test
void changeNames() {
var transitModel = TestStopConsolidationModel.buildTransitModel();

var groups = List.of(new ConsolidatedStopGroup(STOP_C.getId(), List.of(STOP_D.getId())));
var repo = new DefaultStopConsolidationRepository();
repo.addGroups(groups);

var service = new DefaultStopConsolidationService(repo, transitModel);
var filter = new ConsolidatedStopNameFilter(service);

var itinerary = TestItineraryBuilder
.newItinerary(Place.forStop(STOP_C))
.bus(TestStopConsolidationModel.ROUTE, 1, T11_05, T11_12, Place.forStop(STOP_C))
.bus(1, T11_05, T11_12, PlanTestConstants.E)
.bus(1, T11_05, T11_12, PlanTestConstants.F)
.build();

var filtered = filter.filter(List.of(itinerary));
assertFalse(filtered.isEmpty());

var updatedLeg = filtered.get(0).getLegs().get(0);
assertEquals(STOP_D.getName(), updatedLeg.getFrom().name);
assertEquals(STOP_D.getName(), updatedLeg.getTo().name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.opentripplanner.ext.stopconsolidation;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.opentripplanner.ext.stopconsolidation.TestStopConsolidationModel.PATTERN;
import static org.opentripplanner.ext.stopconsolidation.TestStopConsolidationModel.STOP_A;
import static org.opentripplanner.ext.stopconsolidation.TestStopConsolidationModel.STOP_B;
import static org.opentripplanner.ext.stopconsolidation.TestStopConsolidationModel.STOP_C;
import static org.opentripplanner.ext.stopconsolidation.TestStopConsolidationModel.STOP_D;

import java.util.List;
import org.junit.jupiter.api.Test;
import org.opentripplanner.ext.stopconsolidation.internal.DefaultStopConsolidationRepository;
import org.opentripplanner.ext.stopconsolidation.model.ConsolidatedStopGroup;
import org.opentripplanner.transit.model.network.TripPattern;

class StopConsolidationModuleTest {

@Test
void replacePatterns() {
var groups = List.of(new ConsolidatedStopGroup(STOP_D.getId(), List.of(STOP_B.getId())));

var transitModel = TestStopConsolidationModel.buildTransitModel();
transitModel.addTripPattern(PATTERN.getId(), PATTERN);
var repo = new DefaultStopConsolidationRepository();
var module = new StopConsolidationModule(transitModel, repo, groups);
module.buildGraph();

var modifiedPattern = transitModel.getTripPatternForId(PATTERN.getId());
assertFalse(modifiedPattern.getRoutingTripPattern().getPattern().sameAs(PATTERN));
assertFalse(modifiedPattern.sameAs(PATTERN));

var modifiedStop = modifiedPattern
.getRoutingTripPattern()
.getPattern()
.getStopPattern()
.getStop(1);
assertEquals(modifiedStop, STOP_D);

var patterns = List.copyOf(transitModel.getAllTripPatterns());

var stops = patterns.stream().map(TripPattern::getStops).toList();
assertEquals(List.of(List.of(STOP_A, STOP_D, STOP_C)), stops);
}
}
Loading

0 comments on commit e5652eb

Please sign in to comment.