knowledge(요구): 요구서 558(라이다 내장 개선) · 내역16(M02 정리 · 일위대가표 · M01 상세) · 조사 셋 · PLAN 640~643 · 720~724

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-01 20:55:56 +09:00
co-authored by Claude Opus 5.5
parent f189832ed5
commit 4ba8ca793b
5 changed files with 554 additions and 0 deletions
@@ -0,0 +1,229 @@
# 558 — 라이다 내장 개선: 고칠 자리 지도(제안만) · 측정
읽기 · 실측만. 코드 · storage · git 손대지 않음. 실측 = `검증_sub8`(`9872e9a7…`) 용화.las(4,900.7 만 점) · 앱 `TerrainContext` · `RBFInterpolator` 그대로. 측정 산출 = `tmp/brain_558/m2_result.json`(스크립트는 지움).
## ① 결론 다섯 줄
1. **기본 고름 바꿈은 작음** — 설정 둘(확정 표현 `implicit` · 평활 끔) + WF1 두 줄(자동 표현에 implicit 더함 · `methods[0]` 대신 확정 표현). 「분류 있음」 판정(class 2 ≥ 0.2 %)은 용화 2.33 % · 10 m 칸 덮임 98.2 % 로 넉넉함. 다만 일부만 분류된 LAS 는 못 거름 → 덮임 조건 하나 더 권함(값은 사용자 협의).
2. **수치지형도는 같은 로직이 아님** — 1 단계(지면점 필터)가 없음(입력 = 등고선 · 표고점은 안 씀). 2 단계는 등고선 보간 여섯 가지(기본 라플라스)이고, implicit 와 같은 박판(TPS) 집안이 이미 「TPS(박판)」 단추로 있음. D03 이 같은 라플라스를 짝으로 씀 → 바꾸지 않음 권함.
3. **점 수 = 1 차 지면점 × 0.175**(용화 분류 19.96 만 · CSF 13.4 만) · 하한 5 만 · 상한 200 만. 뽑기는 파일 차례 대신 **격자 칸마다 하나(씨앗 고정)**. 같은 20 만 점에서 검사점 RMS 0.133 → 0.105 m, 1 m 넘게 틀린 점 0.20 → 0.045 %.
4. **화면 일치 = implicit 를 확정 DTM 과 같은 축 · 같은 유효 칸의 1 m 격자로 굳힘**(`implicit_{필터}_grid.npz`). 서버 표본 · 브라우저 · Node 측점 · B06 계산이 같은 파일을 읽음(용화 54 초 · 1~3 MB). B07 유역 · D03 은 코드 그대로 둠. 단 평활을 끄면 이 둘은 **평활 DTM 대신 평활 안 한 DTM(계단 무늬)** 을 읽고, 틈 닫기 3 m 면 범위가 넓어짐.
5. 내장 CSF 는 이름만 CSF(1.5 m 칸 최저 + 0.5 m)임. 진짜 천 모사는 553 에서 원 라이브러리도 실패해 비권장. 후보는 넷 — 지면점 이상치 거르기(S) · 경사 비례 문턱(S) · SMRF 내장(M) · PTD 내장(L · DJI 와 같은 집안). 모두 numpy · scipy 로 됨. 기존 프로젝트는 확정값이 남아 그대로임. 단 설정 지문이 바뀐 뒤 다시 지으면 **implicit 는 옛 파일을 그대로 씀(흠)** → 같이 고침.
## ② 고칠 자리 표
크기 S = 몇 줄 · M = 함수 하나 · L = 새 모듈.
### 가. 기본 고름(분류 있으면 classification + implicit · 없으면 csf + implicit · 평활 끔)
| 일 | 파일:줄 | 크기 | 위험 |
| --- | --- | --- | --- |
| 확정 기본값 — 표현 `implicit` · 평활 `False` | `config/config_system_terrain.py:168-172` | S | 같은 키를 쓰는 B05 라이다 나중 올리기(`B05_Profile_Router_Lidar.py:147-158`)도 따라감 — 의도대로임 |
| 자동 짓기 표현에 확정 표현을 더함(`SURFACE_AUTO_METHODS` 는 `dtm` 그대로) | `B03_FileInput/B03_FileInput_Service_WF1.py:70` | S | `SURFACE_AUTO_METHODS` 를 바꾸면 D03 자체 라이다(`D03_PlanAnalysis_Lidar.py:68` · 기본 목록)까지 implicit 를 지음 → 키는 그대로 두고 WF1 만 더함 |
| WF1 자동 확정 표현 `methods[0]` → 확정 표현 | `B03_FileInput_Service_WF1.py:182` | S | 안 고치면 dtm 으로 확정됨(파이프라인이 dtm 을 맨 앞으로 옮김 `Pipeline.py:189-191`) |
| 분류 있는 LAS 는 csf 도 같이 짓는데(`Engine_Ground.py:56`) 표현 목록이 필터 전부에 붙음 → implicit 가 두 번 지어짐(+약 100 초) · 기본 필터만 implicit | `B04_PreProcess_Engine_Pipeline.py:187-192` · `:222-232` | S~M | WF1 시간 |
| 「분류 있음」 판정에 덮임 조건 더함(예 점 있는 10 m 칸 중 class 2 칸 x % 이상) · 자동 기본 고름에만 적용(드롭다운은 지금 비율 그대로) | `B04_PreProcess_Engine_Filter_Classification.py:28-40` · `Engine_Ground.py:46-56` · `config_system_terrain.py:63-65` | S | 문턱 값은 사용자 협의. 용화 = 98.2 % |
| B04 화면 처음 고름 상수(`csf` · `dtm`) — 확정본을 먼저 고르는지 확인 | `B04_PreProcess/B04_PreProcess_UI_Page.ts:56-57` | S | 화면만 |
판정 근거(용화 · 유일한 DJI 자료 — storage LAS 프로젝트 14 개 모두 같은 용화.las):
- class 값 = 1(4,786.6 만) · 2(114.1 만 = 2.33 %)뿐 → 문턱 0.2 % 의 11.7 배.
- 점 있는 10 m 칸 3,852 개 중 class 2 가 있는 칸 98.2 %(CSF 100 %) → 10 m 단위로는 고르게 분류돼 있음. 557 의 「빈 띠 36 %」는 1 m 단위로 지면점이 성긴 것이지 분류가 빠진 것이 아님.
- DJI Terra 에서 지면 분류를 안 돌리면 class 0 · 1 뿐 → 0 % → csf 길로 감(556-A). 위험은 여러 장을 합칠 때 일부만 분류된 경우뿐 → 덮임 조건이 막음.
### 나. 점 수 비율 · 뽑기
| 일 | 파일:줄 | 크기 | 위험 |
| --- | --- | --- | --- |
| 설정 키 바꿈 — `SURFACE_IMPLICIT_MAX_POINTS_PER_TILE`(타일 없음) → `SURFACE_IMPLICIT_POINT_RATIO` 0.175 · `_MIN_POINTS` 50,000 · `_MAX_POINTS` 2,000,000 | `config_system_terrain.py:114-117` · `:193` | S | 설정 지문이 바뀜(⑥) |
| 점 수 = clip(지면점 × 비율, 하한, 상한) · 지면점이 하한보다 적으면 전부 | `B04_PreProcess_Engine_ModelBuild.py:174-175` | S | — |
| 격자 칸 뽑기 — 칸 = √(외곽 넓이 / 점 수) · 씨앗 고정 · 칸마다 하나 · 모자라면 무작위로 채움 · implicit 전용 새 함수(`sample()` 은 TIN · meshfree 도 씀 → 그대로) | `B04_PreProcess_Engine_ModelContext.py:253-263` 곁 | S~M | 씨앗 고정이라 다시 지어도 같은 값 |
### 다. 외곽 틈 닫기 3 m
| 일 | 파일:줄 | 크기 | 위험 |
| --- | --- | --- | --- |
| `SURFACE_FOOTPRINT_GAP_CLOSE_M` 1.0 → 3.0 | `config_system_terrain.py:98` | S | 모든 필터 · 표현의 `valid_mask` 가 넓어짐(아래) |
| 「가장 큰 덩이만」은 그대로 둠 | `ModelContext.py:294-301` | — | 3 m 면 덮임이 스캔의 97 % → 남는 섬이 작음(557-A: 이 자료는 넓이 문턱과 결과 같음) |
넓어지는 곳(코드는 그대로 · 값만):
- B05 노선 끝 자르기(`common_util_route_geometry.py:476-490` — valid 로 자름)
- B05 추천 비용면 · 뼈대(`B05_Profile_Engine_Solver.py:46-60` · `Skeleton.py:278-295`)
- B05 · B06 표본 · 격자
- B07 음영 · D03 겹 · E 복사본
- B04 등고선 · 3D 미리보기
넓어지지 않는 곳:
- 도엽 300 m 띠 · 지도 · GIS 받기 — 노선 · LAS 상자 기준임(`SheetSurface.py:526-529` · `Engine.py:355-369`).
| 필터 | 닫기 1 m | 닫기 3 m | 점 찍힌 1 m 칸(모든 점) |
| --- | --- | --- | --- |
| classification | 23.2 만 m² | 34.4 만 m²(+48 %) | 35.6 만 |
| csf | 32.1 만 m² | 36.3 만 m²(+13 %) | 35.6 만 |
- 되살린 띠는 지면점이 성김 → DTM 에선 가까운 값 메움 칸이 49 → 59 %(557-A)로 늚.
- implicit 20 만 점이면 그 띠 RMS 0.24 m(557-A).
### 라. 굳힌 격자(기준 서피스 일치)
꼴: `implicit_{필터}_grid.npz` = `x` · `y` · `z` · `valid_mask` · `resolution`.
- **축 · 유효 칸 = 같은 짓기의 `dtm_{필터}.npz` 와 똑같음.**
- valid 칸만 RBF 로 셈. 밖은 dtm 값 그대로라 NaN 이 없음.
- DtmGridSampler 꼴 그대로 → `dtm_grid_file` 이 `.bin` 을 그대로 만듦.
- 이름에 `dtm_` 머리를 안 붙임. `dtm_*` glob(B07 대체 후보 · 등고선 필터 이름 풀기)과 겹치지 않게 하려는 것임.
- 옛 필터 지우기(`common_util_lidar_upload.py:180-182`)는 `implicit_{필터}_*` 로 같이 지움.
| 일 | 파일:줄 | 크기 | 위험 |
| --- | --- | --- | --- |
| 굳히기 함수(dtm 축 · valid + implicit 중심점 → 격자) · 짓기 끝에서 부름 · 옛 implicit 는 처음 부를 때 한 번 굳힘(파생 저장본 규칙) | `common_util/common_util_surface_sampler.py` 새 함수(283 줄 → 약 320) · `ModelBuild.py:205-211` | M | 짓기 +54 초(용화) |
| 서버 표본기 implicit → 굳힌 격자 `DtmGridSampler.open_cached`(없으면 지금 RBF) | `common_util_surface_sampler.py:257-272` | S | **Node 측점은 저절로 따라옴** — `Sections_Node.py:146-160` 은 표본기의 `source` 만 봄(`_open_dtm` 이 붙임 `:132-136`) |
| B05 브라우저 격자 — `_design_model`(dtm 행만) · `_design_grid`(method dtm 만) · `dtm_grid_npz`(dtm 행만) | `B05_Profile/B05_Profile_Router_Terrain.py:161-206` · `:208-231` | S | 시험 `test_344_1_terrain_grid_route` · `test_509_2_sheet_grid` |
| B06 서버 재계산 격자 · 계산 창구 | `B06_Section/B06_Section_Server_Calc_Prebuild.py:257-275` · `B06_Section_Router_Calc.py:62-71`(위 `dtm_grid_npz` 거침) | S | 시험 `test_472_8` · `test_511_b4_cross_save_mirror` |
| B04 등고선 — 부를 때마다 RBF 다시 셈 → 굳힌 격자(regular_grid 길) | `B04_PreProcess_Engine_Contour.py:430-451` · `Pipeline.py:48-83` · `Router_Contour.py:101-132` | S | 등고선 = 표본과 같은 면 |
| B05 추천 비용면 — implicit 면 2 m 자리에서 RBF → 굳힌 격자 | `B05_Profile_Engine_Solver.py:116-134` | S(선택) | — |
| **짓기 캐시 흠** — 설정 지문이 바뀌면 manifest 는 새로 쓰나(`Pipeline.py:203-204`) 평활 없는 표현(implicit · nurbs · meshfree)은 파일만 있으면 재사용(`:151-172` · `:236`) → 옛 1 만 점 implicit 가 새 설정 이름표로 남음 · 굳힌 격자 있음도 판정에 넣음 | `B04_PreProcess_Engine_Pipeline.py:151-172` · `:203-204` | S | **위험 높음** — 안 고치면 점 수 · 틈 닫기가 기존 파일에 안 먹음 |
| E 복사본 — 쓰인 dtm 하나만 복사 → implicit 확정이면 굳힌 격자 | `common_util/common_util_project_copy.py:472-478` · 읽는 쪽 `E04_Stab3D_Run.py:220-222` | S | — |
| B04 3D 미리보기 — 상자 전체 50 만 꼭짓점 RBF → 굳힌 격자에서 솎음 | `ModelBuild.py:188-197` | S(선택 · 속도) | 짓기 시간 약 반 |
짝(CLAUDE.md 5장):
- 굳히기 = ③ 서버 단독(초기값 · [확정] 때 한 번). 브라우저는 셈하지 않고 같은 격자를 받음.
- 표본 = 이미 있는 ① 짝 `DtmGridSampler.sample_xy` ↔ `common_util_surface_sampler.ts` `heightAt` · 거울 시험 `test_344_1_surface_sampler_mirror.py`. **새 짝 없음.**
- 지문 — 브라우저 주소 `signature` = manifest 설정 지문(점 수 · 틈 닫기 포함 `ModelContext.py:96-106`) · `.bin` = npz 수정시각(`surface_sampler.py:169-177`).
손대지 않는 곳 — 그대로 둘 때 차이 한 줄:
| 곳 | 파일:줄 | 차이 |
| --- | --- | --- |
| B07 유역 음영 | `B07_DesignDetail_Router_Support_Basin.py:478-484` | 평활 끔 → `dtm_{필터}_smooth.npz` 대신 평활 안 한 `dtm_{필터}.npz` 로 음영(계단 무늬 보임) · 3 m 면 범위 넓어짐 · 기준 서피스(implicit)와는 다른 면 |
| D03 겹(B 프로젝트 · 설계 지형 / lidar) | `D03_PlanAnalysis_TerrainSource.py:93-110` | 급경사 · 0차곡이 평활 DTM → 평활 안 한 DTM. D 자체 라이다(`D03_PlanAnalysis_Lidar.py:57-85`)는 원래 평활 안 한 DTM → 범위만 넓어짐 |
- 차이를 없애는 길은 둘 — 확정 때 `smooth` 를 그대로 True 로 적기(implicit 표본은 평활을 안 봄 · 다만 B04 화면 재확정은 False 를 보냄) 또는 둘이 굳힌 격자를 읽게 한 줄씩 고치기. 둘 다 사용자 판단.
## ③ 수치지형도 — 「동일한 로직?」 답
**아님.** 수치지형도는 점이 아니라 등고선에서 짓기 때문에 1 단계가 없음.
| 단계 | 라이다 | 수치지형도(도엽) |
| --- | --- | --- |
| ① 지면점 필터 | 분류 · CSF · PMF … | **없음** — 입력 = 1:5,000 등고선 선만 격자에 구움(`B04_PreProcess_Engine_SheetSurface.py:219-277` · 파일 `:55`). 표고점은 받기만 하고 안 씀(`SheetParser.py:30`) |
| ② 서피스 | dtm · tin · implicit … | 등고선 보간 여섯 — TIN(도엽선) · TIN 격자 · **TPS(박판)** · ANUDEM형 · 다중해상도 · 라플라스(`SheetMethods.py:24-31`). 기본 = 라플라스(`config_system_terrain.py:157` · 08-30 사용자 확정) · 자동은 기본 하나(`:161-165`). 결과는 늘 1 m DTM 격자(`SheetSurface.py:341-388`) |
| ③ 평활 | 켬 → 끔 예정 | 켬 — WF1 고정(`WF1.py:169-175`) · 분석 지형 「수치지형도」도 켬(`common_util_surface_confirmation.py:97-98`) |
- implicit 와 가까운 것 = 「TPS(박판)」(`SheetMethods.py:136-215` · 최소곡률 격자 · ANUDEM 계열). implicit(RBF 박판)와 같은 집안이 이미 있음.
- 등고선 점에 implicit 를 그대로 걸면 이웃 64 점이 한 등고선 위에서만 뽑힘 → 계단 · 발산 위험. 옮길 거리가 없음.
- 바꾸지 않음 권함. D 공용 지형 창고가 같은 라플라스를 비트 동일 짝으로 씀(`common_util_terrain_laplace.py:3-4` · 시험 `test_617_2_laplace_mirror.py`). 바꾸면 D03 까지 번짐. 비교만 원하면 B04 의 TPS 단추로 보면 됨.
- 평활 끔도 라이다에만 해당. 도엽은 등고선 사이를 메운 면이라 평활이 계단 · 꺾임을 덜어 줌(그대로 둠).
## ④ 점 수 비율 제안
비율 0.175 = 용화 분류 지면점 114.07 만 × 0.175 = 19.96 만(사용자 「20 만 수준」).
| 자료 | 1 차 지면점 | × 0.175 | 외곽(3 m) | 점 사이(격자 칸) |
| --- | --- | --- | --- | --- |
| 용화 분류 | 1,140,716 | 199,625 | 34.4 만 m² | 1.31 m(실측) |
| 용화 CSF | 768,273 | 134,448 | 36.3 만 m² | 약 1.65 m |
| 큰 사업 2 km²(추정 · 분류 밀도 4.92 점/m²) | 약 984 만 | 약 172 만 | 2 km² | 약 1.1 m |
- CSF 쪽이 점이 적은 것은 괜찮음. 557 에서 CSF 는 점을 늘릴수록 덤불 혹이 늘었음(꺾임 6 → 67 /km).
- 하한 5 만 — 557-A 에서 5 만 = RMS 0.16 m · 잔고리 13(1 만은 0.27 m · 능선 3~4 m 깎임).
- 상한 200 만 — 시간 · 메모리는 점 수가 아니라 셀 칸 수가 정함(아래). 상한은 파일 크기 · KD 나무 몫. 2.3 km² 까지 비율 그대로 감.
뽑기 셋 비교(같은 20 만 점 · 업체 지면점 90 % 에서 뽑고 남긴 10 % 중 10 만 점으로 잼 · 외곽 3 m):
| 뽑기 | 중심점 | 검사점 RMS | 95 % | 1 m 넘음 | 최대 | 외곽 칸 → 가장 가까운 점 95 % / 최대 |
| --- | --- | --- | --- | --- | --- | --- |
| 파일 차례(지금) | 199,555 | 0.133 m | 0.223 | 0.20 % | 3.95 | 4.6 / 14.6 m |
| 무작위(씨앗 558) | 199,568 | 0.136 m | 0.225 | 0.22 % | 3.89 | 4.6 / 14.6 m |
| **격자 칸 1.31 m(씨앗 558)** | 199,590 | **0.105 m** | 0.200 | **0.045 %** | 2.73 | **3.3 / 11.8 m** |
- 무작위는 파일 차례와 같음(둘 다 점 많은 트인 곳에 몰림). 격자 칸만 숲 밑 성긴 곳을 메움.
비용(이 랩탑 · 한 프로세스):
| 항목 | 용화 | 2 km²(추정) |
| --- | --- | --- |
| 중심점 KD 나무 짓기 | 0.1 초 | 1 초 안팎 |
| 1 m 굳힌 격자 셈(외곽 안만) | 34.4 만 칸 53.7 초(10 만 칸당 약 16 초) | 약 200 만 칸 · 5 분 남짓 |
| 미리보기(50 만 꼭짓점 상한) | 약 50 초(557-A) | 같음(상한) |
| 메모리 | +0.17 GB(557-A) | +0.3 GB 안팎 |
| 저장 — 중심점 npz · 격자 npz · `.bin` | 1.3 · 1.1~3 · 2.4 MB | 약 11 · 6~10 · 12~16 MB |
- WF1(용화 239 초)에 +약 105 초. 미리보기를 굳힌 격자에서 솎으면 +약 60 초. 분류 LAS 에서 csf 까지 implicit 를 지으면 그 두 배.
- 속도는 기능 뒤에 따로 봄. 굳힌 격자 셈을 덩이로 나눠 여러 프로세스로 돌리면 줄어듦(제안).
## ⑤ 내장 CSF 개선 후보 · 측정 계획
지금 `B04_PreProcess_Engine_Filter_CSF.py`:
- 충돌면 = 1.5 m 칸 최저점(`:78-82`).
- 낙하 0.32 m/회(9.8 × 0.05 × 0.65)가 스프링(0.25 × 0.5 · 한 번)을 이김 → 천이 칸 최저에 붙음(`:93-108`).
- 판정 = 천에서 0.5 m 안(`:111-113`).
- 「slope_smooth」 = 칸 최저 위 1.8 m 거름(`:118-120`). 이름과 달리 경사 처리가 아님.
- 결과(553) — 업체 지면 대비 맞음 55.1 · 놓침 44.9(주로 급경사) · 잘못 넣음 12.2 %.
- 557 — CSF + implicit 20 만 RMS 0.178 · 꺾임 47 /km(분류 + implicit 0.118 · 19).
| 후보 | 하는 일 | numpy · scipy | 크기 | 기대 | 걸림 |
| --- | --- | --- | --- | --- | --- |
| 1 지면점 이상치 거르기(분류 · CSF 공통 · implicit 앞) | 지면점마다 둘레 r m 안 낮은 쪽 기준면(칸 최저 TIN 또는 하위 백분위)보다 h m 넘게 뜬 점을 버림 | 됨(cKDTree · 칸 연산) | S | 557 의 「CSF 가 고른 덤불 · 그루터기 혹」을 바로 겨눔 · 분류 자료의 잡점도 같이 | 깎기 비탈 끝 점까지 버릴 위험 → h 를 경사에 비례 |
| 2 CSF 경사 비례 문턱 | 0.5 m 고정 → 0.5 + 칸 기울기 × 천 칸 / 2 | 됨 | S | 놓침 45 % 의 주인(급경사)을 줄임 | 잘못 넣음이 늘 수 있음 |
| 3 SMRF 내장(Pingel 2013) | 1 m 최저 격자 → 빈칸 메움 → 창을 넓혀 가며 열림(`ndimage.grey_opening`) · 경사 문턱 → 잠정 DEM → 점마다 높이 + 경사 문턱 | 됨 | M | 553 PDAL SMRF 0.265 m · 맞음 94.6 % 근처 | 잘못 넣음 53 %(덤불) → 1 과 짝. 지금 PMF(`Filter_PMF.py` 91 줄)가 같은 집안의 옛 꼴 → 고쳐 쓰거나 새 열쇠 |
| 4 PTD 내장(점진 TIN · DJI Terra 와 같은 집안 · 556-A) | 20 m 칸 최저 씨앗 → 삼각망 → 0.5 m 칸 최저 후보(약 140 만)마다 면까지 거리 · 각을 봐 묶음으로 넣고 다시 삼각망 · 5~10 회 → 끝에 전체 점을 최종 TIN 거리로 판정 | 됨(`spatial.Delaunay` · `find_simplex`) | L | 사용자 비교 대상(DJI)과 같은 결 · 장면 셋(평지 3°·0.3 m / 완경사 6°·0.5 m / 급경사 10°·0.7 m) | 시간 · 조율 값 많음 · 무료 구현은 GPL(lidR)뿐 → 참고만 하고 새로 씀 |
| 5 진짜 천 모사(원 CSF) | 입자 · 스프링 반복 · 경사 후처리 | 됨 | M | — | **비권장** — 553 원 라이브러리 RMS 0.93 m · 급경사 80 % 빔 |
- 열쇠 — 새 필터는 `_FILTERS` 에 새 열쇠(`Engine_Ground.py:23-29`)로 더함. 그러면 마스크 · 모델 · 드롭다운이 따라옴(553-A). 그 뒤 `SURFACE_AUTO_FALLBACK_FILTER`(`config_system_terrain.py:86`)만 바꿈.
- `csf` 열쇠 자리를 고쳐 쓰면 기존 `mask_csf.npy` 캐시가 옛 값으로 남음(마스크 캐시는 길이만 봄 `Engine.py:286-292`).
- 사용자 말 「csf + implicit」는 「분류 없을 때 내장 필터 + implicit」로 읽힘. 알고리즘을 바꾸면 이름(`csf`)을 둘지는 사용자 협의.
측정 계획(557 잣대 그대로):
1. 자료 — 용화에서 업체 분류를 숨김(필터에 xyz 만 줌). **둘째 자료 필요** — 분류 없는 원자료 · 완경사 사업 하나(사용자에게 받음 · 지금 storage 는 용화뿐).
2. 잣대
- ⓐ 분류 대조 — 업체 class 2 기준 맞음 · 놓침 · 잘못 넣음 %
- ⓑ implicit(× 0.175 · 격자 칸 뽑기)로 지은 뒤 업체 지면점 20 %(씨앗 553) 검사점 RMS · 95 % · 1 m 넘음 %
- ⓒ 단면 S1~S3 최대 차(557)
- ⓓ 등고선 꺾임 /km · 기울기 p90 · 빨강 %
- ⓔ 외곽(3 m) 덮임
- ⓕ 시간 · 최대 메모리
3. 비교 기준
- I2 지금(CSF + implicit 20 만 · 0.178 m · 47 /km)
- I1(분류 + implicit 20 만 · 0.118 m · 19 /km)
- 553 B2 PDAL SMRF 0.265 m · C2 WBT 0.257 m(값은 데스크탑 `lidar_lab/` 기록)
4. 차례 — 1 → 2 → 1+2 → 3 → 3+1 → 4. ⓑ 0.15 m 안 · 꺾임 30 /km 안이 나오면 멈춤(문턱은 제안 · 사용자 협의).
5. 자리 — 자기 `tmp/` 일회 스크립트. 앱 `TerrainContext` · `build_implicit` 를 그대로 부름(557 방식).
## ⑥ 기존 프로젝트 · 다시 셈
- storage LAS 프로젝트 14 개 = 모두 용화.las 복제 · 설정 지문 모두 `d5004159dc8bf8b6`. implicit 파일은 검증_sub8(`9872e9a7`) 하나뿐(1 만 점 · 분류 · csf · grid_min_z).
- **기본값을 바꿔도 기존 확정은 그대로임.**
- stage 1 params(필터 · 표현 · 평활)를 그대로 읽고, 빈 칸만 기본값으로 메움(`common_util_surface_confirmation.py:52-75`).
- 그래서 화면 · 설계 값 · 초기값(`initial_snapshot`)이 안 바뀜.
- **점 수 · 틈 닫기는 설정 지문에 들어감**(`ModelContext.py:96-106`). 다음 「짓기」 때 manifest 를 새로 씀.
- 짓기가 도는 때: B03 다시 올리기 · B04 관리자 모델 요청(확정을 지움 `B04_PreProcess_Router.py:110`) · B05 라이다 나중 올리기.
- 그때 dtm · tin 은 다시 지어 유효 범위가 넓어짐(z 값은 그대로 · `valid_mask` 만).
- implicit · nurbs · meshfree 는 옛 파일을 그대로 씀(② 라 「짓기 캐시 흠」) → 함께 고침.
- 굳힌 격자가 없는 옛 implicit → 표본기가 처음 부를 때 한 번 굳힘(또는 지금 RBF 길).
- 파생 저장본(B05 비용면 · 뼈대 · `_derived`)은 원천 파일 지문을 봄 → 원천이 바뀌면 스스로 다시 셈. 손댈 것 없음.
- 기존 프로젝트를 새 기본으로 옮기는 길 = B04 에서 implicit 짓기 요청 → 확정(지금 길 그대로). 일괄 옮김은 만들지 않음(제안).
## ⑦ 창 나누기 제안
| 창 | 맡음 | 파일 경계 | 시험 |
| --- | --- | --- | --- |
| 1 기본 · 점 수 · 틈 | ② 가 · 나 · 다 + 짓기 캐시 흠 + 기본 필터만 implicit | `config/config_system_terrain.py` · `B03_FileInput_Service_WF1.py` · `Engine_Ground.py` · `Filter_Classification.py` · `ModelContext.py` · `ModelBuild.py`(점 수 · 뽑기) · `Pipeline.py` | `test_b04_ground_filters.py` + 새 지킴 시험(점 수 셈 · 격자 칸 뽑기 씨앗 · 지문 바뀌면 implicit 다시 지음 · 덮임 판정) |
| 2 굳힌 격자 · 화면 일치 | ② 라 | `common_util_surface_sampler.py` · `ModelBuild.py`(짓기 끝 굳히기 한 줄 — 창 1 뒤) · `B05_Profile_Router_Terrain.py` · `B06_Section_Server_Calc_Prebuild.py` · `B06_Section_Router_Calc.py` · `Engine_Contour.py` · `Router_Contour.py` · `B05_Profile_Engine_Solver.py` · `common_util_project_copy.py` | `test_344_1_terrain_grid_route` · `test_509_2_sheet_grid` · `test_472_8_section_mode_default` · `test_511_b4_cross_save_mirror` · `test_344_1_surface_sampler_mirror` + 새 시험(굳힌 격자 = RBF 값 · 축 = dtm 축) |
| 3 내장 필터 측정 | ⑤ 측정만 → 결과 보고 → 고른 후보만 새 `Filter_*.py`(나중 일감) | 자기 `tmp/` 만 · 창 1 · 2 와 파일 안 겹침 | 측정 결과 |
- 차례 — 창 1 → 창 2(`ModelBuild.py` 겹침 · 굳힌 격자는 새 점 수로 지어야 뜻이 있음). 창 3 은 바로 병행.
- 창 2 끝 화면 검증(ORCA · 배정 프로젝트) — 같은 측점 몇 개의 지반고를 세 길로 대조:
- B05 브라우저 격자 · 서버 `/terrain/sample` · B06 횡단
- B04 등고선이 같은 면인지
- B07 음영 · D03 겹이 그대로 열리는지
- 결정 대기(브레인 → 사용자):
- ⓐ 덮임 문턱 값
- ⓑ 평활 칸 기록(False 사실대로 / True 로 B07 · D03 무변화)
- ⓒ 새 내장 필터의 열쇠 이름(`csf` 유지 / 새 열쇠)
- ⓓ 둘째 시험 자료
@@ -0,0 +1,48 @@
# 요구서 — 요구 558 · 라이다 지표면 내장 개선 (분류 · implicit · 점 수 비율 · 틈 닫기 · 화면 일치)
원문 = `사용자요구_1101.md` 요구 553~558 · 근거 = 같은 폴더 `_분석_553-A` · `_결과_553` · `_분석_556-A · B` · `_결과_557_implicit_시험.md` · `_분석_557-A_implicit_코드.md` · `_분석_558_라이다_내장개선.md`.
## 1. 한 줄 방향
외부 도구 없이 내장 개선 · 기본 지표면 = 분류 있으면 classification + implicit · 없으면 csf(개선) + implicit · 평활 끔 · implicit 점 수 = 지면점 비율 · 확정 지표면 하나를 B05 · B06 화면이 함께 씀.
## 2. 사용자 확정
- 외부 도구(PDAL 등) 쓰지 않음 · 내장 개선(요구 558-2).
- 기본 = classification + implicit · 분류 없으면 csf + implicit(558-2).
- 점 수 = 1 차 지면점 총수 기준 비율 · 용화 20 만 수준으로 시작(558-2).
- 외곽 틈 닫기 3 m(558-2).
- 확정 지표면 = 기준 서피스 · 화면 일치(558-3) · B07 유역 · D03 격자지형은 손대지 않음(558-3).
## 3. 정함(브레인) — 사용자가 바꿀 수 있음
1. 비율 = 지면점 × 0.175(용화 업체 분류 19.96 만 · CSF 13.4 만) · 하한 5 만 · 상한 200 만 · 지면점이 하한보다 적으면 전부 · 설정 키 `SURFACE_IMPLICIT_POINT_RATIO` · `_MIN_POINTS` · `_MAX_POINTS`(옛 `…_PER_TILE` 걷음).
2. 점 뽑기 = 파일 차례 대신 격자 칸마다 하나(씨앗 고정) — 같은 20 만 점에서 RMS 0.133 → 0.105 m · 1 m 넘게 틀린 점 0.20 → 0.045 %.
3. 평활 = 끔(implicit 이 이미 매끈 · 상용 DJI Terra · Metashape 도 지형 평활 없음).
4. 「분류 있음」 = 지금 class 2 비율 0.2 % 문턱 + 덮임 조건(점 있는 10 m 칸 중 class 2 있는 칸 50 % 이상) · 자동 기본 고름에만.
5. 화면 일치 = 확정(또는 지을 때) implicit 를 확정 DTM 과 같은 축 · 유효 칸의 1 m 격자 `implicit_{필터}_grid.npz` 로 한 번 굳힘 · B05 브라우저 · 서버 표본 · Node 측점 · B06 이 같은 파일을 읽음 · 새 짝 없음.
6. B07 유역 · D03 = 동작 그대로 — 평활을 꺼도 지금처럼 평활 DTM 을 읽게(읽는 길만 「확정이 implicit 면 `dtm_{필터}_smooth`」) · 계산 코드는 안 고침.
7. 수치지형도(도엽) = 바꾸지 않음 — 점이 아니라 등고선으로 지어 1 단계(지면점 필터)가 없음 · 박판(TPS) 계열은 이미 「TPS(박판)」 방식으로 있음 · D03 이 같은 라플라스 계산을 짝으로 씀.
8. 분류 있는 LAS 의 implicit 는 기본 필터에만 지음(csf 쪽까지 두 번 짓지 않음 · +약 100 초 막음).
9. 설정 지문이 바뀐 뒤 다시 지을 때 implicit 가 옛 파일을 그대로 쓰는 흠(`Pipeline.py:151-172`) 고침.
10. 기존 프로젝트 = 확정값 그대로(다시 계산 · 다시 확정할 때만 새 기본).
11. 내장 CSF 개선 = 차례: 지면점 이상치 거르기(S) · 경사 비례 문턱(S) → 분류 숨긴 용화로 557 잣대 측정 → 모자라면 SMRF 내장(M) → 그래도 모자라면 PTD 내장(L · DJI · Metashape 계열) · 고르는 것은 측정 뒤 사용자.
## 4. 사용자 물음
- 내장 CSF 개선 방식(아래 3장 11 차례로 갈지 · 처음부터 PTD 내장으로 갈지).
## 5. 단계 · 창 (PLAN 640~643)
| 절 | 무엇 | 창 |
| --- | --- | --- |
| 640 | 기본 고름 · 점 수 비율 · 격자 칸 뽑기 · 틈 닫기 3 m · 지문 흠 | sub3 |
| 641 | implicit 격자 굳힘 · B05 · B06 화면 일치 · B07 · D03 동작 그대로 | sub4 · 640 뒤 |
| 642 | 내장 CSF 개선 · 측정 | sub5 |
| 643 | 용화 검증 프로젝트로 WF1 다시 · 화면 · 등고선 · 종횡단 확인 | sub4 · 641 · 642 뒤 |
## 6. 검증 기준
- 고친 대상 시험 · typecheck 0 · 같은 1 m 격자 557 잣대(누락 · 자기 점 · 외부 결과(데스크탑 lidar_lab)와의 차 · 등고선 꺾임 · 단면) 전후 표.
- 기존 확정 프로젝트 13 개 값 그대로(다시 계산 안 하면).
- ORCA(자기 포트 · 자기 검증 프로젝트) — B04 등고선 · 3D · B05 종단 · B06 횡단이 같은 지형(같은 측점 표고 차 0).
@@ -0,0 +1,106 @@
# 분석 내역16-A — M02 폴더 · 층 단추 · 단가산출 · 고르기 검색
원문 = `사용자요구_내역서.md` 내역 16(118-125 · 136 줄) · 앞선 이음 = `docs/raw/plans/2026-11-01_plan_709-719.md` 716-4 · 717-2a · 717-2b · 717-4. 코드 읽기만(서버 · 화면 안 켬) · 「추정」 표시 = 화면 확인 필요.
## ① 결론 다섯 줄
1. 폴더 나무 = 부품 둘 — 단가산출 · 일위대가 = 같은 부품(`UI_Side_Library.ts` buildLibrarySection) · 구조물 조합 = 따로(`TreePanel.ts` createTreePanel) · 층 단추 줄(`TreePanel_Roots.ts` createLayerBar)과 메뉴 · 고름 · 끌기 조각은 셋이 같이 씀. 맨 위 폴더 길 = 빈 자리 오른쪽 클릭뿐(나무가 차면 빈 자리 거의 없음 · `min-height: 4rem`) · 「+」 단추는 구조물 조합에만 있음.
2. 단가산출 · 일위대가 층 단추 = 왼쪽 나무만 다시 그림 · 오른쪽 상세는 그대로(켜진 단추를 한 번 더 눌러야 목록표). 고칠 곳 = `UI_Page.ts` 두 줄(S).
3. 구조물 조합 층 단추 = 주소(해시) 바꿈 → 라우터가 페이지 통째 다시 세움 → 왼쪽 칸 모두 접힘 · 단가산출 층 개인으로 되돌림 · 오른쪽 처음 화면. 뿌리 = 구조물 층이 곧 페이지 층(권한 · 활성 층 · 나무 · 공유 띠 · 도면 칸이 모두 그 층에 묶여 세워짐). 제자리 바꿈 = M(파일 넷).
4. 단가산출 — 나무 문서 줄 메뉴에 [이름 바꾸기] 없음(폴더만) · 상세 이름 칸은 있으나 [저장] 전엔 어디도 안 바뀌고 저장 뒤에도 머리 제목은 옛 이름(추정 · 「안 됨」으로 보인 까닭). 서버 새 길 필요 없음(문서 `이름` 칸 · 읽고 갈아 저장). 단추 이름 · 자리 = S. M01 요소 넣기 = 서버는 인력 · 재료 · 기계 더한줄 받음 · 화면 고르기에 인력 · 기계 없음 · 재료는 숨은 한 줄 → 5번 고르기 창에 합침.
5. 고르기 창 = `Labor_Pick.ts` openLogicPicker 하나를 일곱 곳이 씀(M02 다섯 · B09 둘) · 거름 넷 + 층 단추 + 탭 + 목록 둘. 권함 = M02 단가산출 · 일위대가 세 곳만 새 창 파일(탭 [M01 전체 | M02 단가산출 | 일위대가] · 찾기 칸 하나 · 표 하나 · 거름 값 = 열) · 서버 새 길 없이 있는 길 넷 이음(M) · 옛 창은 B09 · 불러오기용으로 그대로.
## ② 줄마다
### 1. 폴더 나무 「최상위 폴더 만들기」
| 항목 | 내용 |
| --- | --- |
| 요구 | 세 칸(단가산출 · 일위대가 · 구조물 조합) 오른쪽 클릭에 최상위 폴더 만들기 · 구조물 조합 「+」 기능 활용 |
| 지금 | 단가산출 · 일위대가: 폴더 줄 메뉴 = 폴더 추가(그 안) · 여기에 새 … · 이름 · 지우기 · 복사 · 붙여넣기 / 문서 줄 = 복사 · 지우기 · 덧메뉴 / 맨 위 = 빈 자리 오른쪽 클릭만 · 「+」 없음. 구조물 조합: 층 단추 옆 「+」(맨 위 폴더 · 항목 추가 · 붙여넣기) + 빈 자리 오른쪽 클릭 · 폴더 · 항목 줄 메뉴엔 맨 위 없음 |
| 근거 | 부품 갈림 = `UI_Side_Library.ts:125`(두 칸 공용 · 래퍼 `UI_Side_Precombo.ts:67-74` · `UI_Side_Sangeun.ts:34-41`) · `TreePanel.ts:129`(구조물). 메뉴 = `UI_Side_Library.ts:244-267`(menuAt) · `:291-299`(문서 줄) · `:392-396`(빈 자리 = menuAt([])). 구조물 「+」 = `TreePanel.ts:146-154` · topMenu `:204-207` · 줄 메뉴 `:209-251` · 빈 자리 `:616-622`. 층 단추 줄 `add` 칸 = `TreePanel_Roots.ts:34-35 · 89-92` — 단가산출 · 일위대가 층 줄은 안 넘김 `UI_Page.ts:242-251 · 254-261`. 빈 자리 좁음 = `TreePanel.css:14-15`(max 55vh · min 4rem) |
| 고칠 자리 | ⓐ 메뉴 줄 「최상위 폴더 만들기」 = 「+」와 같은 함수(addFolder([])) — `UI_Side_Library.ts` menuAt(path 있을 때) · 문서 줄 single · `TreePanel.ts` menuFor 한 줄 짜리 갈래 ⓑ 단가산출 · 일위대가 층 줄에 「+」 — 서고 칸이 단추를 만들어 `bar(add)` 로 넘김 · `UI_Page.ts` preBar · skBar 가 createLayerBar `add` 로 · `PrecomboOptions.bar` 꼴(`UI_Side_Precombo.ts:57`) ⓒ 글 키 하나(`ui_template_locale_m2.ts` · 지금 「+」 글 = `M02_TreeAddTop` :107) |
| 크기 | S(서고 칸 +15 · 페이지 +4) · 단 `TreePanel.ts` = 700 줄 꽉 참 → 먼저 나눔(복사 · 여럿 · 붙여넣기 묶음 `:549-607` → 새 파일) 또는 같은 줄 수 |
| 위험 | 낮음 · 읽기만 층 = 흐림(`tc("ReadOnly")`) 그대로 · 낡은 시험 `test_m02_layer_screen.py:293` · `test_m02_precombo_scope.py:79`(구조물 layerBar 줄 글 대조 — 구조물 줄 안 건드리면 그대로) |
### 2. 단가산출 · 일위대가 층 단추 → 상세에 그 층 목록
| 항목 | 내용 |
| --- | --- |
| 요구 | 마스터 · 회사 · 개인 누름 = 오른쪽 상세에 그 층 목록 |
| 지금 | 다른 층 단추 = 왼쪽 나무만 그 층으로 · 오른쪽은 먼저 열린 것 그대로(처음 = 구조물 조합 표) · 켜진 단추 다시 누름(reselect)만 목록표 |
| 근거 | 일위대가 `UI_Page.ts:247` go → setPre(`:235-241` · replaceState + `side.refreshPrecombo()`) · reselect `:249-250` → `main.open(precomboList(pre))` / 단가산출 `:259` go → `sk = preScope(next)` + `side.refreshSangeun()` · reselect `:260` / 목록표 = `UI_Main.ts:512-525` → `Library_List.ts` mountLibraryList |
| 고칠 자리 | `UI_Page.ts:247 · 259` — go = `main.confirmLeave()` → 층 바꿈 → `main.open(precomboList(pre) | sangeunList(sk))` |
| 크기 | S(두 줄 → 각 3 줄) |
| 위험 | 낮음 · 저장 안 한 고침 물음을 층 바꾸기 앞에(취소 = 층도 그대로) · 낡은 시험 `test_m02_precombo_scope.py:72-73`(replaceState · navigateTo 없음 — 그대로 지켜짐) |
### 3. 구조물 조합 층 단추 = 화면 새로고침
| 항목 | 내용 |
| --- | --- |
| 요구 | 새로고침 안 되게 |
| 지금 | 누름 → 저장 안 한 고침 물음 → 해시 바꿈 → 페이지 통째 다시 세움: 왼쪽 칸 모두 접힘(구조물 조합 칸도) · 단가산출 층 = 개인으로 되돌림 · 오른쪽 = 새 층 구조물 표 · 불러오는 중 깜빡임 |
| 근거 | `UI_Page.ts:134-141`(go → `navigateTo`) · `router.ts:162-170`(hashchange) → `:185-190` renderCurrentRoute(옛 페이지 dispose · 새로 mount). 접힘 = `UI_Side.ts:106`(칸 처음 `is-collapsed`) · `UI_Page.ts:300`(`open=구조물조합` 이면 reveal 안 함) · `UI_Side.ts:351-361`(reveal 에 구조물조합 갈래 없음). 단가산출 층 = `UI_Page.ts:253`(주소에 안 실음). 뿌리 = 구조물 층 = 페이지 층 — `UI_Page.ts:68-88`(layer · owner · isAdmin · `setActiveLayer` 한 번) · 그 값으로 세움: 나무 `:121-133` · 공유 띠 `:101-120` · 읽기만 막대 `:88` · 복사 대상 `:93-99` · 편집 칸 `createMain(isAdmin)` `:174` · 왼쪽 `buildSide({layer, isAdmin})` `:262` → 도면 양식 · 실무문서 칸 = 구조물 층이 마스터일 때만 `UI_Side.ts:164-176` · 불러오기 쓰기 권한 `:178-183` |
| 고칠 자리 | (가) 제자리 바꿈[권함] — `UI_Page.ts` go = confirmLeave → replaceState(setPre 꼴) → `switchStructure(root, owner)`: setActiveLayer · 나무 · 층 줄 · 사람 나무 다시 만들어 감싼 칸에 갈아 끼움 · 공유 띠 · 읽기만 막대 = 머리 감싼 칸 갈아 끼움 · `main.openTable(새 층, null, true)`(home 에 `canWrite`). `UI_Main.ts` = 구조물 카드 권한 `:283-286`(isAdmin → writable(at)) · 「새 항목」 숨김 `:186-188` 에 writable · 머리 이름 고침은 이미 readOnly 로 가름 `:395`. `UI_Side.ts` = 도면 · 실무문서 칸을 구조물 층에서 떼어냄(시스템 관리자면 늘) · 불러오기 canWrite 를 함수로(`Hopyo_Side.ts:17`). 사람 나무 첫 사람 = `fetchOwners` 기다림(`UI_Page.ts:76-81` 꼴). (나) 다시 세우되 모양 지킴 — reveal 에 구조물조합 · 단가산출 층 주소 칸 `sk` · 깜빡임 남음 |
| 크기 | (가) M — `UI_Page.ts` 305 → 약 355 · `UI_Main.ts` +5 · `UI_Side.ts` +6 · `Hopyo_Side.ts` +3 / (나) S |
| 위험 | (가) 중간 — 활성 층 전역(`Api_Fetch.ts:64-82` · 구조물 표 · 카드 · 불러오기 `Hopyo_Api.ts:321` 가 부를 때 읽음)을 표 열기 전에 바꿔야 함 · 공유 띠(706-1) 다시 만듦 · 사람 나무 · 회사 없음 → 마스터 갈래 · 낡은 시험 `test_m02_layer_screen.py:184`(go 안 navigateTo 글 대조) · `:293`. (나) 낮음 · 요구(「안 되게」) 못 채움 |
### 4. 단가산출 칸 · 내용
| # | 요구 | 지금 | 근거 | 고칠 자리 | 크기 | 위험 |
| --- | --- | --- | --- | --- | --- | --- |
| ① | 상세 · 왼쪽 나무 이름 바꾸기 | 나무: 문서 줄 메뉴 = 복사 · 지우기만 · 폴더 줄만 이름 바꾸기 · 서버 나무 rename = 폴더만. 상세: 이름 칸 있음 · 고치면 [저장] 뒤에만 서버 · 나무는 저장 뒤 다시 읽어 바뀜 · 머리 제목은 열 때 글 그대로(추정 = 「안 됨」으로 보인 까닭). 셈 답이 오면 머리 줄을 통째 다시 그려 치는 중 이름 칸이 갈릴 수 있음(추정) | 문서 줄 `UI_Side_Library.ts:291-299` · 폴더 rename `:204-210 · 256-259` · 서버 `M02_Precombo_Folders.py:25 · 117-119` · 문서 이름 = `M02_Sangeun_Doc.py:221-237` · 목록 제목 = `M02_Labor_Ref.py:300`(doc 이름) · 상세 이름 칸 `Sangeun_Detail.ts:153-163 · 181-195` · 저장 뒤 왼쪽 다시 `UI_Page.ts:176` · 머리 제목 `UI_Main.ts:511`(doSave `:558-611` 에서 안 고침) · 같은 꼴 선례 = 목록표 [복사] `Library_List.ts:247-256`(읽고 이름 갈아 저장) | 나무 문서 줄 [이름 바꾸기] = askText → 열린 문서면 confirmLeave → readTemplate · saveTemplate(이름만) → refresh · 열려 있으면 새 이름으로 다시 엶(`UI_Side_Library.ts` · 일위대가도 같이 받음). 상세 = 저장 뒤 머리 제목 새 이름(`UI_Main.ts` doSave · 종류 sangeun · precombo) | S~M(서고 +25 · Main +3) | 저장 = 서버가 `바탕판` 다시 적음(`M02_Sangeun_Calc.py:194-210`) → 이름만 바꿔도 「M01 개정」 배지 사라짐(지금 [저장]과 같은 규칙) · `UI_Main.ts` 671 줄 |
| ② | [M01 로직 고름] → 「기본 로직 선택」 · 자리 = [줄 더하기] 왼쪽 | 첫 줄(이름 · 단위 · 바탕 꼬리표 뒤) · 바탕 없고 고침 없을 때만 보임 | 단추 `Sangeun_Detail.ts:167-180` · 첫 줄 `:181-195` · 둘째 줄 `:107`(`[views.root, addButton, baseHint]`) · 글 `ui_template_locale_m2.ts:205` | 단추를 한 번만 만들고 둘째 줄 `[views.root, pick, addButton, baseHint]` · drawHead 는 hidden 만 · 글 고침 | S | 낮음 · 글 대조 시험 없음 |
| ③ | [줄 더하기] → 「항목 추가」 | 「줄 더하기」 | `Sangeun_Detail.ts:85-98` · `ui_template_locale_m2.ts:206` | 글 한 줄 | S | 없음 |
| ④ | 항목 추가 = M01 요소(인력 · 재료 · 단가산출 로직) 찾아 넣기 | 고르기 = [로직 추가] 창 재사용 — M01 로직(기준 목록 표) · 일위대가 서고(한 층씩) · 단가산출 탭 · 「M01 요소 · 재료 고르기」 한 줄 → 따로 재료 창. 인력 · 기계 없음 · 재료는 찾기에 안 걸림(두 걸음) | `Sangeun_Pick.ts:75-104` · 재료 창 `M02_Material_Pick.ts:76` · 서버 받음 = 더한줄 `{종류: 인력|재료|기계, 요소, 수량}` `M02_Sangeun_Doc.py:9-11 · 178-187` · 찾기 길 있음(로그인) = `/api/m02/logic-lib/pick?kind=job`(노임 · 값 있는 직종) · `kind=price`(자재품목) · `/elements?group=인력|재료|기계|로직` `M02_Logic_Lib_Read.py:34-41` → `M01_MasterData_Store.py:440-488` · 화면 부름 `M01_MasterData_Api_Fetch.ts:187-191`(길 주입 717-1) | 5번 새 고르기 창의 「M01 전체」 탭에 합침(인력 · 재료 줄 고름 → 더한줄 `{종류, 요소, 이름, 단위, 수량: 1}`) | 5번에 포함 | 서버 없음 · 낡은 시험 `test_717_4_sangeun_pick.py:41-43`(done 글 대조) |
### 5. 고르기 창 검색 — 드롭다운 없앰 · 대상 셋 · 거름 값 = 구분 열
| 항목 | 내용 |
| --- | --- |
| 요구 | M02 단가산출 · 일위대가 고르기 = 드롭다운 거름 없앰 · 대상 = M01 전체 · M02 단가산출 · 일위대가 · 지금 거름 값은 구분 정보로 |
| 지금 | 한 창(openLogicPicker): 거름 넷(종류 · 공종 · 구분 · 상세구분) + 일위대가 서고 층 단추(한 층씩) + 탭 [기준 목록 | 단가산출](717-4) + 목록 둘(위 = 찾기 목록: 일위대가 서고 · 실무 호표 · 덧줄 / 아래 = M01 로직 기준 목록 표 · 품명 · 규격 · 단위 · 금액 · 출처). M01 로직 줄 누름 = 계산서 보기(704-2) → [담기] |
| 근거 | 창 `Labor_Pick.ts:101-430` — 거름 `:111-114 · 126-132` · 찾기 칸 `:138-142` · 층 단추 `:121 · 298-311` · 탭 `:344-362` · 거르기 `:172-252` · 단가산출 탭 `:254-278`. 덧줄 = `Hopyo_Pick.ts:20-45`(withHopyo · 실무 호표 · 종류 거름) · `Sangeun_Pick.ts:75-104`. 부르는 곳 일곱 = M02 `Labor_Detail.ts:527`(일위대가 [로직 추가]) · `Sangeun_Detail.ts:172`(바탕) · `Sangeun_Pick.ts:80`(더한줄) · `Hopyo_Choose.ts:310` · `Hopyo_Ledger.ts:219` / B09 `B09_Estimation_UI_Link.ts:102` · `B09_Estimation_UI_Tabs_Bill_Link.ts:221`. 자료 길(모두 있음) = 기준 목록 `/api/m02/logic-lib/standard`(`Router_Combos.py:119` · 화면 캐시 `Labor_Api.ts:155`) · 로직 `/logic-lib/logics` · `subs`(`Labor_Api.ts:113-117`) · 층 목록 `/layers/{층}/templates`(`Labor_Api.ts:347` · `Sangeun_Pick.ts:44` listSangeun = 위 층 모두) · 인력 · 재료 `/m02/logic-lib/pick`(위 4④) |
| 고칠 자리 | 새 파일 `M02_MasterTemplete_Find_Pick.ts`(가칭) — 탭 [M01 전체 | M02 단가산출 | 일위대가] · 찾기 칸 하나 · 표 하나 · 열 = 품명 · 규격 · 단위 · 금액 · 구분(종류 · 공종 · 구분 · 상세구분) · 출처(층). M01 전체 = 기준 목록(전부 · 캐시) + 인력(job 전부 받아 둠) + 재료(price · 친 낱말로 서버 찾기 · 상한 200). 단가산출 = listSangeun · 일위대가 = 위 층 서고 모두(층 단추 없앰 → 출처 열). 로직 줄 누름 = 계산서 보기 그대로(`Labor_Sheet.ts`). 부르는 쪽 세 줄 갈아 끼움 = `Sangeun_Pick.ts`(더한줄) · `Sangeun_Detail.ts`(기본 로직 선택 = 로직 줄만) · `Labor_Detail.ts:527`. 옛 창 = B09 · 불러오기 넷이 그대로 씀 |
| 서버 묶는 길 | 권함 = 안 만듦(있는 길 넷 화면에서 이음 · 재료만 서버 찾기). 따로 만들면 `GET /api/m02/find?target=&q=&layer=&owner=` 새 파이썬 + 시험(M 하나 더) |
| 크기 | M — 새 파일 약 250 · 부르는 곳 셋 한 줄씩 · 글 키 |
| 위험 | 중간 — ⓐ 일위대가가 M01 인력 · 재료를 받는 꼴 정해야 함(지금 일위대가 줄 = 로직 · 참조 · 고정 줄(이음 LB · MT · EQ) `M02_Labor_Fixed.py:1-12` + 자재 목록) ⓑ 실무 호표 덧줄 넣을지 ⓒ `Labor_Detail.ts` 679 줄 · 같은 파일을 내역 16 일위대가 줄(수량산출 걷기 · 일위대가표만) 창이 만짐 → 차례 ⓓ 낡은 시험 `test_717_4_sangeun_pick.py:41-43` · 옛 창 안 바꾸면 `test_704_logic_standard.py:121` 그대로 |
### 6. 겹침 · 700 줄
| 파일(줄) | 만지는 줄 | 비고 |
| --- | --- | --- |
| `UI_Page.ts`(305) | 1(층 줄 「+」) · 2 · 3 | 한 창 · 3 이 가장 큼 → 2 · 1 먼저 |
| `UI_Side_Library.ts`(451) | 1(메뉴 · 「+」) · 4①(문서 이름 바꾸기) | 한 창 |
| `TreePanel.ts`(700) | 1 | 꽉 참 — 줄 늘리면 먼저 나눔 |
| `UI_Main.ts`(671) | 3(권한 몇 줄) · 4①(머리 제목) | 한 창 · 합 +8 이내 |
| `UI_Side.ts`(372) · `Hopyo_Side.ts`(81) | 3 | — |
| `Sangeun_Detail.ts`(326) | 4② · 4③ · 5(바탕 고르기 부름) | 한 창 · 상세 보기 부품 = M01 `buildLabDetail`(`:252-266`) → 내역 16 M01 단가산출 상세 창이 그쪽을 바꿈 · 머리 단추만 만지면 안 겹침 |
| `Sangeun_Pick.ts`(104) | 4④ · 5 | 4④ = 5 에 합침 |
| `Labor_Detail.ts`(679) | 5(부름 한 줄) | 내역 16 일위대가 줄 창과 겹침 · 줄 수 같게 |
| `Labor_Pick.ts`(430) | 5(옛 창 그대로면 안 만짐) | — |
| `ui_template_locale_m2.ts`(246) | 1 · 4② · 4③ · 5 | 두 창 다 키 더함 · 자리 나눠 적음 |
## ③ 결정 거리 (권함 하나씩)
1. 최상위 폴더 자리 → 권함 = 오른쪽 클릭 메뉴(폴더 · 문서 줄)에 「최상위 폴더 만들기」 + 단가산출 · 일위대가 층 줄에도 「+」(구조물 조합 꼴) · 메뉴 줄과 「+」는 같은 함수.
2. 다른 층 단추 누름 → 권함 = 저장 안 한 고침 물음 뒤 그 층 목록표 엶(켜진 단추 다시 누름도 목록표 그대로).
3. 구조물 조합 층 바꿈 → 권함 = (가) 제자리 바꿈(M). 딸린 것 = 도면 양식 · 실무문서 칸을 구조물 층에서 떼어 「시스템 관리자면 늘 보임」(지금은 구조물 층이 마스터일 때만 보임).
4. 문서 이름 바꾸기 → 권함 = 나무 문서 줄 [이름 바꾸기](읽고 이름만 갈아 저장 · 서버 새 길 없음) + 저장 뒤 상세 머리 제목 새 이름 · 저장 때 바탕판 다시 적힘은 지금 [저장]과 같은 규칙으로 받아들임.
5. 고르기 창 범위 → 권함 = M02 단가산출 · 일위대가 세 곳만 새 창 · B09 · 불러오기 넷은 옛 창 그대로.
6. 「M01 전체」 안 → 권함 = 원문대로 단가산출 로직 · 인력 · 재료 셋(기계 뺌).
7. 일위대가에서 M01 인력 · 재료를 고르면 → 권함 = 고정 줄 이음 꼴(이음 노임 LB · 자재 MT · 수량 1 · 705-1 서버 꼴 그대로 · 단가 책 따라감).
8. 실무 호표(불러오기 결과) 줄 → 권함 = 새 창 대상에서 뺌(불러오기 = 한시 · 결과는 이미 일위대가 · 단가산출).
9. 서버 하나로 묶는 찾기 길 → 권함 = 안 만듦(있는 길 넷 이음 · 재료만 서버 찾기).
## ④ 창 나누기 제안
| 창 | 맡음 · 차례 | 파일 경계 |
| --- | --- | --- |
| 창 1 · 왼쪽 패널 · 페이지 | 2 → 1 → 4① → 3 | `UI_Page.ts` · `UI_Side_Library.ts` · `UI_Side_Precombo.ts`(bar 꼴) · `TreePanel.ts`(+ 나눈 새 파일) · `UI_Main.ts` · `UI_Side.ts` · `Hopyo_Side.ts` · 글 키 |
| 창 2 · 단가산출 상세 · 고르기 | 4② · 4③ → 5(4④ 포함) | `Sangeun_Detail.ts`(머리 단추 · 바탕 부름) · `Sangeun_Pick.ts` · 새 `Find_Pick.ts` · `Labor_Detail.ts`(부름 한 줄) · 글 키 |
- 두 창 파일 안 겹침(글 키 파일만 · 자리 나눔).
- `Labor_Detail.ts` 부름 한 줄 = 내역 16 일위대가 줄 창 뒤(또는 그 창이 같이).
- 700 줄 — `TreePanel.ts` 700(먼저 나눔) · `Labor_Detail.ts` 679(줄 수 같게) · `UI_Main.ts` 671(+8 이내) · 새 줄 많으면 새 파일.
- 시험 — 고친 대상: `test_m02_layer_screen.py` · `test_m02_precombo_scope.py` · `test_717_2b_sangeun_detail.py` · `test_717_4_sangeun_pick.py` · `test_m02_tree_paste.py`(구조물 나무 나눔 뒤) · 새 고르기 창 지킴 하나.
@@ -0,0 +1,116 @@
# 분석 내역 16-B — 일위대가 수량 · 일위대가표만 · M01 단가산출 상세 개편
원문 = `docs/raw/요구_내역서/사용자요구_내역서.md` 내역 16(126~135 줄) · 앞 = 내역 12~15 · 요구서 `요구서_내역12_단가산출로직_M02단가산출.md` · 완료 `docs/raw/plans/2026-11-01_plan_709-719.md`. 코드 읽기만 · 서버 · 브라우저 안 켬 · storage 읽기만.
## ① 결론 다섯 줄
1. 일위대가(서고 · 옛 사전조합) 줄의 「수량산출 · 수기」 = 실제로는 **1 단위 당 수량(호표 수량)** — 일위대가 값 = Σ 안 줄 1 단위 값 × 손(`M02_Labor_Ref.py:10-12`) · 짝 셈(`M02_Labor_Doc.py:436-463` ↔ `Labor_Calc.ts`) · storage 일위대가 12 중 불러온 6 개가 모두 ≠1(21/26 줄 · 예 L형옹벽 H0.9 1 m 당 GC000214 2.34) → 수량을 없애면 값이 바뀜. 권함 = 「수량산출」 이름 · 면적 모달 개념만 걷고 수량은 일위대가표 「수량」 칸에서 바로 고침 + 머리 「확인 수량」(휘발) — 서버 · 문서 꼴 안 바뀜(S).
2. 「일위대가표만」 — 격자(704-3)에 옵션 값이 이미 규격 칸에 있음(`Labor_Grid.ts:73`). 빠지는 것 = 담은 로직 표 · 자재목록에 붙은 동작(옵션 · 순서 · 삭제 · 별칭 · 참조 [수정] · 자재 세 칸 · 로직 자재 바꾸기 · 빈 값 n 경고) → 새 파일(일위대가표 상세) + 줄 누름 모달 + 우클릭으로 옮김(L). `Labor_Detail.ts` 679 줄이라 그 안에서 못 키움.
3. 717-3 모달 = 보기 둘(인력·재료·경비 · 고침 표)뿐 · 시험 계산 · 텍스트 수식 · 옵션 바꿈 없음. 717-2b SK 상세가 이미 M01 보기 넷을 씀(`Sangeun_Detail.ts:82 · 252-266`) · 줄 → SK 문서 변환도 있음(`M02_Sangeun_Cover_Model.ts:72-94`) → SK 상세를 「줄 모드」로 모달에 얹는 길이 가장 짧음.
4. M01 개편 ① 기본 = 단가산출 상세는 이미 됨(`LogicLab_View.ts:25`). ②~⑤ = 단가산출 상세가 인력·재료·경비(서버 `묶음` · 머리 변수 칸) · 텍스트 수식(값 식 · 입력 칩)을 빨아들이는 꼴 = 「보기 하나」로 가는 길. M02 SK 상세 · 일위대가 모달은 M01 상세 표의 **사본**(`Sangeun_Cover.ts:8-10` skCoverTable · coverTable)이라 M01 을 고쳐도 안 따라감 → 새 단가산출 상세를 공용 부품 하나로(덮개 칸 고리) 만들어야 함.
5. 정본 불변 — M01 「모달로 고침」 이 요소(노임 · 자재 단가) 정본 쓰기인지 시험 값인지 사용자 확인 필요(지금 = [마스터 화면에서 보기] → 요소 표 · `LogicLab_Modal.ts:182-190` · `UI_Page.ts:264-300`). 700 줄 근처 = `Labor_Detail.ts` 679 · `UI_Main.ts` 671 · `M02_Labor_Doc.py` 690 · `LogicLab.ts` 627 → 모두 새 파일로 · 창 셋(M01 상세 · M02 일위대가표 · 뒤 공용 갈아 끼움).
## ② 줄마다
### 1. M02 일위대가 수량산출 수기입력 걷음 · 휘발 확인
| 항목 | 내용 |
| --- | --- |
| 요구 | 일위대가 · 일위대가 불러오기에 수량산출 수기입력 필요 없음(구조물 조합 · 프로젝트 몫) · 넣으면 저장 안 하고 휘발로 확인 |
| 지금 화면 | 서고 모드 담은 로직 표 「수량산출」 칸 = 위 「10 ㎡」(배 · 분모) / 아래 「수기 1 ㎡」(`Labor_Rows.ts:109-150` · 글 `Labor_Text.ts:83-86 · 153`) · 누름 → 수량산출 모달 수기 입력만(`Labor_Detail.ts:313-342` · `handOnly` 329 · `Labor_QtyModal.ts:42-43 · 93 · 139`) · 자재 줄 같음(`Labor_MatTable.ts:46 · 81`) · 새 줄 기본 손 1(`Labor_Detail.ts:518 · 532`) · 합계 줄 「수량 없음 n」(`Labor_Rows.ts:506-512`) |
| 불러오기 | 확인 화면 「수량산출」 칸 = 위 엑셀 수량 / 아래 우리 = 엑셀 × 배(입력 아님 · `Hopyo_CheckRows.ts:82-99` · 배 칸 113-123) · 만들 때 줄 수량 = `{손: 엑셀 수량 × 배}`(`M02_Hopyo_Build.py:12-13 · 236-290`) |
| 저장 꼴 | 줄 `수량 = null | {손: 수}`(`M02_Labor_Doc.py:8-9 · 91-94`) · 서고 줄 null → 손 1(`M02_Labor_Ref.py:226-237`) |
| 셈이 씀 | 예 — 반영 = 기준 × 수량 ÷ 배(`M02_Labor_Doc.py:405-422 · 436-463`) · 참조 펼침 = Σ 안 줄 × 손(`M02_Labor_Expand.py:125-145`) · 짝 `M02_MasterTemplete_Labor_Calc.ts:1-10` · 거울 `test_m02_labor_calc_mirror.py` · 격자 수량 칸 = 그 값(`Labor_Grid.ts:56 · 74`) |
| 실제 자료 | storage 일위대가 12 — 손으로 만든 6 = 모두 1 · 불러온(실무자료) 6 = 모두 ≠1(21/26 줄) |
| 걷으면 깨짐(수량 자체를 없앨 때) | 불러온 일위대가 값 · 엑셀 대조(`비교` 같음) · 구조물 조합 참조 줄(SC 1 단위 값) · B09 서고 사본 [업데이트] 금액 · 704-4 규격 열(줄 수량만 다른 열) · 거울 시험 |
| 고칠 자리(권함) | 서고 모드에서 「수량산출」 칸 · 모달 걷음(2번 일위대가표만으로 자연히 사라짐) · 일위대가표 「수량」 칸 고침 = `createHopyoGrid` extra `editable`/`onEdit`(`ui_template_data_table` DataTableOptions) → 줄 `수량 {손}` 그대로 · 불러오기 칸 이름 「수량산출」 → 「수량」(`Hopyo_CheckRows.ts:89` 가 `Labor_QtyCol` 을 빌려 씀 → 제 글로) · `QtyModal` handOnly 갈래 걷음 · 휘발 확인 = 일위대가표 머리 「확인 수량 [ ] 단위」(기본 1 · 문서 · dirty 안 건드림 · 「n 단위 × 1 단위 합계 = 노 · 재 · 경 · 계」 한 줄) |
| 크기 | S(2번과 함께면 2번 안) |
| 위험 | 사용자 뜻이 「줄 수량 모두 없앰」이면 위 깨짐 전부 · 낡은 시험 글 대조 `test_m02_labor_values.py:186` · `test_m02_labor_national.py:58` · `test_m02_hopyo_screen.py:495` · ui_checks 43-1 · 51-6(브라우저 · pytest 밖) |
### 2. M02 일위대가 상세 = 일위대가표만 · 줄 누름 모달(보기 넷)
| 항목 | 내용 |
| --- | --- |
| 요구 | 일위대가표 · 단가산출 로직 · 자재목록 → 일위대가표만 · 옵션 값 = 규격 열 · 줄 누름 = 단가산출 로직 · 인력 · 자재 모달 · 모달 = M01 보기 넷과 같게 |
| 지금 | 세 덩이(`Labor_Detail.ts:628-633`) · 격자(`Labor_Grid.ts:45-133` · 서고는 규격 열 끔 `Labor_Detail.ts:217` `!lib`) · 담은 로직 표(`Labor_Rows.ts:333-568`) · 자재목록(`Labor_MatTable.ts` · 로직 자재 `Labor_LogicMats.ts`) · 줄 하위 행(`Labor_Rows.ts:274-330`) |
| 옵션 = 규격 열 | 이미 됨 — `Labor_Grid.ts:73` `valueLine(입력, 입력값)` + 켜쌓기 · 모자람: 「빈 값 n」 붉게는 담은 로직 표에만(`Labor_Rows.ts:418-425`) · SK · SC 참조 줄 규격 빈칸 |
| 격자 능력 | 읽기 전용 + 붙여넣기(`ui_template_hopyo_grid.ts:254-277`) · 줄 누름 · 칸 고침은 공용 표 옵션으로 바로 됨(`onRowClick` · `editable` · `onEdit`) · 줄 → 문서 줄 짝 = `hopyoGrid().at` |
| 옮길 동작 | 줄 단추 `RowHandlers`(`Labor_Rows.ts:203-222` — 옵션 · 참조 [수정] · 수량 · 순서 · 삭제 · 별칭 · 구분 · 단가산출 보기 · 자재 세 칸) · 로직 자재 바꾸기 · 기본 · 세 칸(`Labor_Detail.ts:386-419`) · 입력값 · 메모 · 켜쌓기율 모달(`Labor_Detail.ts:421-445` · `Labor_Input.ts`) · 참조 원본 모달(`Labor_Detail.ts:449-458` · `Labor_RefEdit.ts`) · [로직 추가] · [자재 추가](`Labor_Detail.ts:523-551`) · 원문 패널(`Labor_Detail.ts:147-165`) |
| 717-3 모달과 차이 | 717-3(`Labor_SangeunModal.ts:100-111`) = 인력·재료·경비 · 고침 표 두 보기 · 고침 = 품 · 단가 덮개만(`coverTable`) · 줄 빼기 · 더하기 · 옵션 · 자재 고름 · 시험 계산 · 텍스트 수식 없음 · 로직 줄 · SK 참조 줄만(고정 줄 · SC 참조 · 자재 줄 없음). 717-2b SK 상세 = 보기 넷 + 덮개 · 뺀줄 · 더한줄 · 바탕 로직 시험 계산 · 텍스트 수식(`Sangeun_Detail.ts:3-8 · 218-295`) = 상위 집합 |
| 고칠 자리 | 새 `M02_MasterTemplete_Labor_Table.ts`(서고 상세 = 정보 칸 + 머리 [로직 추가][자재 추가] + 일위대가표 + 확인 수량 + 줄 우클릭 위로 · 아래로 · 삭제 · 별칭(`ui_template_context_menu`) + 원문 패널) · `UI_Main.ts:424-436` 서고 갈래만 새 부품으로 · `Labor_RefEdit.ts:64-71` 같음 · 줄 모달 = 줄 갈래별 — 로직 줄 · SK 참조 줄 = SK 상세 줄 모드(보기 넷 · [적용] = 줄 `입력값` · `고름`(cid → 차례 되돌림) · `패치{덮개, 뺀줄, 더한줄}` · `켜쌓기율` · `메모`) · SC 참조 줄 = 그 일위대가의 일위대가표(지금 RefEdit) · 고정 줄 = 이음 · 단가 보기 · 사용자 자재 줄 = 재료 모달(값 다섯 · 세 칸 · 수량) · `Labor_Grid.ts` = 줄 누름 고리 · 수량 칸 고침 · 빈 값 경고 · 다 옮긴 뒤 `Labor_Detail.ts` 서고(lib) 갈래 걷음(줄 줄어듦) |
| 크기 | L |
| 위험 | 구조물 조합 일위대가(열 · 카드 `ItemCards.ts:328-340`)는 면적 잇기 · 수량산출이 살아야 함 → 같은 부품에서 갈래로 섞지 말고 파일을 가름 · 로직 자재 세 칸(148-4) · 별도계상(222-1)이 금액에 닿음 → 모달 안 자리 빠뜨리면 값 바뀜 · 줄 끌기(102-2) 사라짐 · 원문 패널 고름 기준 바뀜 · 낡은 시험 `test_175_precombo_mats` · `test_m02_labor_logic_mats` · `test_m02_labor_mat_marks` · `test_m02_labor_rowdrag` · `test_m02_precombo_scope` · `test_704_3_hopyo_grid` · `test_717_3_sangeun_modal` · `test_222_1_m02_apart` · `test_149_4_pct_extra`(서고 갈래 글 대조분만) |
### 3. M01 단가산출 로직 상세 개편
| # | 요구 | 지금 | 근거 | 고칠 자리 | 크기 | 위험 |
| --- | --- | --- | --- | --- | --- | --- |
| ① | 기본 화면 = 단가산출 상세 | 이미 기본(`ho`) | `LogicLab_View.ts:15 · 25 · 33` · `LogicLab.ts:134 · 587` | 없음 | — | — |
| ② | 인력 · 재료 · 경비 하위 박스 | 묶음마다 h4 + 표(박스 아님) · 묶음 = 화면이 호표 `종류` + 계산 줄 비목 몫으로 가름 · 장비 줄은 노 · 재 · 경 묶음에 몫으로 흩어짐(「노무비 몫」 배지) | `LogicLab_Detail.ts:91-98 · 245-262 · 409-453` | 묶음 = 서버 `묶음`(712-1 · CalcLine 덧칸)으로 · 장비 = 경비(장비) 박스 한 줄 + 펼침(정함 6 · `calc_sheet.ts:94-132 · 182-230` 꼴) · 박스 = `ui_template_card`/`group_cards` · 맨 아래 비목 소계 셋 + 계 | M | 박스 소계 ≠ 지금 비목 소계(장비 노무가 경비 박스) · 「그 밖」 → 「하위」 이름 · 자체 로직 편집기 접힘(`:503-510`) |
| ③ | 「로직」 열 → 「계산식」 · 텍스트 수식처럼 | 열 = 꼬리표 배지 + 서버 수량 조각(알약) | `LogicLab_Text.ts:47` · `LogicLab_Detail.ts:381-407 · 343-377` · 텍스트 수식 = 왼 변수 식(`이름글`) · 오른 「이름 : 값 식」 + 입력 칩(`LogicLab_Formula.ts:150-193 · 49-94`) | 열 이름 글 하나 · 칸 = 짝 TextLine `글`(값 식 · `sheetFormula` 꼴) + 그 줄 입력 칩(`inputChip` 재사용) · `이름글` 은 풍선 · 알약(표 · 요소 · 로직)은 칸 안 그대로 | M | 줄 ↔ TextLine 짝 = 이름 차례 맞춤(`attachPieces`) 의존 · 미리보기(계산 전) 빈칸 |
| ④ | 박스 안 제목 바로 아래 옵션 고르기 | 인력·재료·경비 보기만 묶음 머리 변수 칸 | `calc_sheet.ts:157-179`(varRow · export 안 됨) · `:233-253`(headRows · 줄 `쓰는입력`) · `LogicLab_Est.ts:35-43` | varRow export → 박스 머리에서 씀 · 값 = 시험 입력(`values`) · 바꾸면 `onInput`(`LogicLab.ts:259` → syncLeft) | M | 어느 줄도 안 쓰는 입력 = 박스에 안 나옴 → 「공통 옵션」 줄 필요 · 같은 입력 두 박스에 겹쳐 나옴 |
| ⑤ | 하위 값 바꿈 = 마스터 화면 대신 그 자리 모달 | 줄 누름 → 단가 모달 + [마스터 화면에서 보기] → M01 요소 표로 이동(돌아가기 알림) · 재료 줄만 후보 고르면 시험 입력 | `LogicLab_Modal.ts:182-190 · 250 · 282 · 428-525` · `LogicLab_Detail.ts:286-304` · `UI_Page.ts:264-300` | 모달 안 고침 칸 — 4번 결정에 따라 (가) 시험 덮개(값 · 품목 · 값 칸 고름 · 저장 안 함) 또는 (나) 요소 줄 저장(`/api/m01/save` 요소 파일 · 713-1 개정확인 불요) | M(가) · L(나) | (나) = 노임 · 자재 값이 모든 로직 · M02 층 셈에 번짐 · 기계 고르기 PickTable 따로 |
| ⑥ | 보기 넷 → 하나 · 나머지 지움 예정 | 보기 넷 · 시험 계산은 숨어도 셈 엔진 역할(입력 · 자동값 · [계산] · lines/text 공급) | `LogicLab_View.ts:3-7` · `LogicLab.ts:209-233` · `LogicLab_Style.css:194-198` | 이번 ②~④ = 단가산출 상세가 인력·재료·경비(박스 · 머리 옵션)와 텍스트 수식(계산식 · 칩)을 흡수 = 「단가산출 상세를 그 하나로 키우는 꼴」 · 지우는 차례 = 결정 10 | — | 흐름 로직은 단가산출 상세 대신 FlowView(`LogicLab_Detail.ts:536-537` · `LogicLab_FlowView.ts`) — 같이 맞춰야 함 · 돈 아닌 로직(`:481-490`) |
M02 가 따라가나:
| 쓰는 곳 | 지금 쓰는 부품 | M01 ②~⑤ 고치면 |
| --- | --- | --- |
| 717-2b M02 단가산출(SK) 상세 | `buildLabDetail` + `createViewPicker` · 단가산출 상세 덩이 = `cover.ho` = `skCoverTable`(M01 표 꼴 사본) | 보기 고르기 · 기본정보 · 시험 계산 · 텍스트 수식은 따라감 · 단가산출 상세 표는 **안 따라감** |
| 717-3 일위대가 줄 모달 | `calcSheetRows` + `coverTable` | 안 따라감 |
| 719-4 B09 단가산출근거 탭 | `calcSheetRows`(`B09_Estimation_UI_Tabs_Sangeun_Sheet.ts:238`) | 안 따라감(calc_sheet 를 고칠 때만) |
| 704-2 기준 목록 계산서 | `Labor_Sheet.ts:49` calc_sheet | 안 따라감 |
→ 새 단가산출 상세를 공용 부품(입력 · 줄 누름 · 품 · 단가 고침 고리)으로 만들어 M01(보기 · 시험) · M02 SK(덮개) · 일위대가 모달(줄 패치)이 한 벌로 씀 · `skCoverTable` · `coverTable` 걷음. 둘 다 같은 이름 모델 파일 둘(`M02_Sangeun_Cover_Model.ts` 166 · `logic/M02_Sangeun_Cover_Model.ts` 312)도 하나로.
### 4. M01 정본 불변 — 「모달로 고침」 뜻
| 갈래 | 쓰는 곳 | 근거 | 결과 |
| --- | --- | --- | --- |
| (가) 시험 값만 | 화면 값(단가 · 품목 · 값 칸) · 저장 안 함 | 원칙 1(요구서 13 줄) · 4-1 확정 8(91 줄 · 텍스트 수식 = 시험 값만) · 711-1 재료 고름 = 시험 입력 | 정본 · 요소 그대로 · M02 일위대가 모달에선 같은 칸이 줄 덮개(확정 5) |
| (나) 요소 정본 고침 | M01 요소 파일(노임 · 자재 · 기계 줄) · 시스템 관리자 | 정함 10(64 줄 · 요소 화면은 개정 확인 대상 밖) · 713-1 요소 파일 저장 200 | 그 요소를 쓰는 모든 로직 · M02 층 셈 값 바뀜 · 프로젝트 사본은 [업데이트] 때 |
| (다) 로직 정본 줄 고침(품 · 식) | 로직 정본 파일 | 713-1 · 713-2 「원문 개정 반영」 확인 창 | 원문 개정 때만 |
물음 = 「하위 값 모달로 고침」 이 (가) · (나) 중 어느 것인지 · 「마스터 화면에서 보기가 나오는데」 는 (나) 쪽 읽힘도 됨(지금 요소 표가 고치는 자리).
### 5. 겹침 · 700 줄
| 파일 | 줄 | 이번 손댐 | 처리 |
| --- | --- | --- | --- |
| `M02_MasterTemplete_Labor_Detail.ts` | 679 | 서고 갈래 빼기만 | 늘리지 않음 · 새 `Labor_Table.ts` |
| `M02_MasterTemplete_UI_Main.ts` | 671 | 서고 갈래 1~2 줄 | 줄 수 그대로 |
| `M02_Labor_Doc.py` | 690 | 권함 길이면 안 손댐 | — |
| `M01_MasterData_UI_LogicLab.ts` | 627 | 보기 고르기 · onInput 길 | 늘리지 않음 |
| `M01_MasterData_UI_LogicLab_Detail.ts` | 551 | 단가산출 상세 크게 | hoBox · entries · groupTable · lineRow 떼어 새 `LogicLab_Ho.ts`(공용 상세) |
| `M01_MasterData_UI_LogicLab_Modal.ts` | 562 | ⑤ 고침 칸 | 고침 칸은 새 파일 |
| `M02_MasterTemplete_Labor_Rows.ts` | 571 | 구조물 조합 그대로 | — |
| `M02_MasterTemplete_Sangeun_Detail.ts` | 326 | 줄 모드(B) · 단추 이름 · 「기본 로직 선택」(내역 16 다른 줄) | 차례 정함 |
| `M02_MasterTemplete_Labor_Pick.ts` | 430 | 검색 개편(내역 16 다른 줄) | B 는 부르기만 |
| `A00_Common/calc_sheet.ts` | 279 | varRow export | 작음 |
## ③ 결정 거리(권함 하나씩)
1. 일위대가 줄 「수량」 = **1 단위 당 수량으로 남김**(호표 수량 · 「수량산출 · 수기」 이름과 면적 모달만 걷음 · 일위대가표 수량 칸에서 고침) — 반대(줄 수량 모두 1)는 불러온 일위대가 6/12 값이 바뀌고 엑셀 금액과 어긋남.
2. 휘발 확인 = **일위대가표 머리 「확인 수량」 한 칸**(기본 1 · 저장 · dirty 없음 · 합계 × n 한 줄) — 줄마다 휘발 수량은 안 둠.
3. 일위대가표만 범위 = **M02 일위대가(서고) 상세 + 참조 [수정] 모달만** · 구조물 조합 일위대가 카드 · 열 상세는 그대로(면적 잇기 · 수량산출 필요).
4. 줄 모달 = **717-2b SK 상세를 줄 모드로 모달에 얹음**(보기 넷 · 717-3 두 보기 몸 갈음 · [적용] · [단가산출로 저장] 그대로).
5. 모달 안 옵션 바꿈 = **그 일위대가 줄 `입력값` 에 남김**([적용] 뒤 · 규격 칸에 보임) · M01 에서만 시험 값.
6. 표에서 빠지는 동작 = **우클릭(위로 · 아래로 · 삭제 · 별칭) + 머리 [로직 추가][자재 추가] + 자재 세 칸 · 로직 자재 바꾸기 · 켜쌓기율 · 메모 = 줄 모달 안**.
7. M01 하위 박스 기준 = **서버 `묶음`(인력 · 재료 · 경비(장비) · 하위) · 장비 한 줄 + 펼침**(정함 6 · 인력·재료·경비 보기와 같은 셈).
8. 계산식 칸 = **값 식(「268,908 × 0.12」) + 그 줄 입력 칩** · 변수 식(`이름글`)은 풍선.
9. M01 「모달로 고침」 = **시험 값만(정본 · 요소 안 바뀜)** · 요소 고침은 [마스터 화면에서 보기] 그대로 남김 — 사용자 뜻 확인 거리.
10. 보기 넷 지우는 때 = **이번엔 단가산출 상세만 키움(고르기 그대로)** → 사용자 확인 뒤 텍스트 수식 · 인력·재료·경비 지움 → 시험 계산은 「공통 옵션」 줄 + 셈 엔진(`buildCalc`) 화면에서 떼어 낸 뒤 지움.
11. 공용 부품 = **새 단가산출 상세 한 벌(M01 폴더 · 고침 고리)** → M02 SK 상세 · 일위대가 모달이 갈아 끼움 · `skCoverTable` · `coverTable` · 겹친 모델 파일 걷음.
12. B09 단가산출근거 탭(719-4) = **지금 인력·재료·경비 꼴 그대로** · 보기 하나가 정해진 뒤 따라감(내역 14-7 「같은 보기」).
## ④ 창 나누기
| 창 | 맡음 | 파일 경계 | 차례 |
| --- | --- | --- | --- |
| A M01 상세 | 3번 ②~⑤ · 공용 단가산출 상세 | `M01_MasterData_UI_LogicLab_Detail.ts` → 새 `M01_MasterData_UI_LogicLab_Ho.ts` · `LogicLab_Text.ts` · `LogicLab_Modal.ts`(+ 새 고침 칸 파일) · `A00_Common/calc_sheet.ts`(varRow export) · `LogicLab_Style.css` | A1 hoBox 떼어 냄(동작 같음 · 시험) → A2 하위 박스(서버 묶음) + 머리 옵션 → A3 계산식 칸 → A4 모달 고침(결정 9 뒤) |
| B M02 일위대가표 | 1번 · 2번 | 새 `M02_MasterTemplete_Labor_Table.ts` · 새 줄 모달 파일 · `Labor_Grid.ts` · `UI_Main.ts:424-436` · `Labor_RefEdit.ts` · `Sangeun_Detail.ts`(줄 모드) · `Hopyo_CheckRows.ts` · `Labor_Text.ts` · `Labor_QtyModal.ts` | B1 수량 이름 · 확인 수량 · 수량 칸(S) → B2 일위대가표 상세 새 파일 + 우클릭 → B3 줄 모달 보기 넷 → B4 `Labor_Detail.ts` 서고 갈래 걷음 |
| C 공용 갈아 끼움(뒤) | 결정 10 · 11 | `Sangeun_Detail.ts` · `Sangeun_Cover.ts` · 두 `Sangeun_Cover_Model.ts` · `LogicLab_View.ts` · (뒤) B09 근거 탭 | A3 · B3 뒤 · 보기 셋 지움은 사용자 확인 뒤 |
- 같은 파일 차례 — `Sangeun_Detail.ts` = 내역 16 다른 줄(「기본 로직 선택」 · 「항목 추가」 단추) → B → C · `Labor_Pick.ts` = 검색 개편 창만 손댐(B 는 부르기만) · `calc_sheet.ts` = A 만 · `LogicLab_Detail.ts` = A 만.
- 700 줄 — 새 줄은 새 파일 · `Labor_Detail.ts` · `UI_Main.ts` · `LogicLab.ts` 늘리지 않음.
- 시험 — A: `test_714_2_calc_sheet` · `test_714_3_formula_inputs` · `test_m01_logic_source` · `test_713_2_revise_ui` · `test_717_2b_sangeun_detail` · M01 계 전후 같음(표본 셋) / B: 2번 위험 칸 시험 + `test_m02_labor_calc_mirror`(안 바뀌어야 함) · `test_717_3` · `test_m02_hopyo_screen`.
@@ -0,0 +1,55 @@
# 요구서 — 내역 16 · M02 폴더 · 층 · 단가산출 정리 · 고르기 창 하나 · 일위대가표만 · M01 단가산출 상세 개편
원문 = `사용자요구_내역서.md` 내역 16 · 근거 = 같은 폴더 `_분석_내역16-A_M02_폴더층단가산출검색.md` · `_분석_내역16-B_일위대가표_M01상세.md` · 앞 일 = `요구서_내역12_단가산출로직_M02단가산출.md` · `docs/raw/plans/2026-11-01_plan_709-719.md`.
## 1. 요구 · 지금 · 고칠 것
| # | 요구 | 지금 | 고칠 것 | PLAN |
| --- | --- | --- | --- | --- |
| 1 | 폴더 나무 우클릭에 최상위 폴더 만들기(세 칸) · 구조물 조합 + 단추 기능 | 빈 자리 우클릭뿐 · + 는 구조물 조합만 | 우클릭(폴더 · 문서 줄) 「최상위 폴더 만들기」 + 단가산출 · 일위대가 층 줄에도 「+」 · 같은 함수 | 720 |
| 2 | 단가산출 · 일위대가 층 단추 → 상세에 그 층 목록 | 왼쪽 나무만 바뀜(`UI_Page.ts:247 · 259`) | 누르면 그 층 목록표 · 저장 안 한 고침은 먼저 물음 | 720 |
| 3 | 구조물 조합 층 단추 새로고침 막음 | 해시 바꿈 → 통째 다시 그림(`UI_Page.ts:134-141`) | 제자리 바꿈 · 도면 양식 · 실무문서 칸은 시스템 관리자면 늘 보임 | 720 |
| 4 | 단가산출 이름 바꾸기 | 나무 문서 줄 메뉴 없음 · 머리 제목 옛 이름 | 나무 문서 줄 [이름 바꾸기] + 저장 뒤 머리 제목 새 이름 | 720 |
| 5 | [M01 로직 고름] → 「기본 로직 선택」 · [줄 더하기] 왼쪽 · [줄 더하기] → 「항목 추가」 | 이름 · 자리 다름 | 이름 · 자리 바꿈 | 721 |
| 6 | 항목 추가 때 M01 요소(인력 · 재료 · 단가산출 로직) 찾아 넣기 | 서버는 받으나 고르기 창에 인력 · 기계 없음 · 재료 숨은 한 줄 | 7 번 새 고르기 창으로 | 721 |
| 7 | 고르기 창 검색 = M01 전체 · M02 단가산출 · 일위대가 · 드롭다운 거름 없앰 · 거름 값 = 구분 정보 | `Labor_Pick.ts` openLogicPicker 하나를 일곱 곳이 씀 · 드롭다운 넷 | M02 단가산출 · 일위대가 세 곳만 새 창(탭 셋 · 찾기 칸 하나 · 거름 값 = 열) · B09 · 불러오기는 옛 창 | 721 |
| 8 | 일위대가 수량산출 수기 정보 필요 없음 · 넣은 값은 휘발성 확인만 | 「수량산출 · 수기」 = 줄 1 단위 당 수량(셈에 씀 · 불러온 6 개 모두 ≠ 1) | 4 장 물음 1 답에 따름 | 723 |
| 9 | 일위대가 상세 = 일위대가표만 · 옵션 값 = 규격 열 · 줄 누름 = 단가산출 로직 · 인력 · 자재 모달(M01 보기 넷과 같게) | 세 표 · 옵션은 이미 규격 칸(`Labor_Grid.ts:73`) · 717-3 모달 보기 둘 | 일위대가표만 · 다른 표 동작 옮김 · 줄 모달 = 717-2b 상세 줄 모드(보기 넷) | 723 |
| 10 | M01 단가산출 상세 = 기본 · 인력 · 재료 · 경비 하위 박스 · 「로직」 → 「계산식」(텍스트 수식처럼) · 박스 안 제목 아래 옵션 고르기 · 하위 값 고침 = 그 자리 모달 | 기본은 이미 상세 · 표 하나 · 마스터 화면 이동 | 하위 박스 · 계산식 열 · 박스 옵션 · 모달 고침 · 공용 부품 한 벌 | 722 · 724 |
| 11 | 보기 넷은 결국 하나만 | 넷 | 이번엔 상세만 키움 · 지우기는 사용자 확인 뒤(시험 계산은 숨은 셈 엔진이라 맨 뒤) | 722 |
## 2. 정함(브레인) — 사용자가 바꿀 수 있음
1. 고르기 창 「M01 전체」 = 단가산출 로직 · 인력 · 재료(기계 뺌) · 실무 호표 줄은 대상 밖(불러오기 = 한시) · 서버 하나로 묶는 찾기 길은 안 만듦(있는 길 넷 이음 · 재료만 서버 찾기).
2. 일위대가 · 단가산출에서 M01 인력 · 재료를 고르면 = 고정 줄 이음 꼴(노임 LB · 자재 MT · 수량 1 · 705-1 꼴).
3. 일위대가표만 = M02 일위대가 상세 + 참조 [수정] 모달 · 구조물 조합의 일위대가는 그대로(면적 잇기 · 수량산출 필요).
4. 일위대가표에서 빠지는 동작 자리 = 우클릭(위로 · 아래로 · 삭제 · 별칭) · 머리 단추([로직 추가] [자재 추가]) · 줄 모달 안(자재 세 칸 · 로직 자재 바꾸기 · 켜쌓기율 · 메모).
5. 줄 모달에서 옵션을 바꾸면 그 일위대가 줄에 남김([적용] 뒤 규격 칸) · M01 에서만 시험 값.
6. M01 하위 박스 = 서버 묶음(인력 · 재료 · 경비(장비) · 하위) · 장비 한 줄 + 펼침 · 계산식 칸 = 값 든 식(「268,908 × 0.12」) + 그 줄 입력 칩 · 변수 이름 식은 마우스 올리면.
7. 공용 부품 = 새 단가산출 상세 한 벌(M01 폴더 · 고침 연결 지점)을 M02 단가산출 상세 · 일위대가 모달이 같이 씀 · `skCoverTable` · `coverTable` · 이름 같은 모델 파일 둘 걷음 · B09 근거 탭(719-4)은 보기 하나가 정해진 뒤 따라감.
8. 「기본 로직 선택」 · 「항목 추가」 단추 이름 바꿈은 `Sangeun_Detail.ts` 를 먼저 만지는 721 이 하고 722 · 724 가 그 위에.
## 3. 사용자 확정
- 내역 16 원문 전부 · 보기 넷 중 하나만 남김(나머지 지움은 뒤).
## 4. 사용자 물음
1. 일위대가 「수량산출 · 수기」 = 실제로 줄 1 단위 당 수량(예 L형옹벽 1 m 당 2.34 · 일위대가 금액이 이 값으로 셈해짐) — 권함 = 수량은 남기고 「수량산출」 이름 · 수량 모달만 걷음 · 일위대가표 「수량」 칸에서 바로 고침 + 표 머리 「확인 수량」(저장 안 함 · 합계 × n 휘발 확인).
2. M01 하위 값 「모달로 고침」 = 시험 값만(정본 · 요소 안 바뀜 · 권함) · 요소(노임 · 자재 단가) 실제 고침.
## 5. 단계 · 창 (PLAN 720~724)
| 절 | 무엇 | 창 |
| --- | --- | --- |
| 720 | 층 단추 → 상세 목록 · 최상위 폴더 · 단가산출 이름 바꾸기 · 구조물 조합 층 제자리(TreePanel 700 줄 나눔 먼저) | sub6 |
| 721 | 단추 이름 · 자리 · 새 고르기 창(M01 요소 포함) | sub7 |
| 722 | M01 단가산출 상세 개편 · 공용 부품 | sub2 · 721 뒤 Sangeun_Detail |
| 723 | M02 일위대가표만 · 수량 · 줄 모달 | sub1 |
| 724 | M02 단가산출 상세 · 일위대가 모달이 722 공용 부품으로 · 낡은 시험 | sub2 · 722 · 723 뒤 |
## 6. 검증 기준
- 고친 대상 시험 · typecheck 0 · 짝(Labor_Doc ↔ Labor_Calc.ts) 거울 시험 · 기존 일위대가 · 구조물 조합 · 공사 금액 전후 같음.
- ORCA(자기 포트 · 자기 개인 층) · 일반 · 시스템 관리자 · 밝음 · 어두움 · 1080 · 768 · 만든 것 지움.
- 낡은 시험(`test_m02_layer_screen.py:184 · 293` · `test_717_4_sangeun_pick.py:41-43` 등)은 그 걸음이 고침.