Build 20nm final release candidate
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# PBF Step Record
|
||||
|
||||
Last Updated: 2026-03-31
|
||||
Last Updated: 2026-04-01
|
||||
Repo: `/root/sourceserver/pbf`
|
||||
Remote: `ssh://git@nas:2222/tei/pbf.git`
|
||||
|
||||
@@ -12,13 +12,13 @@ Remote: `ssh://git@nas:2222/tei/pbf.git`
|
||||
|
||||
当前页面版本体系:
|
||||
|
||||
- `HTML: compare-r10-20260331-2213`
|
||||
- `HTML: compare-r11-20260401-0115`
|
||||
|
||||
当前页面支持 2 个 profile,但仍是同一张 HTML:
|
||||
|
||||
1. 唐津 20 海里
|
||||
- `Style: karatsu-style-r7-20260331-2045 (style.navsea-delivery-karatsu-10nm.json)`
|
||||
- `PBF: karatsu-pbf-20nm-20260325-1757 (pbf-delivery-karatsu-20nm)`
|
||||
- `Style: karatsu-final-style-r1-20260401-0115 (style.navsea-delivery-karatsu-20nm-final.json)`
|
||||
- `PBF: karatsu-final-pbf-20nm-20260401-0115 (pbf-delivery-karatsu-20nm-final)`
|
||||
|
||||
2. 九州
|
||||
- `Style: kyushu-style-r1-20260331-2205 (style.navsea-delivery-kyushu.json)`
|
||||
@@ -45,6 +45,7 @@ Remote: `ssh://git@nas:2222/tei/pbf.git`
|
||||
当前继续收敛的主 style 文件:
|
||||
|
||||
- [`src/pbf/style.navsea-delivery-karatsu-10nm.json`](/root/sourceserver/pbf/src/pbf/style.navsea-delivery-karatsu-10nm.json)
|
||||
- [`src/pbf/style.navsea-delivery-karatsu-20nm-final.json`](/root/sourceserver/pbf/src/pbf/style.navsea-delivery-karatsu-20nm-final.json)
|
||||
|
||||
当前九州 delivery style:
|
||||
|
||||
@@ -64,6 +65,65 @@ Remote: `ssh://git@nas:2222/tei/pbf.git`
|
||||
- 当前主矛盾是 `style`
|
||||
- 不是 `PBF 不见了`
|
||||
|
||||
## Final Release Candidate
|
||||
|
||||
当前已经产出一版 `20nm final release` 候选:
|
||||
|
||||
- 输出目录:
|
||||
- `/home/wwwroot/pbf-delivery-karatsu-20nm-final`
|
||||
- 当前在线对比页:
|
||||
- `http://192.168.200.184/newpec/navsea-compare-karatsu-20nm.html`
|
||||
- 当前候选 style:
|
||||
- [`src/pbf/style.navsea-delivery-karatsu-20nm-final.json`](/root/sourceserver/pbf/src/pbf/style.navsea-delivery-karatsu-20nm-final.json)
|
||||
|
||||
当前候选的生成方式:
|
||||
|
||||
- 以当前稳定 `/home/wwwroot/pbf-delivery-karatsu-20nm` 为输入
|
||||
- 使用 protobuf 层属性裁剪脚本生成
|
||||
- 脚本:
|
||||
- [`navsea_minimize_release_tiles.py`](/root/sourceserver/pbf/navsea_minimize_release_tiles.py)
|
||||
|
||||
当前 final candidate 只保留 `18` 个字段:
|
||||
|
||||
- `canonical_object_type`
|
||||
- `chart_fill_pattern`
|
||||
- `chart_fill_style`
|
||||
- `chart_icon_image`
|
||||
- `chart_label_position_code`
|
||||
- `chart_label_subtext`
|
||||
- `chart_label_text`
|
||||
- `chart_line_color`
|
||||
- `chart_line_width`
|
||||
- `chart_symbol_code`
|
||||
- `chart_text_color`
|
||||
- `chart_text_style`
|
||||
- `depth_value_m`
|
||||
- `least_depth_m`
|
||||
- `light_color_code`
|
||||
- `light_sector_mode`
|
||||
- `name_ja`
|
||||
- `place_name_en`
|
||||
|
||||
当前 final candidate 量化结果:
|
||||
|
||||
- feature instances:
|
||||
- `269883`
|
||||
- distinct `canonical_object_type`:
|
||||
- `137`
|
||||
- 原始 `20nm delivery` 大小:
|
||||
- `29,527,981` bytes
|
||||
- final candidate 大小:
|
||||
- `23,988,432` bytes
|
||||
- 压缩幅度约:
|
||||
- `18.8%`
|
||||
|
||||
当前判断:
|
||||
|
||||
- 这版已经满足“字段显著裁剪”
|
||||
- 但当前 AOI 图像审计仍显示大差异
|
||||
- 所以它目前只能算 `final release candidate`
|
||||
- 还不能直接认定为稳定最终版
|
||||
|
||||
## Audit Method
|
||||
|
||||
当前认可的可信审计方法:
|
||||
|
||||
127
navsea_minimize_release_tiles.py
Normal file
127
navsea_minimize_release_tiles.py
Normal file
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
from mapbox_vector_tile.Mapbox import vector_tile_pb2
|
||||
|
||||
|
||||
FINAL_RELEASE_PROPERTY_ALLOWLIST = frozenset(
|
||||
{
|
||||
"canonical_object_type",
|
||||
"chart_fill_pattern",
|
||||
"chart_fill_style",
|
||||
"chart_icon_image",
|
||||
"chart_label_position_code",
|
||||
"chart_label_subtext",
|
||||
"chart_label_text",
|
||||
"chart_line_color",
|
||||
"chart_line_width",
|
||||
"chart_symbol_code",
|
||||
"chart_text_color",
|
||||
"chart_text_style",
|
||||
"depth_value_m",
|
||||
"least_depth_m",
|
||||
"light_color_code",
|
||||
"light_sector_mode",
|
||||
"name_ja",
|
||||
"place_name_en",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Create a final release tile set by stripping non-style properties from an existing PBF tree."
|
||||
)
|
||||
parser.add_argument("--src-root", type=Path, required=True)
|
||||
parser.add_argument("--dst-root", type=Path, required=True)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def clone_value(dst_value: vector_tile_pb2.tile.value, src_value: vector_tile_pb2.tile.value) -> None:
|
||||
dst_value.CopyFrom(src_value)
|
||||
|
||||
|
||||
def strip_tile_properties(raw_tile: bytes, allowlist: set[str] | frozenset[str]) -> bytes:
|
||||
src_tile = vector_tile_pb2.tile()
|
||||
src_tile.ParseFromString(raw_tile)
|
||||
|
||||
dst_tile = vector_tile_pb2.tile()
|
||||
|
||||
for src_layer in src_tile.layers:
|
||||
dst_layer = dst_tile.layers.add()
|
||||
dst_layer.version = src_layer.version
|
||||
dst_layer.name = src_layer.name
|
||||
dst_layer.extent = src_layer.extent
|
||||
|
||||
kept_key_index: dict[int, int] = {}
|
||||
kept_value_index: dict[tuple[int, bytes], int] = {}
|
||||
|
||||
for src_feature in src_layer.features:
|
||||
dst_feature = dst_layer.features.add()
|
||||
if src_feature.HasField("id"):
|
||||
dst_feature.id = src_feature.id
|
||||
dst_feature.type = src_feature.type
|
||||
dst_feature.geometry.extend(src_feature.geometry)
|
||||
|
||||
tags = list(src_feature.tags)
|
||||
for pos in range(0, len(tags), 2):
|
||||
src_key_idx = tags[pos]
|
||||
src_val_idx = tags[pos + 1]
|
||||
key = src_layer.keys[src_key_idx]
|
||||
if key not in allowlist:
|
||||
continue
|
||||
|
||||
dst_key_idx = kept_key_index.get(src_key_idx)
|
||||
if dst_key_idx is None:
|
||||
dst_key_idx = len(dst_layer.keys)
|
||||
dst_layer.keys.append(key)
|
||||
kept_key_index[src_key_idx] = dst_key_idx
|
||||
|
||||
src_value = src_layer.values[src_val_idx]
|
||||
value_sig = (src_val_idx, src_value.SerializeToString())
|
||||
dst_val_idx = kept_value_index.get(value_sig)
|
||||
if dst_val_idx is None:
|
||||
dst_val_idx = len(dst_layer.values)
|
||||
clone_value(dst_layer.values.add(), src_value)
|
||||
kept_value_index[value_sig] = dst_val_idx
|
||||
|
||||
dst_feature.tags.extend([dst_key_idx, dst_val_idx])
|
||||
|
||||
return dst_tile.SerializeToString()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
src_root = args.src_root
|
||||
dst_root = args.dst_root
|
||||
|
||||
if not src_root.exists():
|
||||
raise SystemExit(f"src root not found: {src_root}")
|
||||
|
||||
if dst_root.exists():
|
||||
for path in sorted(dst_root.glob("**/*"), reverse=True):
|
||||
if path.is_file() or path.is_symlink():
|
||||
path.unlink()
|
||||
elif path.is_dir():
|
||||
try:
|
||||
path.rmdir()
|
||||
except OSError:
|
||||
pass
|
||||
dst_root.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
written = 0
|
||||
for src_path in sorted(src_root.glob("*/*/*.pbf")):
|
||||
rel = src_path.relative_to(src_root)
|
||||
dst_path = dst_root / rel
|
||||
dst_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
dst_path.write_bytes(strip_tile_properties(src_path.read_bytes(), FINAL_RELEASE_PROPERTY_ALLOWLIST))
|
||||
written += 1
|
||||
|
||||
print(f"minimized release tiles written={written} dst={dst_root}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -53,6 +53,7 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("--toolbar-height", type=int, default=150)
|
||||
parser.add_argument("--bottom-trim", type=int, default=170)
|
||||
parser.add_argument("--timeout-sec", type=int, default=120)
|
||||
parser.add_argument("--virtual-time-budget-ms", type=int, default=30000)
|
||||
parser.add_argument("--hotspots-json", type=Path, default=DEFAULT_HOTSPOTS_PATH)
|
||||
return parser.parse_args()
|
||||
|
||||
@@ -62,11 +63,18 @@ def fetch_compare_version(compare_url: str) -> str | None:
|
||||
text = requests.get(compare_url, timeout=20).text
|
||||
except Exception:
|
||||
return None
|
||||
match = re.search(r'const\s+COMPARE_VERSION\s*=\s*"([^"]+)"', text)
|
||||
match = re.search(r'const\s+(?:COMPARE_VERSION|HTML_VERSION)\s*=\s*"([^"]+)"', text)
|
||||
return match.group(1) if match else None
|
||||
|
||||
|
||||
def run_browser_screenshot(compare_url: str, output_png: Path, width: int, height: int, timeout_sec: int) -> None:
|
||||
def run_browser_screenshot(
|
||||
compare_url: str,
|
||||
output_png: Path,
|
||||
width: int,
|
||||
height: int,
|
||||
timeout_sec: int,
|
||||
virtual_time_budget_ms: int,
|
||||
) -> None:
|
||||
cmd = [
|
||||
"timeout",
|
||||
f"{timeout_sec}s",
|
||||
@@ -75,7 +83,7 @@ def run_browser_screenshot(compare_url: str, output_png: Path, width: int, heigh
|
||||
"--disable-gpu",
|
||||
"--enable-unsafe-swiftshader",
|
||||
"--no-sandbox",
|
||||
"--virtual-time-budget=15000",
|
||||
f"--virtual-time-budget={virtual_time_budget_ms}",
|
||||
f"--window-size={width},{height}",
|
||||
f"--screenshot={output_png}",
|
||||
compare_url,
|
||||
@@ -354,7 +362,14 @@ def main() -> None:
|
||||
right_png = args.output_dir / "compare_right.png"
|
||||
diff_png = args.output_dir / "compare_diff.png"
|
||||
|
||||
run_browser_screenshot(args.compare_url, full_png, args.width, args.height, args.timeout_sec)
|
||||
run_browser_screenshot(
|
||||
args.compare_url,
|
||||
full_png,
|
||||
args.width,
|
||||
args.height,
|
||||
args.timeout_sec,
|
||||
args.virtual_time_budget_ms,
|
||||
)
|
||||
visual_metrics = save_visual_outputs(
|
||||
full_png=full_png,
|
||||
left_png=left_png,
|
||||
|
||||
@@ -52,6 +52,29 @@ LIGHT_COLOR_REMARK_MAP = {
|
||||
"A": "amber",
|
||||
}
|
||||
|
||||
FINAL_RELEASE_PROPERTY_ALLOWLIST = frozenset(
|
||||
{
|
||||
"canonical_object_type",
|
||||
"chart_fill_pattern",
|
||||
"chart_fill_style",
|
||||
"chart_icon_image",
|
||||
"chart_label_position_code",
|
||||
"chart_label_subtext",
|
||||
"chart_label_text",
|
||||
"chart_line_color",
|
||||
"chart_line_width",
|
||||
"chart_symbol_code",
|
||||
"chart_text_color",
|
||||
"chart_text_style",
|
||||
"depth_value_m",
|
||||
"least_depth_m",
|
||||
"light_color_code",
|
||||
"light_sector_mode",
|
||||
"name_ja",
|
||||
"place_name_en",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DbConfig:
|
||||
@@ -163,6 +186,18 @@ def parse_number(value: object) -> float | None:
|
||||
return None
|
||||
|
||||
|
||||
def feature_id_from_current_fid(fid_value: object) -> int | None:
|
||||
text = text_or_none(fid_value)
|
||||
if not text:
|
||||
return None
|
||||
try:
|
||||
if re.fullmatch(r"[0-9A-Fa-f]{8}", text):
|
||||
return int(text, 16)
|
||||
return int(text)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def infer_light_color_code_from_remark(light_remark: object) -> str | None:
|
||||
remark_text = text_or_none(light_remark)
|
||||
if not remark_text:
|
||||
@@ -213,8 +248,10 @@ class NavSeaTileBuilder:
|
||||
zmax: int,
|
||||
workers: int,
|
||||
all_tiles: bool,
|
||||
reference_tile_root: Path | None,
|
||||
engineering_mode: bool,
|
||||
strip_legacy_japanese_delivery: bool,
|
||||
release_minimal: bool,
|
||||
fid_codec: NavSeaFidCodec | None,
|
||||
fid_key_id: str | None,
|
||||
bundle_id: str | None,
|
||||
@@ -229,8 +266,10 @@ class NavSeaTileBuilder:
|
||||
self.zmax = zmax
|
||||
self.workers = workers
|
||||
self.all_tiles = all_tiles
|
||||
self.reference_tile_root = reference_tile_root
|
||||
self.engineering_mode = engineering_mode
|
||||
self.strip_legacy_japanese_delivery = strip_legacy_japanese_delivery
|
||||
self.release_minimal = release_minimal
|
||||
self.fid_codec = fid_codec
|
||||
self.fid_key_id = fid_key_id
|
||||
self.bundle_id = bundle_id
|
||||
@@ -305,6 +344,8 @@ class NavSeaTileBuilder:
|
||||
return [int(row["z"]) for row in cur.fetchall()]
|
||||
|
||||
def build_jobs(self, supported_zooms: list[int]) -> list[TileJob]:
|
||||
if self.reference_tile_root is not None:
|
||||
return self.build_reference_jobs(supported_zooms)
|
||||
if self.all_tiles:
|
||||
return self.build_all_jobs(supported_zooms)
|
||||
|
||||
@@ -335,6 +376,26 @@ class NavSeaTileBuilder:
|
||||
|
||||
return jobs
|
||||
|
||||
def build_reference_jobs(self, supported_zooms: list[int]) -> list[TileJob]:
|
||||
jobs: list[TileJob] = []
|
||||
zoom_set = set(supported_zooms)
|
||||
assert self.reference_tile_root is not None
|
||||
|
||||
for path in sorted(self.reference_tile_root.glob("*/*/*.pbf")):
|
||||
try:
|
||||
z = int(path.parent.parent.name)
|
||||
x = int(path.parent.name)
|
||||
y = int(path.stem)
|
||||
except ValueError:
|
||||
continue
|
||||
if z not in zoom_set or z < self.zmin or z > self.zmax:
|
||||
continue
|
||||
if not (self.source_root / str(z) / str(x) / f"{y}.pbf").exists():
|
||||
continue
|
||||
jobs.append(TileJob(z=z, x=x, y=y))
|
||||
|
||||
return jobs
|
||||
|
||||
def aoi_bbox(self) -> tuple[float, float, float, float]:
|
||||
radius_km = self.radius_nm * 1.852
|
||||
lat_delta = radius_km / 111.32
|
||||
@@ -380,7 +441,6 @@ class NavSeaTileBuilder:
|
||||
COALESCE(fid.v, '') AS fid,
|
||||
r.source_layer,
|
||||
r.geom_type,
|
||||
COALESCE(NULLIF(r.render_layer, ''), r.source_layer) AS output_layer,
|
||||
r.canonical_object_type,
|
||||
r.canonical_family,
|
||||
r.semantic_key,
|
||||
@@ -438,27 +498,27 @@ class NavSeaTileBuilder:
|
||||
)
|
||||
|
||||
for source_feature, row in zip(source_features, db_rows):
|
||||
output_layer = str(row["output_layer"])
|
||||
source_layer_jp = str(row["source_layer"])
|
||||
output_layer, source_layer_rule_id = self.mapping_registry.resolve_source_layer(source_layer_jp)
|
||||
properties = dict(source_feature.get("properties") or {})
|
||||
legacy_fid_raw = properties.get("fid", row["fid"])
|
||||
current_feature_id = feature_id_from_current_fid(legacy_fid_raw)
|
||||
navsea_fid_pair = None
|
||||
if self.fid_codec is not None:
|
||||
navsea_fid_pair = self.build_engineering_fid(legacy_fid_raw, row, job)
|
||||
navsea_fid_int, navsea_fid_hex = navsea_fid_pair
|
||||
properties["fid"] = navsea_fid_hex
|
||||
properties["fid_algo_id"] = "feistel32_aes_cyclewalk_v1"
|
||||
properties["fid_key_id"] = self.fid_key_id
|
||||
current_feature_id = navsea_fid_int
|
||||
if self.engineering_mode:
|
||||
if navsea_fid_pair is None:
|
||||
raise RuntimeError("engineering mode requires encrypted fid generation")
|
||||
navsea_fid_int, _ = navsea_fid_pair
|
||||
properties["fid_algo_id"] = "feistel32_aes_cyclewalk_v1"
|
||||
properties["fid_key_id"] = self.fid_key_id
|
||||
properties["fid_legacy_raw"] = legacy_fid_raw
|
||||
properties["fid_navsea_int"] = navsea_fid_int
|
||||
properties["source_layer_jp"] = row["source_layer"]
|
||||
source_layer_std, source_layer_rule_id = self.mapping_registry.resolve_source_layer(
|
||||
str(row["source_layer"])
|
||||
)
|
||||
properties["source_layer_std"] = source_layer_std
|
||||
properties["source_layer_jp"] = source_layer_jp
|
||||
properties["source_layer_std"] = output_layer
|
||||
properties["normalization_bundle_id"] = self.bundle_id
|
||||
properties["source_layer_rule_id"] = source_layer_rule_id
|
||||
properties["feature_id"] = row["feature_id"]
|
||||
@@ -470,7 +530,7 @@ class NavSeaTileBuilder:
|
||||
properties["render_layer"] = output_layer
|
||||
chart_properties, feature_unresolved, trace_status = self.build_chart_properties(
|
||||
properties=properties,
|
||||
source_layer=str(row["source_layer"]),
|
||||
source_layer=source_layer_jp,
|
||||
output_layer=output_layer,
|
||||
canonical_object_type=text_or_none(row["canonical_object_type"]) or "",
|
||||
canonical_family=text_or_none(row["canonical_family"]) or "",
|
||||
@@ -483,13 +543,15 @@ class NavSeaTileBuilder:
|
||||
properties["trace_status"] = trace_status
|
||||
properties, field_name_unresolved = self.standardize_output_properties(
|
||||
properties=properties,
|
||||
source_layer=str(row["source_layer"]),
|
||||
source_layer=source_layer_jp,
|
||||
canonical_object_type=text_or_none(row["canonical_object_type"]) or "",
|
||||
canonical_family=text_or_none(row["canonical_family"]) or "",
|
||||
geom_type=str(source_feature.get("geometry", {}).get("type", "")),
|
||||
feature_id=int(row["feature_id"]),
|
||||
tile=job,
|
||||
)
|
||||
if self.release_minimal and not self.engineering_mode:
|
||||
properties = self.minimize_delivery_properties(properties)
|
||||
unresolved.extend(feature_unresolved)
|
||||
unresolved.extend(field_name_unresolved)
|
||||
|
||||
@@ -497,11 +559,13 @@ class NavSeaTileBuilder:
|
||||
"geometry": source_feature["geometry"],
|
||||
"properties": properties,
|
||||
}
|
||||
if source_feature.get("id") is not None:
|
||||
if current_feature_id is not None:
|
||||
output_feature["id"] = current_feature_id
|
||||
elif source_feature.get("id") is not None:
|
||||
output_feature["id"] = source_feature["id"]
|
||||
|
||||
layer_features[output_layer].append(output_feature)
|
||||
layer_extents.setdefault(output_layer, source_extents[str(row["source_layer"])])
|
||||
layer_extents.setdefault(output_layer, source_extents[source_layer_jp])
|
||||
|
||||
encoded_layers = []
|
||||
per_layer_options: dict[str, dict[str, int]] = {}
|
||||
@@ -779,9 +843,10 @@ class NavSeaTileBuilder:
|
||||
|
||||
for key, value in properties.items():
|
||||
if key == "fid":
|
||||
# By the time we normalize output properties, fid already means
|
||||
# the NavSea public ID, not the original source fid field.
|
||||
normalized[key] = value
|
||||
if self.engineering_mode:
|
||||
# Engineering output keeps the public NavSea fid in properties
|
||||
# so it can be inspected alongside trace metadata.
|
||||
normalized[key] = value
|
||||
continue
|
||||
rule = self.mapping_registry.get_field_name_rule(str(key))
|
||||
if rule is None:
|
||||
@@ -820,6 +885,14 @@ class NavSeaTileBuilder:
|
||||
|
||||
return normalized, unresolved
|
||||
|
||||
@staticmethod
|
||||
def minimize_delivery_properties(properties: dict[str, object]) -> dict[str, object]:
|
||||
return {
|
||||
key: value
|
||||
for key, value in properties.items()
|
||||
if key in FINAL_RELEASE_PROPERTY_ALLOWLIST
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def make_unresolved_event(
|
||||
*,
|
||||
@@ -1376,6 +1449,7 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("--source-root", type=Path, default=SOURCE_TILE_ROOT)
|
||||
parser.add_argument("--workers", type=int, default=4)
|
||||
parser.add_argument("--all-tiles", action="store_true")
|
||||
parser.add_argument("--reference-tile-root", type=Path)
|
||||
parser.add_argument("--engineering", action="store_true")
|
||||
parser.add_argument(
|
||||
"--strip-legacy-japanese-delivery",
|
||||
@@ -1389,13 +1463,18 @@ def parse_args() -> argparse.Namespace:
|
||||
dest="strip_legacy_japanese_delivery",
|
||||
help="keep mapped Japanese structural keys in delivery tiles for legacy compatibility",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--release-minimal",
|
||||
action="store_true",
|
||||
help="emit a final release tile payload with only the minimal style-driven delivery fields",
|
||||
)
|
||||
parser.add_argument("--fid-key")
|
||||
parser.add_argument("--fid-key-id", default="navsea-fid-key-v1")
|
||||
parser.add_argument("--bundle-id", default="navsea-reversible-v1")
|
||||
parser.set_defaults(strip_legacy_japanese_delivery=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.all_tiles:
|
||||
if not args.all_tiles and args.reference_tile_root is None:
|
||||
missing = [
|
||||
name
|
||||
for name in ("center_lat", "center_lon", "radius_nm")
|
||||
@@ -1405,8 +1484,8 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.error(
|
||||
"--center-lat, --center-lon, and --radius-nm are required unless --all-tiles is used"
|
||||
)
|
||||
if args.engineering and not args.fid_key:
|
||||
parser.error("--fid-key is required when --engineering is used")
|
||||
if not args.fid_key:
|
||||
parser.error("--fid-key is required for NavSea delivery and engineering builds")
|
||||
|
||||
return args
|
||||
|
||||
@@ -1425,8 +1504,10 @@ def main() -> None:
|
||||
zmax=args.zmax,
|
||||
workers=max(1, args.workers),
|
||||
all_tiles=args.all_tiles,
|
||||
reference_tile_root=args.reference_tile_root,
|
||||
engineering_mode=args.engineering,
|
||||
strip_legacy_japanese_delivery=args.strip_legacy_japanese_delivery,
|
||||
release_minimal=args.release_minimal,
|
||||
fid_codec=fid_codec,
|
||||
fid_key_id=args.fid_key_id,
|
||||
bundle_id=args.bundle_id,
|
||||
|
||||
323
report/strict_audit_final_20nm/strict_audit_summary.json
Normal file
323
report/strict_audit_final_20nm/strict_audit_summary.json
Normal file
@@ -0,0 +1,323 @@
|
||||
{
|
||||
"compare_url": "http://192.168.200.184/newpec/navsea-compare-karatsu-20nm.html",
|
||||
"compare_version": "compare-r11-20260401-0115",
|
||||
"backend_audit_json": "/root/sourceserver/pbf/NavSea_Original_vs_Delivery_Render_Audit_Karatsu_20nm_2026-03-31.r7.json",
|
||||
"visual_metrics": {
|
||||
"image_width": 640,
|
||||
"image_height": 400,
|
||||
"total_pixels": 256000,
|
||||
"changed_pixels": 235857,
|
||||
"changed_ratio": 0.921316,
|
||||
"mean_abs_rgb": [
|
||||
32.7825,
|
||||
31.9123,
|
||||
51.3612
|
||||
],
|
||||
"rms_rgb": [
|
||||
56.2597,
|
||||
51.4488,
|
||||
69.7204
|
||||
],
|
||||
"diff_bbox": [
|
||||
0,
|
||||
0,
|
||||
640,
|
||||
400
|
||||
],
|
||||
"full_crop_left": [
|
||||
0,
|
||||
150,
|
||||
640,
|
||||
550
|
||||
],
|
||||
"full_crop_right": [
|
||||
640,
|
||||
150,
|
||||
1280,
|
||||
550
|
||||
],
|
||||
"hotspots": [
|
||||
{
|
||||
"image_width": 109,
|
||||
"image_height": 64,
|
||||
"total_pixels": 6976,
|
||||
"changed_pixels": 6975,
|
||||
"changed_ratio": 0.999857,
|
||||
"mean_abs_rgb": [
|
||||
111.3999,
|
||||
94.5026,
|
||||
86.0499
|
||||
],
|
||||
"rms_rgb": [
|
||||
148.6028,
|
||||
125.1472,
|
||||
112.7118
|
||||
],
|
||||
"diff_bbox": [
|
||||
0,
|
||||
0,
|
||||
109,
|
||||
64
|
||||
],
|
||||
"id": "east_breakwater_marks",
|
||||
"label": "东侧防波堤灯台区",
|
||||
"notes": "防波堤灯台和港口标记区。优先验证远一点的灯台/航标是否也能稳定打出来。",
|
||||
"backend_focus": [
|
||||
"p航路標識群",
|
||||
"safety_icon_missing"
|
||||
],
|
||||
"pane_box_norm": [
|
||||
0.83,
|
||||
0.0,
|
||||
1.0,
|
||||
0.16
|
||||
],
|
||||
"pane_box_pixels": [
|
||||
531,
|
||||
0,
|
||||
640,
|
||||
64
|
||||
],
|
||||
"left_image": "report/strict_audit_final_20nm/hotspots/east_breakwater_marks_left.png",
|
||||
"right_image": "report/strict_audit_final_20nm/hotspots/east_breakwater_marks_right.png",
|
||||
"diff_image": "report/strict_audit_final_20nm/hotspots/east_breakwater_marks_diff.png"
|
||||
},
|
||||
{
|
||||
"image_width": 147,
|
||||
"image_height": 80,
|
||||
"total_pixels": 11760,
|
||||
"changed_pixels": 9533,
|
||||
"changed_ratio": 0.810629,
|
||||
"mean_abs_rgb": [
|
||||
24.2571,
|
||||
22.39,
|
||||
27.698
|
||||
],
|
||||
"rms_rgb": [
|
||||
48.344,
|
||||
45.8837,
|
||||
50.5034
|
||||
],
|
||||
"diff_bbox": [
|
||||
0,
|
||||
0,
|
||||
147,
|
||||
80
|
||||
],
|
||||
"id": "takashima_main_harbor_marks",
|
||||
"label": "高岛主港航标区",
|
||||
"notes": "主港口灯塔、红绿标识、近岸障碍标记。优先验证灯塔、小灯、红绿标识是否真正恢复。",
|
||||
"backend_focus": [
|
||||
"p航路標識群",
|
||||
"safety_icon_missing",
|
||||
"text_missing_in_engineering"
|
||||
],
|
||||
"pane_box_norm": [
|
||||
0.34,
|
||||
0.05,
|
||||
0.57,
|
||||
0.25
|
||||
],
|
||||
"pane_box_pixels": [
|
||||
218,
|
||||
20,
|
||||
365,
|
||||
100
|
||||
],
|
||||
"left_image": "report/strict_audit_final_20nm/hotspots/takashima_main_harbor_marks_left.png",
|
||||
"right_image": "report/strict_audit_final_20nm/hotspots/takashima_main_harbor_marks_right.png",
|
||||
"diff_image": "report/strict_audit_final_20nm/hotspots/takashima_main_harbor_marks_diff.png"
|
||||
},
|
||||
{
|
||||
"image_width": 115,
|
||||
"image_height": 68,
|
||||
"total_pixels": 7820,
|
||||
"changed_pixels": 5708,
|
||||
"changed_ratio": 0.729923,
|
||||
"mean_abs_rgb": [
|
||||
21.7578,
|
||||
22.3706,
|
||||
25.4146
|
||||
],
|
||||
"rms_rgb": [
|
||||
36.6022,
|
||||
39.835,
|
||||
43.5517
|
||||
],
|
||||
"diff_bbox": [
|
||||
0,
|
||||
0,
|
||||
115,
|
||||
68
|
||||
],
|
||||
"id": "takashima_inner_nearshore_hazards",
|
||||
"label": "高岛近岸碍航与鱼礁区",
|
||||
"notes": "近岸鱼礁、碍航点、灯塔与小灯混合区。优先验证鱼礁和碍航物是否还在。",
|
||||
"backend_focus": [
|
||||
"p投錨注意障害物",
|
||||
"p航行危険障害物",
|
||||
"p航路標識群"
|
||||
],
|
||||
"pane_box_norm": [
|
||||
0.44,
|
||||
0.14,
|
||||
0.62,
|
||||
0.31
|
||||
],
|
||||
"pane_box_pixels": [
|
||||
282,
|
||||
56,
|
||||
397,
|
||||
124
|
||||
],
|
||||
"left_image": "report/strict_audit_final_20nm/hotspots/takashima_inner_nearshore_hazards_left.png",
|
||||
"right_image": "report/strict_audit_final_20nm/hotspots/takashima_inner_nearshore_hazards_right.png",
|
||||
"diff_image": "report/strict_audit_final_20nm/hotspots/takashima_inner_nearshore_hazards_diff.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
"backend_summary": {
|
||||
"original_feature_instances": 258317,
|
||||
"engineering_feature_instances": 258317,
|
||||
"result_count": 516634,
|
||||
"status_counts": {
|
||||
"extra_in_engineering": 258317,
|
||||
"missing_in_engineering": 258317
|
||||
},
|
||||
"top_annotation_issues": [
|
||||
{
|
||||
"issue": "extra_text_in_engineering",
|
||||
"source_layer": "bathymetry_line",
|
||||
"count": 24168
|
||||
},
|
||||
{
|
||||
"issue": "text_missing_in_engineering",
|
||||
"source_layer": "L海底地形",
|
||||
"count": 24168
|
||||
},
|
||||
{
|
||||
"issue": "extra_text_in_engineering",
|
||||
"source_layer": "depth_contour",
|
||||
"count": 11929
|
||||
},
|
||||
{
|
||||
"issue": "text_missing_in_engineering",
|
||||
"source_layer": "L等深線",
|
||||
"count": 11929
|
||||
},
|
||||
{
|
||||
"issue": "extra_text_in_engineering",
|
||||
"source_layer": "seabed_text_point",
|
||||
"count": 4499
|
||||
},
|
||||
{
|
||||
"issue": "text_missing_in_engineering",
|
||||
"source_layer": "p底質",
|
||||
"count": 4499
|
||||
},
|
||||
{
|
||||
"issue": "extra_text_in_engineering",
|
||||
"source_layer": "navigation_marks",
|
||||
"count": 1516
|
||||
},
|
||||
{
|
||||
"issue": "text_missing_in_engineering",
|
||||
"source_layer": "p航路標識群",
|
||||
"count": 1516
|
||||
},
|
||||
{
|
||||
"issue": "text_missing_in_engineering",
|
||||
"source_layer": "p地名",
|
||||
"count": 1444
|
||||
},
|
||||
{
|
||||
"issue": "extra_text_in_engineering",
|
||||
"source_layer": "place_label_sea",
|
||||
"count": 1444
|
||||
}
|
||||
],
|
||||
"top_style_semantic_issues": [
|
||||
{
|
||||
"issue": "depth_numeric_missing",
|
||||
"source_layer": "L海底地形",
|
||||
"count": 24168
|
||||
},
|
||||
{
|
||||
"issue": "depth_numeric_missing",
|
||||
"source_layer": "L等深線",
|
||||
"count": 11929
|
||||
},
|
||||
{
|
||||
"issue": "safety_icon_missing",
|
||||
"source_layer": "p航路標識群",
|
||||
"count": 1885
|
||||
},
|
||||
{
|
||||
"issue": "depth_numeric_missing",
|
||||
"source_layer": "L概略等深線",
|
||||
"count": 350
|
||||
},
|
||||
{
|
||||
"issue": "clearance_numeric_missing",
|
||||
"source_layer": "p高さ制限",
|
||||
"count": 33
|
||||
}
|
||||
],
|
||||
"top_source_layer_issues": [
|
||||
{
|
||||
"status": "extra_in_engineering",
|
||||
"source_layer": "baseline_outline",
|
||||
"count": 87709
|
||||
},
|
||||
{
|
||||
"status": "missing_in_engineering",
|
||||
"source_layer": "P基本線ククリ",
|
||||
"count": 87709
|
||||
},
|
||||
{
|
||||
"status": "extra_in_engineering",
|
||||
"source_layer": "baseline_area",
|
||||
"count": 50421
|
||||
},
|
||||
{
|
||||
"status": "missing_in_engineering",
|
||||
"source_layer": "P基本線",
|
||||
"count": 50421
|
||||
},
|
||||
{
|
||||
"status": "extra_in_engineering",
|
||||
"source_layer": "depth_contour",
|
||||
"count": 36460
|
||||
},
|
||||
{
|
||||
"status": "missing_in_engineering",
|
||||
"source_layer": "L等深線",
|
||||
"count": 36460
|
||||
},
|
||||
{
|
||||
"status": "extra_in_engineering",
|
||||
"source_layer": "bathymetry_line",
|
||||
"count": 35940
|
||||
},
|
||||
{
|
||||
"status": "missing_in_engineering",
|
||||
"source_layer": "L海底地形",
|
||||
"count": 35940
|
||||
},
|
||||
{
|
||||
"status": "extra_in_engineering",
|
||||
"source_layer": "onshore_structure_line",
|
||||
"count": 10544
|
||||
},
|
||||
{
|
||||
"status": "missing_in_engineering",
|
||||
"source_layer": "L陸上構造物陸",
|
||||
"count": 10544
|
||||
}
|
||||
]
|
||||
},
|
||||
"notes": [
|
||||
"Visual metrics are browser-rendered screenshot diff metrics from the fixed compare page.",
|
||||
"Backend metrics are current render-audit summary metrics and may remain blind to sprite-load failures."
|
||||
]
|
||||
}
|
||||
104
report/strict_audit_final_20nm/strict_audit_summary.md
Normal file
104
report/strict_audit_final_20nm/strict_audit_summary.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# NavSea Strict Audit Summary
|
||||
|
||||
## Scope
|
||||
|
||||
- Compare page: `http://192.168.200.184/newpec/navsea-compare-karatsu-20nm.html`
|
||||
- Compare version: `compare-r11-20260401-0115`
|
||||
- Backend render audit: `/root/sourceserver/pbf/NavSea_Original_vs_Delivery_Render_Audit_Karatsu_20nm_2026-03-31.r7.json`
|
||||
|
||||
## Visual Audit
|
||||
|
||||
- changed pixels: `235857` / `256000`
|
||||
- changed ratio: `0.921316`
|
||||
- mean abs rgb: `[32.7825, 31.9123, 51.3612]`
|
||||
- rms rgb: `[56.2597, 51.4488, 69.7204]`
|
||||
- left crop: `[0, 150, 640, 550]`
|
||||
- right crop: `[640, 150, 1280, 550]`
|
||||
|
||||
Artifacts:
|
||||
|
||||
- `report/strict_audit_final_20nm/compare_full.png`
|
||||
- `report/strict_audit_final_20nm/compare_left.png`
|
||||
- `report/strict_audit_final_20nm/compare_right.png`
|
||||
- `report/strict_audit_final_20nm/compare_diff.png`
|
||||
|
||||
## Hotspot AOI Audit
|
||||
|
||||
### 东侧防波堤灯台区
|
||||
|
||||
- id: `east_breakwater_marks`
|
||||
- notes: `防波堤灯台和港口标记区。优先验证远一点的灯台/航标是否也能稳定打出来。`
|
||||
- backend focus: `['p航路標識群', 'safety_icon_missing']`
|
||||
- changed pixels: `6975` / `6976`
|
||||
- changed ratio: `0.999857`
|
||||
- mean abs rgb: `[111.3999, 94.5026, 86.0499]`
|
||||
- pane box norm: `[0.83, 0.0, 1.0, 0.16]`
|
||||
- pane box pixels: `[531, 0, 640, 64]`
|
||||
- left image: `report/strict_audit_final_20nm/hotspots/east_breakwater_marks_left.png`
|
||||
- right image: `report/strict_audit_final_20nm/hotspots/east_breakwater_marks_right.png`
|
||||
- diff image: `report/strict_audit_final_20nm/hotspots/east_breakwater_marks_diff.png`
|
||||
|
||||
### 高岛主港航标区
|
||||
|
||||
- id: `takashima_main_harbor_marks`
|
||||
- notes: `主港口灯塔、红绿标识、近岸障碍标记。优先验证灯塔、小灯、红绿标识是否真正恢复。`
|
||||
- backend focus: `['p航路標識群', 'safety_icon_missing', 'text_missing_in_engineering']`
|
||||
- changed pixels: `9533` / `11760`
|
||||
- changed ratio: `0.810629`
|
||||
- mean abs rgb: `[24.2571, 22.39, 27.698]`
|
||||
- pane box norm: `[0.34, 0.05, 0.57, 0.25]`
|
||||
- pane box pixels: `[218, 20, 365, 100]`
|
||||
- left image: `report/strict_audit_final_20nm/hotspots/takashima_main_harbor_marks_left.png`
|
||||
- right image: `report/strict_audit_final_20nm/hotspots/takashima_main_harbor_marks_right.png`
|
||||
- diff image: `report/strict_audit_final_20nm/hotspots/takashima_main_harbor_marks_diff.png`
|
||||
|
||||
### 高岛近岸碍航与鱼礁区
|
||||
|
||||
- id: `takashima_inner_nearshore_hazards`
|
||||
- notes: `近岸鱼礁、碍航点、灯塔与小灯混合区。优先验证鱼礁和碍航物是否还在。`
|
||||
- backend focus: `['p投錨注意障害物', 'p航行危険障害物', 'p航路標識群']`
|
||||
- changed pixels: `5708` / `7820`
|
||||
- changed ratio: `0.729923`
|
||||
- mean abs rgb: `[21.7578, 22.3706, 25.4146]`
|
||||
- pane box norm: `[0.44, 0.14, 0.62, 0.31]`
|
||||
- pane box pixels: `[282, 56, 397, 124]`
|
||||
- left image: `report/strict_audit_final_20nm/hotspots/takashima_inner_nearshore_hazards_left.png`
|
||||
- right image: `report/strict_audit_final_20nm/hotspots/takashima_inner_nearshore_hazards_right.png`
|
||||
- diff image: `report/strict_audit_final_20nm/hotspots/takashima_inner_nearshore_hazards_diff.png`
|
||||
|
||||
## Backend Render Audit
|
||||
|
||||
- original feature instances: `258317`
|
||||
- delivery feature instances: `258317`
|
||||
- result count: `516634`
|
||||
|
||||
Status counts:
|
||||
|
||||
- `extra_in_engineering`: `258317`
|
||||
- `missing_in_engineering`: `258317`
|
||||
|
||||
Top annotation issues:
|
||||
|
||||
- `extra_text_in_engineering` | `bathymetry_line` | `24168`
|
||||
- `text_missing_in_engineering` | `L海底地形` | `24168`
|
||||
- `extra_text_in_engineering` | `depth_contour` | `11929`
|
||||
- `text_missing_in_engineering` | `L等深線` | `11929`
|
||||
- `extra_text_in_engineering` | `seabed_text_point` | `4499`
|
||||
- `text_missing_in_engineering` | `p底質` | `4499`
|
||||
- `extra_text_in_engineering` | `navigation_marks` | `1516`
|
||||
- `text_missing_in_engineering` | `p航路標識群` | `1516`
|
||||
- `text_missing_in_engineering` | `p地名` | `1444`
|
||||
- `extra_text_in_engineering` | `place_label_sea` | `1444`
|
||||
|
||||
Top style semantic issues:
|
||||
|
||||
- `depth_numeric_missing` | `L海底地形` | `24168`
|
||||
- `depth_numeric_missing` | `L等深線` | `11929`
|
||||
- `safety_icon_missing` | `p航路標識群` | `1885`
|
||||
- `depth_numeric_missing` | `L概略等深線` | `350`
|
||||
- `clearance_numeric_missing` | `p高さ制限` | `33`
|
||||
|
||||
## Interpretation
|
||||
|
||||
- This report intentionally puts browser-rendered visual output and backend render-audit summary in one place.
|
||||
- If the browser screenshot improves but backend counts do not, the current backend audit is likely blind to a browser/runtime issue such as sprite resolution.
|
||||
@@ -314,21 +314,21 @@
|
||||
|
||||
<script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script>
|
||||
<script>
|
||||
const HTML_VERSION = "compare-r10-20260331-2213";
|
||||
const HTML_VERSION = "compare-r11-20260401-0115";
|
||||
const ORIGINAL_STYLE_URL = "./style.json";
|
||||
const ORIGINAL_TILE_URL = "http://192.168.200.184/newpec/exported_auto/tile.mapple-on.jp__newpec-mvt-20260106__z___x___y_.pbf/tiles/{z}/{x}/{y}.pbf";
|
||||
const PROFILES = {
|
||||
karatsu20: {
|
||||
label: "唐津 20 海里",
|
||||
pageTitle: "唐津 20 海里对比页",
|
||||
subtitle: "左侧是原始版 <code>style.json</code>,右侧是当前交付线使用的 delivery 样式 + 唐津 20 海里 delivery PBF。两边联动同步,点击任一对象可查看属性,适合直接肉眼排差异。",
|
||||
deliveryPaneLabel: "正式 delivery style + 唐津 20 海里 PBF",
|
||||
deliveryStyleUrl: "./domain/style.navsea-delivery-karatsu-10nm.json",
|
||||
deliveryTileUrl: "http://192.168.200.184/pbf-delivery-karatsu-20nm/{z}/{x}/{y}.pbf",
|
||||
deliveryStyleVersion: "karatsu-style-r7-20260331-2045",
|
||||
deliveryPbfVersion: "karatsu-pbf-20nm-20260325-1757",
|
||||
deliveryStyleFile: "style.navsea-delivery-karatsu-10nm.json",
|
||||
deliveryPbfName: "pbf-delivery-karatsu-20nm",
|
||||
subtitle: "左侧是原始版 <code>style.json</code>,右侧是当前交付线使用的 final release style + 唐津 20 海里 final PBF。两边联动同步,点击任一对象可查看属性,适合直接肉眼排差异。",
|
||||
deliveryPaneLabel: "最终 release style + 唐津 20 海里 final PBF",
|
||||
deliveryStyleUrl: "./domain/style.navsea-delivery-karatsu-20nm-final.json",
|
||||
deliveryTileUrl: "http://192.168.200.184/pbf-delivery-karatsu-20nm-final/{z}/{x}/{y}.pbf",
|
||||
deliveryStyleVersion: "karatsu-final-style-r1-20260401-0115",
|
||||
deliveryPbfVersion: "karatsu-final-pbf-20nm-20260401-0115",
|
||||
deliveryStyleFile: "style.navsea-delivery-karatsu-20nm-final.json",
|
||||
deliveryPbfName: "pbf-delivery-karatsu-20nm-final",
|
||||
initialView: {
|
||||
center: [129.9697, 33.4425],
|
||||
zoom: 11,
|
||||
|
||||
2197
src/pbf/style.navsea-delivery-karatsu-20nm-final.json
Normal file
2197
src/pbf/style.navsea-delivery-karatsu-20nm-final.json
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user