
org.opentripplanner.ext.fares.impl.HighestFareInFreeTransferWindowFareService Maven / Gradle / Ivy
The newest version!
package org.opentripplanner.ext.fares.impl;
import java.time.Duration;
import java.util.Collection;
import java.util.Currency;
import java.util.List;
import java.util.Optional;
import org.opentripplanner.ext.fares.model.FareRuleSet;
import org.opentripplanner.model.fare.FareProduct;
import org.opentripplanner.model.fare.ItineraryFares;
import org.opentripplanner.model.plan.Leg;
import org.opentripplanner.model.plan.ScheduledTransitLeg;
import org.opentripplanner.routing.core.FareType;
import org.opentripplanner.transit.model.basic.Money;
import org.opentripplanner.transit.model.framework.FeedScopedId;
/**
* This calculator is maintained by IBI Group.
*/
public class HighestFareInFreeTransferWindowFareService extends DefaultFareService {
private final boolean analyzeInterlinedTransfers;
private final Duration freeTransferWindow;
public HighestFareInFreeTransferWindowFareService(
Collection regularFareRules,
Duration freeTransferWindow,
boolean analyzeInterlinedTransfers
) {
addFareRules(FareType.regular, regularFareRules);
this.freeTransferWindow = freeTransferWindow;
this.analyzeInterlinedTransfers = analyzeInterlinedTransfers;
}
public boolean analyzeInterlinedTransfers() {
return analyzeInterlinedTransfers;
}
public Duration freeTransferWindow() {
return freeTransferWindow;
}
/**
* The fare calculation is designed to charge the rider incrementally as they use each service.
* The total cost of the itinerary will be equal to the leg of the journey that had the maximum
* fare. Additionally, a free transfer window is taken into account that will apply the current
* highest cost within the free transfer window and then reset to a new window and calculate
* additional free transfers from there.
*/
@Override
protected ItineraryFares calculateFaresForType(
Currency currency,
FareType fareType,
List legs,
Collection fareRules
) {
var zero = Money.ofFractionalAmount(currency, 0);
Money cost = zero;
Money currentTransferWindowCost = zero;
// The initial value of -1 indicates that the free transfer window end time has not yet been set
long freeTransferWindowEndTimeEpochSeconds = -1;
for (var leg : legs) {
Optional rideCost = calculateCost(fareType, List.of(leg), fareRules);
if (leg.getStartTime().toEpochSecond() > freeTransferWindowEndTimeEpochSeconds) {
// free transfer window has expired or has not yet been initialized. Reset some items and add to the
// overall cost. This is fine to do if the free transfer window hasn't been initialized since the
// overall cost will be 0.
cost = cost.plus(currentTransferWindowCost);
// reset current window cost
currentTransferWindowCost = zero;
// reset transfer window end time to trigger recalculation in next block
freeTransferWindowEndTimeEpochSeconds = -1;
}
// recalculate free transfer window if needed
if (freeTransferWindowEndTimeEpochSeconds == -1) {
// the new transfer window end time should be calculated by adding the ride's start time (which is in
// seconds past the epoch) and the number of equivalent seconds in the free transfer window minutes.
freeTransferWindowEndTimeEpochSeconds = leg
.getStartTime()
.plus(freeTransferWindow)
.toEpochSecond();
}
currentTransferWindowCost = Money.max(currentTransferWindowCost, rideCost.orElse(zero));
}
cost = cost.plus(currentTransferWindowCost);
var fp = new FareProduct(
new FeedScopedId("fares", fareType.name()),
fareType.name(),
cost,
null,
null,
null
);
var fare = ItineraryFares.empty();
if (cost.greaterThan(zero)) {
fare.addItineraryProducts(List.of(fp));
}
return fare;
}
@Override
protected boolean shouldCombineInterlinedLegs(
ScheduledTransitLeg current,
ScheduledTransitLeg previous
) {
return !analyzeInterlinedTransfers;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy