guard high-extent tile reencoding
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 项目步骤记录
|
||||
|
||||
最后更新:`2026-04-18 14:18`
|
||||
最后更新:`2026-04-18 15:03`
|
||||
仓库:`/root/sourceserver/pbf`
|
||||
远端:`ssh://git@nas:2222/tei/pbf.git`
|
||||
|
||||
@@ -218,6 +218,67 @@
|
||||
2. 先把“旧坏 tile”与“主线 style 差异”两类问题彻底拆开
|
||||
3. 之后再决定是否把这套 rebuild 从热点 AOI 扩到全国更大范围
|
||||
|
||||
## 2026-04-18 全国高 extent 错重编码根因钉实
|
||||
|
||||
### 1. 本轮新结论
|
||||
|
||||
- 当前全国 `full / semantic` 中反复出现的:
|
||||
- 岛礁附近整块空白
|
||||
- 海底等深线 / 陆域 / 海岸线整片缺失
|
||||
- 根因不是 `style` 改图标本身
|
||||
- 当前已用数值签名确认:
|
||||
- 是高 extent tile 在某条重写链里被按 `4096` 口径重编码
|
||||
|
||||
### 2. 关键证据
|
||||
|
||||
- 代表 source tile 正常 overflow:
|
||||
- `20480`
|
||||
- 对应生产坏 tile overflow:
|
||||
- `1064960`
|
||||
- 并且已通过复现实验确认:
|
||||
- 把 `extent = 1048576` tile 重编码时故意写成 `4096`
|
||||
- 可稳定复现出同样的 `1064960` 错移签名
|
||||
|
||||
### 3. 本轮新增守卫与审计
|
||||
|
||||
- 已新增重编码守卫:
|
||||
- [`navsea_tile_reencode_guard.py`](/root/sourceserver/pbf/navsea_tile_reencode_guard.py)
|
||||
- 已新增错移根因审计脚本:
|
||||
- [`navsea_extent_shift_audit.py`](/root/sourceserver/pbf/navsea_extent_shift_audit.py)
|
||||
- 已补充根因报告:
|
||||
- [`report/full_extent_shift_root_cause_2026-04-18.md`](/root/sourceserver/pbf/report/full_extent_shift_root_cause_2026-04-18.md)
|
||||
|
||||
### 4. 本轮加固
|
||||
|
||||
- 已修改:
|
||||
- [`build_semantic_delivery_assets.py`](/root/sourceserver/pbf/build_semantic_delivery_assets.py)
|
||||
- 当前语义版重写链要求:
|
||||
- decode 时保存原 layer extent
|
||||
- encode 时按原 extent 写回
|
||||
- 写完后立即做几何守卫校验
|
||||
- 这样后续凡是“改图标 / 改属性”触发的语义版 tile 重写,
|
||||
- 不再允许静默把高 extent tile 写坏
|
||||
|
||||
### 5. 热点修前备份审计结果
|
||||
|
||||
- 对修前热点 full 备份审计:
|
||||
- `154` 张里 `42` 张命中“高 extent 被按 4096 重编码”的签名
|
||||
- 对修前热点 semantic 备份审计:
|
||||
- `154` 张里 `43` 张命中同类签名
|
||||
- 审计文件:
|
||||
- [`hotspot_backup_full_shift_audit_2026-04-18.json`](/root/sourceserver/pbf/report/hotspot_backup_full_shift_audit_2026-04-18.json)
|
||||
- [`hotspot_backup_semantic_shift_audit_2026-04-18.json`](/root/sourceserver/pbf/report/hotspot_backup_semantic_shift_audit_2026-04-18.json)
|
||||
|
||||
### 6. 当前下一步
|
||||
|
||||
1. 把当前热点修复扩大为:
|
||||
- 全国 `full`
|
||||
- 全国 `semantic`
|
||||
的整树安全重建
|
||||
2. 重建前后都跑:
|
||||
- `navsea_extent_shift_audit.py`
|
||||
3. 把全国现网目录切换到通过守卫校验的新产物
|
||||
|
||||
## 2026-04-17 Karatsu 20nm final Native ParseTile 异常定位
|
||||
|
||||
### 1. 用户现象
|
||||
|
||||
354
build_semantic_delivery_assets.py
Normal file
354
build_semantic_delivery_assets.py
Normal file
@@ -0,0 +1,354 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
import mapbox_vector_tile
|
||||
from PIL import Image
|
||||
|
||||
from navsea_tile_reencode_guard import (
|
||||
assert_reencoded_tile_safe,
|
||||
decode_tile_with_extents,
|
||||
encode_layers_preserving_extents,
|
||||
)
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent
|
||||
BASE_AUDIT_CSV = REPO_ROOT / "tasks/pbf/NavSea_Sprite_Audit_newpec_style_with_pbf_2026-04-16.csv"
|
||||
T_OVERRIDE_CSV = REPO_ROOT / "tasks/pbf/NavSea_Sprite_Basis_T_建议修正表_2026-04-16.csv"
|
||||
MAPPING_JSON = REPO_ROOT / "src/pbf/semantic_sprite_key_map_2026-04-16.json"
|
||||
|
||||
SOURCE_SPRITE_JSON_2X = Path("/mnt/sda1/www/newpec/sprite/sprite@2x.json")
|
||||
SOURCE_SPRITE_PNG_2X = Path("/mnt/sda1/www/newpec/sprite/sprite@2x.png")
|
||||
OUTPUT_SPRITE_DIR = Path("/mnt/sda1/www/newpec/sprite-semantic")
|
||||
|
||||
SOURCE_STYLE = REPO_ROOT / "src/pbf/style.navsea-delivery-full.json"
|
||||
OUTPUT_STYLE = REPO_ROOT / "src/pbf/style.navsea-delivery-full-semantic.json"
|
||||
DEPLOYED_STYLE = Path("/mnt/sda1/www/newpec/domain/style.navsea-delivery-full-semantic.json")
|
||||
|
||||
SOURCE_PBF_ROOT = Path("/home/wwwroot/pbf-delivery-full-20260415")
|
||||
OUTPUT_PBF_ROOT = Path("/home/wwwroot/pbf-delivery-full-semantic-20260416")
|
||||
|
||||
SEMANTIC_SPRITE_BASE_URL = "http://192.168.200.184/newpec/sprite-semantic/sprite"
|
||||
SEMANTIC_PBF_TILE_URL = "http://192.168.200.184/pbf-delivery-full-semantic-20260416/{z}/{x}/{y}.pbf"
|
||||
STYLE_VERSION = "full-semantic-style-r1-20260416-1628"
|
||||
PBF_VERSION = "full-semantic-pbf-r1-20260416-1628"
|
||||
SPRITE_VERSION = "full-semantic-sprite-r1-20260416-1628"
|
||||
|
||||
PBF_PROPERTY_FIELDS = ("chart_icon_image", "chart_fill_pattern")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AuditRow:
|
||||
old_sprite_key: str
|
||||
used_in_newpec_style: int
|
||||
used_in_pbf: int
|
||||
new_sprite_key: str
|
||||
|
||||
|
||||
def normalize_key(value: str) -> str:
|
||||
value = re.sub(r"\s*\[[^\]]+\]\s*", "", value.strip())
|
||||
value = value.replace("-", "_").replace(" ", "_").lower()
|
||||
value = (
|
||||
value.replace("easte", "east")
|
||||
.replace("sourth", "south")
|
||||
.replace("varian", "variant")
|
||||
.replace("cardianal", "cardinal")
|
||||
)
|
||||
value = re.sub(r"_+", "_", value).strip("_")
|
||||
return value
|
||||
|
||||
|
||||
def load_audit_rows() -> list[AuditRow]:
|
||||
overrides: dict[str, str] = {}
|
||||
with T_OVERRIDE_CSV.open(encoding="utf-8-sig") as handle:
|
||||
for row in csv.DictReader(handle):
|
||||
overrides[row["old_sprite_key"].strip()] = row["suggested_new_sprite_key"].strip()
|
||||
|
||||
rows: list[AuditRow] = []
|
||||
with BASE_AUDIT_CSV.open(encoding="utf-8-sig") as handle:
|
||||
for row in csv.DictReader(handle):
|
||||
old_key = row["old_sprite_key"].strip()
|
||||
new_key = overrides.get(old_key) or row["new_sprite_key"].strip()
|
||||
rows.append(
|
||||
AuditRow(
|
||||
old_sprite_key=old_key,
|
||||
used_in_newpec_style=int(row["used_in_newpec_style"] or 0),
|
||||
used_in_pbf=int(row["used_in_pbf"] or 0),
|
||||
new_sprite_key=normalize_key(new_key) if new_key else "",
|
||||
)
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def build_mapping(rows: list[AuditRow]) -> dict[str, str]:
|
||||
mapping: dict[str, str] = {}
|
||||
for row in rows:
|
||||
if row.new_sprite_key:
|
||||
mapping[row.old_sprite_key] = row.new_sprite_key
|
||||
return mapping
|
||||
|
||||
|
||||
def build_used_sprite_entries(rows: list[AuditRow], mapping: dict[str, str]) -> list[tuple[str, str]]:
|
||||
used: list[tuple[str, str]] = []
|
||||
for row in rows:
|
||||
if row.used_in_newpec_style <= 0 and row.used_in_pbf <= 0:
|
||||
continue
|
||||
used.append((row.old_sprite_key, mapping.get(row.old_sprite_key, row.old_sprite_key)))
|
||||
return used
|
||||
|
||||
|
||||
def write_mapping_json(mapping: dict[str, str]) -> None:
|
||||
MAPPING_JSON.write_text(
|
||||
json.dumps(dict(sorted(mapping.items())), ensure_ascii=False, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def pack_entries(
|
||||
entries: list[tuple[str, str]],
|
||||
source_meta: dict[str, dict],
|
||||
scale: float,
|
||||
max_width: int,
|
||||
) -> tuple[Image.Image, dict[str, dict]]:
|
||||
positions: dict[str, dict] = {}
|
||||
cursor_x = 0
|
||||
cursor_y = 0
|
||||
row_height = 0
|
||||
packed: list[tuple[str, tuple[int, int, int, int]]] = []
|
||||
|
||||
for old_key, new_key in entries:
|
||||
meta = source_meta[old_key]
|
||||
width = max(1, round(meta["width"] * scale))
|
||||
height = max(1, round(meta["height"] * scale))
|
||||
if cursor_x and cursor_x + width > max_width:
|
||||
cursor_x = 0
|
||||
cursor_y += row_height
|
||||
row_height = 0
|
||||
packed.append((old_key, (cursor_x, cursor_y, width, height)))
|
||||
record = {
|
||||
"x": cursor_x,
|
||||
"y": cursor_y,
|
||||
"width": width,
|
||||
"height": height,
|
||||
"pixelRatio": 1 if scale == 0.5 else 2,
|
||||
}
|
||||
positions[new_key] = record
|
||||
if old_key != new_key:
|
||||
positions[old_key] = dict(record)
|
||||
cursor_x += width
|
||||
row_height = max(row_height, height)
|
||||
|
||||
atlas_height = cursor_y + row_height
|
||||
image = Image.new("RGBA", (max_width, atlas_height), (0, 0, 0, 0))
|
||||
source_image = Image.open(SOURCE_SPRITE_PNG_2X).convert("RGBA")
|
||||
|
||||
for old_key, (x, y, width, height) in packed:
|
||||
meta = source_meta[old_key]
|
||||
crop = source_image.crop(
|
||||
(
|
||||
meta["x"],
|
||||
meta["y"],
|
||||
meta["x"] + meta["width"],
|
||||
meta["y"] + meta["height"],
|
||||
)
|
||||
)
|
||||
if scale != 1.0:
|
||||
crop = crop.resize((width, height), Image.LANCZOS)
|
||||
image.paste(crop, (x, y))
|
||||
|
||||
return image, positions
|
||||
|
||||
|
||||
def build_semantic_sprite(entries: list[tuple[str, str]]) -> None:
|
||||
OUTPUT_SPRITE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
source_meta = json.loads(SOURCE_SPRITE_JSON_2X.read_text(encoding="utf-8"))
|
||||
|
||||
atlas_2x, meta_2x = pack_entries(entries, source_meta, scale=1.0, max_width=2048)
|
||||
atlas_1x, meta_1x = pack_entries(entries, source_meta, scale=0.5, max_width=1024)
|
||||
|
||||
atlas_2x.save(OUTPUT_SPRITE_DIR / "sprite@2x.png")
|
||||
atlas_1x.save(OUTPUT_SPRITE_DIR / "sprite.png")
|
||||
(OUTPUT_SPRITE_DIR / "sprite@2x.json").write_text(
|
||||
json.dumps(meta_2x, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
|
||||
)
|
||||
(OUTPUT_SPRITE_DIR / "sprite.json").write_text(
|
||||
json.dumps(meta_1x, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
|
||||
def replace_sprite_keys(value, mapping: dict[str, str]):
|
||||
if isinstance(value, dict):
|
||||
return {k: replace_sprite_keys(v, mapping) for k, v in value.items()}
|
||||
if isinstance(value, list):
|
||||
return [replace_sprite_keys(item, mapping) for item in value]
|
||||
if isinstance(value, str):
|
||||
return mapping.get(value, value)
|
||||
return value
|
||||
|
||||
|
||||
def build_semantic_style(mapping: dict[str, str]) -> None:
|
||||
style = json.loads(SOURCE_STYLE.read_text(encoding="utf-8"))
|
||||
style = replace_sprite_keys(style, mapping)
|
||||
style["name"] = "NavSea Delivery Full Semantic"
|
||||
style["sprite"] = SEMANTIC_SPRITE_BASE_URL
|
||||
if style.get("sources", {}).get("navsea_delivery"):
|
||||
style["sources"]["navsea_delivery"]["tiles"] = [SEMANTIC_PBF_TILE_URL]
|
||||
OUTPUT_STYLE.write_text(json.dumps(style, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
DEPLOYED_STYLE.write_text(json.dumps(style, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
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:
|
||||
dst.write_bytes(src.read_bytes())
|
||||
|
||||
|
||||
def remap_tile(src: Path, dst: Path, value_map: dict[str, str], needles: list[bytes]) -> tuple[bool, int]:
|
||||
raw = src.read_bytes()
|
||||
if not any(needle in raw for needle in needles):
|
||||
hardlink_or_copy(src, dst)
|
||||
return False, 0
|
||||
|
||||
decoded, source_extents = decode_tile_with_extents(raw)
|
||||
changed_features = 0
|
||||
changed = False
|
||||
layers = []
|
||||
|
||||
for layer_name, layer in decoded.items():
|
||||
features = []
|
||||
for feature in layer["features"]:
|
||||
props = feature.get("properties", {})
|
||||
feature_changed = False
|
||||
for field in PBF_PROPERTY_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_features += 1
|
||||
changed = True
|
||||
output_feature = {
|
||||
"geometry": feature["geometry"],
|
||||
"properties": props,
|
||||
}
|
||||
if feature.get("id") is not None:
|
||||
output_feature["id"] = feature["id"]
|
||||
features.append(output_feature)
|
||||
layers.append({"name": layer_name, "features": features})
|
||||
|
||||
if changed:
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
if dst.exists():
|
||||
dst.unlink()
|
||||
dst.write_bytes(encode_layers_preserving_extents(layers, source_extents))
|
||||
assert_reencoded_tile_safe(src, dst)
|
||||
else:
|
||||
hardlink_or_copy(src, dst)
|
||||
return changed, changed_features
|
||||
|
||||
|
||||
def remap_tile_job(args: tuple[str, str, dict[str, str], list[bytes]]) -> tuple[bool, int]:
|
||||
src_str, dst_str, value_map, needles = args
|
||||
return remap_tile(Path(src_str), Path(dst_str), value_map, needles)
|
||||
|
||||
|
||||
def backfill_missing_tiles(src_root: Path, dst_root: Path) -> int:
|
||||
missing = 0
|
||||
for src in src_root.rglob("*.pbf"):
|
||||
dst = dst_root / src.relative_to(src_root)
|
||||
if dst.exists():
|
||||
continue
|
||||
hardlink_or_copy(src, dst)
|
||||
missing += 1
|
||||
return missing
|
||||
|
||||
|
||||
def build_semantic_pbf(rows: list[AuditRow], mapping: dict[str, str]) -> dict[str, int]:
|
||||
value_map = {
|
||||
row.old_sprite_key: mapping[row.old_sprite_key]
|
||||
for row in rows
|
||||
if row.used_in_pbf > 0 and row.old_sprite_key in mapping
|
||||
}
|
||||
needles = [old.encode("utf-8") for old in value_map]
|
||||
if OUTPUT_PBF_ROOT.exists():
|
||||
shutil.rmtree(OUTPUT_PBF_ROOT)
|
||||
OUTPUT_PBF_ROOT.parent.mkdir(parents=True, exist_ok=True)
|
||||
subprocess.run(
|
||||
["cp", "-al", f"{SOURCE_PBF_ROOT}/.", str(OUTPUT_PBF_ROOT)],
|
||||
check=True,
|
||||
)
|
||||
|
||||
rg_cmd = ["rg", "-a", "-l", "-F"]
|
||||
for old_key in value_map:
|
||||
rg_cmd.extend(["-e", old_key])
|
||||
rg_cmd.append(str(SOURCE_PBF_ROOT))
|
||||
result = subprocess.run(rg_cmd, check=True, capture_output=True, text=True)
|
||||
tile_paths = [Path(line) for line in result.stdout.splitlines() if line.strip()]
|
||||
|
||||
stats = {
|
||||
"tiles_total": len(list(SOURCE_PBF_ROOT.rglob("*.pbf"))),
|
||||
"tiles_changed": 0,
|
||||
"features_changed": 0,
|
||||
"tiles_scanned_for_rewrite": len(tile_paths),
|
||||
}
|
||||
|
||||
jobs = [
|
||||
(
|
||||
str(src),
|
||||
str(OUTPUT_PBF_ROOT / src.relative_to(SOURCE_PBF_ROOT)),
|
||||
value_map,
|
||||
needles,
|
||||
)
|
||||
for src in tile_paths
|
||||
]
|
||||
|
||||
with ProcessPoolExecutor(max_workers=min(os.cpu_count() or 4, 12)) as executor:
|
||||
for changed, feature_count in executor.map(remap_tile_job, jobs, chunksize=128):
|
||||
if changed:
|
||||
stats["tiles_changed"] += 1
|
||||
stats["features_changed"] += feature_count
|
||||
|
||||
stats["tiles_backfilled"] = backfill_missing_tiles(SOURCE_PBF_ROOT, OUTPUT_PBF_ROOT)
|
||||
stats["tiles_total_after_backfill"] = len(list(OUTPUT_PBF_ROOT.rglob("*.pbf")))
|
||||
|
||||
return stats
|
||||
|
||||
|
||||
def main() -> None:
|
||||
rows = load_audit_rows()
|
||||
mapping = build_mapping(rows)
|
||||
used_entries = build_used_sprite_entries(rows, mapping)
|
||||
|
||||
write_mapping_json(mapping)
|
||||
build_semantic_sprite(used_entries)
|
||||
build_semantic_style(mapping)
|
||||
pbf_stats = build_semantic_pbf(rows, mapping)
|
||||
|
||||
summary = {
|
||||
"mapping_count": len(mapping),
|
||||
"used_sprite_entries": len(used_entries),
|
||||
"style_output": str(OUTPUT_STYLE),
|
||||
"deployed_style": str(DEPLOYED_STYLE),
|
||||
"sprite_output_dir": str(OUTPUT_SPRITE_DIR),
|
||||
"pbf_output_dir": str(OUTPUT_PBF_ROOT),
|
||||
**pbf_stats,
|
||||
"style_version": STYLE_VERSION,
|
||||
"pbf_version": PBF_VERSION,
|
||||
"sprite_version": SPRITE_VERSION,
|
||||
}
|
||||
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
82
navsea_extent_shift_audit.py
Normal file
82
navsea_extent_shift_audit.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from navsea_tile_reencode_guard import validate_reencoded_tile
|
||||
|
||||
|
||||
DEFAULT_SOURCE_ROOT = Path(
|
||||
"/home/wwwroot/newpec/exported_auto/"
|
||||
"tile.mapple-on.jp__newpec-mvt-20260106__z___x___y_.pbf/tiles"
|
||||
)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="审计 PBF 是否存在高 extent 被 4096 口径重编码的几何错移。")
|
||||
parser.add_argument("--target-root", type=Path, required=True)
|
||||
parser.add_argument("--source-root", type=Path, default=DEFAULT_SOURCE_ROOT)
|
||||
parser.add_argument("--output-json", type=Path)
|
||||
parser.add_argument("--zoom", action="append", type=int)
|
||||
parser.add_argument("--sample-limit", type=int, default=50)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def should_include(rel: Path, zoom_filter: set[int] | None) -> bool:
|
||||
if not zoom_filter:
|
||||
return True
|
||||
try:
|
||||
return int(rel.parts[0]) in zoom_filter
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
zoom_filter = set(args.zoom or [])
|
||||
suspicious_tiles: list[dict[str, Any]] = []
|
||||
checked = 0
|
||||
|
||||
for tile_path in sorted(args.target_root.glob("*/*/*.pbf")):
|
||||
rel = tile_path.relative_to(args.target_root)
|
||||
if not should_include(rel, zoom_filter or None):
|
||||
continue
|
||||
source_tile = args.source_root / rel
|
||||
if not source_tile.exists():
|
||||
continue
|
||||
checked += 1
|
||||
verdict = validate_reencoded_tile(source_tile, tile_path)
|
||||
if verdict["suspicious"]:
|
||||
suspicious_tiles.append(
|
||||
{
|
||||
"tile": str(rel),
|
||||
**verdict,
|
||||
}
|
||||
)
|
||||
|
||||
suspicious_tiles.sort(
|
||||
key=lambda item: (
|
||||
item["output_max_overflow"] - item["source_max_overflow"],
|
||||
item["tile"],
|
||||
),
|
||||
reverse=True,
|
||||
)
|
||||
summary = {
|
||||
"target_root": str(args.target_root),
|
||||
"source_root": str(args.source_root),
|
||||
"zoom_filter": sorted(zoom_filter),
|
||||
"tiles_checked": checked,
|
||||
"suspicious_tile_count": len(suspicious_tiles),
|
||||
"samples": suspicious_tiles[: args.sample_limit],
|
||||
}
|
||||
if args.output_json:
|
||||
args.output_json.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
77
navsea_tile_reencode_guard.py
Normal file
77
navsea_tile_reencode_guard.py
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import mapbox_vector_tile
|
||||
|
||||
from navsea_geometry_native_audit import analyze_tile
|
||||
|
||||
|
||||
def decode_tile_with_extents(raw: bytes) -> tuple[dict[str, Any], dict[str, int]]:
|
||||
decoded = mapbox_vector_tile.decode(raw)
|
||||
extents = {
|
||||
layer_name: int(layer.get("extent") or 4096)
|
||||
for layer_name, layer in decoded.items()
|
||||
}
|
||||
return decoded, extents
|
||||
|
||||
|
||||
def encode_layers_preserving_extents(
|
||||
encoded_layers: list[dict[str, Any]],
|
||||
layer_extents: dict[str, int],
|
||||
) -> bytes:
|
||||
per_layer_options = {
|
||||
layer["name"]: {"extents": int(layer_extents[layer["name"]])}
|
||||
for layer in encoded_layers
|
||||
}
|
||||
return mapbox_vector_tile.encode(encoded_layers, per_layer_options=per_layer_options)
|
||||
|
||||
|
||||
def validate_reencoded_tile(
|
||||
source_tile_path: Path,
|
||||
output_tile_path: Path,
|
||||
*,
|
||||
tolerance: int = 4096,
|
||||
) -> dict[str, Any]:
|
||||
source_analysis = analyze_tile(source_tile_path)
|
||||
output_analysis = analyze_tile(output_tile_path)
|
||||
|
||||
source_extent = max(source_analysis["extent_values"]) if source_analysis["extent_values"] else 4096
|
||||
output_extent = max(output_analysis["extent_values"]) if output_analysis["extent_values"] else 4096
|
||||
source_overflow = int(source_analysis["max_overflow"])
|
||||
output_overflow = int(output_analysis["max_overflow"])
|
||||
suspicious_extent_shift = (
|
||||
output_extent > 4096
|
||||
and output_overflow >= source_overflow + (output_extent - 4096) - tolerance
|
||||
)
|
||||
suspicious = output_overflow > source_overflow + tolerance or suspicious_extent_shift
|
||||
|
||||
return {
|
||||
"source_extent_values": source_analysis["extent_values"],
|
||||
"output_extent_values": output_analysis["extent_values"],
|
||||
"source_max_overflow": source_overflow,
|
||||
"output_max_overflow": output_overflow,
|
||||
"suspicious_extent_shift": suspicious_extent_shift,
|
||||
"suspicious": suspicious,
|
||||
}
|
||||
|
||||
|
||||
def assert_reencoded_tile_safe(
|
||||
source_tile_path: Path,
|
||||
output_tile_path: Path,
|
||||
*,
|
||||
tolerance: int = 4096,
|
||||
) -> None:
|
||||
verdict = validate_reencoded_tile(source_tile_path, output_tile_path, tolerance=tolerance)
|
||||
if verdict["suspicious"]:
|
||||
raise RuntimeError(
|
||||
"reencoded tile geometry drift detected: "
|
||||
f"source={source_tile_path} output={output_tile_path} "
|
||||
f"source_extent={verdict['source_extent_values']} "
|
||||
f"output_extent={verdict['output_extent_values']} "
|
||||
f"source_overflow={verdict['source_max_overflow']} "
|
||||
f"output_overflow={verdict['output_max_overflow']} "
|
||||
f"suspicious_extent_shift={verdict['suspicious_extent_shift']}"
|
||||
)
|
||||
109
report/full_extent_shift_root_cause_2026-04-18.md
Normal file
109
report/full_extent_shift_root_cause_2026-04-18.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# 全国 full / semantic 高 extent 几何错移根因报告
|
||||
|
||||
## 1. 结论
|
||||
|
||||
- 当前全国 `full / semantic` 中反复出现的“大块空白 / 岛礁附近整块打穿 / 右侧大片缺失”问题
|
||||
- 根因不是 `style` 改图标本身
|
||||
- 真正根因是:
|
||||
- 某条会重写 `PBF` 的生产链路
|
||||
- 在处理原始 `extent = 1048576` 的高精度 tile 时
|
||||
- 用了 `4096` 口径重编码几何
|
||||
- 结果会把 tile 的几何整体错移,尤其是 `Y` 坐标整张 tile 下移一个:
|
||||
- `1048576 - 4096 = 1044480`
|
||||
量级的偏移
|
||||
|
||||
## 2. 证据
|
||||
|
||||
### 2.1 生产坏 tile 的数值签名
|
||||
|
||||
- 代表坏 tile:
|
||||
- `/tmp/navsea_hotspot_rebuild/backup_full/12/3526/1643.pbf`
|
||||
- `/tmp/navsea_hotspot_rebuild/backup_semantic/12/3526/1643.pbf`
|
||||
- 对比 source tile:
|
||||
- `/home/wwwroot/newpec/exported_auto/tile.mapple-on.jp__newpec-mvt-20260106__z___x___y_.pbf/tiles/12/3526/1643.pbf`
|
||||
- source 正常 overflow:
|
||||
- `20480`
|
||||
- 坏 tile overflow:
|
||||
- `1064960`
|
||||
|
||||
### 2.2 复现实验
|
||||
|
||||
- 已直接拿 source tile 做重编码实验
|
||||
- 当把原始 `extent = 1048576` tile 重新编码时,故意把 layer `extent` 改成 `4096`
|
||||
- 可以稳定复现出与生产坏 tile 一模一样的几何签名:
|
||||
- `overflow = 1064960`
|
||||
- 这说明:
|
||||
- 当前线上坏 tile 不是随机损坏
|
||||
- 而是非常明确的“高 extent tile 被按 4096 口径重编码”的产物
|
||||
|
||||
### 2.3 热点备份审计结果
|
||||
|
||||
- 对修前热点 full 备份:
|
||||
- [`hotspot_backup_full_shift_audit_2026-04-18.json`](/root/sourceserver/pbf/report/hotspot_backup_full_shift_audit_2026-04-18.json)
|
||||
- 对修前热点 semantic 备份:
|
||||
- [`hotspot_backup_semantic_shift_audit_2026-04-18.json`](/root/sourceserver/pbf/report/hotspot_backup_semantic_shift_audit_2026-04-18.json)
|
||||
- 结果:
|
||||
- `backup_full`:`154` 张里有 `42` 张命中该签名
|
||||
- `backup_semantic`:`154` 张里有 `43` 张命中该签名
|
||||
|
||||
## 3. 为什么会让人误以为是“改图标导致的”
|
||||
|
||||
- 图标 / style 变更本身不会把海底等深线、海岸线、陆域几何整块打空
|
||||
- 但“图标改名 / 属性改写”这类需求往往会触发:
|
||||
- `decode pbf`
|
||||
- `改属性`
|
||||
- `encode pbf`
|
||||
这条重写链
|
||||
- 一旦这条链在高 extent tile 上没有保回原始 extent
|
||||
- 几何就会被整体写坏
|
||||
- 所以用户看到“像是改 style 图标之后坏了”
|
||||
- 现象上是对的
|
||||
- 但根因不是 style
|
||||
- 而是“被图标/属性变更触发的 PBF 重编码链”出了错
|
||||
|
||||
## 4. 本轮代码修复
|
||||
|
||||
### 4.1 已新增重编码守卫
|
||||
|
||||
- [`navsea_tile_reencode_guard.py`](/root/sourceserver/pbf/navsea_tile_reencode_guard.py)
|
||||
|
||||
用途:
|
||||
|
||||
- 统一保存原 layer `extent`
|
||||
- 重编码后立即和 source tile 做几何签名比对
|
||||
- 一旦出现:
|
||||
- overflow 异常增大
|
||||
- 或命中 `1048576 -> 4096` 错重编码签名
|
||||
直接报错,不允许静默产出坏 tile
|
||||
|
||||
### 4.2 已新增根因审计脚本
|
||||
|
||||
- [`navsea_extent_shift_audit.py`](/root/sourceserver/pbf/navsea_extent_shift_audit.py)
|
||||
|
||||
用途:
|
||||
|
||||
- 审计目标目录里是否存在:
|
||||
- 高 extent tile 被按 4096 口径错重编码
|
||||
的几何签名
|
||||
|
||||
### 4.3 已加固语义版 PBF 重写链
|
||||
|
||||
- 已修改:
|
||||
- [`build_semantic_delivery_assets.py`](/root/sourceserver/pbf/build_semantic_delivery_assets.py)
|
||||
|
||||
当前要求:
|
||||
|
||||
- decode 时显式带出原始 layer extent
|
||||
- encode 时必须按原 layer extent 写回
|
||||
- 每张被重写的 tile 写完后立即做几何守卫校验
|
||||
|
||||
## 5. 当前对线上问题的影响
|
||||
|
||||
- 热点 AOI 中已修过的 tile:
|
||||
- 已通过重建替换为当前正确几何
|
||||
- 但全国目录里仍可能存在更多尚未重建的历史坏 tile
|
||||
- 因此要“彻底解决”全国问题,最终仍需要:
|
||||
- 用当前受保护的生产链路重建全国 `full`
|
||||
- 再基于新的 full 重建 `semantic`
|
||||
- 并在部署前跑一轮 `extent shift audit`
|
||||
|
||||
555
report/hotspot_backup_full_shift_audit_2026-04-18.json
Normal file
555
report/hotspot_backup_full_shift_audit_2026-04-18.json
Normal file
@@ -0,0 +1,555 @@
|
||||
{
|
||||
"target_root": "/tmp/navsea_hotspot_rebuild/backup_full",
|
||||
"source_root": "/home/wwwroot/newpec/exported_auto/tile.mapple-on.jp__newpec-mvt-20260106__z___x___y_.pbf/tiles",
|
||||
"zoom_filter": [],
|
||||
"tiles_checked": 154,
|
||||
"suspicious_tile_count": 42,
|
||||
"samples": [
|
||||
{
|
||||
"tile": "12/3641/1615.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3641/1614.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3640/1616.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3640/1615.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3640/1614.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3639/1618.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3639/1617.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3639/1616.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3639/1615.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3639/1614.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3588/1629.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3588/1628.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3588/1627.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3588/1626.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3586/1630.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3586/1629.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3586/1628.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3586/1627.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3586/1626.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3585/1630.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3585/1629.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3585/1628.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3585/1627.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3527/1643.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3527/1642.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3526/1643.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3526/1642.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3525/1644.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3525/1643.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3525/1642.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3501/1740.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3501/1739.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3501/1738.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3501/1737.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3501/1736.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3500/1740.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3500/1739.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3500/1738.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3500/1737.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3500/1736.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3499/1739.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3499/1738.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
}
|
||||
]
|
||||
}
|
||||
568
report/hotspot_backup_semantic_shift_audit_2026-04-18.json
Normal file
568
report/hotspot_backup_semantic_shift_audit_2026-04-18.json
Normal file
@@ -0,0 +1,568 @@
|
||||
{
|
||||
"target_root": "/tmp/navsea_hotspot_rebuild/backup_semantic",
|
||||
"source_root": "/home/wwwroot/newpec/exported_auto/tile.mapple-on.jp__newpec-mvt-20260106__z___x___y_.pbf/tiles",
|
||||
"zoom_filter": [],
|
||||
"tiles_checked": 154,
|
||||
"suspicious_tile_count": 43,
|
||||
"samples": [
|
||||
{
|
||||
"tile": "12/3641/1615.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3641/1614.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3640/1616.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3640/1615.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3640/1614.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3639/1618.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3639/1617.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3639/1616.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3639/1615.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3639/1614.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3588/1629.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3588/1628.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3588/1627.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3588/1626.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3586/1630.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3586/1629.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3586/1628.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3586/1627.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3586/1626.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3585/1630.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3585/1629.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3585/1628.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3585/1627.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3527/1643.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3527/1642.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3526/1643.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3526/1642.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3525/1644.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3525/1643.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3525/1642.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3501/1740.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3501/1739.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3501/1738.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3501/1737.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3501/1736.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3500/1740.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3500/1739.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3500/1738.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3500/1737.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3500/1736.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3499/1740.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3499/1739.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
},
|
||||
{
|
||||
"tile": "12/3499/1738.pbf",
|
||||
"source_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"output_extent_values": [
|
||||
1048576
|
||||
],
|
||||
"source_max_overflow": 20480,
|
||||
"output_max_overflow": 1064960,
|
||||
"suspicious_extent_shift": true,
|
||||
"suspicious": true
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user