Skip to content

Commit

Permalink
Merge pull request #390 from mattwigway/fareto
Browse files Browse the repository at this point in the history
Fareto
  • Loading branch information
rafapereirabr authored Jun 12, 2024
2 parents eeabebb + bac9083 commit a25bae5
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 2 deletions.
57 changes: 57 additions & 0 deletions java-r5rcore/src/org/ipea/r5r/Process/FaretoDebug.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.ipea.r5r.Process;

import java.text.ParseException;
import java.util.concurrent.ForkJoinPool;

import org.ipea.r5r.RDataFrame;
import org.ipea.r5r.RoutingProperties;
import org.ipea.r5r.R5.R5ParetoServer;

import com.conveyal.r5.analyst.cluster.RegionalTask;
import com.conveyal.r5.transit.TransportNetwork;

/**
* This outputs the Pareto itinerary planner results directly to JSON.
*/
public class FaretoDebug extends R5Process {
public FaretoDebug(ForkJoinPool threadPool, TransportNetwork transportNetwork,
RoutingProperties routingProperties) {
super(threadPool, transportNetwork, routingProperties);
}

public R5ParetoServer.ParetoReturn pathResults = null;

@Override
protected boolean isOneToOne() {
return true;
}

@Override
protected RDataFrame runProcess(int index) throws ParseException {
RegionalTask request = buildRequest(index);
request.fromLat = fromLats[0];
request.fromLon = fromLons[0];
request.toLat = toLats[0];
request.toLon = toLons[0];
request.maxFare = 150_000_000;

R5ParetoServer computer = new R5ParetoServer(request, transportNetwork);
pathResults = computer.handle();

// to match R5Process interface, return type must be dataframe. We work around this by returning an empty
// dataframe. Cannot return null because value is used in mergeDataFrame
return new RDataFrame(0);
}

@Override
protected RDataFrame buildDataFrameStructure(String fromId, int nRows) {
// to avoid NPEs in mergeDataFrame, something must be returned
return new RDataFrame(nRows);
}

@Override
protected void buildDestinationPointSet() {
// not needed in this class
}

}
11 changes: 9 additions & 2 deletions java-r5rcore/src/org/ipea/r5r/R5/R5ParetoServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.conveyal.r5.SoftwareVersion;
import com.conveyal.r5.analyst.cluster.RegionalTask;
import com.conveyal.r5.analyst.fare.BostonInRoutingFareCalculator;
import com.conveyal.r5.analyst.fare.TransferAllowance;
import com.conveyal.r5.api.util.LegMode;
import com.conveyal.r5.common.GeometryUtils;
Expand Down Expand Up @@ -100,7 +101,11 @@ public R5ParetoServer.ParetoReturn handle () {

ParetoReturn ret = null;
try {
ret = new ParetoReturn(trips, totalTime);
ProfileRequest newReq = (ProfileRequest) profileRequest.clone();
// don't serialize the whole R5R fare calculator. HACK fareto requires something here
// so we just return a BostonInRoutingFareCalculator.
newReq.inRoutingFareCalculator = new BostonInRoutingFareCalculator();
ret = new ParetoReturn(trips, totalTime, newReq);
} catch (NullPointerException e){
LOG.error("exception in building return");
e.printStackTrace();
Expand Down Expand Up @@ -139,10 +144,12 @@ public static final class ParetoReturn {
/** save backend version in JSON output - useful for JSON that's being pushed to fareto-examples */
public SoftwareVersion backendVersion = SoftwareVersion.instance;
public String generationTime = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME);
public ProfileRequest request;

public ParetoReturn(Collection<R5ParetoServer.ParetoTrip> trips, long computeTimeMillis) {
public ParetoReturn(Collection<R5ParetoServer.ParetoTrip> trips, long computeTimeMillis, ProfileRequest request) {
this.trips = trips;
this.computeTimeMillis = computeTimeMillis;
this.request = request;
}
}

Expand Down
25 changes: 25 additions & 0 deletions java-r5rcore/src/org/ipea/r5r/R5RCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import com.conveyal.r5.analyst.cluster.PathResult;
import com.conveyal.r5.analyst.decay.*;
import com.conveyal.r5.transit.TransportNetwork;
import com.fasterxml.jackson.core.JsonProcessingException;

import org.ipea.r5r.Fares.FareStructure;
import org.ipea.r5r.Fares.FareStructureBuilder;
import org.ipea.r5r.Fares.RuleBasedInRoutingFareCalculator;
Expand Down Expand Up @@ -440,6 +442,29 @@ public RDataFrame paretoFrontier(String[] fromIds, double[] fromLats, double[] f
return paretoFrontierCalculator.run();
}

/**
* This functions returns the Pareto frontier as raw R5 JSON (for visualization with Fareto)
* @throws JsonProcessingException
*/
public String faretoJson(String fromId, double fromLat, double fromLon,
String toId, double toLat, double toLon,
String directModes, String transitModes, String accessModes, String egressModes,
String date, String departureTime,
int maxWalkTime, int maxBikeTime, int maxCarTime, int maxTripDuration) throws ExecutionException, InterruptedException, JsonProcessingException {
FaretoDebug calculator = new FaretoDebug(r5rThreadPool, transportNetwork, routingProperties);
calculator.setOrigins(new String[] {fromId}, new double[] {fromLat}, new double[] {fromLon});
calculator.setDestinations(new String[] {toId}, new double[] {toLat}, new double[] {toLon});
calculator.setModes(directModes, accessModes, transitModes, egressModes);
calculator.setDepartureDateTime(date, departureTime);
calculator.setTripDuration(maxWalkTime, maxBikeTime, maxCarTime, maxTripDuration);

calculator.run();

// we use the conveyal objectmapper, because it is already configured to properly serialize pareto returns
// notably, it can handle GeoJson and names the properties the way fareto expects (camelCase rather than snake_case)
return com.conveyal.analysis.util.JsonUtil.objectMapper.writeValueAsString(calculator.pathResults);
}

// -------------------------------------- ACCESSIBILITY ----------------------------------------------

public RDataFrame accessibility(String[] fromIds, double[] fromLats, double[] fromLons,
Expand Down
96 changes: 96 additions & 0 deletions r-package/R/fareto_debug.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#' Output Fareto-format JSON for visualization with Fareto
#'
#' This is primarily intended for debugging the fare system code. Fareto is an external tool
#' that provides visualization for R5's McRAPTOR fare calculator. To use this,
#' run fareto_debug(...) as if you were running paretoFrontier(...). The return value is a JSON-formatted
#' string. Use [rfareto](https://github.com/mattwigway/rfareto) to visualize the results.
fareto_debug <- function(r5r_core,
origins,
destinations,
mode = c("WALK", "TRANSIT"),
mode_egress = "WALK",
departure_datetime = Sys.time(),
time_window = 10L,
percentiles = 50L,
max_walk_time = Inf,
max_bike_time = Inf,
max_car_time = Inf,
max_trip_duration = 120L,
fare_structure = NULL,
walk_speed = 3.6,
bike_speed = 12,
max_rides = 3,
max_lts = 2,
n_threads = Inf,
verbose = FALSE,
progress = FALSE) {

checkmate::assert_class(r5r_core, "jobjRef")

origins <- assign_points_input(origins, "origins")
destinations <- assign_points_input(destinations, "destinations")
mode_list <- assign_mode(mode, mode_egress)
departure <- assign_departure(departure_datetime)
max_walk_time <- assign_max_street_time(
max_walk_time,
walk_speed,
max_trip_duration,
"walk"
)
max_bike_time <- assign_max_street_time(
max_bike_time,
bike_speed,
max_trip_duration,
"bike"
)
max_car_time <- assign_max_street_time(
max_car_time,
8, # 8 km/h, R5's default.
max_trip_duration,
"car"
)
max_trip_duration <- assign_max_trip_duration(
max_trip_duration,
mode_list,
max_walk_time,
max_bike_time
)

if (nrow(origins) != 1 || nrow(destinations) != 1) {
stop("fareto_debug requires exactly one origin and one destination")
}

set_time_window(r5r_core, time_window)
set_percentiles(r5r_core, percentiles)
set_monte_carlo_draws(r5r_core, 1, time_window)
set_speed(r5r_core, walk_speed, "walk")
set_speed(r5r_core, bike_speed, "bike")
set_max_rides(r5r_core, max_rides)
set_max_lts(r5r_core, max_lts)
set_n_threads(r5r_core, n_threads)
set_verbose(r5r_core, verbose)
set_progress(r5r_core, progress)
set_fare_structure(r5r_core, fare_structure)

# call r5r_core method and process result -------------------------------
result <- r5r_core$faretoJson(
origins$id,
origins$lat,
origins$lon,
destinations$id,
destinations$lat,
destinations$lon,
mode_list$direct_modes,
mode_list$transit_mode,
mode_list$access_mode,
mode_list$egress_mode,
departure$date,
departure$time,
max_walk_time,
max_bike_time,
max_car_time,
max_trip_duration
)

return(result)
}
Binary file modified r-package/inst/jar/r5r.jar
Binary file not shown.

0 comments on commit a25bae5

Please sign in to comment.