/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package org.opentripplanner.routing.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Currency;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.opentripplanner.common.model.T2;
import org.opentripplanner.routing.core.Fare;
import org.opentripplanner.routing.core.Fare.FareType;
import org.opentripplanner.routing.core.FareRuleSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SeattleFareServiceImpl extends DefaultFareServiceImpl {
private static final long serialVersionUID = 1L;
// Agency IDs defined in King Metro Transit GTFS
public static final String KCM_EOS_AGENCY_ID = "EOS";
public static final String KCM_KCM_AGENCY_ID = "KCM";
public static final String KCM_ST_AGENCY_ID = "ST";
public static final String KCM_KMD_AGENCY_ID = "KMD";
// Agency IDs defined in Pierce Transit GTFS
public static final String PT_PT_AGENCY_ID = "3";
public static final String PT_ST_AGENCY_ID = "40";
// Agency IDs defined in Sound Transit GTFS
public static final String ST_ST_AGENCY_ID = "SoundTransit";
// Agency IDs defined in Community Transit GTFS
public static final String CT_CT_AGENCY_ID = "29";
public static final int TRANSFER_DURATION_SEC = 7200;
// Fallback in case no rules apply for an agency
private Map<T2<FareType, String>, Float> defaultFares = new HashMap<>();
private static final Logger LOG = LoggerFactory.getLogger(SeattleFareServiceImpl.class);
public SeattleFareServiceImpl(Collection<FareRuleSet> regularFareRules,
Collection<FareRuleSet> youthFareRules, Collection<FareRuleSet> seniorFareRules) {
super();
addFareRules(FareType.regular, regularFareRules);
addFareRules(FareType.youth, youthFareRules);
addFareRules(FareType.senior, seniorFareRules);
}
public void addDefaultFare(FareType fareType, String agencyId, float cost) {
defaultFares.put(new T2<FareType, String>(fareType, agencyId), cost);
}
@Override
protected boolean populateFare(Fare fare, Currency currency, FareType fareType, List<Ride> rides,
Collection<FareRuleSet> fareRules) {
float lowestCost = getLowestCost(fareType, rides, fareRules);
if(lowestCost != Float.POSITIVE_INFINITY) {
fare.addFare(fareType, getMoney(currency, lowestCost));
return true;
}
return false;
}
@Override
protected float getLowestCost(FareType fareType, List<Ride> rides,
Collection<FareRuleSet> fareRules) {
// Split rides per agency
List<List<Ride>> ridesPerAgency = new ArrayList<List<Ride>>();
String lastAgency = null;
List<Ride> currentRides = null;
for (Ride ride : rides) {
if (ride.agency != lastAgency) {
currentRides = new ArrayList<Ride>();
ridesPerAgency.add(currentRides);
lastAgency = ride.agency;
}
currentRides.add(ride);
}
LOG.debug("=== Rides for fare class {} ===", fareType);
for (List<Ride> ridesForAgency : ridesPerAgency) {
LOG.debug("Ride for agency {} : {}", ridesForAgency.get(0).agency,
Arrays.toString(ridesForAgency.toArray()));
}
float currentCost = 0f;
float totalCost = 0f;
long lastStartSec = 0L;
for (List<Ride> ridesForAgency : ridesPerAgency) {
String agencyId = ridesForAgency.get(0).agency;
long startSec = ridesForAgency.get(0).startTime; // seconds
float costForAgency = super.getLowestCost(fareType, ridesForAgency, fareRules);
if (costForAgency == Float.POSITIVE_INFINITY) {
Float def = defaultFares.get(new T2<FareType, String>(fareType, agencyId));
if (def == null) {
LOG.error("No fares and no fallback for class {}, agency {}, rides {}",
fareType, agencyId, ridesForAgency);
return Float.POSITIVE_INFINITY;
}
costForAgency = def;
}
LOG.debug("Agency {} cost is {}", agencyId, costForAgency);
// Check for transfer
if (startSec < lastStartSec + TRANSFER_DURATION_SEC) {
// Transfer OK
if (costForAgency > currentCost) {
// Add top-up
float deltaCost = costForAgency - currentCost;
totalCost += deltaCost;
// Record max ticket price for current transfer
currentCost = costForAgency;
LOG.debug("Transfer, additional cost is {}, total is {}", deltaCost, totalCost);
} else {
LOG.debug("New ticket cost lower than current {}", currentCost);
}
// TODO Record discount
} else {
// New one needed
currentCost = costForAgency;
totalCost += costForAgency;
LOG.debug("New ticket, cost is {}, total is {}", costForAgency, totalCost);
lastStartSec = startSec;
}
}
return totalCost;
}
}