
org.opentripplanner.netex.mapping.StopTimesMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
The newest version!
package org.opentripplanner.netex.mapping;
import static org.opentripplanner.model.PickDrop.COORDINATE_WITH_DRIVER;
import static org.opentripplanner.model.PickDrop.NONE;
import static org.opentripplanner.model.PickDrop.SCHEDULED;
import jakarta.xml.bind.JAXBElement;
import java.math.BigInteger;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.opentripplanner.framework.i18n.I18NString;
import org.opentripplanner.framework.i18n.NonLocalizedString;
import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore;
import org.opentripplanner.model.StopTime;
import org.opentripplanner.netex.index.api.ReadOnlyHierarchicalMap;
import org.opentripplanner.netex.index.api.ReadOnlyHierarchicalMapById;
import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory;
import org.opentripplanner.netex.support.JourneyPatternHelper;
import org.opentripplanner.transit.model.framework.ImmutableEntityById;
import org.opentripplanner.transit.model.site.AreaStop;
import org.opentripplanner.transit.model.site.GroupStop;
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.model.site.StopLocation;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.booking.BookingInfo;
import org.rutebanken.netex.model.DestinationDisplay;
import org.rutebanken.netex.model.DestinationDisplay_VersionStructure;
import org.rutebanken.netex.model.FlexibleLine;
import org.rutebanken.netex.model.JourneyPattern_VersionStructure;
import org.rutebanken.netex.model.LineRefStructure;
import org.rutebanken.netex.model.MultilingualString;
import org.rutebanken.netex.model.PointInLinkSequence_VersionedChildStructure;
import org.rutebanken.netex.model.Route;
import org.rutebanken.netex.model.ServiceJourney;
import org.rutebanken.netex.model.StopPointInJourneyPattern;
import org.rutebanken.netex.model.TimetabledPassingTime;
import org.rutebanken.netex.model.VersionOfObjectRefStructure;
import org.rutebanken.netex.model.Via_VersionedChildStructure;
import org.rutebanken.netex.model.Vias_RelStructure;
/**
* This maps a list of TimetabledPassingTimes to a list of StopTimes. It also makes sure the
* StopTime has a reference to the correct stop. DestinationDisplay is mapped to HeadSign. There is
* logic to take care of the the fact that DestinationsDisplay is also valid for each subsequent
* TimeTabledPassingTime, while HeadSign has to be explicitly defined for each StopTime.
* This class does not take Daylight Saving Time transitions into account, this is an error and
* should be fixed. See https://github.com/opentripplanner/OpenTripPlanner/issues/5109
*/
class StopTimesMapper {
private static final int DAY_IN_SECONDS = 3600 * 24;
private final DataImportIssueStore issueStore;
private final FeedScopedIdFactory idFactory;
private final ReadOnlyHierarchicalMap destinationDisplayById;
private final ImmutableEntityById stopsById;
private final ImmutableEntityById flexibleStopLocationsById;
private final ImmutableEntityById groupStopById;
private final ReadOnlyHierarchicalMap quayIdByStopPointRef;
private final ReadOnlyHierarchicalMap flexibleStopPlaceIdByStopPointRef;
private final ReadOnlyHierarchicalMap routeByid;
private final ReadOnlyHierarchicalMapById flexibleLinesById;
private I18NString currentHeadSign;
private List currentHeadSignVias;
StopTimesMapper(
DataImportIssueStore issueStore,
FeedScopedIdFactory idFactory,
ImmutableEntityById stopsById,
ImmutableEntityById areaStopById,
ImmutableEntityById groupStopById,
ReadOnlyHierarchicalMap destinationDisplayById,
ReadOnlyHierarchicalMap quayIdByStopPointRef,
ReadOnlyHierarchicalMap flexibleStopPlaceIdByStopPointRef,
ReadOnlyHierarchicalMapById flexibleLinesById,
ReadOnlyHierarchicalMap routeById
) {
this.issueStore = issueStore;
this.idFactory = idFactory;
this.destinationDisplayById = destinationDisplayById;
this.stopsById = stopsById;
this.flexibleStopLocationsById = areaStopById;
this.groupStopById = groupStopById;
this.quayIdByStopPointRef = quayIdByStopPointRef;
this.flexibleStopPlaceIdByStopPointRef = flexibleStopPlaceIdByStopPointRef;
this.flexibleLinesById = flexibleLinesById;
this.routeByid = routeById;
}
static int calculateOtpTime(LocalTime time, BigInteger dayOffset) {
int otpTime = time.toSecondOfDay();
if (dayOffset != null) {
otpTime += DAY_IN_SECONDS * dayOffset.intValue();
}
return otpTime;
}
/**
* @return a map of stop-times indexed by the TimetabledPassingTime id.
*/
@Nullable
StopTimesMapperResult mapToStopTimes(
JourneyPattern_VersionStructure journeyPattern,
Trip trip,
List passingTimes,
ServiceJourney serviceJourney
) {
StopTimesMapperResult result = new StopTimesMapperResult();
List scheduledStopPointIds = new ArrayList<>();
for (int i = 0; i < passingTimes.size(); i++) {
TimetabledPassingTime currentPassingTime = passingTimes.get(i);
String pointInJourneyPattern = currentPassingTime
.getPointInJourneyPatternRef()
.getValue()
.getRef();
StopPointInJourneyPattern stopPoint = findStopPoint(pointInJourneyPattern, journeyPattern);
StopLocation stop = lookUpStopLocation(stopPoint);
if (stop == null) {
if (
stopPoint != null &&
isFalse(stopPoint.isForAlighting()) &&
isFalse(stopPoint.isForBoarding())
) {
continue;
}
issueStore.add(
"JourneyPatternStopNotFound",
"Stop with id %s not found for StopPoint %s in JourneyPattern %s. " +
"Trip %s will not be mapped.",
stopPoint != null && stopPoint.getScheduledStopPointRef() != null
? stopPoint.getScheduledStopPointRef().getValue().getRef()
: "null",
stopPoint != null ? stopPoint.getId() : "null",
journeyPattern.getId(),
trip.getId()
);
return null;
}
scheduledStopPointIds.add(stopPoint.getScheduledStopPointRef().getValue().getRef());
StopTime stopTime = mapToStopTime(trip, stopPoint, stop, currentPassingTime, i);
if (stopTime == null) {
return null;
}
BookingInfo bookingInfo = new BookingInfoMapper(issueStore)
.map(stopPoint, serviceJourney, lookUpFlexibleLine(serviceJourney, journeyPattern));
stopTime.setDropOffBookingInfo(bookingInfo);
stopTime.setPickupBookingInfo(bookingInfo);
result.addStopTime(currentPassingTime.getId(), stopTime);
}
result.setScheduledStopPointIds(scheduledStopPointIds);
return result;
}
/**
* @return a map of stop-times indexed by the TimetabledPassingTime id.
*/
@Nullable
String findTripHeadsign(
JourneyPattern_VersionStructure journeyPattern,
TimetabledPassingTime firstPassingTime
) {
String pointInJourneyPattern = firstPassingTime
.getPointInJourneyPatternRef()
.getValue()
.getRef();
var stopPoint = findStopPoint(pointInJourneyPattern, journeyPattern);
if (stopPoint == null) {
return null;
}
if (stopPoint.getDestinationDisplayRef() == null) {
return null;
}
var destinationDisplay = destinationDisplayById.lookup(
stopPoint.getDestinationDisplayRef().getRef()
);
return destinationDisplay == null
? null
: MultilingualStringMapper.nullableValueOf(destinationDisplay.getFrontText());
}
@Nullable
private static StopPointInJourneyPattern findStopPoint(
String pointInJourneyPatterRef,
JourneyPattern_VersionStructure journeyPattern
) {
var points = journeyPattern
.getPointsInSequence()
.getPointInJourneyPatternOrStopPointInJourneyPatternOrTimingPointInJourneyPattern();
for (PointInLinkSequence_VersionedChildStructure point : points) {
if (point instanceof StopPointInJourneyPattern stopPoint) {
if (stopPoint.getId().equals(pointInJourneyPatterRef)) {
return stopPoint;
}
}
}
return null;
}
private static int calculateOtpTime(
LocalTime time,
BigInteger dayOffset,
LocalTime fallbackTime,
BigInteger fallbackDayOffset
) {
return time != null
? calculateOtpTime(time, dayOffset)
: calculateOtpTime(fallbackTime, fallbackDayOffset);
}
private static boolean isFalse(Boolean value) {
return value != null && !value;
}
private StopTime mapToStopTime(
Trip trip,
StopPointInJourneyPattern stopPoint,
StopLocation stop,
TimetabledPassingTime passingTime,
int stopSequence
) {
StopTime stopTime = new StopTime();
stopTime.setTrip(trip);
stopTime.setStopSequence(stopSequence);
stopTime.setStop(stop);
if (passingTime.getArrivalTime() != null || passingTime.getDepartureTime() != null) {
stopTime.setArrivalTime(
calculateOtpTime(
passingTime.getArrivalTime(),
passingTime.getArrivalDayOffset(),
passingTime.getDepartureTime(),
passingTime.getDepartureDayOffset()
)
);
stopTime.setDepartureTime(
calculateOtpTime(
passingTime.getDepartureTime(),
passingTime.getDepartureDayOffset(),
passingTime.getArrivalTime(),
passingTime.getArrivalDayOffset()
)
);
// From NeTEx we define timepoint as a waitpoint with waiting time defined (also 0)
if (Boolean.TRUE.equals(stopPoint.isIsWaitPoint()) && passingTime.getWaitingTime() != null) {
stopTime.setTimepoint(1);
}
} else if (
passingTime.getEarliestDepartureTime() != null && passingTime.getLatestArrivalTime() != null
) {
stopTime.setFlexWindowStart(
calculateOtpTime(
passingTime.getEarliestDepartureTime(),
passingTime.getEarliestDepartureDayOffset()
)
);
stopTime.setFlexWindowEnd(
calculateOtpTime(
passingTime.getLatestArrivalTime(),
passingTime.getLatestArrivalDayOffset()
)
);
} else {
return null;
}
if (stopPoint != null) {
if (isFalse(stopPoint.isForAlighting())) {
stopTime.setDropOffType(NONE);
} else if (Boolean.TRUE.equals(stopPoint.isRequestStop())) {
stopTime.setDropOffType(COORDINATE_WITH_DRIVER);
} else {
stopTime.setDropOffType(SCHEDULED);
}
if (isFalse(stopPoint.isForBoarding())) {
stopTime.setPickupType(NONE);
} else if (Boolean.TRUE.equals(stopPoint.isRequestStop())) {
stopTime.setPickupType(COORDINATE_WITH_DRIVER);
} else {
stopTime.setPickupType(SCHEDULED);
}
if (stopPoint.getDestinationDisplayRef() != null) {
DestinationDisplay destinationDisplay = destinationDisplayById.lookup(
stopPoint.getDestinationDisplayRef().getRef()
);
if (destinationDisplay != null) {
currentHeadSign = new NonLocalizedString(destinationDisplay.getFrontText().getValue());
Vias_RelStructure viaValues = destinationDisplay.getVias();
if (viaValues != null && viaValues.getVia() != null) {
currentHeadSignVias =
viaValues
.getVia()
.stream()
.map(Via_VersionedChildStructure::getDestinationDisplayRef)
.filter(Objects::nonNull)
.map(VersionOfObjectRefStructure::getRef)
.filter(Objects::nonNull)
.map(destinationDisplayById::lookup)
.filter(Objects::nonNull)
.map(DestinationDisplay_VersionStructure::getFrontText)
.filter(Objects::nonNull)
.map(MultilingualString::getValue)
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (currentHeadSignVias.isEmpty()) {
currentHeadSignVias = null;
}
} else {
currentHeadSignVias = null;
}
}
}
}
if (
passingTime.getArrivalTime() == null &&
passingTime.getDepartureTime() == null &&
passingTime.getEarliestDepartureTime() == null &&
passingTime.getLatestArrivalTime() == null
) {
issueStore.add("TripWithoutTime", "Time missing for trip %s", trip.getId());
}
if (currentHeadSign != null) {
stopTime.setStopHeadsign(currentHeadSign);
}
if (currentHeadSignVias != null) {
stopTime.setHeadsignVias(currentHeadSignVias);
}
return stopTime;
}
@Nullable
private StopLocation lookUpStopLocation(StopPointInJourneyPattern stopPointInJourneyPattern) {
if (stopPointInJourneyPattern == null) {
return null;
}
String stopPointRef = stopPointInJourneyPattern.getScheduledStopPointRef().getValue().getRef();
String stopId = quayIdByStopPointRef.lookup(stopPointRef);
String flexibleStopPlaceId = flexibleStopPlaceIdByStopPointRef.lookup(stopPointRef);
if (stopId == null && flexibleStopPlaceId == null) {
issueStore.add(
"PassengerStopAssignmentNotFound",
"No passengerStopAssignment found for %s",
stopPointRef
);
return null;
}
StopLocation stopLocation;
if (stopId != null) {
stopLocation = stopsById.get(idFactory.createId(stopId));
} else {
AreaStop areaStop = flexibleStopLocationsById.get(idFactory.createId(flexibleStopPlaceId));
GroupStop groupStop = groupStopById.get(idFactory.createId(flexibleStopPlaceId));
if (areaStop != null) {
stopLocation = areaStop;
} else stopLocation = groupStop;
}
if (stopLocation == null) {
issueStore.add(
"StopPointInJourneyPatternMissingStopLocation",
"No Quay or FlexibleStopPlace found for %s",
stopPointRef
);
}
return stopLocation;
}
private FlexibleLine lookUpFlexibleLine(
ServiceJourney serviceJourney,
JourneyPattern_VersionStructure journeyPattern
) {
if (serviceJourney == null) {
return null;
}
String lineRef = null;
// Check for direct connection to Line
JAXBElement extends LineRefStructure> lineRefStruct = serviceJourney.getLineRef();
if (lineRefStruct != null) {
// Connect to Line referenced directly from ServiceJourney
lineRef = lineRefStruct.getValue().getRef();
} else if (serviceJourney.getJourneyPatternRef() != null) {
// Connect to Line referenced through JourneyPattern->Route
lineRef = JourneyPatternHelper.getLineFromRoute(routeByid, journeyPattern);
}
return flexibleLinesById.lookup(lineRef);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy