docs(pbf): archive Full production provenance and engineering history
This commit is contained in:
92
navsea_reencode_unified_semantic_pbf.py
Normal file
92
navsea_reencode_unified_semantic_pbf.py
Normal file
@@ -0,0 +1,92 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
from pathlib import Path
|
||||
|
||||
import mapbox_vector_tile
|
||||
|
||||
from navsea_tile_reencode_guard import decode_tile_with_extents, encode_layers_preserving_extents
|
||||
|
||||
|
||||
BASE_ROOT = Path("/home/wwwroot/pbf-delivery-full-20260418-rebuild")
|
||||
OUTPUT_ROOT = Path("/home/wwwroot/pbf-delivery-full-semantic-unified-20260808")
|
||||
MAP_JSON = Path(__file__).resolve().parent / "src/pbf/semantic_sprite_key_map_2026-08-08.json"
|
||||
FIELDS = ("chart_icon_image", "chart_fill_pattern", "chart_line_pattern")
|
||||
|
||||
|
||||
def hardlink_or_copy(src: Path, dst: Path) -> None:
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
if dst.exists():
|
||||
dst.unlink()
|
||||
try:
|
||||
os.link(src, dst)
|
||||
except OSError:
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
|
||||
def rewrite_one(args):
|
||||
src_str, dst_str, value_map = args
|
||||
src = Path(src_str)
|
||||
dst = Path(dst_str)
|
||||
raw = src.read_bytes()
|
||||
decoded, extents = decode_tile_with_extents(raw)
|
||||
changed = 0
|
||||
layers = []
|
||||
for layer_name, layer in decoded.items():
|
||||
features = []
|
||||
for feature in layer.get("features", []):
|
||||
props = dict(feature.get("properties") or {})
|
||||
feature_changed = False
|
||||
for field in FIELDS:
|
||||
value = props.get(field)
|
||||
if isinstance(value, str) and value in value_map:
|
||||
props[field] = value_map[value]
|
||||
feature_changed = True
|
||||
if feature_changed:
|
||||
changed += 1
|
||||
out = {"geometry": feature["geometry"], "properties": props}
|
||||
if feature.get("id") is not None:
|
||||
out["id"] = feature["id"]
|
||||
features.append(out)
|
||||
layers.append({"name": layer_name, "features": features})
|
||||
if not changed:
|
||||
hardlink_or_copy(src, dst)
|
||||
return 0, 0
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
# The output tree is initially made with hard links to BASE_ROOT. Break
|
||||
# that link before writing so the legacy baseline can never be modified.
|
||||
if dst.exists():
|
||||
dst.unlink()
|
||||
dst.write_bytes(encode_layers_preserving_extents(layers, extents))
|
||||
return 1, changed
|
||||
|
||||
|
||||
def main() -> None:
|
||||
mapping = json.loads(MAP_JSON.read_text(encoding="utf-8"))
|
||||
mapping["fill-daytime-405"] = mapping["fill-daytime-401"]
|
||||
if OUTPUT_ROOT.exists():
|
||||
shutil.rmtree(OUTPUT_ROOT)
|
||||
OUTPUT_ROOT.mkdir(parents=True, exist_ok=True)
|
||||
subprocess.run(["cp", "-al", f"{BASE_ROOT}/.", str(OUTPUT_ROOT)], check=True)
|
||||
|
||||
command = ["rg", "-a", "-l", "--no-messages", "-F"]
|
||||
for key in mapping:
|
||||
command.extend(["-e", key])
|
||||
command.append(str(BASE_ROOT))
|
||||
result = subprocess.run(command, check=True, capture_output=True, text=True)
|
||||
paths = [Path(line) for line in result.stdout.splitlines() if line.strip()]
|
||||
jobs = [(str(src), str(OUTPUT_ROOT / src.relative_to(BASE_ROOT)), mapping) for src in paths]
|
||||
changed_tiles = changed_features = 0
|
||||
with ProcessPoolExecutor(max_workers=12) as executor:
|
||||
for tile_changed, feature_count in executor.map(rewrite_one, jobs, chunksize=64):
|
||||
changed_tiles += tile_changed
|
||||
changed_features += feature_count
|
||||
print(json.dumps({"base_tiles": len(list(BASE_ROOT.rglob("*.pbf"))), "scanned_tiles": len(paths), "changed_tiles": changed_tiles, "changed_features": changed_features, "output": str(OUTPUT_ROOT)}, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user