From 5cb0eead7c46c17e2bca9a5a864ce91ce243119b Mon Sep 17 00:00:00 2001 From: OpenAI Codex Date: Sat, 18 Apr 2026 14:26:54 +0800 Subject: [PATCH] guard high-extent tile reencoding --- build_semantic_delivery_assets.py | 354 +++++++++++ navsea_extent_shift_audit.py | 82 +++ navsea_tile_reencode_guard.py | 77 +++ ...full_extent_shift_root_cause_2026-04-18.md | 109 ++++ ...ot_backup_full_shift_audit_2026-04-18.json | 555 +++++++++++++++++ ...ackup_semantic_shift_audit_2026-04-18.json | 568 ++++++++++++++++++ 6 files changed, 1745 insertions(+) create mode 100644 build_semantic_delivery_assets.py create mode 100644 navsea_extent_shift_audit.py create mode 100644 navsea_tile_reencode_guard.py create mode 100644 report/full_extent_shift_root_cause_2026-04-18.md create mode 100644 report/hotspot_backup_full_shift_audit_2026-04-18.json create mode 100644 report/hotspot_backup_semantic_shift_audit_2026-04-18.json diff --git a/build_semantic_delivery_assets.py b/build_semantic_delivery_assets.py new file mode 100644 index 0000000..4060543 --- /dev/null +++ b/build_semantic_delivery_assets.py @@ -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() diff --git a/navsea_extent_shift_audit.py b/navsea_extent_shift_audit.py new file mode 100644 index 0000000..8fcb952 --- /dev/null +++ b/navsea_extent_shift_audit.py @@ -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() diff --git a/navsea_tile_reencode_guard.py b/navsea_tile_reencode_guard.py new file mode 100644 index 0000000..ed9635d --- /dev/null +++ b/navsea_tile_reencode_guard.py @@ -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']}" + ) diff --git a/report/full_extent_shift_root_cause_2026-04-18.md b/report/full_extent_shift_root_cause_2026-04-18.md new file mode 100644 index 0000000..daaf60b --- /dev/null +++ b/report/full_extent_shift_root_cause_2026-04-18.md @@ -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` + diff --git a/report/hotspot_backup_full_shift_audit_2026-04-18.json b/report/hotspot_backup_full_shift_audit_2026-04-18.json new file mode 100644 index 0000000..7b92672 --- /dev/null +++ b/report/hotspot_backup_full_shift_audit_2026-04-18.json @@ -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 + } + ] +} diff --git a/report/hotspot_backup_semantic_shift_audit_2026-04-18.json b/report/hotspot_backup_semantic_shift_audit_2026-04-18.json new file mode 100644 index 0000000..53a1b60 --- /dev/null +++ b/report/hotspot_backup_semantic_shift_audit_2026-04-18.json @@ -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 + } + ] +}