"""Write a traceable derived POINT DXF from actual original-PDF sounding labels.""" import hashlib,json from pathlib import Path import numpy as np import ezdxf from build_sample_grounds import ROOT,OUTPUT,save from pierzainer_ground.dtm import DTM from pierzainer_ground.project_coordinates import workbook_piers folder=OUTPUT/'03_대호지교';recovered=ROOT/'data/input/samples_20261102/recovered' source=json.loads((recovered/'03_pdf_source.json').read_text(encoding='utf-8')) expected='7284235502370bfc99741cf0996a1464cff16e71821760654e22ea1b0553f6e5' assert hashlib.sha256(Path(source['local']).read_bytes()).hexdigest()==expected registration=json.loads((folder/'pdf_plan_registration.json').read_text(encoding='utf-8')) assert registration['status']=='registered' and len(registration['solutions'])==1 solution=registration['solutions'][0] assert len(solution['controls'])==12 and max(solution['residual_m'])<.20 assert [c['pier'] for c in solution['controls']]==[f'P{i}' for i in range(1,13)] assert np.all(np.diff([c['pdf_center'][0] for c in solution['controls']])>0) associations=json.loads((folder/'pdf_marker_associations.json').read_text(encoding='utf-8')) rotation=np.array(solution['rotation']);translation=np.array(solution['translation_EN']);scale=solution['scale_m_per_pt'] points={};conflicts=[] for item in associations['matched']: key=tuple(item['line_indices']) if key in points: if points[key]['text']!=item['text']:conflicts.append(key) continue east,north=np.array(item['pdf_marker'])*[1,-1]*scale@rotation+translation points[key]={**item,'X':float(north),'Y':float(east),'Z':item['elevation_m']} for key in conflicts:points.pop(key,None) points=list(points.values()) # The original longitudinal ground line supplies independent ordinates exactly # at pier stations, including the steep shoreline. It is not a constant-height # strip: transverse geometry still comes only from the original plan soundings. vectors=json.loads((folder/'original_profile_vectors.json').read_text(encoding='utf-8')) bed=vectors['curves'][29595] assert bed['stroke'] and bed['dash'] is None and bed['width']>600 axis_ticks=[(7072,20.),(7070,10.),(7069,0.),(7067,-10.),(7066,-20.),(7074,-30.),(7221,-40.)] tick_y=np.array([vectors['lines'][i]['top'] for i,_ in axis_ticks]);tick_el=np.array([z for _,z in axis_ticks]) vertical=np.polyfit(tick_y,tick_el,1) assert max(abs(np.polyval(vertical,tick_y)-tick_el))<.10 bed_xy=np.array(bed['pts']);bed_xy=bed_xy[np.argsort(bed_xy[:,0])] profile_points=[] for control in solution['controls']: x,y=control['pdf_center'];profile_y=float(np.interp(x,bed_xy[:,0],bed_xy[:,1]));z=float(np.polyval(vertical,profile_y)) east,north=np.array([x,-y])*scale@rotation+translation point=dict(X=float(north),Y=float(east),Z=z,pier=control['pier'],source_kind='original_profile_ground_ordinate', pdf_profile_point=[x,profile_y],pdf_plan_point=[x,y],ground_curve_index=29595, axis_tick_line_indices=[i for i,_ in axis_ticks]) profile_points.append(point) points.extend(profile_points) assert len(points)>500 and sum(p['Z']<0 for p in points)>400 provenance=dict(**source,method='original_PDF_vector_digit_signatures_at_original_POINT_or_cross_markers', normalized_points=len(points),conflicting_markers_rejected=len(conflicts), ambiguous_labels_excluded=True,AI_or_OCR_service_used=False, control_registration=registration,geometry_quantum_m=.12*scale, profile_ground_curve_index=29595,profile_ground_ordinates=profile_points, vertical_axis=dict(ticks=[dict(line_index=i,EL=z,pdf_y=float(vectors['lines'][i]['top'])) for i,z in axis_ticks], coefficients=vertical.tolist(),max_residual_m=float(max(abs(np.polyval(vertical,tick_y)-tick_el)))), source_vector_cache_sha256=hashlib.sha256((folder/'original_profile_vectors.json').read_bytes()).hexdigest()) save(recovered/'03_original_pdf_soundings.json',dict(provenance=provenance,points=points)) destination=recovered/'03_original_pdf_soundings.dxf' doc=ezdxf.new('R2010');doc.layers.new('PDF_SOUNDING');doc.appids.new('GROUND_SOURCE') for p in points: entity=doc.modelspace().add_point((p['Y'],p['X'],p['Z']),dxfattribs={'layer':'PDF_SOUNDING'}) if p.get('source_kind')=='original_profile_ground_ordinate': entity.set_xdata('GROUND_SOURCE',[(1000,p['pier']),(1000,'PDF_GROUND_CURVE:29595')]) else: entity.set_xdata('GROUND_SOURCE',[(1000,p['text']),(1000,'PDF_LINE:'+','.join(map(str,p['line_indices']))),(1000,'PDF_GLYPH:'+','.join(map(str,p['glyph_indices'])))]) doc.saveas(destination) save(recovered/'03_soundings_normalization.json',provenance) job=next(j for j in json.loads((OUTPUT/'inputs.json').read_text(encoding='utf-8')) if j['sample']==3) centers=np.array([np.mean(p['axis'],axis=0) for p in workbook_piers(job['workbook'])]);dtm=DTM(np.array([[p['X'],p['Y'],p['Z']] for p in points]),100) values=dtm.z_many(centers);assert np.isfinite(values).all() save(folder/'original_pdf_pier_elevations.json',dict(piers=[dict(id=f'P{i+1}',ground_el=float(z)) for i,z in enumerate(values)],point_count=len(points),provenance=provenance)) print('Original PDF source points',len(points),'Z',min(p['Z'] for p in points),max(p['Z'] for p in points)) print('Original sounding TIN at all twelve piers:',values.tolist())