93 lines
3.5 KiB
Python
93 lines
3.5 KiB
Python
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()
|