Skip to content

Commit

Permalink
Merge pull request #5407 from entur/fix_leg_validation
Browse files Browse the repository at this point in the history
Add validation for scheduled transit leg reference
  • Loading branch information
vpaturet authored Oct 13, 2023
2 parents 25401df + 1583f4d commit 87a4f19
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.opentripplanner.model.plan.legreference;

import javax.annotation.Nullable;
import org.opentripplanner.model.plan.Leg;
import org.opentripplanner.transit.service.TransitService;

/**
* Marker interface for various types of leg references
*/
public interface LegReference {
@Nullable
Leg getLeg(TransitService transitService);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.time.LocalDate;
import java.time.ZoneId;
import javax.annotation.Nullable;
import org.opentripplanner.framework.time.ServiceDateUtils;
import org.opentripplanner.model.Timetable;
import org.opentripplanner.model.plan.ScheduledTransitLeg;
Expand All @@ -11,10 +12,12 @@
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.TripTimes;
import org.opentripplanner.transit.service.TransitService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A reference which can be used to rebuild an exact copy of a {@link ScheduledTransitLeg} using the
* {@Link RoutingService}
* {@link org.opentripplanner.routing.api.RoutingService}
*/
public record ScheduledTransitLegReference(
FeedScopedId tripId,
Expand All @@ -23,30 +26,71 @@ public record ScheduledTransitLegReference(
int toStopPositionInPattern
)
implements LegReference {
private static final Logger LOG = LoggerFactory.getLogger(ScheduledTransitLegReference.class);

/**
* Reconstruct a scheduled transit leg from this scheduled transit leg reference.
* Since the transit model could have been modified between the time the reference is created
* and the time the transit leg is reconstructed (either because new planned data have been
* rolled out, or because a realtime update has modified a trip),
* it may not be possible to reconstruct the leg.
* In this case the method returns null.
*/
@Override
@Nullable
public ScheduledTransitLeg getLeg(TransitService transitService) {
Trip trip = transitService.getTripForId(tripId);

if (trip == null) {
LOG.info("Invalid transit leg reference: trip {} not found", tripId);
return null;
}

TripPattern tripPattern = transitService.getPatternForTrip(trip, serviceDate);

// no matching pattern found anywhere
if (tripPattern == null) {
LOG.info(
"Invalid transit leg reference: trip pattern not found for trip {} and service date {} ",
tripId,
serviceDate
);
return null;
}

Timetable timetable = transitService.getTimetableForTripPattern(tripPattern, serviceDate);
int numStops = tripPattern.numberOfStops();
if (fromStopPositionInPattern >= numStops || toStopPositionInPattern >= numStops) {
LOG.info(
"Invalid transit leg reference: boarding stop {} or alighting stop {} is out of range" +
" in trip {} and service date {} ({} stops in trip pattern) ",
fromStopPositionInPattern,
toStopPositionInPattern,
tripId,
serviceDate,
numStops
);
return null;
}

Timetable timetable = transitService.getTimetableForTripPattern(tripPattern, serviceDate);
TripTimes tripTimes = timetable.getTripTimes(trip);

if (tripTimes == null) {
LOG.info(
"Invalid transit leg reference: trip times not found for trip {} and service date {} ",
tripId,
serviceDate
);
return null;
}

if (
!transitService
.getServiceCodesRunningForDate(serviceDate)
.contains(tripTimes.getServiceCode())
) {
LOG.info(
"Invalid transit leg reference: the trip {} does not run on service date {} ",
tripId,
serviceDate
);
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package org.opentripplanner.model.plan.legreference;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.opentripplanner.transit.model._data.TransitModelForTest.id;

import java.time.LocalDate;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore;
import org.opentripplanner.model.Timetable;
import org.opentripplanner.model.calendar.CalendarServiceData;
import org.opentripplanner.model.plan.PlanTestConstants;
import org.opentripplanner.model.plan.ScheduledTransitLeg;
import org.opentripplanner.transit.model._data.TransitModelForTest;
import org.opentripplanner.transit.model.framework.Deduplicator;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.TripTimes;
import org.opentripplanner.transit.service.DefaultTransitService;
import org.opentripplanner.transit.service.StopModel;
import org.opentripplanner.transit.service.TransitModel;
import org.opentripplanner.transit.service.TransitService;

class ScheduledTransitLegReferenceTest {

private static final int SERVICE_CODE = 555;
private static final LocalDate SERVICE_DATE = LocalDate.of(2023, 1, 1);
private static final int NUMBER_OF_STOPS = 3;
private static TransitService transitService;
private static FeedScopedId tripId;

@BeforeAll
static void buildTransitService() {
// build transit data
TripPattern tripPattern = TransitModelForTest
.tripPattern("1", TransitModelForTest.route(id("1")).build())
.withStopPattern(TransitModelForTest.stopPattern(NUMBER_OF_STOPS))
.build();
Timetable timetable = tripPattern.getScheduledTimetable();
Trip trip = TransitModelForTest.trip("1").build();
tripId = trip.getId();
TripTimes tripTimes = new TripTimes(
trip,
TransitModelForTest.stopTimesEvery5Minutes(5, trip, PlanTestConstants.T11_00),
new Deduplicator()
);
tripTimes.setServiceCode(SERVICE_CODE);
timetable.addTripTimes(tripTimes);

// build transit model
TransitModel transitModel = new TransitModel(StopModel.of().build(), new Deduplicator());
transitModel.addTripPattern(tripPattern.getId(), tripPattern);
transitModel.getServiceCodes().put(tripPattern.getId(), SERVICE_CODE);
CalendarServiceData calendarServiceData = new CalendarServiceData();
calendarServiceData.putServiceDatesForServiceId(tripPattern.getId(), List.of(SERVICE_DATE));
transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP);
transitModel.index();

// build transit service
transitService = new DefaultTransitService(transitModel);
}

@Test
void getLegFromReference() {
int boardAtStop = 0;
int alightAtStop = 1;
ScheduledTransitLegReference scheduledTransitLegReference = new ScheduledTransitLegReference(
tripId,
SERVICE_DATE,
boardAtStop,
alightAtStop
);
ScheduledTransitLeg leg = scheduledTransitLegReference.getLeg(transitService);
assertNotNull(leg);
assertEquals(tripId, leg.getTrip().getId());
assertEquals(SERVICE_DATE, leg.getServiceDate());
assertEquals(boardAtStop, leg.getBoardStopPosInPattern());
assertEquals(alightAtStop, leg.getAlightStopPosInPattern());
}

@Test
void getLegFromReferenceUnknownTrip() {
ScheduledTransitLegReference scheduledTransitLegReference = new ScheduledTransitLegReference(
FeedScopedId.ofNullable("XXX", "YYY"),
SERVICE_DATE,
0,
1
);
assertNull(scheduledTransitLegReference.getLeg(transitService));
}

@Test
void getLegFromReferenceInvalidServiceDate() {
ScheduledTransitLegReference scheduledTransitLegReference = new ScheduledTransitLegReference(
tripId,
LocalDate.EPOCH,
0,
1
);
assertNull(scheduledTransitLegReference.getLeg(transitService));
}

@Test
void getLegFromReferenceInvalidBoardingStop() {
ScheduledTransitLegReference scheduledTransitLegReference = new ScheduledTransitLegReference(
tripId,
SERVICE_DATE,
NUMBER_OF_STOPS,
1
);
assertNull(scheduledTransitLegReference.getLeg(transitService));
}

@Test
void getLegFromReferenceInvalidAlightingStop() {
ScheduledTransitLegReference scheduledTransitLegReference = new ScheduledTransitLegReference(
tripId,
SERVICE_DATE,
0,
NUMBER_OF_STOPS
);
assertNull(scheduledTransitLegReference.getLeg(transitService));
}
}

0 comments on commit 87a4f19

Please sign in to comment.