lmao
This commit is contained in:
parent
03445188ea
commit
524580eb25
102 changed files with 36625 additions and 1295 deletions
|
|
@ -20,17 +20,25 @@ import java.util.Arrays;
|
|||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
/** R5 routing: network loading, point set construction, travel time computation. */
|
||||
/** R5 routing: network loading, spatial filtering, travel time computation. */
|
||||
public class Router {
|
||||
|
||||
private static final int ZOOM = 9;
|
||||
private static final int ZOOM = 9; // R5 enforces range 9-12
|
||||
private static final int MAX_GRID_CELLS = 4_900_000; // under R5's 5M limit
|
||||
|
||||
/**
|
||||
* A chunk of destinations that fits within R5's grid cell limit at zoom 9.
|
||||
* originalIndices maps each position in this chunk back to the full destinations array.
|
||||
*/
|
||||
record DestinationChunk(FreeFormPointSet pointSet, WebMercatorExtents extents, int[] originalIndices) {}
|
||||
/** Result of computing travel times for a single origin with spatial pre-filtering. */
|
||||
record FilteredResult(int[] originalIndices, short[] times) {}
|
||||
|
||||
/** Max plausible travel radius in km for 120-minute trips. */
|
||||
static double maxRadiusKm(String mode) {
|
||||
return switch (mode) {
|
||||
case "car" -> 150;
|
||||
case "transit" -> 150;
|
||||
case "bicycle" -> 60;
|
||||
case "walking" -> 12;
|
||||
default -> throw new IllegalArgumentException("Unknown mode: " + mode);
|
||||
};
|
||||
}
|
||||
|
||||
/** Load or build the transport network with Kryo caching. */
|
||||
static TransportNetwork loadNetwork(String dataDir, String cacheDir) throws Exception {
|
||||
|
|
@ -56,10 +64,80 @@ public class Router {
|
|||
}
|
||||
|
||||
/**
|
||||
* Split destinations into geographic chunks that each fit within R5's grid cell limit.
|
||||
* Sorts by latitude and splits into bands so each band's bounding box at zoom 9 is under 5M cells.
|
||||
* Filter destinations by distance, build chunks, compute travel times for one origin.
|
||||
* Returns only the filtered subset indices and their travel times.
|
||||
*/
|
||||
static List<DestinationChunk> buildDestinationChunks(double[] lats, double[] lons) {
|
||||
static FilteredResult computeForOrigin(
|
||||
TransportNetwork network,
|
||||
double[] allLats, double[] allLons,
|
||||
double originLat, double originLon,
|
||||
String mode, LocalDate date) {
|
||||
|
||||
double maxRadius = maxRadiusKm(mode);
|
||||
|
||||
// 1. Filter destinations by bounding box
|
||||
int[] filtered = filterByDistance(allLats, allLons, originLat, originLon, maxRadius);
|
||||
if (filtered.length == 0) {
|
||||
return new FilteredResult(new int[0], new short[0]);
|
||||
}
|
||||
|
||||
// 2. Extract filtered coordinate arrays
|
||||
double[] fLats = new double[filtered.length];
|
||||
double[] fLons = new double[filtered.length];
|
||||
for (int i = 0; i < filtered.length; i++) {
|
||||
fLats[i] = allLats[filtered[i]];
|
||||
fLons[i] = allLons[filtered[i]];
|
||||
}
|
||||
|
||||
// 3. Build chunks from filtered destinations
|
||||
List<DestinationChunk> chunks = buildDestinationChunks(fLats, fLons);
|
||||
|
||||
// 4. Compute travel times
|
||||
short[] times = computeTravelTimes(network, chunks, originLat, originLon, mode, fLats.length, date);
|
||||
|
||||
return new FilteredResult(filtered, times);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter destination indices to those within a bounding box of maxRadiusKm from origin.
|
||||
* Uses degree-based approximation — slightly overestimates at corners, which is fine.
|
||||
*/
|
||||
private static int[] filterByDistance(
|
||||
double[] lats, double[] lons,
|
||||
double originLat, double originLon,
|
||||
double maxRadiusKm) {
|
||||
|
||||
double degLat = maxRadiusKm / 111.0;
|
||||
double degLon = maxRadiusKm / (111.0 * Math.cos(Math.toRadians(originLat)));
|
||||
|
||||
double minLat = originLat - degLat;
|
||||
double maxLat = originLat + degLat;
|
||||
double minLon = originLon - degLon;
|
||||
double maxLon = originLon + degLon;
|
||||
|
||||
// Two-pass: count then fill (avoids ArrayList/boxing overhead)
|
||||
int count = 0;
|
||||
for (int i = 0; i < lats.length; i++) {
|
||||
if (lats[i] >= minLat && lats[i] <= maxLat && lons[i] >= minLon && lons[i] <= maxLon) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
int[] result = new int[count];
|
||||
int j = 0;
|
||||
for (int i = 0; i < lats.length; i++) {
|
||||
if (lats[i] >= minLat && lats[i] <= maxLat && lons[i] >= minLon && lons[i] <= maxLon) {
|
||||
result[j++] = i;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split destinations into geographic chunks that each fit within R5's grid cell limit.
|
||||
* Sorts by latitude and splits into bands so each band's bounding box is under 5M cells.
|
||||
*/
|
||||
private static List<DestinationChunk> buildDestinationChunks(double[] lats, double[] lons) {
|
||||
int n = lats.length;
|
||||
|
||||
// Sort indices by latitude for geographic chunking
|
||||
|
|
@ -94,13 +172,11 @@ public class Router {
|
|||
start = end;
|
||||
}
|
||||
|
||||
System.err.printf(" Split into %d chunks at zoom %d (grid width %d, max height %d)%n",
|
||||
chunks.size(), ZOOM, gridWidth, maxHeight);
|
||||
return chunks;
|
||||
}
|
||||
|
||||
/** Compute travel times from one origin to all destinations across all chunks. */
|
||||
static short[] computeTravelTimes(
|
||||
private static short[] computeTravelTimes(
|
||||
TransportNetwork network, List<DestinationChunk> chunks,
|
||||
double originLat, double originLon, String mode, int nDest, LocalDate date) {
|
||||
|
||||
|
|
@ -125,6 +201,10 @@ public class Router {
|
|||
return times;
|
||||
}
|
||||
|
||||
// --- Private helpers ---
|
||||
|
||||
private record DestinationChunk(FreeFormPointSet pointSet, WebMercatorExtents extents, int[] originalIndices) {}
|
||||
|
||||
private static DestinationChunk buildChunk(
|
||||
double[] lats, double[] lons, Integer[] sorted, int start, int end) {
|
||||
int size = end - start;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue