package com.evsuite.abrp; import org.json.JSONArray; import org.json.JSONObject; /** * The roads of one map tile as the server sends them (server/maps.go): per road its class * (0 motorway and trunk, 1 primary and secondary, 2 tertiary and unclassified, 3 residential) * and its points in tile units, plus each road's bounding box for skipping far ones quickly. * {@link #parse} fails closed: anything unexpected is no tile at all. Pure Java, unit-tested. */ final class RoadTile { static final int CLASSES = 4; /** Points may lie a little beyond the tile (a road is kept to its first point outside). */ private static final int SLACK = MapGeometry.EXTENT; private static final int MAX_ROADS = 20_000; final int x, y; final int[] classes; /** Per road: x0, y0, x1, y1, … in tile units. */ final int[][] points; /** Per road: minX, minY, maxX, maxY. */ final int[][] bounds; private RoadTile(int x, int y, int[] classes, int[][] points, int[][] bounds) { this.x = x; this.y = y; this.classes = classes; this.points = points; this.bounds = bounds; } /** The tile in {@code json}, which must be the one asked for; null when it is not usable. */ static RoadTile parse(String json, int expectX, int expectY) { try { JSONObject o = new JSONObject(json); if (o.getInt("z") != MapGeometry.ZOOM || o.getInt("x") != expectX || o.getInt("y") != expectY || o.getInt("extent") != MapGeometry.EXTENT) { return null; } JSONArray ways = o.getJSONArray("ways"); if (ways.length() > MAX_ROADS) return null; int n = ways.length(); int[] classes = new int[n]; int[][] points = new int[n][]; int[][] bounds = new int[n][]; for (int i = 0; i < n; i++) { JSONObject way = ways.getJSONObject(i); int c = way.getInt("c"); JSONArray p = way.getJSONArray("p"); if (c < 0 || c >= CLASSES || p.length() < 4 || p.length() % 2 != 0) return null; int[] pts = new int[p.length()]; int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE; for (int k = 0; k < pts.length; k++) { int value = p.getInt(k); if (value < -SLACK || value > MapGeometry.EXTENT + SLACK) return null; pts[k] = value; if (k % 2 == 0) { minX = Math.min(minX, value); maxX = Math.max(maxX, value); } else { minY = Math.min(minY, value); maxY = Math.max(maxY, value); } } classes[i] = c; points[i] = pts; bounds[i] = new int[] { minX, minY, maxX, maxY }; } return new RoadTile(expectX, expectY, classes, points, bounds); } catch (Exception e) { return null; } } }