191 lines
6.0 KiB
Python
191 lines
6.0 KiB
Python
#!/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()
|