/*
* 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.reader.gtfs;
import com.conveyal.gtfs.GTFSFeed;
import com.conveyal.gtfs.model.Stop;
import com.conveyal.gtfs.model.Transfer;
import java.util.*;
import java.util.stream.Collectors;
class Transfers {
private final Map<String, List<Transfer>> transfers;
private final Map<String, Set<String>> routesByStop;
Transfers(GTFSFeed feed) {
this.transfers = feed.transfers.values().stream().collect(Collectors.groupingBy(t -> t.to_stop_id));
this.routesByStop = feed.stop_times.values().stream()
.collect(Collectors.groupingBy(stopTime -> stopTime.stop_id,
Collectors.mapping(stopTime -> feed.trips.get(stopTime.trip_id).route_id, Collectors.toSet())));
}
// Starts implementing the proposed GTFS extension for route and trip specific transfer rules.
// So far, only the route is supported.
List<Transfer> getTransfersToStop(Stop to, String toRouteId) {
final List<Transfer> allInboundTransfers = transfers.getOrDefault(to.stop_id, Collections.emptyList());
final Map<String, List<Transfer>> byFromStop = allInboundTransfers.stream()
.filter(t -> t.transfer_type == 2)
.filter(t -> t.to_route_id == null || toRouteId.equals(t.to_route_id))
.collect(Collectors.groupingBy(t -> t.from_stop_id));
final List<Transfer> result = new ArrayList<>();
byFromStop.forEach((fromStop, transfers) -> {
routesByStop.getOrDefault(fromStop, Collections.emptySet()).forEach(fromRoute -> {
final Transfer mostSpecificRule = findMostSpecificRule(transfers, fromRoute, toRouteId);
final Transfer myRule = new Transfer();
myRule.to_route_id = toRouteId;
myRule.from_route_id = fromRoute;
myRule.to_stop_id = mostSpecificRule.to_stop_id;
myRule.from_stop_id = mostSpecificRule.from_stop_id;
myRule.transfer_type = mostSpecificRule.transfer_type;
myRule.min_transfer_time = mostSpecificRule.min_transfer_time;
myRule.from_trip_id = mostSpecificRule.from_trip_id;
myRule.to_trip_id = mostSpecificRule.to_trip_id;
result.add(myRule);
});
});
return result;
}
private Transfer findMostSpecificRule(List<Transfer> transfers, String fromRouteId, String toRouteId) {
final ArrayList<Transfer> transfersBySpecificity = new ArrayList<>(transfers);
transfersBySpecificity.sort(Comparator.comparingInt(t -> {
int score = 0;
if (fromRouteId.equals(t.from_route_id)) {
score++;
}
if (toRouteId.equals(t.to_route_id)) {
score++;
}
return -score;
}));
if (transfersBySpecificity.isEmpty()) {
throw new RuntimeException();
}
return transfersBySpecificity.get(0);
}
}