#!/usr/bin/env python3
"""
GATE: does the stitched .hblk describe the same world region the service does?
This is the check that would have caught #50 section 2.4 -- the transposed tile
layout that cost a session, where the instrument and the page shared the same
axis bug and therefore agreed with each other.
It re-fetches the block's exact world footprint from the live service in ONE
request and compares to the stitched array. Tolerance is 1 m: tiles are stored
int16 and tiles.py documents that fetching a region as one request or as
quadrants differs by at most that.
AND IT PROVES IT CAN FAIL. A gate that cannot fail is not evidence, so the
CONTROL row feeds the same comparison a transposed copy of the same data and
requires it to be REJECTED. If the control passes, the gate is broken and the
main row means nothing.
python gate_stitch.py /tmp/block.hblk [--url http://127.0.0.1:8817]
"""
import argparse, sys, time, urllib.request
import numpy as np
from stitch_block import read_hblk, MPP
def fetch(url, i1, j1, n):
u = f"{url}/terrain?i1={i1}&j1={j1}&i2={i1+n}&j2={j1+n}&scale=1"
t0 = time.time()
with urllib.request.urlopen(u, timeout=7200) as r:
h = int(r.headers["X-Height"]); w = int(r.headers["X-Width"]); b = r.read()
return (np.frombuffer(b[:h*w*2], dtype="<i2").reshape(h, w).astype(np.int64),
time.time() - t0)
def compare(a, b, tol=1):
d = np.abs(a - b)
return dict(maxdiff=int(d.max()), rmse=float(np.sqrt((d**2).mean())),
frac_over=float((d > tol).mean()), ok=bool(d.max() <= tol))
def main():
ap = argparse.ArgumentParser()
ap.add_argument("hblk"); ap.add_argument("--url", default="http://127.0.0.1:8817")
ap.add_argument("--tol", type=int, default=1)
a = ap.parse_args()
arr, meta = read_hblk(a.hblk)
side = meta["side"]
# The header stores world metres; the service is indexed in pixels, and
# row = z, column = x (see stitch_block.py's axis note).
i1 = int(round(meta["origin_z"] / meta["mpp"])) # first axis
j1 = int(round(meta["origin_x"] / meta["mpp"])) # second axis
print(f"block {side}x{side} px at world x {meta['origin_x']:,.0f} "
f"z {meta['origin_z']:,.0f} -> service i1={i1} j1={j1} n={side}")
live, dt = fetch(a.url, i1, j1, side)
print(f"fetched in {dt:.1f}s")
disk = arr.astype(np.int64)
r = compare(disk, live, a.tol)
print(f"\n STITCH maxdiff {r['maxdiff']:>5} m rmse {r['rmse']:>7.3f} m "
f"over-tol {100*r['frac_over']:.4f}% {'PASS' if r['ok'] else 'FAIL'}")
# CONTROL: the same comparison must REJECT a transposed block.
c = compare(disk.T, live, a.tol)
ctrl_ok = not c["ok"]
print(f" CONTROL transposed copy -> maxdiff {c['maxdiff']:>5} m "
f"{'REJECTED (gate can fail)' if ctrl_ok else 'ACCEPTED -- GATE IS BROKEN'}")
if not ctrl_ok:
print("\nVERDICT: the gate accepted a transposed block, so it is not "
"measuring layout at all. The PASS above means nothing.")
return 2
if not r["ok"]:
print("\nVERDICT: stitched block does NOT match the service.")
return 1
print("\nVERDICT: stitched block matches the service, and the gate rejects "
"a layout error. The .hblk is the world the lookdev showed.")
return 0
if __name__ == "__main__":
sys.exit(main())