Update Pier_v1 project

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
CopilotandCopilot committed 2026-11-03 12:04:58 +09:00
1 parent f25e36c050
commit ad73ddec2b
2476 files changed
+3423 -4902

No files matched your search

+2
View File
@@ -12,3 +12,5 @@ __pycache__/
*.zip
pierzainer_ground/data/cache/
pierzainer_ground/data/output/**/cache/
verify/
도면추출/output/
+10 -7
View File
@@ -11,19 +11,22 @@ npm run dev -- --host 127.0.0.1 --port 5173
브라우저에서 http://127.0.0.1:5173/ 을 엽니다. 랜딩페이지의 Excel 가져오기와 실제 교량 샘플 선택을 사용합니다.
랜딩페이지의 **도면 불러오기**를 누르면 프로그램 내부의 도면추출 팝업이 열립니다. DWG 폴더를 선택하고 자동 추출을 시작하면 진행 로그와 결과가 팝업에 표시됩니다. 결과의 **교각 불러오기**로 바로 Excel 인식·형상 생성 단계에 연결하거나 **Excel 저장**으로 파일을 받을 수 있습니다. 원래 추출 엔진은 화면 없이 실행하며 미확정 값은 검토 대상으로 유지합니다.
결과는 프로젝트의 `도면추출/output/작업ID/`에 저장합니다. 선택한 원본 도면 폴더는 수정하지 않습니다. 팝업을 닫아도 추출은 계속되며 다시 열어 결과를 확인할 수 있습니다.
배포 빌드를 로컬에서 실행하려면 `npm run build` 후 `npm run preview -- --host 127.0.0.1`을 사용합니다. 도면추출은 이 PC의 Windows 로컬 서버와 포함된 `도면추출/app` 엔진을 사용하므로, `dist`만 올린 정적 웹사이트에서는 추출을 실행할 수 없습니다.
## 유지하는 폴더
- `src/`, `index.html`: 실제 교각 프로그램
- `랜딩페이지/`: 랜딩 화면과 지도 포함 통합 샘플 12개
- `pierzainer_ground/`: 지반 생성 프로그램, 원본 자료, 최종 지반 JSON 및 재현에 필요한 데이터
- `도면추출_연동패키지_20261102/`: 도면 추출 프로그램과 교각 샘플 Excel
- `public/`: 프로그램 리소스 및 기본 Excel 샘플
- `tests/`, `scripts/`, `verify/`: 자동 검사와 유지보수 스크립트
- `도면추출/`: 도면 추출 프로그램과 교각 샘플 Excel (기본 P1 샘플 포함)
- `tests/`: 현재 교각·Excel·지도 기능의 자동 검사
`node_modules/`, `dist/`, `.verify/`, 지반 `cache/`는 실행·검증 중 재생성되는 파일이며 Git에 추가하지 않습니다.
## 2026-11-02 혼합 파일 복구
## 정리 기준
혼합 전 Pierzainer 커밋 `d6735a6`에서 로컬 브랜치 `recovery/pierzainer-20261102`를 만들었습니다. π형 Excel 인식, 전·후열 받침 수 불일치 허용, 랜딩페이지 샘플 연결을 포함합니다. 기존 ITAM 브랜치와 원격 기록은 변경하지 않았습니다.
복구본과 분리한 파일은 상위 `_recovery/20261102_mixed_projects/`에 보관했습니다. 기존 `master`는 ITAM 코드이므로 그 브랜치로 전환하면 화면과 파일이 다시 바뀝니다. 현재 복구 브랜치에서 작업하세요. 이 작업에서는 원격 push를 수행하지 않았습니다.
실행에 필요한 코드·원본 도면·샘플 Excel·최종 지반 JSON은 유지합니다. 과거 개발용 도구와 옛 좌표 기준의 검증 파일은 정리했으며 백업 목록은 상위 `_recovery/20261103_cleanup/`에 있습니다.
-1
View File
@@ -24,7 +24,6 @@
<span id="save-status" class="savest">저장 전</span>
<button id="home-from-app" class="btn-s" type="button" hidden>홈으로</button>
<button id="save-project" class="btn-s" type="button" disabled title="프로젝트 파일 저장 (Ctrl+S)">저장</button>
<button id="open-import" class="btn-s" type="button">엑셀 불러오기</button>
<button id="open-project" class="btn-s" type="button">프로젝트 열기</button>
<button id="regen" class="btn-p" type="button" disabled>선택 교각 형상 생성</button>
<input id="project-file" type="file" accept=".json" hidden />
+1
View File
@@ -4,6 +4,7 @@
"description": "교각 3D 형상 생성 웹앱 (1단계: 형상 생성)",
"scripts": {
"dev": "vite",
"preview": "vite preview",
"build": "tsc --noEmit && vite build",
"typecheck": "tsc --noEmit",
"test": "vitest run"
+1 -1
View File
@@ -16,7 +16,7 @@ import traceback
ROOT = Path(__file__).resolve().parent
OUTPUT = ROOT / 'data/output/samples_20261102'
PACKAGE = ROOT.parent / '도면추출_연동패키지_20261102'
PACKAGE = ROOT.parent / '도면추출'
def save(path, value):
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5183",
"cad_axes": "YX",
"terrain_block": "CSSAA14",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플01_산외교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플01_산외교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 1,
"bridge": "산외교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플01_산외교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플01_산외교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample1)_π형교각_산외교\\02.종평면도(울산)_2017.0619-수정.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample1)_π형교각_산외교\\02.종평면도(울산)_2017.0619-수정.dwg"
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5186",
"cad_axes": "YX",
"terrain_block": "",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플02_운곡교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플02_운곡교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "CA002,CA0021,CAA001,CAA002",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 2,
"bridge": "운곡교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플02_운곡교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플02_운곡교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample2)_T형교각_운곡교\\C0050202-001-운곡교 종평면도(완도방향).dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample2)_T형교각_운곡교\\C0050202-001-운곡교 종평면도(완도방향).dwg"
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5186",
"cad_axes": "YX",
"terrain_block": "*Model_Space",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플03_대호지교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플03_대호지교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "PDF_SOUNDING",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 3,
"bridge": "대호지교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플03_대호지교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플03_대호지교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\03_original_pdf_soundings.dxf",
"candidates": [],
"output": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\output\\samples_20261102\\03_대호지교",
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5186",
"cad_axes": "YX",
"terrain_block": "",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플04_목동교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플04_목동교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "CA002,CA0021,CAA001,CAA002",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 4,
"bridge": "목동교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플04_목동교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플04_목동교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\04_@서울-세종 9공구 1000현황.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample4)_π형교각_목동교\\01 공통사항(목동교 종평면도).dwg"
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5186",
"cad_axes": "YX",
"terrain_block": "",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플05_승천천교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플05_승천천교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "F0017111,F0017114,F0027217,F0027132",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 5,
"bridge": "승천천교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플05_승천천교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플05_승천천교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\05_project_topography_original.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample5)_π형교각_승천천교\\01 승천천교(DR) 종평면도.dwg"
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5186",
"cad_axes": "YX",
"terrain_block": "xref-현황(191219)",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플06_계문철교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플06_계문철교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 6,
"bridge": "계문철교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플06_계문철교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플06_계문철교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\06_서수평장1-현황+선형+노리-20200410.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample6)_π형교각_계문철교\\01 계문철교 종평면도.dwg"
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5186",
"cad_axes": "YX",
"terrain_block": "xref-현황(191219)",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플07_기양천교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플07_기양천교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 7,
"bridge": "기양천교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플07_기양천교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플07_기양천교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\06_서수평장1-현황+선형+노리-20200410.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample7)_π형교각_기양천교\\01 기양천교종평면도.dwg"
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5186",
"cad_axes": "YX",
"terrain_block": "XR_현황",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플08_옥성교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플08_옥성교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 8,
"bridge": "옥성교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플08_옥성교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플08_옥성교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample8)_T형교각_옥성교\\01 옥성교 종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample8)_T형교각_옥성교\\01 옥성교 종평면도.dwg"
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5186",
"cad_axes": "YX",
"terrain_block": "*Model_Space",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플09_새재1교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플09_새재1교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "XR_현황-수정$0$Point",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 9,
"bridge": "새재1교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플09_새재1교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플09_새재1교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample9)_T형교각_새재1교\\01 새재1교 종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample9)_T형교각_새재1교\\01 새재1교 종평면도.dwg"
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5186",
"cad_axes": "YX",
"terrain_block": "*Model_Space",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플10_도하4교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플10_도하4교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "CA002,CA0021,CAA001,CAA002",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 10,
"bridge": "도하4교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플10_도하4교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플10_도하4교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample10)_T형교각_도하4교\\01.종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample10)_T형교각_도하4교\\01.종평면도.dwg"
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5186",
"cad_axes": "YX",
"terrain_block": "*Model_Space",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플11_곡천교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플11_곡천교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "CA002,CA0021,CAA001,CAA002",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 11,
"bridge": "곡천교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플11_곡천교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플11_곡천교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample11)_π형교각_곡천교\\01 종평면도_곡천교.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample11)_π형교각_곡천교\\01 종평면도_곡천교.dwg"
@@ -5,7 +5,7 @@
"terrain_crs": "EPSG:5181",
"cad_axes": "YX",
"terrain_block": "",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플12_감북교_교각입력데이터.xlsx",
"piers": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플12_감북교_교각입력데이터.xlsx",
"boreholes": "",
"layers": "CAA001,CAA002",
"margin": 100.0,
@@ -1,7 +1,7 @@
{
"sample": 12,
"bridge": "감북교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플12_감북교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플12_감북교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample12)_π형교각_감북교\\01 감북교 종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample12)_π형교각_감북교\\01 감북교 종평면도.dwg"
@@ -2,7 +2,7 @@
{
"sample": 1,
"bridge": "산외교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플01_산외교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플01_산외교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample1)_π형교각_산외교\\02.종평면도(울산)_2017.0619-수정.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample1)_π형교각_산외교\\02.종평면도(울산)_2017.0619-수정.dwg"
@@ -12,7 +12,7 @@
{
"sample": 2,
"bridge": "운곡교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플02_운곡교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플02_운곡교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample2)_T형교각_운곡교\\C0050202-001-운곡교 종평면도(완도방향).dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample2)_T형교각_운곡교\\C0050202-001-운곡교 종평면도(완도방향).dwg"
@@ -22,7 +22,7 @@
{
"sample": 3,
"bridge": "대호지교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플03_대호지교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플03_대호지교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\03_original_pdf_soundings.dxf",
"candidates": [],
"output": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\output\\samples_20261102\\03_대호지교",
@@ -930,7 +930,7 @@
{
"sample": 4,
"bridge": "목동교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플04_목동교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플04_목동교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\04_@서울-세종 9공구 1000현황.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample4)_π형교각_목동교\\01 공통사항(목동교 종평면도).dwg"
@@ -956,7 +956,7 @@
{
"sample": 5,
"bridge": "승천천교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플05_승천천교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플05_승천천교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\05_project_topography_original.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample5)_π형교각_승천천교\\01 승천천교(DR) 종평면도.dwg"
@@ -984,7 +984,7 @@
{
"sample": 6,
"bridge": "계문철교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플06_계문철교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플06_계문철교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\06_서수평장1-현황+선형+노리-20200410.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample6)_π형교각_계문철교\\01 계문철교 종평면도.dwg"
@@ -1011,7 +1011,7 @@
{
"sample": 7,
"bridge": "기양천교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플07_기양천교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플07_기양천교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\06_서수평장1-현황+선형+노리-20200410.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample7)_π형교각_기양천교\\01 기양천교종평면도.dwg"
@@ -1038,7 +1038,7 @@
{
"sample": 8,
"bridge": "옥성교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플08_옥성교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플08_옥성교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample8)_T형교각_옥성교\\01 옥성교 종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample8)_T형교각_옥성교\\01 옥성교 종평면도.dwg"
@@ -1048,7 +1048,7 @@
{
"sample": 9,
"bridge": "새재1교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플09_새재1교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플09_새재1교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample9)_T형교각_새재1교\\01 새재1교 종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample9)_T형교각_새재1교\\01 새재1교 종평면도.dwg"
@@ -1058,7 +1058,7 @@
{
"sample": 10,
"bridge": "도하4교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플10_도하4교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플10_도하4교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample10)_T형교각_도하4교\\01.종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample10)_T형교각_도하4교\\01.종평면도.dwg"
@@ -1068,7 +1068,7 @@
{
"sample": 11,
"bridge": "곡천교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플11_곡천교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플11_곡천교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample11)_π형교각_곡천교\\01 종평면도_곡천교.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample11)_π형교각_곡천교\\01 종평면도_곡천교.dwg"
@@ -1078,7 +1078,7 @@
{
"sample": 12,
"bridge": "감북교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플12_감북교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플12_감북교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample12)_π형교각_감북교\\01 감북교 종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample12)_π형교각_감북교\\01 감북교 종평면도.dwg"
@@ -7,7 +7,7 @@
{
"sample": 1,
"bridge": "산외교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플01_산외교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플01_산외교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample1)_π형교각_산외교\\02.종평면도(울산)_2017.0619-수정.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample1)_π형교각_산외교\\02.종평면도(울산)_2017.0619-수정.dwg"
@@ -41,7 +41,7 @@
{
"sample": 2,
"bridge": "운곡교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플02_운곡교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플02_운곡교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample2)_T형교각_운곡교\\C0050202-001-운곡교 종평면도(완도방향).dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample2)_T형교각_운곡교\\C0050202-001-운곡교 종평면도(완도방향).dwg"
@@ -75,7 +75,7 @@
{
"sample": 3,
"bridge": "대호지교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플03_대호지교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플03_대호지교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\03_original_pdf_soundings.dxf",
"candidates": [],
"output": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\output\\samples_20261102\\03_대호지교",
@@ -1007,7 +1007,7 @@
{
"sample": 4,
"bridge": "목동교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플04_목동교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플04_목동교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\04_@서울-세종 9공구 1000현황.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample4)_π형교각_목동교\\01 공통사항(목동교 종평면도).dwg"
@@ -1057,7 +1057,7 @@
{
"sample": 5,
"bridge": "승천천교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플05_승천천교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플05_승천천교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\05_project_topography_original.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample5)_π형교각_승천천교\\01 승천천교(DR) 종평면도.dwg"
@@ -1109,7 +1109,7 @@
{
"sample": 6,
"bridge": "계문철교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플06_계문철교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플06_계문철교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\06_서수평장1-현황+선형+노리-20200410.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample6)_π형교각_계문철교\\01 계문철교 종평면도.dwg"
@@ -1160,7 +1160,7 @@
{
"sample": 7,
"bridge": "기양천교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플07_기양천교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플07_기양천교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\pierzainer_ground\\data\\input\\samples_20261102\\recovered\\06_서수평장1-현황+선형+노리-20200410.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample7)_π형교각_기양천교\\01 기양천교종평면도.dwg"
@@ -1211,7 +1211,7 @@
{
"sample": 8,
"bridge": "옥성교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플08_옥성교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플08_옥성교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample8)_T형교각_옥성교\\01 옥성교 종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample8)_T형교각_옥성교\\01 옥성교 종평면도.dwg"
@@ -1245,7 +1245,7 @@
{
"sample": 9,
"bridge": "새재1교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플09_새재1교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플09_새재1교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample9)_T형교각_새재1교\\01 새재1교 종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample9)_T형교각_새재1교\\01 새재1교 종평면도.dwg"
@@ -1279,7 +1279,7 @@
{
"sample": 10,
"bridge": "도하4교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플10_도하4교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플10_도하4교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample10)_T형교각_도하4교\\01.종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample10)_T형교각_도하4교\\01.종평면도.dwg"
@@ -1313,7 +1313,7 @@
{
"sample": 11,
"bridge": "곡천교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플11_곡천교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플11_곡천교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample11)_π형교각_곡천교\\01 종평면도_곡천교.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample11)_π형교각_곡천교\\01 종평면도_곡천교.dwg"
@@ -1347,7 +1347,7 @@
{
"sample": 12,
"bridge": "감북교",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출_연동패키지_20261102\\samples\\샘플12_감북교_교각입력데이터.xlsx",
"workbook": "C:\\Users\\User\\Desktop\\Pier_v1-main\\Pier_v1\\도면추출\\samples\\샘플12_감북교_교각입력데이터.xlsx",
"drawing": "C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample12)_π형교각_감북교\\01 감북교 종평면도.dwg",
"candidates": [
"C:\\Users\\User\\Desktop\\도면추출\\엔지니어(sample12)_π형교각_감북교\\01 감북교 종평면도.dwg"
-12
View File
@@ -1,12 +0,0 @@
#!/usr/bin/env bash
# Headless Chrome screenshot of the sample page (WSL2 → Windows Chrome).
# Usage: scripts/capture-sample.sh <output.png> [url]
# Requires the dev server: npm run dev -- --host 127.0.0.1 --port 5173
set -euo pipefail
OUT_WSL="${1:?output png path (WSL path)}"
URL="${2:-http://127.0.0.1:5173/?sample=1}"
CHROME="/mnt/c/Program Files/Google/Chrome/Application/chrome.exe"
mkdir -p "$(dirname "$OUT_WSL")"
timeout 200 "$CHROME" --headless=new --no-sandbox --use-gl=angle --use-angle=swiftshader \
--enable-unsafe-swiftshader --enable-logging=stderr --v=0 --window-size=1600,1000 \
--timeout=45000 --screenshot="$(wslpath -w "$OUT_WSL")" "$URL"
-43
View File
@@ -1,43 +0,0 @@
// Clicks the template download button in the import dialog through Chrome DevTools
// and reports the saved file. Run with Windows Node (same machine as Chrome):
// node verify-template-download.cjs "C:\\Users\\...\\Temp\\verify-downloads"
const fs = require('fs');
const path = require('path');
const DL_DIR = process.argv[2];
const CDP = 'http://127.0.0.1:9444';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function main() {
fs.mkdirSync(DL_DIR, { recursive: true });
const page = (await (await fetch(`${CDP}/json`)).json()).find((t) => t.type === 'page');
const ws = new WebSocket(page.webSocketDebuggerUrl);
await new Promise((res, rej) => { ws.onopen = res; ws.onerror = rej; });
let seq = 0;
const waiting = new Map();
ws.onmessage = (ev) => {
const m = JSON.parse(ev.data);
if (m.id && waiting.has(m.id)) { waiting.get(m.id)(m); waiting.delete(m.id); }
};
const send = (method, params = {}) => new Promise((res) => {
const id = ++seq; waiting.set(id, res); ws.send(JSON.stringify({ id, method, params }));
});
const evalJs = async (expression) => {
const r = await send('Runtime.evaluate', { expression, awaitPromise: true, returnByValue: true });
if (r.result.exceptionDetails) throw new Error(JSON.stringify(r.result.exceptionDetails).slice(0, 300));
return r.result.result.value;
};
await send('Page.enable');
await send('Runtime.enable');
await send('Browser.setDownloadBehavior', { behavior: 'allow', downloadPath: DL_DIR, eventsEnabled: false });
await send('Page.navigate', { url: 'http://127.0.0.1:5173/' });
await sleep(4000);
await evalJs(`document.getElementById('open-import').click()`);
await sleep(500);
await evalJs(`document.getElementById('download-template').click()`);
await sleep(4000);
const files = fs.readdirSync(DL_DIR);
console.log('DOWNLOADED', JSON.stringify(files));
for (const f of files) console.log('SIZE', f, fs.statSync(path.join(DL_DIR, f)).size);
ws.close();
}
main().catch((e) => { console.error('FAILED', e); process.exit(1); });
-113
View File
@@ -1,113 +0,0 @@
// UI verification driven through the Chrome DevTools Protocol.
// Run with Windows Node (same machine as Chrome), e.g.:
// node verify-ui.cjs "C:\\Users\\...\\input.xlsx" "\\\\wsl.localhost\\Ubuntu\\...\\shots"
// Chrome must be started with --remote-debugging-port=9444 (see DOC/05_검증).
const fs = require('fs');
const path = require('path');
const APP_URL = process.env.APP_URL || 'http://127.0.0.1:5173/';
const CDP = process.env.CDP || 'http://127.0.0.1:9444';
const INPUT_FILE = process.argv[2];
const OUT_DIR = process.argv[3];
if (!INPUT_FILE || !OUT_DIR) throw new Error('INPUT_FILE and OUT_DIR are required');
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function main() {
const targets = await (await fetch(`${CDP}/json`)).json();
const page = targets.find((t) => t.type === 'page');
if (!page) throw new Error('no page target');
const ws = new WebSocket(page.webSocketDebuggerUrl);
await new Promise((res, rej) => {
ws.onopen = res;
ws.onerror = rej;
});
let seq = 0;
const waiting = new Map();
// Console and exception events, collected from the first navigation onward (V10 ③).
const consoleEvents = [];
ws.onmessage = (ev) => {
const m = JSON.parse(ev.data);
if (m.id && waiting.has(m.id)) {
waiting.get(m.id)(m);
waiting.delete(m.id);
} else if (m.method === 'Runtime.consoleAPICalled' && ['error', 'warning'].includes(m.params.type)) {
const text = m.params.args.map((a) => a.value ?? a.description ?? '').join(' ');
consoleEvents.push(`console.${m.params.type}: ${text}`);
} else if (m.method === 'Runtime.exceptionThrown') {
const d = m.params.exceptionDetails;
consoleEvents.push(`exception: ${d.exception?.description || d.text}`);
} else if (m.method === 'Log.entryAdded' && ['error', 'warning'].includes(m.params.entry.level)) {
consoleEvents.push(`log.${m.params.entry.level}: ${m.params.entry.text} ${m.params.entry.url || ''}`.trim());
}
};
const send = (method, params = {}) =>
new Promise((res) => {
const id = ++seq;
waiting.set(id, res);
ws.send(JSON.stringify({ id, method, params }));
});
const evalJs = async (expression) => {
const r = await send('Runtime.evaluate', { expression, awaitPromise: true, returnByValue: true });
if (r.result.exceptionDetails) throw new Error(JSON.stringify(r.result.exceptionDetails).slice(0, 400));
return r.result.result.value;
};
const shot = async (name) => {
const r = await send('Page.captureScreenshot', { format: 'png' });
fs.writeFileSync(path.join(OUT_DIR, name), Buffer.from(r.result.data, 'base64'));
console.log(`SHOT ${name}`);
};
await send('Page.enable');
await send('Runtime.enable');
await send('Log.enable');
await send('DOM.enable');
await send('Emulation.setDeviceMetricsOverride', { width: 1600, height: 1000, deviceScaleFactor: 1, mobile: false });
await send('Page.navigate', { url: APP_URL });
await sleep(4000);
// V11 — dialog with the user's three-sheet workbook.
await evalJs(`document.getElementById('open-import').click()`);
await sleep(500);
await evalJs(`(() => { const i = document.getElementById('project-name'); i.value = '운곡교 검증';
i.dispatchEvent(new Event('input')); return true; })()`);
const doc = await send('DOM.getDocument', { depth: -1 });
const q = await send('DOM.querySelector', { nodeId: doc.result.root.nodeId, selector: '#file' });
await send('DOM.setFileInputFiles', { nodeId: q.result.nodeId, files: [INPUT_FILE] });
await sleep(3000);
console.log('SHEETS_TEXT_BEGIN');
console.log(await evalJs(`document.getElementById('sheets').innerText`));
console.log('SHEETS_TEXT_END');
console.log('SELECTED', await evalJs(`document.getElementById('dlg-selected').textContent`));
console.log('APPLY', await evalJs(`(() => { const b = document.getElementById('dlg-apply');
return b.textContent + ' | disabled=' + b.disabled; })()`));
await shot('v11_dialog_user_file.png');
await evalJs(`document.getElementById('dlg-apply').click()`);
await sleep(2000);
console.log('STATUS', await evalJs(`document.getElementById('status').textContent`));
console.log('PIERS', await evalJs(`[...document.querySelectorAll('#pier-items li')].map(li => li.textContent.trim()).join(' | ')`));
console.log('RESULT_AXIS', await evalJs(`document.getElementById('res-axis').innerText.replace(/\\n+/g, ' / ')`));
console.log('RESULT_FOUND', await evalJs(`document.getElementById('res-found').innerText.replace(/\\n+/g, ' / ')`));
await shot('v11_main_user_file.png');
// V10 ② — the four view presets.
for (const label of ['정면', '측면', '평면', '아이소']) {
await evalJs(`[...document.querySelectorAll('#views button')].find((b) => b.textContent === '${label}').click()`);
await sleep(1500);
await shot(`v10_view_${label}.png`);
console.log('PRESSED', label, await evalJs(`[...document.querySelectorAll('#views button')].filter((b) => b.getAttribute('aria-pressed') === 'true').map((b) => b.textContent).join(',')`));
}
// V10 ③ — every console error/warning and uncaught exception seen during the run above.
console.log(`CONSOLE_EVENTS ${consoleEvents.length}`);
for (const e of consoleEvents) console.log(` ${e}`);
ws.close();
}
main().catch((e) => {
console.error('FAILED', e);
process.exit(1);
});
+1 -21
View File
@@ -3214,16 +3214,8 @@ function applyImport(): void {
setStatus(`${state.piers.length}기 형상을 생성했습니다. 정보·경고는 오른쪽 알림에서 확인하세요.`);
}
function openImport(): void {
const landing = document.querySelector<HTMLDivElement>('.landing-overlay');
if (landing && !landing.hidden) return;
const dlg = $<HTMLDialogElement>('import-dialog');
if (!dlg.open) dlg.showModal();
$<HTMLInputElement>('project-name').focus();
}
async function fetchSample(): Promise<Uint8Array> {
const res = await fetch(new URL('samples/운곡교P1입력.xlsx', document.baseURI));
const res = await fetch(new URL('../도면추출/samples/운곡교P1입력.xlsx', import.meta.url));
if (!res.ok) throw new Error(`샘플 응답 ${res.status}`);
return new Uint8Array(await res.arrayBuffer());
}
@@ -3232,7 +3224,6 @@ declare global {
interface Window {
loadBytesToApp?: (bytes: Uint8Array, fileName: string) => void;
openSampleProject?: () => Promise<void>;
openDrawingFlow?: () => void;
}
}
@@ -3254,16 +3245,6 @@ window.openSampleProject = async () => {
setError(error instanceof Error ? error.message : '샘플 교량 모델을 불러오지 못했습니다.');
}
};
window.openDrawingFlow = () => {
const app = document.querySelector<HTMLElement>('.app');
const landing = document.querySelector<HTMLDivElement>('.landing-overlay');
if (app) {
app.hidden = false;
app.setAttribute('aria-hidden', 'false');
}
if (landing) landing.hidden = true;
setStatus('도면 입력은 현재 개발 중입니다. 기존 도면 분석 모듈과 연결되기 전까지는 파일 선택 흐름만 유지합니다.');
};
/** Piers that need a (re)build: never generated, or with changes not yet in the 3D model. */
function generationTargets(): Pier[] {
@@ -3285,7 +3266,6 @@ function regenerateChanged(): void {
}
// --- events -----------------------------------------------------------------------------
$<HTMLButtonElement>('open-import').addEventListener('click', openImport);
$<HTMLButtonElement>('dlg-close').addEventListener('click', () => {
$<HTMLDialogElement>('import-dialog').close();
document.querySelector<HTMLElement>('.landing-overlay')?.classList.remove('modal-open');
+113
View File
@@ -0,0 +1,113 @@
import { EventEmitter } from 'node:events';
import { PassThrough, Readable } from 'node:stream';
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import type { ChildProcess } from 'node:child_process';
import type { IncomingMessage, ServerResponse } from 'node:http';
import path from 'node:path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { DRAWING_API_PATH, drawingLauncherPlugin } from '../랜딩페이지/drawing-launcher';
class Worker extends EventEmitter {
stdout = new PassThrough(); stderr = new PassThrough(); kill = vi.fn();
send(value: object) { this.stdout.write(JSON.stringify(value) + '\n'); }
}
const base = path.resolve('.verify');
const roots: string[] = [];
function fixture(options: { missing?: boolean; fail?: boolean; platform?: NodeJS.Platform } = {}) {
mkdirSync(base, { recursive: true });
const root = mkdtempSync(path.join(base, 'drawing-api-')); roots.push(root);
const folder = path.join(root, 'DWG & 도면'); mkdirSync(folder);
const workers: Worker[] = [];
const start = vi.fn((_file: string, args: string[]) => {
const worker = new Worker(); workers.push(worker);
queueMicrotask(() => {
if (options.fail) worker.emit('error', new Error('worker unavailable'));
else if (args[2] === 'choose') { worker.send({ type: 'folder', folder }); worker.emit('close', 0); }
});
return worker as unknown as ChildProcess;
});
const plugin = drawingLauncherPlugin(root, {
platform: options.platform ?? 'win32', start,
exists: file => options.missing ? false : file.endsWith('python.exe') || file.endsWith('drawing-worker.py') || existsSync(file),
});
let handle!: (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
(plugin.configureServer as Function)({ middlewares: { use(fn: typeof handle) { handle = fn; } } });
const request = (action: string, body: object = {}, overrides: { method?: string; origin?: string; action?: string; address?: string; host?: string; raw?: string } = {}) => new Promise<{ status: number; body: any; bytes?: Buffer }>(resolve => {
const req = Readable.from([overrides.raw ?? JSON.stringify(body)]) as unknown as IncomingMessage;
Object.assign(req, { url: `${DRAWING_API_PATH}/${action}`, method: overrides.method ?? 'POST', headers: {
host: overrides.host ?? '127.0.0.1:5173', origin: overrides.origin ?? 'http://127.0.0.1:5173', 'x-pierzainer-action': overrides.action ?? 'drawing-extractor',
}, socket: { remoteAddress: overrides.address ?? '127.0.0.1' } });
let status = 0, json = true, sent = false;
const res = { get headersSent() { return sent; }, writeHead(code: number, headers: Record<string, string>) { status = code; json = headers['Content-Type'].includes('application/json'); sent = true; },
end(text: string | Buffer) { resolve(json ? { status, body: JSON.parse(String(text)) } : { status, body: null, bytes: Buffer.from(text) }); } } as unknown as ServerResponse;
handle(req, res, () => resolve({ status: 404, body: {} }));
});
const finish = (outside = false) => {
const args = start.mock.calls[start.mock.calls.length - 1][1];
const output = args[args.indexOf('--output')+1];
const file = path.join(outside ? root : output, '결과.xlsx'); writeFileSync(file, 'test-workbook');
const worker = workers[workers.length-1]; worker.send({ type: 'log', message: '도면 판독 완료' });
worker.send({ type: 'result', result: { status: 'needs_review', output_dir: output, review_count: 2, failed_file_count: 0,
workbooks: [{ name: '테스트 교량', xlsx_path: file, review_count: 2 }] } });
worker.emit('close', 0);
return file;
};
return { root, folder, request, start, workers, finish };
}
afterEach(() => {
for (const root of roots.splice(0)) {
if (!root.startsWith(base + path.sep)) throw new Error('Unsafe test fixture path');
rmSync(root, { recursive: true, force: true });
}
});
describe('in-app drawing extraction API', () => {
it('only accepts local same-origin POST requests with the application header', async () => {
const f = fixture();
expect((await f.request('start', {}, { method: 'GET' })).status).toBe(405);
for (const headers of [{ origin: 'https://other.example' }, { action: '' }, { address: '192.168.0.2' }, { host: 'other.example' }])
expect((await f.request('start', {}, headers)).status).toBe(403);
expect(f.start).not.toHaveBeenCalled();
});
it('returns folder selection without starting the standalone GUI', async () => {
const f = fixture(); expect((await f.request('choose-folder')).body.folder).toBe(f.folder);
expect(f.start.mock.calls[0][1][2]).toBe('choose');
expect(f.start.mock.calls[0][1].join(' ')).not.toContain('desktop_app.py');
expect((await f.request('status')).body.job).toBeNull();
});
it('tracks progress, preserves review warnings, and serves only its generated workbook', async () => {
const f = fixture(); const response = await f.request('start', { folder: f.folder });
expect(response.status).toBe(202); const jobId = response.body.job.id;
expect(f.start.mock.calls[0][1][3]).toBe(f.folder);
expect((await f.request('workbook', { jobId, index: 0 })).status).toBe(400);
const file = f.finish();
const state = (await f.request('status', { jobId })).body.job;
expect(state.status).toBe('needs_review'); expect(state.logs).toContain('도면 판독 완료');
expect(state.result.reviewCount).toBe(2);
const download = await f.request('workbook', { jobId, index: 0, path: 'unrelated.txt' });
expect(download.status).toBe(200); expect(download.bytes).toEqual(readFileSync(file));
expect((await f.request('workbook', { jobId, index: -1 })).status).toBe(400);
expect((await f.request('workbook', { jobId, index: 20 })).status).toBe(404);
});
it('does not run two extraction jobs concurrently', async () => {
const f = fixture(); const states = await Promise.all([f.request('start', { folder: f.folder }), f.request('start', { folder: f.folder })]);
expect(states.map(s => s.status).sort()).toEqual([202, 409]); expect(f.start).toHaveBeenCalledTimes(1); f.finish();
});
it('refuses files outside the controlled output even if a worker lists them', async () => {
const f = fixture(); const { body } = await f.request('start', { folder: f.folder }); f.finish(true);
expect((await f.request('workbook', { jobId: body.job.id, index: 0 })).status).toBe(403);
});
it('reports worker failures so the popup can retry', async () => {
const f = fixture({ fail: true }); const { body } = await f.request('start', { folder: f.folder });
const job = (await f.request('status', { jobId: body.job.id })).body.job;
expect(job.status).toBe('error'); expect(job.error).toContain('worker unavailable');
});
it('validates folders, JSON, and request size before starting a worker', async () => {
const f = fixture();
expect((await f.request('start', { folder: 'missing-folder' })).status).toBe(400);
expect((await f.request('start', {}, { raw: 'bad-json' })).status).toBe(400);
expect((await f.request('start', {}, { raw: 'a'.repeat(8193) })).status).toBe(413);
expect(f.start).not.toHaveBeenCalled();
expect((await fixture({ missing: true }).request('status')).status).toBe(503);
expect((await fixture({ platform: 'linux' }).request('status')).status).toBe(501);
});
});
+2 -2
View File
@@ -5,9 +5,9 @@ import { parseWorkbook } from '../src/core/parseWorkbook';
import type { PierInput } from '../src/core/types';
/** Spec workbook (RAG/Pier ui_xxx.xlsx, sheet intent.PR, all tables on one sheet). Used by single-sheet tests. */
const SPEC_PATH = fileURLToPath(new URL('../public/samples/P1.xlsx', import.meta.url));
const SPEC_PATH = fileURLToPath(new URL('../도면추출/samples/P1.xlsx', import.meta.url));
/** User input (RAG/운곡교P1입력.xlsx, three sheets 교각 · IP · 받침). This is the P1 input used by the app and tests. */
const INPUT_PATH = fileURLToPath(new URL('../public/samples/운곡교P1입력.xlsx', import.meta.url));
const INPUT_PATH = fileURLToPath(new URL('../도면추출/samples/운곡교P1입력.xlsx', import.meta.url));
export function loadWorkbook(path: string = SPEC_PATH): XLSX.WorkBook {
return XLSX.read(readFileSync(path), { type: 'buffer' });
-108
View File
@@ -1,108 +0,0 @@
import { describe, expect, it } from 'vitest';
import { buildPier } from '../src/core/geometry';
import { loadP1 } from './helpers';
/**
* Regression against spec.md §12 (P1, 운곡교, T형, 말뚝기초).
* Coordinates and EL: ±1 mm (0.001 m). Angles: ±0.01°.
*/
const TOL = 0.001;
const model = buildPier(loadP1());
describe('P1 regression — spec.md §12', () => {
it('axis length and azimuth', () => {
expect(model.lengthIp).toBeCloseTo(12.166, 3);
expect(model.azimuthDeg).toBeCloseTo(350.37, 2);
});
it('BP1, BP2 coordinates (국토좌표 E, N)', () => {
expect(model.bp1.e).toBeCloseTo(259866.681, 2);
expect(model.bp1.n).toBeCloseTo(179132.173, 2);
expect(Math.abs(model.bp2.e - 259864.674)).toBeLessThan(TOL);
expect(Math.abs(model.bp2.n - 179144.004)).toBeLessThan(TOL);
});
it('IP2 reference recalculation and d_IP2', () => {
expect(Math.abs(model.dIp2 - 1.134)).toBeLessThan(TOL);
});
it('column centre coordinates', () => {
expect(Math.abs(model.pivot.e - 259865.677)).toBeLessThan(TOL);
expect(Math.abs(model.pivot.n - 179138.089)).toBeLessThan(TOL);
});
it('bearing positions: front row across-axis −0.698, back row +0.600; front s from 0.943 to 10.965', () => {
const front = model.bearings.filter((b) => b.row === '전열');
const back = model.bearings.filter((b) => b.row === '후열');
expect(front[0].n).toBeCloseTo(-0.698, 3);
expect(back[0].n).toBeCloseTo(0.6, 3);
expect(front[0].s).toBeCloseTo(0.943, 3);
expect(front[4].s).toBeCloseTo(10.965, 3);
expect(back[0].s).toBeCloseTo(1.029, 3);
expect(back[4].s).toBeCloseTo(11.05, 3);
});
it('step boundaries at the midpoints of front bearings: 2.196 / 4.701 / 7.207 / 9.712', () => {
const bounds = model.segments.slice(1).map((s) => s.s0);
[2.196, 4.701, 7.207, 9.712].forEach((b, i) => expect(Math.abs(bounds[i] - b)).toBeLessThan(TOL));
});
it('front segment tops: 43.411 / 43.483 / 43.555 / 43.626 / 43.698', () => {
const tops = model.segments.map((s) => s.top);
[43.411, 43.483, 43.555, 43.626, 43.698].forEach((t, i) => expect(Math.abs(tops[i] - t)).toBeLessThan(TOL));
});
it('bearing block heights: h1 = 150, h2 = 200 mm; concrete 100 / 150 mm', () => {
expect(model.h1).toBeCloseTo(0.15, 6);
expect(model.h2).toBeCloseTo(0.2, 6);
expect(model.derived.blockHeightFront).toBeCloseTo(0.1, 6);
expect(model.derived.blockHeightBack).toBeCloseTo(0.15, 6);
});
it('no lateral step (front and back tops agree)', () => {
expect(model.derived.frontBackDiffSpread).toBeLessThan(0.0051);
});
it('coping bottom: 42.411 at the ends, 40.911 at the Level zone', () => {
expect(Math.abs(model.endBottom - 42.411)).toBeLessThan(TOL);
expect(Math.abs(model.levelBottom - 40.911)).toBeLessThan(TOL);
});
it('column height 12.703 m and footing top 28.208 m, soil cover 0.723 m', () => {
expect(Math.abs(model.derived.columnHeight - 12.703)).toBeLessThan(TOL);
expect(Math.abs(model.footings[0].top - 28.208)).toBeLessThan(TOL);
expect(Math.abs(model.derived.soilCover - 0.723)).toBeLessThan(TOL);
});
it('piles: 4 rows × 5 = 20, spacing 2250 mm, tip at 2.208 m', () => {
expect(model.piles).toHaveLength(20);
expect(model.derived.pileCount).toBe(20);
expect(Math.abs((model.derived.pileTipEl ?? NaN) - 2.208)).toBeLessThan(TOL);
const sValues = [...new Set(model.piles.map((p) => p.s.toFixed(4)))];
const nValues = [...new Set(model.piles.map((p) => p.n.toFixed(4)))];
expect(sValues).toHaveLength(5);
expect(nValues).toHaveLength(4);
expect(Number(nValues[1]) - Number(nValues[0])).toBeCloseTo(2.25, 4);
expect(Number(sValues[1]) - Number(sValues[0])).toBeCloseTo(2.25, 4);
});
it('blinding concrete 10600 × 8350 × 100 under the footing', () => {
const b = model.blindings[0];
expect(b.length).toBeCloseTo(10.6, 4);
expect(b.width).toBeCloseTo(8.35, 4);
expect(b.thickness).toBeCloseTo(0.1, 4);
});
it('footing 10400 × 8150 × 2000 mm', () => {
const f = model.footings[0];
expect(f.length).toBeCloseTo(10.4, 4);
expect(f.width).toBeCloseTo(8.15, 4);
expect(f.top - f.bottom).toBeCloseTo(2.0, 4);
});
});
describe('pile layout text (spec §12, §6.8 display)', () => {
it('reads 4@2250 × 3@2250 = 20본', () => {
expect(model.derived.pileLayout).toBe('4@2250 × 3@2250 = 20본');
});
});
+2 -2
View File
@@ -4,9 +4,9 @@ import { fileURLToPath } from 'node:url';
import { parseWorkbook } from '../src/core/parseWorkbook';
import { loadWorkbook } from './helpers';
// The standing input is RAG/운곡교P1입력.xlsx. The app serves a copy in public/samples; the two must stay identical.
// The standing input is RAG/운곡교P1입력.xlsx. The app uses the copy in 도면추출/samples.
const RAG_INPUT = fileURLToPath(new URL('../../../RAG/운곡교P1입력.xlsx', import.meta.url));
const APP_COPY = fileURLToPath(new URL('../public/samples/운곡교P1입력.xlsx', import.meta.url));
const APP_COPY = fileURLToPath(new URL('../도면추출/samples/운곡교P1입력.xlsx', import.meta.url));
describe('표준 입력 파일 (운곡교P1입력.xlsx)', () => {
it('the app sample is byte-identical to RAG/운곡교P1입력.xlsx', () => {
+2 -1
View File
@@ -6,9 +6,10 @@
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"strict": true,
"noEmit": true,
"allowImportingTsExtensions": true,
"skipLibCheck": true,
"isolatedModules": true,
"types": ["node"]
},
"include": ["src", "tests"]
"include": ["src", "tests", "랜딩페이지", "vite.config.ts"]
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

+7
View File
@@ -0,0 +1,7 @@
import { fileURLToPath } from 'node:url';
import { defineConfig } from 'vite';
import { drawingLauncherPlugin } from './랜딩페이지/drawing-launcher.ts';
export default defineConfig({
plugins: [drawingLauncherPlugin(fileURLToPath(new URL('.', import.meta.url)))],
});
@@ -7,11 +7,11 @@ Windows용 오프라인 DWG → 교각 입력 엑셀 추출기입니다. 이 폴
|실행.bat|폴더 선택 화면 실행|
|run_extract.bat|다른 프로그램에서 호출할 명령행 진입점|
|app|프로그램 소스, DWG 변환기, Python 및 엑셀 라이브러리|
|samples|교량별 최종 엑셀 12개. 각 파일에 Pier/IP/Bearing 탭 3개|
|samples|교량별 최종 엑셀 12개와 기본 샘플 `운곡교P1입력.xlsx`, 단일 시트 검증 자료 `P1.xlsx`|
|integration/bridge_extract.py|Python 연동 함수 및 JSON 출력 CLI|
|integration/*.whl|Python 설치형 패키지 0.1.9|
|integration/sample_data|최종 샘플과 대응하는 JSON·검토 CSV|
|history|샘플 보정 내역, 이전 검증 결과, 재현 스크립트와 개발 안내|
|history|샘플 보정 내역, 이전 검증 결과와 출처 기록|
## 화면에서 실행
@@ -31,7 +31,7 @@ Python 프로그램에 직접 연결할 수도 있습니다.
```python
import sys
sys.path.insert(0, r"C:\배포\도면추출_연동패키지_20261102")
sys.path.insert(0, r"C:\배포\도면추출")
from integration.bridge_extract import extract_folder
result = extract_folder(r"C:\도면\교량폴더", output_dir=r"C:\결과")
@@ -58,6 +58,6 @@ for book in result.workbooks:
64교각·652받침, IP4공란·치수산식·엑셀/JSON6484셀 대조를 확인했습니다. 승천천교 방위각12개는 원문과 샘플에 값이 없어 미확인입니다. 기양천교 상충EL 등 이번에 샘플값을 채택한 항목은 history의 보정내역을 확인합니다.
새로운 DWG에 대해서는 미확정 또는 검토필요 값이 나올 수 있습니다. 이미지 PDF OCR, 임의의 기둥 형상 자동 판독, 복잡한 지시선 및 모든 측점 보간은 완성되지 않았습니다. 셀 상태와 근거를 함께 확인합니다. `history/tools`와 `history/tests`는 기존 작업폴더 기준의 이력용 코드이며 실행 진입점은 위의 두 가지입니다.
새로운 DWG에 대해서는 미확정 또는 검토필요 값이 나올 수 있습니다. 이미지 PDF OCR, 임의의 기둥 형상 자동 판독, 복잡한 지시선 및 모든 측점 보간은 완성되지 않았습니다. 셀 상태와 근거를 함께 확인합니다. 과거 작업폴더에 의존하던 개발·검증 스크립트는 정리했으며 실행 진입점은 위의 두 가지입니다.
포함된 Python·openpyxl·LibreDWG 등 제3자 구성요소의 라이선스 및 안내 파일은 app/runtime 및 app/Code/giyang_extract/libredwg 하위에 보존했습니다.
Loaded 100 of 2476 files, more files were not shown because too many files have changed in this diff. Show more