|
| 1 | +package com.conveyal.r5.rastercost; |
| 2 | + |
| 3 | +import java.util.ArrayList; |
| 4 | +import java.util.LinkedList; |
| 5 | +import java.util.List; |
| 6 | +import java.util.stream.Collectors; |
| 7 | +import java.util.stream.Stream; |
| 8 | + |
| 9 | +import org.slf4j.Logger; |
| 10 | +import org.slf4j.LoggerFactory; |
| 11 | + |
| 12 | +import com.conveyal.r5.analyst.TravelTimeComputer; |
| 13 | +import com.conveyal.r5.profile.StreetPath; |
| 14 | +import com.conveyal.r5.streets.EdgeStore; |
| 15 | +import com.conveyal.r5.streets.PointSetTimes; |
| 16 | +import com.conveyal.r5.streets.StreetRouter; |
| 17 | +import com.conveyal.r5.transit.TransportNetwork; |
| 18 | + |
| 19 | +/** |
| 20 | + * CustomCost helper for Greenpaths2 custom cost bi-objective routing |
| 21 | + * create a separate class for modularity and transparency |
| 22 | + * and distinguishing the custom cost logic from the rest of the base code |
| 23 | + * |
| 24 | + * Also this separate class can be helpful if we want to defreeze the R5 version |
| 25 | + * |
| 26 | + * Created by roope on 11.10.2023. |
| 27 | + */ |
| 28 | + |
| 29 | +/* GP2 edit: create this class for handling custom cost logic */ |
| 30 | +public class CustomCost { |
| 31 | + |
| 32 | + private static final Logger LOG = LoggerFactory.getLogger(TravelTimeComputer.class); |
| 33 | + |
| 34 | + /* |
| 35 | + * Get the osmIds from the router state |
| 36 | + * this is used in many-to-many or one-to-may matrices routing e.g. in TravelTimeComputer |
| 37 | + * these osmIds are needed for exposure based routing |
| 38 | + * they are combined by "osmid" with separately calculated exposure values |
| 39 | + * e.g. we calculate a dict of {osmid: exposure values}, run the matrix routing and then |
| 40 | + * see what osmid's we traversed and get the result by accessing the dict with the osmid as the key |
| 41 | + * |
| 42 | + */ |
| 43 | + public static List<List<Long>> getOsmIdsFromRouterState(PointSetTimes nonTransitTravelTimesToDestinations, StreetRouter sr, TransportNetwork network) { |
| 44 | + if(nonTransitTravelTimesToDestinations == null) return null; |
| 45 | + |
| 46 | + // create a list of lists for osmIds |
| 47 | + // populate with empty lists |
| 48 | + // the first list will indicate the point index, the nested list has list of osmids created from StreetRouter.State and StreetPath |
| 49 | + List<List<Long>> osmIdResults = Stream.generate(ArrayList<Long>::new) |
| 50 | + .limit(nonTransitTravelTimesToDestinations.size()) |
| 51 | + .collect(Collectors.toList()); |
| 52 | + |
| 53 | + // loop for each destination point in the matrix grid (i.e. the pointset) |
| 54 | + for(var i = 0; i < nonTransitTravelTimesToDestinations.size(); i++) { |
| 55 | + // get the lat and lon of the destination point |
| 56 | + double destPointLat = nonTransitTravelTimesToDestinations.pointSet.getLat(i); |
| 57 | + double destPointLon = nonTransitTravelTimesToDestinations.pointSet.getLon(i); |
| 58 | + |
| 59 | + // get street router state using the current destination lat and lon |
| 60 | + StreetRouter.State lastState = sr.getState(destPointLat, destPointLon); |
| 61 | + if (lastState == null) { |
| 62 | + // skip the point if lat or lon is 0 or no state is found |
| 63 | + continue; |
| 64 | + } |
| 65 | + // create a street path using the state, used for looping all edges from the path |
| 66 | + StreetPath streetPath = new StreetPath(lastState, network, false); |
| 67 | + // get the all the edge indexes from the path |
| 68 | + LinkedList<Integer> pathEdges = streetPath.getEdges(); |
| 69 | + // initialize empty list for osmIds |
| 70 | + LinkedList<Long> edgeOsmIdsForPath = new LinkedList<>(); |
| 71 | + // loop through all the edges in the path traversed and get osmids from the edge store |
| 72 | + for (Integer edgeIdx : pathEdges) { |
| 73 | + EdgeStore.Edge edge = network.streetLayer.edgeStore.getCursor(edgeIdx); |
| 74 | + edgeOsmIdsForPath.add(edge.getOSMID()); |
| 75 | + } |
| 76 | + // remove dublicate osmIds |
| 77 | + List<Long> uniqueEdgeOsmIdsForPath = edgeOsmIdsForPath.stream().distinct().collect(Collectors.toList()); |
| 78 | + // replace the empty populated list with the unique list if any osmids are found |
| 79 | + if (!uniqueEdgeOsmIdsForPath.isEmpty()) { |
| 80 | + osmIdResults.set(i, uniqueEdgeOsmIdsForPath); |
| 81 | + } |
| 82 | + } |
| 83 | + // check if all lists are empty i.e. nothing was added |
| 84 | + // if all empty, return null |
| 85 | + if (osmIdResults.stream().allMatch(List::isEmpty)) { |
| 86 | + LOG.info("No OsmId's were found for any of the points"); |
| 87 | + return null; |
| 88 | + } |
| 89 | + |
| 90 | + return osmIdResults; |
| 91 | + } |
| 92 | +} |
0 commit comments