Owner directive 2026-07-16: verify the K1 band-hall numbering assumption.
Decisive evidence: 832 electrical circuit labels (P{panel}-{hall}{zone}-{no})
in the trench drawing, zero exceptions: band1=hall5(165) band2=hall4(168)
band3=hall3(168) band4=hall2(168) band5=hall1(163) => band n = hall (6-n).
The previous assumption (band n = hall n) was WRONG - only hall 3 matched by
luck. Mirror features in official JPGs (notch corners, exterior entrances)
independently agree.
- V67: delete all K1 measured rows (old mapping) and reseed with corrected
generators (trench 875, pillars, obstacles, K1 exits). Assembly strips the
V61 y-flip UPDATE (would double-flip) and the global obstacle DELETE
(would wipe K2 rows)
- Swap DWG-tab underlays hall1<->hall5, hall2<->hall4
- All 5 generators now canonically map band = 6 - hall
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
121 lines
5.3 KiB
Python
121 lines
5.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""K1 트렌치 실측 시드 생성기 — CAD(전기,설비 트렌치.dwg → DXF)에서 홀1~5 트렌치 채널을
|
|
추출해 V61 마이그레이션 SQL을 산출한다(PLANNING R4 실측 대체).
|
|
|
|
실측 구조(레이어 5-트랜치): 홀당 가로 채널 7개(쌍선 폭 ~1.3m·피치 9m·로컬 y≈5.85 시작),
|
|
채널을 따라 6m 간격 공급 포인트를 생성한다(포인트 간격은 샘플링 — 채널 위치·연장은 실측).
|
|
|
|
사용:
|
|
python tools/cad/extract_trench_seed.py <k1_trench.dxf> <out.sql>
|
|
|
|
좌표 규약: CAD 전건물 mm → 홀 로컬 m. 홀 바닥 X원점 293.5m·폭 171m, Y밴드 80.1m+63m 피치.
|
|
★밴드↔홀 매핑(2026-07-16 확증): band n = 홀 (6-n) — 전기 회로 라벨 P{패널}-{홀}{구역}-{번호}
|
|
832건 전수 일치(band1=5A 165건 … band5=1A 163건). 예전 "밴드 n=홀 n" 가정은 오류였음(V67 교정).
|
|
"""
|
|
import io
|
|
import math
|
|
import sys
|
|
from collections import defaultdict
|
|
|
|
import ezdxf
|
|
|
|
MM = 1000.0
|
|
FLOOR_X0, FLOOR_W = 293.5, 171.0
|
|
BAND_Y0, BAND_H = 80.1, 63.0
|
|
HALLS = [1, 2, 3, 4, 5]
|
|
LAYER_KEYS = ("트렌치", "트랜치")
|
|
MIN_RUN_M = 30.0
|
|
POINT_STEP_M = 6.0
|
|
ROW_SNAP_M = 1.5 # 쌍선(채널 양변) 병합 허용
|
|
|
|
|
|
def main():
|
|
src, out_sql = sys.argv[1], sys.argv[2]
|
|
doc = ezdxf.readfile(src)
|
|
msp = doc.modelspace()
|
|
|
|
# 홀별 → 채널 행(스냅된 로컬 y) → x구간 목록
|
|
runs = {n: defaultdict(list) for n in HALLS}
|
|
for e in msp:
|
|
if not any(k in e.dxf.layer for k in LAYER_KEYS):
|
|
continue
|
|
pts = None
|
|
if e.dxftype() == "LINE":
|
|
pts = [(e.dxf.start[0] / MM, e.dxf.start[1] / MM), (e.dxf.end[0] / MM, e.dxf.end[1] / MM)]
|
|
elif e.dxftype() == "LWPOLYLINE":
|
|
p = [(q[0] / MM, q[1] / MM) for q in e.get_points()]
|
|
if len(p) >= 2:
|
|
pts = p
|
|
if not pts:
|
|
continue
|
|
for a, b in zip(pts, pts[1:]):
|
|
if math.hypot(b[0] - a[0], b[1] - a[1]) < MIN_RUN_M:
|
|
continue
|
|
if abs(b[1] - a[1]) > 0.5: # 가로 채널만(세로 피더는 M4 후속)
|
|
continue
|
|
my = (a[1] + b[1]) / 2
|
|
for n in HALLS:
|
|
y0 = BAND_Y0 + (5 - n) * BAND_H # band = 6-n (홀번호 역순, 2026-07-16 확증)
|
|
if y0 <= my < y0 + BAND_H:
|
|
lx0 = max(0.0, min(a[0], b[0]) - FLOOR_X0)
|
|
lx1 = min(FLOOR_W, max(a[0], b[0]) - FLOOR_X0)
|
|
if lx1 - lx0 >= MIN_RUN_M:
|
|
# 홀 로컬 y=0은 도면 상단(DWG 언더레이 기준) — CAD y 상하 반전.
|
|
# (V61은 미반전 시드 — V62에서 63-y 보정 완료. 재생성 시 본 반전이 정본.)
|
|
runs[n][round(BAND_H - (my - y0), 1)].append((lx0, lx1))
|
|
|
|
sql = io.StringIO()
|
|
sql.write(
|
|
"-- V61: K1 홀1~5 트렌치 실측 시드 — CAD(전기,설비 트렌치.dwg) 레이어 5-트랜치 추출.\n"
|
|
"-- 생성기: tools/cad/extract_trench_seed.py (재생성 가능·결정적). 근거: docs/analysis/cad-extraction.md.\n"
|
|
"-- 실측: 홀당 가로 채널(쌍선 병합 중심선)·연장 실좌표. 포인트 6m 간격은 샘플링 규약.\n"
|
|
"-- 정책: 실측 확보 홀(H1~H5)의 가정(is_assumed) 포인트를 실측 포인트로 대체. K2(H6~10)는 가정 유지.\n"
|
|
"-- 공급 플래그는 V5 정책 유지(공동구 — 전 서비스 true·gas는 홀 보유 여부), 위치만 실측 대체.\n\n"
|
|
)
|
|
total_rows = 0
|
|
report = []
|
|
for n in HALLS:
|
|
# 쌍선 병합: 인접(<=ROW_SNAP_M) 행을 채널 하나로
|
|
rows = sorted(runs[n].items())
|
|
merged = [] # (center_y, x0, x1)
|
|
for y, spans in rows:
|
|
x0 = min(s[0] for s in spans)
|
|
x1 = max(s[1] for s in spans)
|
|
if merged and y - merged[-1][0] <= ROW_SNAP_M:
|
|
py, px0, px1 = merged[-1]
|
|
merged[-1] = ((py + y) / 2, min(px0, x0), max(px1, x1))
|
|
else:
|
|
merged.append((y, x0, x1))
|
|
report.append(f"H{n}: 채널 {len(merged)}개 " + ", ".join(f"y={m[0]:.1f} x[{m[1]:.1f}..{m[2]:.1f}]" for m in merged))
|
|
if not merged:
|
|
continue
|
|
total_rows += len(merged)
|
|
sql.write(f"-- H{n}: 실측 채널 {len(merged)}개\n")
|
|
sql.write(f"DELETE FROM trench WHERE hall_id = 'H{n}' AND is_assumed;\n")
|
|
values = []
|
|
for ri, (cy, x0, x1) in enumerate(merged, 1):
|
|
k = 0
|
|
x = x0
|
|
while x <= x1 + 1e-6:
|
|
k += 1
|
|
values.append(
|
|
f"('H{n}-RT-{ri}-{k}', 'H{n}', ST_SetSRID(ST_MakePoint({x:.2f}, {cy:.2f}), 0), "
|
|
f"true, true, true, true, (SELECT has_gas FROM hall WHERE id='H{n}'), false)"
|
|
)
|
|
x += POINT_STEP_M
|
|
sql.write(
|
|
"INSERT INTO trench (id, hall_id, geom, supply_power, supply_water, supply_air, supply_network, supply_gas, is_assumed)\nVALUES\n "
|
|
+ ",\n ".join(values)
|
|
+ "\nON CONFLICT (id) DO UPDATE SET geom = EXCLUDED.geom, is_assumed = EXCLUDED.is_assumed;\n\n"
|
|
)
|
|
|
|
with open(out_sql, "w", encoding="utf-8") as f:
|
|
f.write(sql.getvalue())
|
|
for line in report:
|
|
print(line)
|
|
print(f"OK -> {out_sql} (채널 {total_rows}개)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|