Skip to content

Commit a550a8a

Browse files
committed
Merge branch 'dev-2.x' of https://github.com/opentripplanner/OpenTripPlanner into group-area-from-designated-layer-builder
2 parents efd08be + 16108a9 commit a550a8a

File tree

10 files changed

+168
-64
lines changed

10 files changed

+168
-64
lines changed

docs/Changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle
8989
- Remove "fare" [#5645](https://github.com/opentripplanner/OpenTripPlanner/pull/5645)
9090
- Refactor GroupStopBuilder addLocation method [#5651](https://github.com/opentripplanner/OpenTripPlanner/pull/5651)
9191
- Remove `VehicleToStopHeuristics` [#5381](https://github.com/opentripplanner/OpenTripPlanner/pull/5381)
92+
- Set defaults of the modes WALK, even if one and not the others are set [#5675](https://github.com/opentripplanner/OpenTripPlanner/pull/5675)
9293
[](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE)
9394

9495
## 2.4.0 (2023-09-13)

src/ext-test/java/org/opentripplanner/ext/stopconsolidation/DecorateConsolidatedStopNamesTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ void changeNames() {
3737

3838
filter.decorate(itinerary);
3939

40-
var updatedLeg = itinerary.getLegs().get(0);
41-
assertEquals(STOP_D.getName(), updatedLeg.getFrom().name);
40+
var updatedLeg = itinerary.getLegs().getFirst();
41+
assertEquals(STOP_C.getName(), updatedLeg.getFrom().name);
4242
assertEquals(STOP_D.getName(), updatedLeg.getTo().name);
4343
}
4444
}

src/ext/java/org/opentripplanner/ext/stopconsolidation/DecorateConsolidatedStopNames.java

+29-15
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
import org.opentripplanner.routing.algorithm.filterchain.framework.spi.ItineraryDecorator;
88

99
/**
10-
* A decorating filter that checks if a transit leg contains any primary stops and if it does,
11-
* then replaces it with the secondary, agency-specific stop name. This is so that the in-vehicle
12-
* display matches what OTP returns as a board/alight stop name.
10+
* A decorating filter that checks if a transit leg contains any consolidated stops and if it does,
11+
* then replaces it with the appropriate, agency-specific stop name. This is so that the physical
12+
* signage and in-vehicle display matches what OTP returns as a board/alight stop name.
1313
*/
1414
public class DecorateConsolidatedStopNames implements ItineraryDecorator {
1515

@@ -21,31 +21,45 @@ public DecorateConsolidatedStopNames(StopConsolidationService service) {
2121

2222
@Override
2323
public void decorate(Itinerary itinerary) {
24-
replacePrimaryNamesWithSecondary(itinerary);
24+
replaceConsolidatedStops(itinerary);
2525
}
2626

2727
/**
28-
* If the itinerary has a from/to that is the primary stop of a {@link org.opentripplanner.ext.stopconsolidation.model.ConsolidatedStopGroup}
29-
* then we replace its name with the secondary name of the agency that is
30-
* operating the route, so that the name in the result matches the name in the in-vehicle
31-
* display.
28+
* If the itinerary has a "from" stop that is the secondary stop of a
29+
* {@link org.opentripplanner.ext.stopconsolidation.model.ConsolidatedStopGroup}
30+
* then we replace its name with the primary name of the agency that is
31+
* operating the route, so that the name in the result matches the physical signage on the stop.
32+
* <p>
33+
* If the leg has a "to" stop that is a primary stop, then we don't want to show the stop that's on
34+
* the signage but what is shown _inside_ the vehicle. That's why we use the agency-specific (aka
35+
* secondary) stop.
36+
* <p>
37+
* This follows the somewhat idiosyncratic logic of the consolidated stops feature.
3238
*/
33-
private void replacePrimaryNamesWithSecondary(Itinerary i) {
39+
private void replaceConsolidatedStops(Itinerary i) {
3440
i.transformTransitLegs(leg -> {
3541
if (leg instanceof ScheduledTransitLeg stl && needsToRenameStops(stl)) {
3642
var agency = leg.getAgency();
37-
return new ConsolidatedStopLeg(
38-
stl,
39-
service.agencySpecificName(stl.getFrom().stop, agency),
40-
service.agencySpecificName(stl.getTo().stop, agency)
41-
);
43+
// to show the name on the stop signage we use the primary stop's name
44+
var from = service.primaryStop(stl.getFrom().stop.getId());
45+
// to show the name that's on the display inside the vehicle we use the agency-specific name
46+
var to = service.agencySpecificStop(stl.getTo().stop, agency);
47+
return new ConsolidatedStopLeg(stl, from, to);
4248
} else {
4349
return leg;
4450
}
4551
});
4652
}
4753

54+
/**
55+
* Figures out if the from/to stops are part of a consolidated stop group and therefore
56+
* some stops need to be replaced.
57+
* <p>
58+
* Please consult the Javadoc of {@link DecorateConsolidatedStopNames#replaceConsolidatedStops(Itinerary)}
59+
* for details of this idiosyncratic business logic and in particular why the logic is not the same
60+
* for the from/to stops.
61+
*/
4862
private boolean needsToRenameStops(ScheduledTransitLeg stl) {
49-
return (service.isPrimaryStop(stl.getFrom().stop) || service.isPrimaryStop(stl.getTo().stop));
63+
return (service.isSecondaryStop(stl.getFrom().stop) || service.isPrimaryStop(stl.getTo().stop));
5064
}
5165
}

src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationService.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import java.util.List;
44
import org.opentripplanner.ext.stopconsolidation.model.StopReplacement;
5-
import org.opentripplanner.framework.i18n.I18NString;
65
import org.opentripplanner.transit.model.framework.FeedScopedId;
76
import org.opentripplanner.transit.model.organization.Agency;
87
import org.opentripplanner.transit.model.site.StopLocation;
@@ -24,13 +23,23 @@ public interface StopConsolidationService {
2423
*/
2524
boolean isPrimaryStop(StopLocation stop);
2625

26+
/**
27+
* Is the given stop a secondary stop as defined by the stop consolidation configuration?
28+
*/
29+
boolean isSecondaryStop(StopLocation stop);
30+
2731
/**
2832
* Are any stop consolidations defined?
2933
*/
3034
boolean isActive();
3135

3236
/**
33-
* For a given primary stop look up the name as it was originally defined in the agency's feed.
37+
* For a given primary stop look up secondary feed as it was originally defined in the agency's feed.
38+
*/
39+
StopLocation agencySpecificStop(StopLocation stop, Agency agency);
40+
41+
/**
42+
* For a given stop id return the primary stop if it is part of a consolidated stop group.
3443
*/
35-
I18NString agencySpecificName(StopLocation stop, Agency agency);
44+
StopLocation primaryStop(FeedScopedId id);
3645
}

src/ext/java/org/opentripplanner/ext/stopconsolidation/internal/DefaultStopConsolidationService.java

+35-14
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
import java.util.List;
44
import java.util.Objects;
5+
import java.util.Optional;
56
import java.util.stream.Stream;
7+
import javax.annotation.Nonnull;
68
import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository;
79
import org.opentripplanner.ext.stopconsolidation.StopConsolidationService;
10+
import org.opentripplanner.ext.stopconsolidation.model.ConsolidatedStopGroup;
811
import org.opentripplanner.ext.stopconsolidation.model.StopReplacement;
9-
import org.opentripplanner.framework.i18n.I18NString;
1012
import org.opentripplanner.transit.model.framework.FeedScopedId;
1113
import org.opentripplanner.transit.model.organization.Agency;
12-
import org.opentripplanner.transit.model.site.RegularStop;
1314
import org.opentripplanner.transit.model.site.StopLocation;
1415
import org.opentripplanner.transit.service.TransitModel;
1516
import org.slf4j.Logger;
@@ -61,26 +62,46 @@ public boolean isPrimaryStop(StopLocation stop) {
6162
return repo.groups().stream().anyMatch(r -> r.primary().equals(stop.getId()));
6263
}
6364

65+
@Override
66+
public boolean isSecondaryStop(StopLocation stop) {
67+
return repo.groups().stream().anyMatch(r -> r.secondaries().contains(stop.getId()));
68+
}
69+
6470
@Override
6571
public boolean isActive() {
6672
return !repo.groups().isEmpty();
6773
}
6874

6975
@Override
70-
public I18NString agencySpecificName(StopLocation stop, Agency agency) {
76+
public StopLocation agencySpecificStop(StopLocation stop, Agency agency) {
7177
if (agency.getId().getFeedId().equals(stop.getId().getFeedId())) {
72-
return stop.getName();
78+
return stop;
7379
} else {
74-
return repo
75-
.groups()
76-
.stream()
77-
.filter(r -> r.primary().equals(stop.getId()))
78-
.flatMap(g -> g.secondaries().stream())
79-
.filter(secondary -> secondary.getFeedId().equals(agency.getId().getFeedId()))
80-
.findAny()
81-
.map(id -> transitModel.getStopModel().getRegularStop(id))
82-
.map(RegularStop::getName)
83-
.orElseGet(stop::getName);
80+
return findAgencySpecificStop(stop, agency).orElse(stop);
8481
}
8582
}
83+
84+
@Nonnull
85+
private Optional<StopLocation> findAgencySpecificStop(StopLocation stop, Agency agency) {
86+
return repo
87+
.groups()
88+
.stream()
89+
.filter(r -> r.primary().equals(stop.getId()))
90+
.flatMap(g -> g.secondaries().stream())
91+
.filter(secondary -> secondary.getFeedId().equals(agency.getId().getFeedId()))
92+
.findAny()
93+
.map(id -> transitModel.getStopModel().getRegularStop(id));
94+
}
95+
96+
@Override
97+
public StopLocation primaryStop(FeedScopedId id) {
98+
var primaryId = repo
99+
.groups()
100+
.stream()
101+
.filter(g -> g.secondaries().contains(id))
102+
.map(ConsolidatedStopGroup::primary)
103+
.findAny()
104+
.orElse(id);
105+
return transitModel.getStopModel().getRegularStop(primaryId);
106+
}
86107
}
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
package org.opentripplanner.ext.stopconsolidation.model;
22

3-
import org.opentripplanner.framework.i18n.I18NString;
43
import org.opentripplanner.model.plan.Place;
54
import org.opentripplanner.model.plan.ScheduledTransitLeg;
65
import org.opentripplanner.model.plan.ScheduledTransitLegBuilder;
6+
import org.opentripplanner.transit.model.site.StopLocation;
77

88
public class ConsolidatedStopLeg extends ScheduledTransitLeg {
99

10-
private final I18NString fromName;
11-
private final I18NString toName;
10+
private final StopLocation from;
11+
private final StopLocation to;
1212

13-
public ConsolidatedStopLeg(ScheduledTransitLeg original, I18NString fromName, I18NString toName) {
13+
public ConsolidatedStopLeg(ScheduledTransitLeg original, StopLocation from, StopLocation to) {
1414
super(new ScheduledTransitLegBuilder<>(original));
15-
this.fromName = fromName;
16-
this.toName = toName;
15+
this.from = from;
16+
this.to = to;
1717
}
1818

1919
@Override
2020
public Place getFrom() {
21-
return Place.forStop(super.getFrom().stop, fromName);
21+
return Place.forStop(from);
2222
}
2323

2424
@Override
2525
public Place getTo() {
26-
return Place.forStop(super.getTo().stop, toName);
26+
return Place.forStop(to);
2727
}
2828
}

src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java

+20-13
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,30 @@
77

88
class RequestModesMapper {
99

10+
private static final String accessModeKey = "accessMode";
11+
private static final String egressModeKey = "egressMode";
12+
private static final String directModeKey = "directMode";
13+
1014
/**
11-
* Maps a GraphQL Modes input type to a RequestModes.
12-
*
13-
* This only maps access, egress, direct & transfer.
14-
* Transport modes are now part of filters.
15-
* Only in case filters are not present we will use this mapping
15+
* Maps GraphQL Modes input type to RequestModes.
16+
* <p>
17+
* This only maps access, egress, direct & transfer modes. Transport modes are set using filters.
18+
* Default modes are WALK for access, egress, direct & transfer.
1619
*/
17-
@SuppressWarnings("unchecked")
1820
static RequestModes mapRequestModes(Map<String, ?> modesInput) {
19-
StreetMode accessMode = (StreetMode) modesInput.get("accessMode");
20-
RequestModesBuilder mBuilder = RequestModes
21-
.of()
22-
.withAccessMode(accessMode)
23-
.withEgressMode((StreetMode) modesInput.get("egressMode"))
24-
.withDirectMode((StreetMode) modesInput.get("directMode"));
21+
RequestModesBuilder mBuilder = RequestModes.of();
2522

26-
mBuilder.withTransferMode(accessMode == StreetMode.BIKE ? StreetMode.BIKE : StreetMode.WALK);
23+
if (modesInput.containsKey(accessModeKey)) {
24+
StreetMode accessMode = (StreetMode) modesInput.get(accessModeKey);
25+
mBuilder.withAccessMode(accessMode);
26+
mBuilder.withTransferMode(accessMode == StreetMode.BIKE ? StreetMode.BIKE : StreetMode.WALK);
27+
}
28+
if (modesInput.containsKey(egressModeKey)) {
29+
mBuilder.withEgressMode((StreetMode) modesInput.get(egressModeKey));
30+
}
31+
if (modesInput.containsKey(directModeKey)) {
32+
mBuilder.withDirectMode((StreetMode) modesInput.get(directModeKey));
33+
}
2734

2835
return mBuilder.build();
2936
}

src/main/java/org/opentripplanner/model/plan/Place.java

+1-5
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,7 @@ public static Place normal(Vertex vertex, I18NString name) {
9090
}
9191

9292
public static Place forStop(StopLocation stop) {
93-
return forStop(stop, stop.getName());
94-
}
95-
96-
public static Place forStop(StopLocation stop, I18NString nameOverride) {
97-
return new Place(nameOverride, stop.getCoordinate(), VertexType.TRANSIT, stop, null, null);
93+
return new Place(stop.getName(), stop.getCoordinate(), VertexType.TRANSIT, stop, null, null);
9894
}
9995

10096
public static Place forFlexStop(StopLocation stop, Vertex vertex) {

src/main/java/org/opentripplanner/transit/model/site/GroupStop.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,12 @@ public Geometry getGeometry() {
9494

9595
/**
9696
* Returns the geometry of the area that encompasses the bounds of this StopLocation group. If the
97-
* group is defined only as a list of stops, this will return the same as getGeometry. If on the
98-
* other hand the group is defined as an area and the stops are inferred from that area, then this
99-
* will return the geometry of the area.
97+
* group is defined as all the stops within an area, then this will return the geometry of the
98+
* area. If the group is defined simply as a list of stops, this will return an empty optional.
10099
*/
101100
@Override
102101
public Optional<? extends Geometry> getEncompassingAreaGeometry() {
103-
return Optional.ofNullable(encompassingAreaGeometry).or(() -> Optional.of(geometry));
102+
return Optional.ofNullable(encompassingAreaGeometry);
104103
}
105104

106105
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.opentripplanner.apis.transmodel.mapping;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import java.util.Map;
6+
import org.junit.jupiter.api.Test;
7+
import org.opentripplanner.routing.api.request.RequestModes;
8+
import org.opentripplanner.routing.api.request.StreetMode;
9+
10+
class RequestModesMapperTest {
11+
12+
@Test
13+
void testMapRequestModesEmptyMapReturnsDefaults() {
14+
Map<String, StreetMode> inputModes = Map.of();
15+
16+
RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes);
17+
18+
assertEquals(RequestModes.of().build(), mappedModes);
19+
}
20+
21+
@Test
22+
void testMapRequestModesAccessSetReturnsDefaultsForOthers() {
23+
Map<String, StreetMode> inputModes = Map.of("accessMode", StreetMode.BIKE);
24+
25+
RequestModes wantModes = RequestModes
26+
.of()
27+
.withAccessMode(StreetMode.BIKE)
28+
.withTransferMode(StreetMode.BIKE)
29+
.build();
30+
31+
RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes);
32+
33+
assertEquals(wantModes, mappedModes);
34+
}
35+
36+
@Test
37+
void testMapRequestModesEgressSetReturnsDefaultsForOthers() {
38+
Map<String, StreetMode> inputModes = Map.of("egressMode", StreetMode.CAR);
39+
40+
RequestModes wantModes = RequestModes.of().withEgressMode(StreetMode.CAR).build();
41+
42+
RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes);
43+
44+
assertEquals(wantModes, mappedModes);
45+
}
46+
47+
@Test
48+
void testMapRequestModesDirectSetReturnsDefaultsForOthers() {
49+
Map<String, StreetMode> inputModes = Map.of("directMode", StreetMode.CAR);
50+
51+
RequestModes wantModes = RequestModes.of().withDirectMode(StreetMode.CAR).build();
52+
53+
RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes);
54+
55+
assertEquals(wantModes, mappedModes);
56+
}
57+
}

0 commit comments

Comments
 (0)