Link Ground Zero weather lookup coordinates
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -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": [
|
||||||
|
|||||||
@@ -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"],
|
||||||
|
|||||||
@@ -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')),
|
||||||
|
|||||||
@@ -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."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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: [
|
||||||
|
|||||||
Reference in New Issue
Block a user