docs(pbf): archive Full production provenance and engineering history
This commit is contained in:
190
coastline/export_fukuoka_saga_map_assets.py
Normal file
190
coastline/export_fukuoka_saga_map_assets.py
Normal file
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
NavSea coastline subset export v1.0
|
||||
|
||||
把福冈/佐贺海岸样例栅格导出为浏览器可直接加载的 GeoJSON 资产:
|
||||
- blocked cells (WGS84 GeoJSON)
|
||||
- coastline lines (WGS84 GeoJSON)
|
||||
- manifest JSON
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import math
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
from shapely import from_wkt
|
||||
from shapely.geometry import mapping, Polygon, LineString
|
||||
|
||||
|
||||
RADIUS = 6378137.0
|
||||
|
||||
|
||||
def mercator_to_lonlat(x: float, y: float) -> tuple[float, float]:
|
||||
lon = math.degrees(x / RADIUS)
|
||||
lat = math.degrees(2.0 * math.atan(math.exp(y / RADIUS)) - math.pi / 2.0)
|
||||
return lon, lat
|
||||
|
||||
|
||||
def cell_polygon_lonlat(minx: float, miny: float, maxx: float, maxy: float) -> Polygon:
|
||||
lon1, lat1 = mercator_to_lonlat(minx, miny)
|
||||
lon2, lat2 = mercator_to_lonlat(maxx, maxy)
|
||||
return Polygon(
|
||||
[
|
||||
(lon1, lat1),
|
||||
(lon2, lat1),
|
||||
(lon2, lat2),
|
||||
(lon1, lat2),
|
||||
(lon1, lat1),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def line_lonlat_from_wkt(wkt: str) -> LineString:
|
||||
geom = from_wkt(wkt)
|
||||
coords = [mercator_to_lonlat(x, y) for x, y in geom.coords]
|
||||
return LineString(coords)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Export coastline grid assets to GeoJSON")
|
||||
parser.add_argument(
|
||||
"--source-db",
|
||||
default="out/coastline/fukuoka_saga_test/fukuoka_saga_coast_grid.sqlite",
|
||||
help="输入 SQLite",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output-dir",
|
||||
default="src/pbf/coastline",
|
||||
help="输出目录",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--slug",
|
||||
default="fukuoka_saga",
|
||||
help="输出文件前缀,如 fukuoka_saga / north_kyushu",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
project_root = Path(__file__).resolve().parent.parent
|
||||
source_db = Path(args.source_db)
|
||||
if not source_db.is_absolute():
|
||||
source_db = project_root / source_db
|
||||
out_dir = Path(args.output_dir)
|
||||
if not out_dir.is_absolute():
|
||||
out_dir = project_root / out_dir
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if not source_db.exists():
|
||||
raise SystemExit(f"missing source db: {source_db}")
|
||||
|
||||
conn = sqlite3.connect(source_db)
|
||||
conn.row_factory = sqlite3.Row
|
||||
|
||||
manifest = {
|
||||
"version": "v1.0",
|
||||
"source_db": str(source_db),
|
||||
"grid_file": f"{args.slug}_blocked_cells.geojson",
|
||||
"coastline_file": f"{args.slug}_coastline_lines.geojson",
|
||||
"source_scope": conn.execute("SELECT value FROM meta WHERE key='source_scope'").fetchone()[0],
|
||||
"source_code": conn.execute("SELECT value FROM meta WHERE key='source_code'").fetchone()[0],
|
||||
"cell_size_m": float(conn.execute("SELECT value FROM meta WHERE key='cell_size_m'").fetchone()[0]),
|
||||
"buffer_m": float(conn.execute("SELECT value FROM meta WHERE key='buffer_m'").fetchone()[0]),
|
||||
"grid_bounds_mercator": json.loads(
|
||||
conn.execute("SELECT value FROM meta WHERE key='grid_bounds_mercator'").fetchone()[0]
|
||||
),
|
||||
}
|
||||
|
||||
blocked_features = []
|
||||
blocked_rows = conn.execute(
|
||||
"""
|
||||
SELECT cell_id, ix, iy, minx, miny, maxx, maxy, state, state_name, source_mask, blocked_permille
|
||||
FROM grid_cell
|
||||
WHERE state = 2
|
||||
ORDER BY ix, iy
|
||||
"""
|
||||
).fetchall()
|
||||
|
||||
for row in blocked_rows:
|
||||
geom = cell_polygon_lonlat(row["minx"], row["miny"], row["maxx"], row["maxy"])
|
||||
blocked_features.append(
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"cell_id": row["cell_id"],
|
||||
"ix": row["ix"],
|
||||
"iy": row["iy"],
|
||||
"state": row["state"],
|
||||
"state_name": row["state_name"],
|
||||
"source_mask": row["source_mask"],
|
||||
"blocked_permille": row["blocked_permille"],
|
||||
},
|
||||
"geometry": mapping(geom),
|
||||
}
|
||||
)
|
||||
|
||||
coastline_features = []
|
||||
coastline_rows = conn.execute(
|
||||
"""
|
||||
SELECT source_code, prefecture, curve_id, geom_wkt
|
||||
FROM coastline_feature
|
||||
ORDER BY feature_id
|
||||
"""
|
||||
).fetchall()
|
||||
|
||||
for row in coastline_rows:
|
||||
geom = line_lonlat_from_wkt(row["geom_wkt"])
|
||||
coastline_features.append(
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"source_code": row["source_code"],
|
||||
"prefecture": row["prefecture"],
|
||||
"curve_id": row["curve_id"],
|
||||
},
|
||||
"geometry": mapping(geom),
|
||||
}
|
||||
)
|
||||
|
||||
blocked_geojson = {
|
||||
"type": "FeatureCollection",
|
||||
"name": "fukuoka_saga_blocked_cells",
|
||||
"features": blocked_features,
|
||||
}
|
||||
coastline_geojson = {
|
||||
"type": "FeatureCollection",
|
||||
"name": "fukuoka_saga_coastline_lines",
|
||||
"features": coastline_features,
|
||||
}
|
||||
|
||||
(out_dir / f"{args.slug}_blocked_cells.geojson").write_text(
|
||||
json.dumps(blocked_geojson, ensure_ascii=False),
|
||||
encoding="utf-8",
|
||||
)
|
||||
(out_dir / f"{args.slug}_coastline_lines.geojson").write_text(
|
||||
json.dumps(coastline_geojson, ensure_ascii=False),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
lonlat_min = mercator_to_lonlat(manifest["grid_bounds_mercator"][0], manifest["grid_bounds_mercator"][1])
|
||||
lonlat_max = mercator_to_lonlat(manifest["grid_bounds_mercator"][2], manifest["grid_bounds_mercator"][3])
|
||||
manifest["grid_bounds_lonlat"] = [lonlat_min[0], lonlat_min[1], lonlat_max[0], lonlat_max[1]]
|
||||
manifest["blocked_count"] = len(blocked_features)
|
||||
manifest["coastline_count"] = len(coastline_features)
|
||||
|
||||
(out_dir / f"{args.slug}_manifest.json").write_text(
|
||||
json.dumps(manifest, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
conn.close()
|
||||
print("已导出:")
|
||||
print(" -", out_dir / f"{args.slug}_blocked_cells.geojson")
|
||||
print(" -", out_dir / f"{args.slug}_coastline_lines.geojson")
|
||||
print(" -", out_dir / f"{args.slug}_manifest.json")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user