Link Ground Zero weather lookup coordinates

This commit is contained in:
2026-05-16 02:09:11 -07:00
parent 08a1df6ebe
commit a7292bbae1
10 changed files with 217 additions and 10 deletions
+1 -1
View File
@@ -442,7 +442,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
- [x] Choose Ground Zero real-world 1 km x 1 km MVP tile. - [x] Choose Ground Zero real-world 1 km x 1 km MVP tile.
- [x] Choose MVP biome based on Ground Zero location. - [x] Choose MVP biome based on Ground Zero location.
- [ ] Link Ground Zero tile coordinates to real-world weather lookup coordinates. - [x] Link Ground Zero tile coordinates to real-world weather lookup coordinates. Added explicit Ground Zero `weather_lookup_metadata` with tile-center latitude `37.5925`, longitude `-122.4995`, Open-Meteo primary routing, NOAA/NWS fallback eligibility, manifest generation support, registry/schema documentation, and a verifier so future source-backed tiles use declared lookup metadata instead of one-off hard-coded coordinates.
- [~] Create playable test map. - [~] Create playable test map.
- [x] Add terrain base from real elevation data. - [x] Add terrain base from real elevation data.
- [x] Add first-pass water depth/shoreline handling if applicable. - [x] Add first-pass water depth/shoreline handling if applicable.
+9
View File
@@ -16,6 +16,15 @@
"center_latitude": 37.5925, "center_latitude": 37.5925,
"center_longitude": -122.4995 "center_longitude": -122.4995
}, },
"weather_lookup_metadata": {
"lookup_latitude": 37.5925,
"lookup_longitude": -122.4995,
"coordinate_source": "tile_center",
"primary_provider": "open-meteo",
"fallback_provider": "noaa-nws",
"fallback_provider_eligible": true,
"lookup_rule": "Use the Ground Zero tile center latitude/longitude for live MVP weather and temperature snapshots."
},
"status": "source_data_found", "status": "source_data_found",
"biome_primary": "coastal_california_scrub_woodland", "biome_primary": "coastal_california_scrub_woodland",
"biome_secondary": [ "biome_secondary": [
+43
View File
@@ -113,6 +113,9 @@
"growing_season_metadata": { "growing_season_metadata": {
"$ref": "#/$defs/growing_season_metadata" "$ref": "#/$defs/growing_season_metadata"
}, },
"weather_lookup_metadata": {
"$ref": "#/$defs/weather_lookup_metadata"
},
"notes": { "notes": {
"type": "string" "type": "string"
} }
@@ -241,6 +244,46 @@
} }
} }
}, },
"weather_lookup_metadata": {
"type": "object",
"required": [
"lookup_latitude",
"lookup_longitude",
"coordinate_source",
"primary_provider",
"fallback_provider_eligible",
"lookup_rule"
],
"additionalProperties": false,
"properties": {
"lookup_latitude": {
"type": "number",
"minimum": -90,
"maximum": 90
},
"lookup_longitude": {
"type": "number",
"minimum": -180,
"maximum": 180
},
"coordinate_source": {
"type": "string",
"enum": ["tile_center", "manual_override", "provider_grid_point"]
},
"primary_provider": {
"type": "string"
},
"fallback_provider": {
"type": "string"
},
"fallback_provider_eligible": {
"type": "boolean"
},
"lookup_rule": {
"type": "string"
}
}
},
"source": { "source": {
"type": "object", "type": "object",
"required": ["source_kind", "source_name", "coverage_status"], "required": ["source_kind", "source_name", "coverage_status"],
+13
View File
@@ -64,6 +64,19 @@ CREATE TABLE IF NOT EXISTS terrain_tile_growing_season_metadata (
FOREIGN KEY (tile_id) REFERENCES terrain_tiles(tile_id) FOREIGN KEY (tile_id) REFERENCES terrain_tiles(tile_id)
); );
CREATE TABLE IF NOT EXISTS terrain_tile_weather_lookup_metadata (
tile_id TEXT PRIMARY KEY,
lookup_latitude REAL NOT NULL CHECK (lookup_latitude >= -90 AND lookup_latitude <= 90),
lookup_longitude REAL NOT NULL CHECK (lookup_longitude >= -180 AND lookup_longitude <= 180),
coordinate_source TEXT NOT NULL CHECK (coordinate_source IN ('tile_center', 'manual_override', 'provider_grid_point')),
primary_provider TEXT NOT NULL DEFAULT 'open-meteo',
fallback_provider TEXT NOT NULL DEFAULT '',
fallback_provider_eligible INTEGER NOT NULL DEFAULT 0 CHECK (fallback_provider_eligible IN (0, 1)),
lookup_rule TEXT NOT NULL DEFAULT '',
generated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (tile_id) REFERENCES terrain_tiles(tile_id)
);
CREATE TABLE IF NOT EXISTS terrain_tile_neighbors ( CREATE TABLE IF NOT EXISTS terrain_tile_neighbors (
tile_id TEXT NOT NULL, tile_id TEXT NOT NULL,
direction TEXT NOT NULL CHECK (direction IN ('n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw')), direction TEXT NOT NULL CHECK (direction IN ('n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw')),
+5 -2
View File
@@ -1,6 +1,6 @@
{ {
"schema_version": 1, "schema_version": 1,
"generated_at_utc": "2026-05-16T05:58:06Z", "generated_at_utc": "2026-05-16T09:06:51Z",
"source_registry": "Data/Tiles/ground_zero_tiles.json", "source_registry": "Data/Tiles/ground_zero_tiles.json",
"generation_rule": "Every source-backed/generated tile with center coordinates is emitted; unknown placeholders are skipped.", "generation_rule": "Every source-backed/generated tile with center coordinates is emitted; unknown placeholders are skipped.",
"tiles": [ "tiles": [
@@ -8,8 +8,11 @@
"tile_id": "gz_us_ca_pacifica_utm10n_e544_n4160", "tile_id": "gz_us_ca_pacifica_utm10n_e544_n4160",
"center_latitude": 37.5925, "center_latitude": 37.5925,
"center_longitude": -122.4995, "center_longitude": -122.4995,
"coordinate_source": "tile_center",
"provider": "open-meteo", "provider": "open-meteo",
"lookup_rule": "Use tile center latitude/longitude for live MVP weather and temperature snapshots." "fallback_provider": "noaa-nws",
"fallback_provider_eligible": true,
"lookup_rule": "Use the Ground Zero tile center latitude/longitude for live MVP weather and temperature snapshots."
} }
] ]
} }
+8
View File
@@ -184,6 +184,14 @@ source-backed, generated, validated, packaged, or published tile with center
coordinates, while placeholder/unknown tiles are skipped. Future source-backed coordinates, while placeholder/unknown tiles are skipped. Future source-backed
tiles therefore become weather-eligible when their registry entries are added. tiles therefore become weather-eligible when their registry entries are added.
Ground Zero weather lookup coordinates are explicit in the tile registry. The
current MVP tile `gz_us_ca_pacifica_utm10n_e544_n4160` uses its tile center,
latitude `37.5925` and longitude `-122.4995`, as the canonical real-world
weather lookup point. `Scripts/generate_tile_weather_manifest.py` reads
`weather_lookup_metadata` when present, falls back to the tile center for
source-backed tiles, and emits the provider routing used by the weather
subsystem.
Open-Meteo is the first global MVP weather source. The provider contract is Open-Meteo is the first global MVP weather source. The provider contract is
stored in `Data/Weather/open_meteo_mvp_source.json`, including the forecast stored in `Data/Weather/open_meteo_mvp_source.json`, including the forecast
endpoint, requested current/daily variables, tile lookup rule, and Agrarian endpoint, requested current/daily variables, tile lookup rule, and Agrarian
+24
View File
@@ -78,6 +78,10 @@ Optional growing-zone fields follow the same rule. Crop viability, frost-free
windows, and growing-season length are generated only for tiles with real source windows, and growing-season length are generated only for tiles with real source
work or published packages and explicit growing-zone data. work or published packages and explicit growing-zone data.
Optional weather lookup fields are stored only for active/source-backed tiles.
For MVP, Ground Zero uses its tile-center latitude/longitude as the canonical
real-world lookup coordinate for live weather and temperature.
### `terrain_tile_neighbors` ### `terrain_tile_neighbors`
Tracks adjacency for stitching and prefetching. Tracks adjacency for stitching and prefetching.
@@ -136,6 +140,26 @@ Required fields:
- `data_basis` - `data_basis`
- `generated_at` - `generated_at`
### `terrain_tile_weather_lookup_metadata`
Tracks the real-world coordinate and provider routing used to fetch live
weather and temperature snapshots for a source-backed tile.
Required fields:
- `tile_id`
- `lookup_latitude`
- `lookup_longitude`
- `coordinate_source`
- `primary_provider`
- `fallback_provider_eligible`
- `lookup_rule`
Optional fields:
- `fallback_provider`
- `generated_at`
### `terrain_tile_packages` ### `terrain_tile_packages`
Tracks generated downloadable packages. Tracks generated downloadable packages.
+11 -4
View File
@@ -28,8 +28,9 @@ def main() -> None:
status = tile.get("status") status = tile.get("status")
tile_id = tile.get("tile_id") tile_id = tile.get("tile_id")
grid = tile.get("grid", {}) grid = tile.get("grid", {})
latitude = grid.get("center_latitude") weather_lookup = tile.get("weather_lookup_metadata", {})
longitude = grid.get("center_longitude") latitude = weather_lookup.get("lookup_latitude", grid.get("center_latitude"))
longitude = weather_lookup.get("lookup_longitude", grid.get("center_longitude"))
if status not in WEATHER_READY_STATUSES or not tile_id or latitude is None or longitude is None: if status not in WEATHER_READY_STATUSES or not tile_id or latitude is None or longitude is None:
continue continue
@@ -38,8 +39,14 @@ def main() -> None:
"tile_id": tile_id, "tile_id": tile_id,
"center_latitude": latitude, "center_latitude": latitude,
"center_longitude": longitude, "center_longitude": longitude,
"provider": "open-meteo", "coordinate_source": weather_lookup.get("coordinate_source", "tile_center"),
"lookup_rule": "Use tile center latitude/longitude for live MVP weather and temperature snapshots.", "provider": weather_lookup.get("primary_provider", "open-meteo"),
"fallback_provider": weather_lookup.get("fallback_provider", ""),
"fallback_provider_eligible": bool(weather_lookup.get("fallback_provider_eligible", False)),
"lookup_rule": weather_lookup.get(
"lookup_rule",
"Use tile center latitude/longitude for live MVP weather and temperature snapshots.",
),
} }
) )
@@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""Verify Ground Zero weather lookup coordinates are registry-driven."""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
REGISTRY = ROOT / "Data" / "Tiles" / "ground_zero_tiles.json"
SCHEMA = ROOT / "Data" / "Tiles" / "tile_registry.schema.json"
MANIFEST = ROOT / "Data" / "Tiles" / "tile_weather_manifest.json"
GENERATOR = ROOT / "Scripts" / "generate_tile_weather_manifest.py"
GAME_STATE_H = ROOT / "Source" / "AgrarianGame" / "AgrarianGameState.h"
TDD = ROOT / "Docs" / "TechnicalDesignDocument.md"
REGISTRY_DOC = ROOT / "Docs" / "Terrain" / "TileRegistrySchema.md"
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
GROUND_ZERO_TILE_ID = "gz_us_ca_pacifica_utm10n_e544_n4160"
GROUND_ZERO_LATITUDE = 37.5925
GROUND_ZERO_LONGITUDE = -122.4995
def assert_close(name: str, actual: float, expected: float) -> None:
if abs(actual - expected) > 0.00001:
raise RuntimeError(f"{name} expected {expected}, got {actual}")
def main() -> None:
registry = json.loads(REGISTRY.read_text(encoding="utf-8"))
schema_text = SCHEMA.read_text(encoding="utf-8")
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
generator_text = GENERATOR.read_text(encoding="utf-8")
game_state_text = GAME_STATE_H.read_text(encoding="utf-8")
ground_zero = next((tile for tile in registry["tiles"] if tile.get("tile_id") == GROUND_ZERO_TILE_ID), None)
if ground_zero is None:
raise RuntimeError("Ground Zero tile is missing from registry")
grid = ground_zero["grid"]
lookup = ground_zero.get("weather_lookup_metadata")
if not lookup:
raise RuntimeError("Ground Zero tile is missing weather_lookup_metadata")
assert_close("grid center latitude", float(grid["center_latitude"]), GROUND_ZERO_LATITUDE)
assert_close("grid center longitude", float(grid["center_longitude"]), GROUND_ZERO_LONGITUDE)
assert_close("weather lookup latitude", float(lookup["lookup_latitude"]), GROUND_ZERO_LATITUDE)
assert_close("weather lookup longitude", float(lookup["lookup_longitude"]), GROUND_ZERO_LONGITUDE)
if lookup.get("coordinate_source") != "tile_center":
raise RuntimeError("Ground Zero weather coordinate_source should be tile_center")
if lookup.get("primary_provider") != "open-meteo":
raise RuntimeError("Ground Zero primary weather provider should be open-meteo")
if lookup.get("fallback_provider") != "noaa-nws":
raise RuntimeError("Ground Zero fallback weather provider should be noaa-nws")
if lookup.get("fallback_provider_eligible") is not True:
raise RuntimeError("Ground Zero should be NOAA/NWS fallback eligible")
weather_records = [tile for tile in manifest.get("tiles", []) if tile.get("tile_id") == GROUND_ZERO_TILE_ID]
if len(weather_records) != 1:
raise RuntimeError(f"Expected exactly one Ground Zero weather manifest record, got {len(weather_records)}")
manifest_record = weather_records[0]
assert_close("manifest latitude", float(manifest_record["center_latitude"]), GROUND_ZERO_LATITUDE)
assert_close("manifest longitude", float(manifest_record["center_longitude"]), GROUND_ZERO_LONGITUDE)
if manifest_record.get("coordinate_source") != "tile_center":
raise RuntimeError("Weather manifest should preserve the coordinate source")
if manifest_record.get("provider") != "open-meteo":
raise RuntimeError("Weather manifest should preserve the primary provider")
if manifest_record.get("fallback_provider") != "noaa-nws":
raise RuntimeError("Weather manifest should preserve the fallback provider")
required_snippets = {
"schema": ["weather_lookup_metadata", "lookup_latitude", "fallback_provider_eligible"],
"generator": ["weather_lookup_metadata", "coordinate_source", "fallback_provider_eligible"],
"game_state": ["ActiveTileLatitude = 37.5925f", "ActiveTileLongitude = -122.4995f"],
"tdd": ["Ground Zero weather lookup coordinates", "37.5925", "-122.4995"],
"registry_doc": ["terrain_tile_weather_lookup_metadata", "lookup_latitude", "primary_provider"],
"roadmap": ["[x] Link Ground Zero tile coordinates to real-world weather lookup coordinates."],
}
checks = {
"schema": schema_text,
"generator": generator_text,
"game_state": game_state_text,
"tdd": TDD.read_text(encoding="utf-8"),
"registry_doc": REGISTRY_DOC.read_text(encoding="utf-8"),
"roadmap": ROADMAP.read_text(encoding="utf-8"),
}
missing = []
for label, snippets in required_snippets.items():
for snippet in snippets:
if snippet not in checks[label]:
missing.append(f"{label}: {snippet}")
if missing:
raise RuntimeError("Ground Zero weather lookup verification failed: " + "; ".join(missing))
print("Ground Zero weather lookup coordinate verification complete.")
if __name__ == "__main__":
main()
+2 -3
View File
@@ -26,14 +26,13 @@ EXPECTED = {
CPP: [ CPP: [
"current=temperature_2m,relative_humidity_2m,precipitation", "current=temperature_2m,relative_humidity_2m,precipitation",
"daily=temperature_2m_max,temperature_2m_min", "daily=temperature_2m_max,temperature_2m_min",
"GameState->SetRegionalTemperatureProfile", "GameState->ApplyMappedWeatherInputs",
"GameState->SetRegionalObservedTemperature",
"GameState->SetWeather",
"MapOpenMeteoWeatherCode", "MapOpenMeteoWeatherCode",
], ],
GENERATOR: [ GENERATOR: [
"WEATHER_READY_STATUSES", "WEATHER_READY_STATUSES",
"Every source-backed/generated tile", "Every source-backed/generated tile",
"weather_lookup_metadata",
"tile_weather_manifest.json", "tile_weather_manifest.json",
], ],
TDD: [ TDD: [