All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphhopper.gtfs.fare.Fares Maven / Gradle / Ivy

There is a newer version: 10.0
Show newest version
/*
 *  Licensed to GraphHopper GmbH under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for
 *  additional information regarding copyright ownership.
 *
 *  GraphHopper GmbH licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.graphhopper.gtfs.fare;

import com.conveyal.gtfs.model.Fare;
import com.conveyal.gtfs.model.FareRule;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

public class Fares {
    public static Optional cheapestFare(Map> fares, Trip trip) {
        return ticketsBruteForce(fares, trip)
                .flatMap(tickets -> tickets.stream()
                        .map(ticket -> {
                            Fare fare = fares.get(ticket.feed_id).get(ticket.getFare().fare_id);
                            final BigDecimal priceOfOneTicket = BigDecimal.valueOf(fare.fare_attribute.price);
                            return new Amount(priceOfOneTicket, fare.fare_attribute.currency_type);
                        })
                        .collect(Collectors.groupingBy(Amount::getCurrencyType, Collectors.mapping(Amount::getAmount, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))))
                        .entrySet()
                        .stream()
                        .findFirst() // TODO: Tickets in different currencies for one trip
                        .map(e -> new Amount(e.getValue(), e.getKey())));
    }

    static Optional> ticketsBruteForce(Map> fares, Trip trip) {
        // Recursively enumerate all packages of tickets with which the trip can be done.
        // Take the cheapest.
        TicketPurchaseScoreCalculator ticketPurchaseScoreCalculator = new TicketPurchaseScoreCalculator();
        return allShoppingCarts(fares, trip)
                .max(Comparator.comparingDouble(ticketPurchaseScoreCalculator::calculateScore))
                .map(TicketPurchase::getTickets);
    }

    static Stream allShoppingCarts(Map> fares, Trip trip) {
        // Recursively enumerate all packages of tickets with which the trip can be done.
        List segments = trip.segments;
        List> result = allFareAssignments(fares, segments);
        return result.stream().map(TicketPurchase::new);
    }

    private static List> allFareAssignments(Map> fares, List segments) {
        // Recursively enumerate all possible ways of assigning trip segments to fares.
        if (segments.isEmpty()) {
            ArrayList> emptyList = new ArrayList<>();
            emptyList.add(Collections.emptyList());
            return emptyList;
        } else {
            List> result = new ArrayList<>();
            Trip.Segment segment = segments.get(0);
            List> tail = allFareAssignments(fares, segments.subList(1, segments.size()));
            Collection possibleFares = Fares.possibleFares(fares.get(segment.feed_id), segment);
            for (Fare fare : possibleFares) {
                for (List tailFareAssignments : tail) {
                    ArrayList fairAssignments = new ArrayList<>(tailFareAssignments);
                    FareAssignment fareAssignment = new FareAssignment(segment);
                    fareAssignment.setFare(fare);
                    fairAssignments.add(0, fareAssignment);
                    result.add(fairAssignments);
                }
            }
            return result;
        }
    }

    static Collection possibleFares(Map fares, Trip.Segment segment) {
        return fares.values().stream().filter(fare -> applies(fare, segment)).collect(toList());
    }

    private static boolean applies(Fare fare, Trip.Segment segment) {
        return fare.fare_rules.isEmpty() || sanitizeFareRules(fare.fare_rules).stream().anyMatch(rule -> rule.appliesTo(segment));
    }

    static List sanitizeFareRules(List gtfsFareRules) {
        // Make proper fare rule objects from the CSV-like FareRule
        ArrayList result = new ArrayList<>();
        result.addAll(gtfsFareRules.stream().filter(rule -> rule.route_id != null).map(rule -> new RouteRule(rule.route_id)).collect(toList()));
        result.addAll(gtfsFareRules.stream().filter(rule -> rule.origin_id != null && rule.destination_id != null).map(rule -> new OriginDestinationRule(rule.origin_id, rule.destination_id)).collect(toList()));
        result.add(gtfsFareRules.stream().filter(rule -> rule.contains_id != null).map(rule -> rule.contains_id).collect(Collectors.collectingAndThen(toList(), ZoneRule::new)));
        return result;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy