Expand 20nm preservation audit and add Hakata hotspot audit

This commit is contained in:
OpenAI Codex
2026-04-01 17:50:01 +08:00
parent 62a709d113
commit 931920023e
13 changed files with 1746 additions and 649 deletions

View File

@@ -19,21 +19,42 @@ DEFAULT_DELIVERY_ROOT = Path("/home/wwwroot/pbf-delivery-karatsu-20nm")
DEFAULT_FINAL_ROOT = Path("/home/wwwroot/pbf-delivery-karatsu-20nm-final")
DEFAULT_REPORT_ROOT = Path("/root/sourceserver/pbf/report/object_preservation_20nm")
HAZARD_LAYER_RULES = (
AUDIT_RULES = (
{
"raw_layer": "p航行危険障害物",
"delivery_layer": "navigation_hazard_point",
"raw_class_field": "分類番号",
"delivery_class_field": "class_code",
"raw_match_field": "分類番号",
"delivery_match_field": "class_code",
"label": "navigation hazards",
},
{
"raw_layer": "p投錨注意障害物",
"delivery_layer": "anchor_caution_hazard_point",
"raw_class_field": "分類番号",
"delivery_class_field": "class_code",
"raw_match_field": "分類番号",
"delivery_match_field": "class_code",
"label": "anchor hazards",
},
{
"raw_layer": "p航路標識群",
"delivery_layer": "navigation_marks",
"raw_match_field": "表示用番号",
"delivery_match_field": "display_code",
"label": "navigation marks",
},
{
"raw_layer": "p施設・境界線等",
"delivery_layer": "facility_boundary_point",
"raw_match_field": "分類番号",
"delivery_match_field": "class_code",
"label": "facility points",
},
{
"raw_layer": "P航路ククリ",
"delivery_layer": "route_outline",
"raw_match_field": "分類番号",
"delivery_match_field": "class_code",
"label": "route outlines",
},
)
MAX_EXAMPLES = 20
@@ -109,19 +130,19 @@ def audit_one_target(
x = int(rel.parts[1])
y = int(Path(rel.parts[2]).stem)
tile = TileCoord(z=z, x=x, y=y)
raw_index = build_tile_index(raw_tile, HAZARD_LAYER_RULES)
target_index = build_tile_index(target_tile, HAZARD_LAYER_RULES)
raw_index = build_tile_index(raw_tile, AUDIT_RULES)
target_index = build_tile_index(target_tile, AUDIT_RULES)
all_target_geoms: dict[str, list[tuple[str, dict[str, Any]]]] = defaultdict(list)
for rule in HAZARD_LAYER_RULES:
for rule in AUDIT_RULES:
for feat in target_index[rule["delivery_layer"]]:
all_target_geoms[geometry_signature(feat)].append((rule["delivery_layer"], feat))
for rule in HAZARD_LAYER_RULES:
for rule in AUDIT_RULES:
raw_layer = rule["raw_layer"]
delivery_layer = rule["delivery_layer"]
raw_class_field = rule["raw_class_field"]
delivery_class_field = rule["delivery_class_field"]
raw_match_field = rule["raw_match_field"]
delivery_match_field = rule["delivery_match_field"]
key = f"{raw_layer}->{delivery_layer}"
target_by_geom: dict[str, list[dict[str, Any]]] = defaultdict(list)
@@ -132,8 +153,8 @@ def audit_one_target(
totals["raw_objects"] += 1
rule_stats[key]["raw_objects"] += 1
raw_props = raw_feat.get("properties", {})
raw_class = raw_props.get(raw_class_field)
class_key = f"{raw_layer}:{raw_class}"
raw_match_value = raw_props.get(raw_match_field)
class_key = f"{raw_layer}:{raw_match_value}"
geom_sig = geometry_signature(raw_feat)
name = raw_props.get("名称") or parse_at_name(raw_props.get("at"))
candidates = target_by_geom.get(geom_sig, [])
@@ -155,11 +176,13 @@ def audit_one_target(
"name": name,
"raw_layer": raw_layer,
"delivery_layer": delivery_layer,
"raw_class": raw_class,
"raw_match_field": raw_match_field,
"raw_match_value": raw_match_value,
"other_layer_hits": [
{
"delivery_layer": layer_name,
"delivery_class": feat.get("properties", {}).get(delivery_class_field),
"delivery_match_field": delivery_match_field,
"delivery_match_value": feat.get("properties", {}).get(delivery_match_field),
"canonical_object_type": feat.get("properties", {}).get("canonical_object_type"),
}
for layer_name, feat in other_layer_hits[:5]
@@ -170,7 +193,7 @@ def audit_one_target(
for candidate in candidates:
target_props = candidate.get("properties", {})
if target_props.get(delivery_class_field) == raw_class:
if target_props.get(delivery_match_field) == raw_match_value:
matched_class = True
break
@@ -180,10 +203,10 @@ def audit_one_target(
class_stats[class_key]["preserved"] += 1
continue
if all(candidate.get("properties", {}).get(delivery_class_field) is None for candidate in candidates):
issue = "raw_class_lost"
if all(candidate.get("properties", {}).get(delivery_match_field) is None for candidate in candidates):
issue = "identifier_lost"
else:
issue = "class_mismatch"
issue = "identifier_mismatch"
totals[issue] += 1
rule_stats[key][issue] += 1
@@ -196,10 +219,12 @@ def audit_one_target(
"name": name,
"raw_layer": raw_layer,
"delivery_layer": delivery_layer,
"raw_class": raw_class,
"raw_match_field": raw_match_field,
"raw_match_value": raw_match_value,
"candidates": [
{
"delivery_class": candidate.get("properties", {}).get(delivery_class_field),
"delivery_match_field": delivery_match_field,
"delivery_match_value": candidate.get("properties", {}).get(delivery_match_field),
"canonical_object_type": candidate.get("properties", {}).get("canonical_object_type"),
"chart_symbol_code": candidate.get("properties", {}).get("chart_symbol_code"),
"chart_icon_image": candidate.get("properties", {}).get("chart_icon_image"),
@@ -218,13 +243,13 @@ def audit_one_target(
"preserved": stats.get("preserved", 0),
"missing_object": stats.get("missing_object", 0),
"wrong_relayer": stats.get("wrong_relayer", 0),
"raw_class_lost": stats.get("raw_class_lost", 0),
"class_mismatch": stats.get("class_mismatch", 0),
"identifier_lost": stats.get("identifier_lost", 0),
"identifier_mismatch": stats.get("identifier_mismatch", 0),
}
)
top_class_issues.sort(
key=lambda item: (
item["missing_object"] + item["wrong_relayer"] + item["raw_class_lost"] + item["class_mismatch"],
item["missing_object"] + item["wrong_relayer"] + item["identifier_lost"] + item["identifier_mismatch"],
item["raw_objects"],
),
reverse=True,
@@ -254,8 +279,8 @@ def audit_one_target(
f"- preserved: `{totals.get('preserved', 0)}`",
f"- missing object: `{totals.get('missing_object', 0)}`",
f"- wrong relayer: `{totals.get('wrong_relayer', 0)}`",
f"- raw class lost: `{totals.get('raw_class_lost', 0)}`",
f"- class mismatch: `{totals.get('class_mismatch', 0)}`",
f"- identifier lost: `{totals.get('identifier_lost', 0)}`",
f"- identifier mismatch: `{totals.get('identifier_mismatch', 0)}`",
"",
"## Per Rule",
"",
@@ -269,8 +294,8 @@ def audit_one_target(
f"- preserved: `{stats.get('preserved', 0)}`",
f"- missing object: `{stats.get('missing_object', 0)}`",
f"- wrong relayer: `{stats.get('wrong_relayer', 0)}`",
f"- raw class lost: `{stats.get('raw_class_lost', 0)}`",
f"- class mismatch: `{stats.get('class_mismatch', 0)}`",
f"- identifier lost: `{stats.get('identifier_lost', 0)}`",
f"- identifier mismatch: `{stats.get('identifier_mismatch', 0)}`",
"",
]
)
@@ -279,7 +304,7 @@ def audit_one_target(
for item in top_class_issues[:12]:
lines.append(
"- `{class_key}` raw=`{raw_objects}` preserved=`{preserved}` missing=`{missing_object}` "
"wrong_relayer=`{wrong_relayer}` class_lost=`{raw_class_lost}` class_mismatch=`{class_mismatch}`".format(
"wrong_relayer=`{wrong_relayer}` identifier_lost=`{identifier_lost}` identifier_mismatch=`{identifier_mismatch}`".format(
**item
)
)
@@ -299,7 +324,7 @@ def audit_one_target(
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Audit raw-object preservation for hazard layers between original and delivery/final PBFs."
description="Audit raw-object preservation for hazards, navigation marks, facility points, and route outlines between original and delivery/final PBFs."
)
parser.add_argument("--raw-root", type=Path, default=DEFAULT_RAW_ROOT)
parser.add_argument("--delivery-root", type=Path, default=DEFAULT_DELIVERY_ROOT)