diff --git a/src/main/java/com/conveyal/analysis/results/MultiOriginAssembler.java b/src/main/java/com/conveyal/analysis/results/MultiOriginAssembler.java index dc2b0b150..6aa3c9fad 100644 --- a/src/main/java/com/conveyal/analysis/results/MultiOriginAssembler.java +++ b/src/main/java/com/conveyal/analysis/results/MultiOriginAssembler.java @@ -139,7 +139,8 @@ public MultiOriginAssembler (RegionalAnalysis regionalAnalysis, Job job, FileSto // We might want to record a grid of dual accessibility values, but this will require some serious // refactoring of the GridResultWriter. // if (job.templateTask.dualAccessibilityThreshold > 0) { ... } - throw new IllegalArgumentException("Temporal density of opportunities cannot be recorded for gridded origin points."); + // throw new IllegalArgumentException("Temporal density of opportunities cannot be recorded for " + + // "gridded origin points."); } else { // Freeform origins. // Output includes temporal density of opportunities and optionally dual accessibility. diff --git a/src/main/java/com/conveyal/r5/analyst/TemporalDensityResult.java b/src/main/java/com/conveyal/r5/analyst/TemporalDensityResult.java index 6ba2294b4..573dd9075 100644 --- a/src/main/java/com/conveyal/r5/analyst/TemporalDensityResult.java +++ b/src/main/java/com/conveyal/r5/analyst/TemporalDensityResult.java @@ -1,6 +1,7 @@ package com.conveyal.r5.analyst; import com.conveyal.r5.analyst.cluster.AnalysisWorkerTask; +import com.conveyal.r5.analyst.cluster.RegionalTask; import com.google.common.base.Preconditions; import static com.conveyal.r5.common.Util.notNullOrEmpty; @@ -89,6 +90,36 @@ public int[][] minutesToReachOpportunities(int n) { return result; } + public int[][][] fakeDualAccess (RegionalTask task) { + int nPointSets = task.destinationPointSets.length; + int nCutoffs = task.cutoffsMinutes.length; + int[][][] dualAccess = new int[nPointSets][nPercentiles][nCutoffs]; + for (int d = 0; d < nPointSets; d++) { + for (int p = 0; p < nPercentiles; p++) { + // Hack: use cutoffs as dual access thresholds + for (int c = 0; c < nCutoffs - 1; c++) { + int m = 0; + double sum = 0; + while (sum < task.cutoffsMinutes[c] && m < 120) { + sum += opportunitiesPerMinute[d][p][m]; + m += 1; + } + dualAccess[d][p][c] = m == 0 ? 999 : m; + } + // But hack above won't allow thresholds over 120; so use the dualAccessibilityThreshold instead of + // the last cutoff + int m = 0; + double sum = 0; + while (sum < task.dualAccessibilityThreshold && m < 120) { + sum += opportunitiesPerMinute[d][p][m]; + m += 1; + } + dualAccess[d][p][nCutoffs - 1] = m == 0 ? 999 : m; + } + } + return dualAccess; + } + public int[][] minutesToReachOpportunities() { return minutesToReachOpportunities(opportunityThreshold); } diff --git a/src/main/java/com/conveyal/r5/analyst/TravelTimeReducer.java b/src/main/java/com/conveyal/r5/analyst/TravelTimeReducer.java index 213091d56..a4d5c50b2 100644 --- a/src/main/java/com/conveyal/r5/analyst/TravelTimeReducer.java +++ b/src/main/java/com/conveyal/r5/analyst/TravelTimeReducer.java @@ -145,7 +145,7 @@ public TravelTimeReducer (AnalysisWorkerTask task, TransportNetwork network) { if (task.includePathResults) { pathResult = new PathResult(task, network.transitLayer); } - if (task.includeTemporalDensity) { + if (task.includeTemporalDensity || task.flags.contains("gridDualAccess")) { temporalDensityResult = new TemporalDensityResult(task); } diff --git a/src/main/java/com/conveyal/r5/analyst/cluster/RegionalWorkResult.java b/src/main/java/com/conveyal/r5/analyst/cluster/RegionalWorkResult.java index c606acc0f..7f10e77b8 100644 --- a/src/main/java/com/conveyal/r5/analyst/cluster/RegionalWorkResult.java +++ b/src/main/java/com/conveyal/r5/analyst/cluster/RegionalWorkResult.java @@ -63,7 +63,7 @@ public RegionalWorkResult(OneOriginResult result, RegionalTask task) { this.jobId = task.jobId; this.taskId = task.taskId; this.travelTimeValues = result.travelTimes == null ? null : result.travelTimes.values; - this.accessibilityValues = result.accessibility == null ? null : result.accessibility.getIntValues(); + this.accessibilityValues = result.accessibility == null ? null : result.density.fakeDualAccess(task); this.pathResult = result.paths == null ? null : result.paths.summarizeIterations(PathResult.Stat.MINIMUM); this.opportunitiesPerMinute = result.density == null ? null : result.density.opportunitiesPerMinute; // TODO checkTravelTimeInvariants, checkAccessibilityInvariants to verify that values are monotonically increasing