Analyze Ground Zero shoreline requirements

This commit is contained in:
2026-05-14 05:53:53 -07:00
parent e9896cdce1
commit fcb3d18b0c
4 changed files with 242 additions and 2 deletions
+2 -2
View File
@@ -417,7 +417,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
- [x] Choose MVP biome based on Ground Zero location.
- [~] Create playable test map.
- [x] Add terrain base from real elevation data.
- [ ] Add first-pass water depth/shoreline handling if applicable.
- [x] Add first-pass water depth/shoreline handling if applicable.
- [ ] Add first-pass hill, mountain, river, stream, lake, and coastline handling if present in Ground Zero.
- [x] Add source metadata record for the MVP tile.
- [x] Add generated tile metadata record for the MVP tile.
@@ -1402,4 +1402,4 @@ Next version .01 priorities:
Immediate next item:
- [ ] Add first-pass water depth/shoreline handling if applicable.
- [ ] Add first-pass hill, mountain, river, stream, lake, and coastline handling if present in Ground Zero.
@@ -0,0 +1,74 @@
{
"schema_version": 1,
"tile_id": "gz_us_ca_pacifica_utm10n_e544_n4160",
"generated_at_utc": "2026-05-14T12:52:25Z",
"source_dem": "Data/Terrain/Extracted/gz_us_ca_pacifica_utm10n_e544_n4160/gz_us_ca_pacifica_utm10n_e544_n4160_1m_dem_subset.tif",
"thresholds": {
"sea_level_m": 0.0,
"shoreline_buffer_m": 2.0,
"low_coastal_buffer_m": 5.0
},
"elevation_summary": {
"sample_count": 1000000,
"min_elevation_m": 3.1603012084960938,
"max_elevation_m": 96.50570678710938,
"mean_elevation_m": 18.240529416484833,
"sea_level_or_below_samples": 0,
"near_sea_level_samples": 0,
"low_coastal_samples": 57812,
"sea_level_or_below_percent": 0.0,
"near_sea_level_percent": 0.0,
"low_coastal_percent": 5.7812
},
"edge_summary": {
"north": {
"sample_count": 10000,
"min_elevation_m": 3.1603012084960938,
"max_elevation_m": 37.224822998046875,
"mean_elevation_m": 19.415938945102692,
"sea_level_or_below_samples": 0,
"near_sea_level_samples": 0,
"low_coastal_samples": 2710
},
"south": {
"sample_count": 10000,
"min_elevation_m": 11.210755348205566,
"max_elevation_m": 57.378944396972656,
"mean_elevation_m": 19.908063950920106,
"sea_level_or_below_samples": 0,
"near_sea_level_samples": 0,
"low_coastal_samples": 0
},
"west": {
"sample_count": 10000,
"min_elevation_m": 3.5281026363372803,
"max_elevation_m": 59.69044876098633,
"mean_elevation_m": 15.149734993314743,
"sea_level_or_below_samples": 0,
"near_sea_level_samples": 0,
"low_coastal_samples": 2892
},
"east": {
"sample_count": 10000,
"min_elevation_m": 24.61846351623535,
"max_elevation_m": 96.50570678710938,
"mean_elevation_m": 59.40558109054565,
"sea_level_or_below_samples": 0,
"near_sea_level_samples": 0,
"low_coastal_samples": 0
}
},
"classification": {
"contains_open_water": false,
"contains_shoreline": false,
"water_depth_required_for_mvp_tile": false,
"shoreline_mesh_required_for_mvp_tile": false,
"freshwater_source_required_for_gameplay": true,
"recommended_first_pass": "No ocean/shoreline actor required inside the current Ground Zero tile. Add a small freshwater source for gameplay separately, and defer ocean/bathymetry work to west/southwest neighbor coastal tiles."
},
"notes": [
"This analysis uses the extracted 1-meter USGS DEM for the selected 1 km tile.",
"The selected Ground Zero tile is coastal-influenced but the DEM minimum is above sea level.",
"Ocean depth/bathymetry remains required for adjacent coastal/ocean tiles before multi-tile expansion."
]
}
+50
View File
@@ -0,0 +1,50 @@
# Ground Zero Water And Shoreline Pass
This pass determines whether the current 1 km Ground Zero MVP tile needs ocean,
bathymetry, or shoreline handling inside the playable demo map.
## Result
The current Ground Zero tile does not contain ocean water or a shoreline inside
its 1 km bounds. First-pass ocean depth and shoreline mesh work is therefore not
required for this tile.
The analysis found no sea-level or near-sea-level samples:
- Minimum DEM elevation: approximately `3.16 m`
- Sea-level or below samples: `0`
- Near-sea-level samples at `<= 2.0 m`: `0`
- Low coastal samples at `<= 5.0 m`: `57,812` out of `1,000,000`
The tile is still coastal-influenced, and water remains important for gameplay.
For the MVP map, add a small freshwater source separately from ocean handling.
Ocean, bathymetry, coastline, and surf/shoreline systems should be handled when
the west and southwest neighboring coastal tiles are brought into scope.
## Source
- Tile: `gz_us_ca_pacifica_utm10n_e544_n4160`
- DEM:
`Data/Terrain/Extracted/gz_us_ca_pacifica_utm10n_e544_n4160/gz_us_ca_pacifica_utm10n_e544_n4160_1m_dem_subset.tif`
- Analysis output:
`Data/Terrain/Analysis/gz_us_ca_pacifica_utm10n_e544_n4160/gz_us_ca_pacifica_utm10n_e544_n4160_water_shoreline_analysis.json`
## Thresholds
- Sea level: `0.0 m`
- Shoreline buffer: `<= 2.0 m`
- Low coastal buffer: `<= 5.0 m`
## Implementation Decision
- Do not place an ocean plane in the current Ground Zero tile.
- Do not fake bathymetry in the current tile.
- Keep NOAA/NCEI bathymetry in the source plan for coastal/ocean neighbor tiles.
- Add gameplay water as a freshwater source item or small placed water actor in
a later MVP map pass.
## Follow-Up
The next terrain/map pass should classify hills, slopes, streams, coastline
absence, and other visible landform features from the DEM so resource placement,
movement modifiers, and future World Partition stitching can use the same data.
+116
View File
@@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""Analyze first-pass water and shoreline applicability for the Ground Zero tile."""
from __future__ import annotations
import json
from datetime import datetime, timezone
from pathlib import Path
import numpy as np
from osgeo import gdal
gdal.UseExceptions()
PROJECT_ROOT = Path(__file__).resolve().parents[1]
TILE_ID = "gz_us_ca_pacifica_utm10n_e544_n4160"
DEM_PATH = PROJECT_ROOT / "Data" / "Terrain" / "Extracted" / TILE_ID / f"{TILE_ID}_1m_dem_subset.tif"
OUTPUT_DIR = PROJECT_ROOT / "Data" / "Terrain" / "Analysis" / TILE_ID
OUTPUT_PATH = OUTPUT_DIR / f"{TILE_ID}_water_shoreline_analysis.json"
SEA_LEVEL_M = 0.0
SHORELINE_BUFFER_M = 2.0
LOW_COASTAL_BUFFER_M = 5.0
def summarize_edge(values: np.ndarray) -> dict[str, float | int]:
finite = values[np.isfinite(values)]
return {
"sample_count": int(finite.size),
"min_elevation_m": float(np.min(finite)) if finite.size else None,
"max_elevation_m": float(np.max(finite)) if finite.size else None,
"mean_elevation_m": float(np.mean(finite)) if finite.size else None,
"sea_level_or_below_samples": int(np.count_nonzero(finite <= SEA_LEVEL_M)),
"near_sea_level_samples": int(np.count_nonzero(finite <= SHORELINE_BUFFER_M)),
"low_coastal_samples": int(np.count_nonzero(finite <= LOW_COASTAL_BUFFER_M)),
}
def main() -> None:
dataset = gdal.Open(str(DEM_PATH), gdal.GA_ReadOnly)
if dataset is None:
raise RuntimeError(f"Could not open DEM: {DEM_PATH}")
band = dataset.GetRasterBand(1)
data = band.ReadAsArray().astype(float)
nodata = band.GetNoDataValue()
if nodata is not None:
data[data == nodata] = np.nan
finite = data[np.isfinite(data)]
if finite.size == 0:
raise RuntimeError(f"DEM has no finite elevation samples: {DEM_PATH}")
total_samples = int(finite.size)
sea_or_below = int(np.count_nonzero(finite <= SEA_LEVEL_M))
near_sea = int(np.count_nonzero(finite <= SHORELINE_BUFFER_M))
low_coastal = int(np.count_nonzero(finite <= LOW_COASTAL_BUFFER_M))
edge_width = 10
edges = {
"north": summarize_edge(data[:edge_width, :].reshape(-1)),
"south": summarize_edge(data[-edge_width:, :].reshape(-1)),
"west": summarize_edge(data[:, :edge_width].reshape(-1)),
"east": summarize_edge(data[:, -edge_width:].reshape(-1)),
}
has_open_water = sea_or_below > 0
has_shoreline = near_sea > 0
result = {
"schema_version": 1,
"tile_id": TILE_ID,
"generated_at_utc": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"),
"source_dem": str(DEM_PATH.relative_to(PROJECT_ROOT)),
"thresholds": {
"sea_level_m": SEA_LEVEL_M,
"shoreline_buffer_m": SHORELINE_BUFFER_M,
"low_coastal_buffer_m": LOW_COASTAL_BUFFER_M,
},
"elevation_summary": {
"sample_count": total_samples,
"min_elevation_m": float(np.min(finite)),
"max_elevation_m": float(np.max(finite)),
"mean_elevation_m": float(np.mean(finite)),
"sea_level_or_below_samples": sea_or_below,
"near_sea_level_samples": near_sea,
"low_coastal_samples": low_coastal,
"sea_level_or_below_percent": sea_or_below / total_samples * 100.0,
"near_sea_level_percent": near_sea / total_samples * 100.0,
"low_coastal_percent": low_coastal / total_samples * 100.0,
},
"edge_summary": edges,
"classification": {
"contains_open_water": has_open_water,
"contains_shoreline": has_shoreline,
"water_depth_required_for_mvp_tile": False,
"shoreline_mesh_required_for_mvp_tile": False,
"freshwater_source_required_for_gameplay": True,
"recommended_first_pass": "No ocean/shoreline actor required inside the current Ground Zero tile. Add a small freshwater source for gameplay separately, and defer ocean/bathymetry work to west/southwest neighbor coastal tiles.",
},
"notes": [
"This analysis uses the extracted 1-meter USGS DEM for the selected 1 km tile.",
"The selected Ground Zero tile is coastal-influenced but the DEM minimum is above sea level.",
"Ocean depth/bathymetry remains required for adjacent coastal/ocean tiles before multi-tile expansion.",
],
}
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
OUTPUT_PATH.write_text(json.dumps(result, indent=2) + "\n", encoding="utf-8")
print(f"Wrote {OUTPUT_PATH.relative_to(PROJECT_ROOT)}")
print(json.dumps(result["classification"], indent=2))
if __name__ == "__main__":
main()