96 lines
6.8 KiB
Python
96 lines
6.8 KiB
Python
import json,pathlib,collections,math
|
||
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
||
raw=(ROOT/'public/drawings/광터지하차도_일반도(BLOCK7).dxf').read_text(encoding='utf8',errors='replace').splitlines()
|
||
pairs=[(int(raw[i].strip()),raw[i+1].strip()) for i in range(0,len(raw)-1,2)]
|
||
entities=[]; current=None; section=''; block=None; blocks={}
|
||
for code,value in pairs:
|
||
if code==0:
|
||
if current:
|
||
if section=='ENTITIES': entities.append(current)
|
||
elif section=='BLOCKS' and block and current['type'] not in ('BLOCK','ENDBLK'): blocks.setdefault(block,[]).append(current)
|
||
current={'type':value,'data':[]}
|
||
if value=='ENDBLK': block=None
|
||
elif current:
|
||
current['data'].append((code,value))
|
||
if current['type']=='SECTION' and code==2: section=value
|
||
if current['type']=='BLOCK' and code==2: block=value
|
||
|
||
def get(e,c,d=None): return next((v for k,v in e['data'] if k==c),d)
|
||
def point(e,c=10): return [float(get(e,c,0)),float(get(e,c+10,0))]
|
||
def layer(e): return get(e,8,'')
|
||
out=ROOT/'docs/extracted';out.mkdir(exist_ok=True)
|
||
texts=[{'text':get(e,1,''),'p':point(e),'layer':layer(e)} for e in entities if e['type'] in ('TEXT','MTEXT')]
|
||
lines=[{'a':point(e),'b':point(e,11),'layer':layer(e),'handle':get(e,5)} for e in entities if e['type']=='LINE']
|
||
for name,data in [('texts',texts),('lines',lines),('blocks',blocks),('inserts',[{'name':get(e,2),'p':point(e),'sx':get(e,41,'1'),'sy':get(e,42,'1'),'rotation':get(e,50,'0'),'layer':layer(e)} for e in entities if e['type']=='INSERT'])]:
|
||
(out/(name+'.json')).write_text(json.dumps(data,ensure_ascii=False,indent=2),encoding='utf8')
|
||
print('Entities',collections.Counter(e['type'] for e in entities))
|
||
for t in texts:
|
||
if any(s in t['text'] for s in ('BLOCK7','106.675','107.307','101.636','101.069','102.436','101.869','299.010','318.990','선형','측구','20.100')): print(t)
|
||
for name,top in [('A',-74271),('B',-91211)]:
|
||
selected=[l for l in lines if l['layer'] in ('CS-CONC-MAJR','CS-CONC-MINR') and all(1118000<p[0]<1139000 and top-7500<p[1]<top+100 for p in (l['a'],l['b']))]
|
||
print(name,len(selected),collections.Counter(l['layer'] for l in selected))
|
||
(out/f'section-{name}.json').write_text(json.dumps(selected,ensure_ascii=False,indent=2),encoding='utf8')
|
||
|
||
# 도면 근거를 보존하는 모델 데이터와 원도면 검토 SVG 생성
|
||
sections=[]
|
||
for name,top,el,sta in [('A',-74271.37855593952,106.675,299.000),('B',-91211.37855593952,107.307,319.000)]:
|
||
selected=[l for l in lines if l['layer'] in ('CS-CONC-MAJR','CS-CONC-MINR') and all(1118000<p[0]<1139000 and top-7500<p[1]<top+100 for p in (l['a'],l['b']))]
|
||
sections.append({'name':name,'topCad':top,'topEL':el,'station':sta,'lines':selected})
|
||
|
||
def group(i):
|
||
if i<5 or i==100: return '底版'
|
||
if 5<=i<=15: return '좌측 측구·포장 경계'
|
||
if 16<=i<=26: return '우측 측구·포장 경계'
|
||
if 27<=i<=86: return '포장 반복선·내부선'
|
||
if 87<=i<=89: return '중앙 분리부'
|
||
if 90<=i<=94: return '좌측 벽체'
|
||
if 95<=i<=99: return '우측 벽체'
|
||
if 101<=i<=107: return '하부·외측 보조 윤곽'
|
||
return '바닥판 상면·세부선'
|
||
|
||
def norm(p,s): return [round((p[0]-1128486.218066491)/1000,7),round(s['topEL']+(p[1]-s['topCad'])/1000,7)]
|
||
parts=[]
|
||
for i,(a,b) in enumerate(zip(sections[0]['lines'],sections[1]['lines'])):
|
||
assert a['layer']==b['layer']
|
||
assert all(abs(a[k][0]-b[k][0])<0.01 for k in ['a','b'])
|
||
parts.append({'id':'line-'+a['handle'],'name':group(i).replace('底版','바닥판 하단')+' · '+str(i+1),'group':group(i).replace('底版','바닥판 하단'),'layer':a['layer'],'handles':[a['handle'],b['handle']],'a':[norm(a['a'],sections[0]),norm(a['b'],sections[0])],'b':[norm(b['a'],sections[1]),norm(b['b'],sections[1])],'repeat':27<=i<=86,'kind':'line','source':'일반도 14 / 단면 A-A·B-B / '+a['layer']})
|
||
# 삽입 블록의 반전과 실제 LINE/ARC 윤곽을 보존한다. 원호는 5도 간격으로 표시한다.
|
||
ductName='A$C2017021411375356'
|
||
for side,x,sx in [('좌측',1119736.218066468,1),('우측',1137236.218066513,-1)]:
|
||
for j,e in enumerate(blocks[ductName]):
|
||
if e['type']=='LINE': pts=[point(e),point(e,11)]
|
||
elif e['type']=='ARC':
|
||
center=point(e); r=float(get(e,40)); start=float(get(e,50)); end=float(get(e,51))
|
||
if end<=start:end+=360
|
||
pts=[[center[0]+r*math.cos(math.radians(t)),center[1]+r*math.sin(math.radians(t))] for t in [start+(end-start)*k/36 for k in range(37)]]
|
||
else: continue
|
||
profiles=[]
|
||
for s,y in zip(sections,[-77447.89255584464,-95669.3155558447]):profiles.append([norm([x+sx*p[0],y+p[1]],s) for p in pts])
|
||
parts.append({'id':f'duct-{side}-{j}','name':f'{side} 공동구·상부 상세 · {j+1}','group':f'{side} 공동구·상부 상세','layer':layer(e),'handles':[get(e,5)],'a':profiles[0],'b':profiles[1],'repeat':False,'kind':e['type'].lower(),'source':'일반도 14 / '+ductName+' / 원본 삽입 블록'})
|
||
model={'source':'광터지하차도_일반도(BLOCK7).dxf','units':'m','length':19.98,'sectionStations':[299,319],'longitudinalStations':[299.01,318.99],'sectionTops':[106.675,107.307],'sectionBottoms':[101.636,101.069],'centerEL':[None,None],'alignment':None,'status':'확인 필요','parts':parts}
|
||
modelDir=ROOT/'src/model';modelDir.mkdir(exist_ok=True)
|
||
(modelDir/'block7.json').write_text(json.dumps(model,ensure_ascii=False,indent=2),encoding='utf8')
|
||
import html
|
||
previewDir=ROOT/'public/analysis';previewDir.mkdir(exist_ok=True)
|
||
views=[('단면A',1113000,-84300,1142000,-69500),('단면B',1113000,-102300,1142000,-85500),('평면검토',1020000,-96000,1105000,-64000),('종단검토',1040000,-113000,1085000,-95000),('측구상세',1165000,-85000,1185000,-77000)]
|
||
for title,x0,y0,x1,y1 in views:
|
||
content=[]
|
||
for l in lines:
|
||
if all(x0<p[0]<x1 and y0<p[1]<y1 for p in (l['a'],l['b'])):
|
||
color='#326c5d' if l['layer'] in ('CS-CONC-MAJR','CS-CONC-MINR') else '#8d969e'
|
||
content.append(f'<line x1="{l["a"][0]}" y1="{-l["a"][1]}" x2="{l["b"][0]}" y2="{-l["b"][1]}" stroke="{color}" stroke-width="15"><title>{l["handle"]} / {l["layer"]}</title></line>')
|
||
for t in texts:
|
||
x,y=t['p']
|
||
if x0<x<x1 and y0<y<y1:content.append(f'<text x="{x}" y="{-y}" font-size="200" fill="#28334b">{html.escape(t["text"])}</text>')
|
||
# 공동구 삽입 블록의 실제 선 포함
|
||
for e in entities:
|
||
if e['type']=='INSERT' and get(e,2)==ductName:
|
||
x,y=point(e);sx=float(get(e,41,1));sy=float(get(e,42,1))
|
||
if x0<x<x1 and y0<y<y1:
|
||
for q in blocks[ductName]:
|
||
if q['type']=='LINE':
|
||
a,b=point(q),point(q,11);content.append(f'<line x1="{x+sx*a[0]}" y1="{-y-sy*a[1]}" x2="{x+sx*b[0]}" y2="{-y-sy*b[1]}" stroke="#a97635" stroke-width="15"/>')
|
||
svg=f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="{x0} {-y1} {x1-x0} {y1-y0}"><rect x="{x0}" y="{-y1}" width="{x1-x0}" height="{y1-y0}" fill="#fff"/>'+''.join(content)+'</svg>'
|
||
(previewDir/(title+'.svg')).write_text(svg,encoding='utf8')
|
||
print('모델 추출:',len(parts),'객체. 단면 원본 선 114쌍, 공동구 20쌍 × 좌우.')
|