#!/usr/bin/env python3 """Verify Ground Zero neighbor tile edges before multi-tile stitching.""" from __future__ import annotations import json from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] REGISTRY_PATH = REPO_ROOT / "Data/Tiles/ground_zero_tiles.json" OUTPUT_PATH = ( REPO_ROOT / "Data/Terrain/Analysis/gz_us_ca_pacifica_utm10n_e544_n4160/" / "gz_us_ca_pacifica_utm10n_e544_n4160_neighbor_edge_verification.json" ) GROUND_ZERO_TILE_ID = "gz_us_ca_pacifica_utm10n_e544_n4160" TILE_SIZE_M = 1000 DIRECTION_OFFSETS = { "n": (0, 1), "ne": (1, 1), "e": (1, 0), "se": (1, -1), "s": (0, -1), "sw": (-1, -1), "w": (-1, 0), "nw": (-1, 1), } def load_registry() -> dict: return json.loads(REGISTRY_PATH.read_text()) def grid(tile: dict) -> dict: return tile["grid"] def expected_tile_id(tile_family: str, easting_min_m: int, northing_min_m: int) -> str: return f"{tile_family}_e{easting_min_m // 1000}_n{northing_min_m // 1000}" def verify_tile_shape(tile: dict) -> list[str]: errors: list[str] = [] g = grid(tile) width = g["easting_max_m"] - g["easting_min_m"] height = g["northing_max_m"] - g["northing_min_m"] if width != TILE_SIZE_M: errors.append(f"width is {width}m, expected {TILE_SIZE_M}m") if height != TILE_SIZE_M: errors.append(f"height is {height}m, expected {TILE_SIZE_M}m") if g["tile_size_m"] != TILE_SIZE_M: errors.append(f"tile_size_m is {g['tile_size_m']}, expected {TILE_SIZE_M}") if g["utm_zone"] != "10N": errors.append(f"utm_zone is {g['utm_zone']}, expected 10N") if g["projection"] != "WGS84 / UTM zone 10N": errors.append(f"projection is {g['projection']}, expected WGS84 / UTM zone 10N") tile_family = "_".join(tile["tile_id"].split("_")[:-2]) expected_id = expected_tile_id(tile_family, g["easting_min_m"], g["northing_min_m"]) if tile["tile_id"] != expected_id: errors.append(f"tile_id is {tile['tile_id']}, expected {expected_id}") return errors def expected_bounds(center_grid: dict, dx: int, dy: int) -> dict: easting_min = center_grid["easting_min_m"] + dx * TILE_SIZE_M northing_min = center_grid["northing_min_m"] + dy * TILE_SIZE_M return { "easting_min_m": easting_min, "northing_min_m": northing_min, "easting_max_m": easting_min + TILE_SIZE_M, "northing_max_m": northing_min + TILE_SIZE_M, } def verify_neighbor_edge(direction: str, center: dict, neighbor: dict) -> dict: center_grid = grid(center) neighbor_grid = grid(neighbor) dx, dy = DIRECTION_OFFSETS[direction] expected = expected_bounds(center_grid, dx, dy) errors: list[str] = [] for key, expected_value in expected.items(): actual_value = neighbor_grid[key] if actual_value != expected_value: errors.append(f"{key} is {actual_value}, expected {expected_value}") if direction == "n": shared_edge = { "center_edge": "north", "neighbor_edge": "south", "shared_northing_m": center_grid["northing_max_m"], "easting_span_m": [center_grid["easting_min_m"], center_grid["easting_max_m"]], } if neighbor_grid["northing_min_m"] != center_grid["northing_max_m"]: errors.append("north neighbor south edge does not meet Ground Zero north edge") elif direction == "s": shared_edge = { "center_edge": "south", "neighbor_edge": "north", "shared_northing_m": center_grid["northing_min_m"], "easting_span_m": [center_grid["easting_min_m"], center_grid["easting_max_m"]], } if neighbor_grid["northing_max_m"] != center_grid["northing_min_m"]: errors.append("south neighbor north edge does not meet Ground Zero south edge") elif direction == "e": shared_edge = { "center_edge": "east", "neighbor_edge": "west", "shared_easting_m": center_grid["easting_max_m"], "northing_span_m": [center_grid["northing_min_m"], center_grid["northing_max_m"]], } if neighbor_grid["easting_min_m"] != center_grid["easting_max_m"]: errors.append("east neighbor west edge does not meet Ground Zero east edge") elif direction == "w": shared_edge = { "center_edge": "west", "neighbor_edge": "east", "shared_easting_m": center_grid["easting_min_m"], "northing_span_m": [center_grid["northing_min_m"], center_grid["northing_max_m"]], } if neighbor_grid["easting_max_m"] != center_grid["easting_min_m"]: errors.append("west neighbor east edge does not meet Ground Zero west edge") else: shared_edge = { "center_corner_touch": direction, "corner_touch_only": True, "expected_bounds": expected, } return { "direction": direction, "neighbor_tile_id": neighbor["tile_id"], "expected_bounds_utm_m": expected, "actual_bounds_utm_m": { "easting_min_m": neighbor_grid["easting_min_m"], "northing_min_m": neighbor_grid["northing_min_m"], "easting_max_m": neighbor_grid["easting_max_m"], "northing_max_m": neighbor_grid["northing_max_m"], }, "shared_edge": shared_edge, "passed": not errors, "errors": errors, } def main() -> None: registry = load_registry() tiles_by_id = {tile["tile_id"]: tile for tile in registry["tiles"]} center = tiles_by_id[GROUND_ZERO_TILE_ID] tile_shape_checks = [] for tile in registry["tiles"]: errors = verify_tile_shape(tile) tile_shape_checks.append( { "tile_id": tile["tile_id"], "passed": not errors, "errors": errors, } ) neighbor_checks = [] missing_neighbors = [] for direction in ["n", "ne", "e", "se", "s", "sw", "w", "nw"]: neighbor_id = center["neighbors"].get(direction) if not neighbor_id or neighbor_id not in tiles_by_id: missing_neighbors.append({"direction": direction, "neighbor_tile_id": neighbor_id}) continue neighbor_checks.append(verify_neighbor_edge(direction, center, tiles_by_id[neighbor_id])) passed = ( not missing_neighbors and all(check["passed"] for check in neighbor_checks) and all(check["passed"] for check in tile_shape_checks) ) result = { "schema_version": 1, "tile_id": GROUND_ZERO_TILE_ID, "registry_path": str(REGISTRY_PATH.relative_to(REPO_ROOT)), "grid_scheme": registry["grid_scheme"], "verified_tile_count": len(registry["tiles"]), "expected_neighbor_count": 8, "found_neighbor_count": len(neighbor_checks), "all_passed": passed, "missing_neighbors": missing_neighbors, "tile_shape_checks": tile_shape_checks, "neighbor_edge_checks": neighbor_checks, "stitching_decision": { "ready_for_coordinate_based_neighbor_stitching": passed, "notes": [ "All Ground Zero neighbor placeholders align on exact 1000m UTM boundaries.", "Cardinal neighbors share full 1km edges with no gap or overlap.", "Diagonal neighbors touch at corners only.", "This verifies registry coordinates, not yet elevation seam continuity.", ], }, } OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) OUTPUT_PATH.write_text(json.dumps(result, indent=2) + "\n") print(f"Wrote {OUTPUT_PATH.relative_to(REPO_ROOT)}") print( json.dumps( { "all_passed": result["all_passed"], "verified_tile_count": result["verified_tile_count"], "found_neighbor_count": result["found_neighbor_count"], "missing_neighbors": result["missing_neighbors"], }, indent=2, ) ) if __name__ == "__main__": main()