Files
Pier_v1/pierzainer_ground/normalize_pdf_soundings.py

78 lines
5.3 KiB
Python

"""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())