"""Regenerate the user's two inputs in a separate folder, reusing exact caches.""" import hashlib import json import shutil import sys from pathlib import Path import numpy as np root=Path(__file__).resolve().parents[1] sys.path.insert(0,str(root/'pierzainer_ground')) from pierzainer_ground.drawing_job import DrawingJob,build_drawing old=root/'pierzainer_ground/data/output/app_b614afc0-6829-4aef-96e3-3ff1365ff5db' target=root/'pierzainer_ground/data/output/ungok_corrected_20261110' evidence=root/'.verify/ungok-terrain-fixed-20261110' evidence.mkdir(parents=True,exist_ok=True) settings=json.loads((old/'generation_settings.json').read_text()) old_file=old/'ground_model_3d_with_maps.json' sources=[Path(settings['drawing']),Path(settings['piers']),old_file] digest=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() before={str(p):digest(p) for p in sources} if (target/'ground_model_3d_with_maps.json').exists(): raise SystemExit('Corrected result already exists; preserve it rather than overwrite.') (target/'cache').mkdir(parents=True,exist_ok=True) for path in (old/'cache').iterdir(): if path.name.endswith(('.dxf','.dxf.source.json')) or path.name=='map_layers.json': shutil.copyfile(path,target/'cache'/path.name) settings.update(output=str(target),allow_download=False) log=[] data=build_drawing(DrawingJob(**settings),lambda text:(log.append(text),print(text,flush=True))) original=json.loads(old_file.read_text()) def metrics(d): g=d['terrain_grid'];z=np.array([np.nan if v is None else v for v in g['z']]).reshape(g['ny'],g['nx']) edges=np.concatenate([np.abs(np.diff(z,axis=0)).ravel(),np.abs(np.diff(z,axis=1)).ravel()]);edges=edges[np.isfinite(edges)] return {'valid_nodes':int(np.isfinite(z).sum()),'min_el':float(np.nanmin(z)),'max_el':float(np.nanmax(z)), 'max_adjacent_dz':float(edges.max()),'edges_over_5m':int((edges>5).sum()),'edges_over_8m':int((edges>8).sum())} checks={ 'original_inputs_and_previous_result_unchanged':all(digest(p)==before[str(p)] for p in sources), 'all_16_centers_have_ground':len(data['piers'])==16 and all(p['ground']['center'] is not None for p in data['piers']), 'same_structure_ids_and_coordinates':[(p['id'],p['X'],p['Y']) for p in data['piers']]==[(p['id'],p['X'],p['Y']) for p in original['piers']], 'same_grid_layout':all(data['terrain_grid'][k]==original['terrain_grid'][k] for k in ('origin','step','nx','ny')), 'semantic_source_filter_used':data['terrain']['source_counts'].get('semantic_layer_filter')==1, 'no_adjacent_jumps_over_5m':metrics(data)['edges_over_5m']==0, 'both_embedded_maps_retained':data['map_status']['status']=='mapped' and {l['id'] for l in data['visualization']['layers']}=={'satellite','ground'}, 'original_source_provenance_matches':data['surface_provenance']['sha256']==before[str(sources[0])] and data['surface_provenance']['pier_sha256']==before[str(sources[1])], } report={'result':'PASS' if all(checks.values()) else 'FAIL','checks':checks,'before':metrics(original),'after':metrics(data),'output':str(target/'ground_model_3d_with_maps.json'),'inputs_sha256':before,'terrain':data['terrain'],'log':log, 'centers':[{'id':new['id'],'before':prior['ground']['center'],'after':new['ground']['center']} for prior,new in zip(original['piers'],data['piers'])]} (evidence/'report.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)) print(json.dumps(report,ensure_ascii=False)) if not all(checks.values()):raise SystemExit(1)