Files
map-asset-gateway/demo/create_sample_raster.py
T
Harmon 73ad03fa59 Rewrite the gateway around health, retries, slicing and a Windows service
Scanner
- manifest lookup is now depth-bounded and prunes numeric zoom directories, so
  a scan no longer walks the whole tile payload
- rescans only report a version as updated when something actually changed
- overlapping runs are refused instead of interleaving their bookkeeping

Hot path
- token authorisation loads only that token grants and throttles the
  last_used_at write instead of touching it on every tile
- SQLite runs in WAL with a busy timeout and a real connection pool
- the catalog lookup used by /tiles no longer loads every basemap

Security
- the admin surface requires ADMIN_TOKEN (bearer) or ADMIN_USER/ADMIN_PASSWORD
  (basic), and the service refuses to bind off-host without credentials
- CORS is an explicit allow-list; newly minted tokens never reach a redirect URL
- client tokens are redacted from the access log

Operations
- resource health inspection flags a moved or deleted tile root before a 404
- pushes retry with backoff and can be re-queued by hand; the schedule lives in
  the database so a restart does not lose it
- access log plus /metrics counters

Image slicing
- tile_jobs drive the GDAL bundled with QGIS via scripts/gdal2tiles-qgis.bat
- layer_sources remembers what a layer is made of: submit one source to append
  a flight, or all of them to rebuild the layer as a VRT mosaic with no tile
  seams; the console exposes both, including a one-click rebuild
- slicing is available to integrations through a narrow slice token scope
  (submit, poll, idempotent by code, completion callback)

Console
- rebuilt around a fixed rail and single-view navigation with health and
  outstanding work leading, instead of one long scrolling page

Windows service
- native install/start/stop/status with crash recovery, auto-start and a
  rotating log file, so no NSSM wrapper is needed
- start-all.bat and stop-all.bat are removed: they killed the process by port
  with taskkill, which fought the service restart policy

Verified with 49 tests and live runs against real tile data and real GDAL.
2026-10-16 10:35:30 +08:00

87 lines
2.5 KiB
Python

import argparse
import os
import sys
from pathlib import Path
def bootstrap_gdal_env() -> None:
env_root = Path(sys.executable).resolve().parent
gdal_paths = [
env_root / "Lib" / "site-packages" / "rasterio" / "gdal_data",
env_root / "Library" / "share" / "gdal",
env_root / "share" / "gdal",
]
proj_paths = [
env_root / "Lib" / "site-packages" / "rasterio" / "proj_data",
env_root / "Library" / "share" / "proj",
env_root / "share" / "proj",
]
for path in gdal_paths:
if path.exists():
os.environ["GDAL_DATA"] = str(path)
break
for path in proj_paths:
if path.exists():
os.environ["PROJ_LIB"] = str(path)
os.environ["PROJ_DATA"] = str(path)
break
bootstrap_gdal_env()
import numpy as np
import rasterio
from rasterio.transform import from_origin
def build_values(width: int, height: int, preset: str) -> np.ndarray:
yy, xx = np.mgrid[0:height, 0:width]
x = (xx / max(width - 1, 1)).astype("float32")
y = (yy / max(height - 1, 1)).astype("float32")
if preset == "phase":
values = np.sin((x * 8.0) + (y * 4.0)) * np.cos(y * 10.0)
else:
values = (x * 2.0 - 1.0) * 0.7 + np.sin(y * np.pi * 3.0) * 0.3
return values.astype("float32")
def main() -> int:
parser = argparse.ArgumentParser(description="Create a sample GeoTIFF for demos.")
parser.add_argument("--output", required=True, help="Output GeoTIFF path.")
parser.add_argument("--width", type=int, default=512)
parser.add_argument("--height", type=int, default=512)
parser.add_argument("--preset", choices=["ndvi", "phase"], default="ndvi")
parser.add_argument("--origin-x", type=float, default=110.0)
parser.add_argument("--origin-y", type=float, default=22.0)
parser.add_argument("--pixel-size", type=float, default=0.01)
args = parser.parse_args()
output = Path(args.output).resolve()
output.parent.mkdir(parents=True, exist_ok=True)
data = build_values(args.width, args.height, args.preset)
with rasterio.open(
output,
"w",
driver="GTiff",
width=args.width,
height=args.height,
count=1,
dtype="float32",
crs="EPSG:4326",
transform=from_origin(args.origin_x, args.origin_y, args.pixel_size, args.pixel_size),
nodata=-9999.0,
) as dst:
dst.write(data, 1)
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())