분석이 30초 걸리는데 B05는 일반 사용자 화면이다. 관리자 확인용 B04에서
한 번 돌려 저장하고, B05는 그 결과를 읽어 관 보충과 세부유역만 처리한다
(2026-07-31 사용자 지시).
노선 원천 변경
- B05 확정 경로 -> B03 업로드 계획 노선 파일(CSV). 분석이 노선 설계보다
먼저 끝나 있어야 하기 때문. 샘플 planned_route_sample_epsg5187.csv 로 검증.
- common_util_route_geometry.py 신설 — RouteVertex/StructureCandidate/누가거리
보간/세류 교차점/계획 노선 CSV 리더. B04와 B05가 같은 표현을 쓰도록 공용화.
열 이름은 대소문자·한글 표기를 함께 받는다(B03이 여러 형식 수용 예정).
B04 (관리자 확인용, 신규)
- Engine_Watershed_{Grid,Stream,Descent,Flow,Expand,Export} — B05에서 git mv
- Engine_Watershed_Analyze.py — 1~8단계 오케스트레이션
- Router_Watershed.py — GET /drainage/primary-region
- UI_Watershed.ts — 2D 지도 GIS 레이어 그룹에 "배수유역" 토글 추가.
격자/화살표/세류망/1차영역/2차유역/기본관을 겹쳐 그린다.
- 저장 위치 B05_wf2_Route/drainage -> B04_wf1_Surface/drainage
- 03_road_routing 단계 추가: B05가 세부유역을 나눌 최소 배열(셀->도로셀 귀속,
유하장, 강도, 도로셀 제원, 셀 표고) + 계획도로선/기본배관/2차유역 기하
B05 (일반 사용자용, 축소)
- Engine_Drainage_Basin.py — B04 산출물 로더 + 관 보충(9) + 측구 라우팅/세부유역(10,11)
- Engine_Drainage.py 는 관경 산정만 남기고 322 -> 27줄
- Router_Drainage.py 509 -> 142줄. POST /drainage/basins 만 남김
- 화살표·격자·강도 띠 렌더 제거. 계획도로선/기본배관/2차유역만 받는다
삭제
- _legacy_watershed/ 4파일 (능선 행진 방식 원본 보관본)
- Engine_Watershed_Basin.py (B04 Analyze + B05 Drainage_Basin 으로 분할)
- GET /drainage/candidates 와 propose_structure_stations (구방식 후보 제안)
E2E 검증 (실데이터)
B04 분석 28.2s -> 저장(geojson 11KB + npz 2.6MB)
B05 로드 + 세부 설계 0.11s <-- 30초가 0.1초로
면적 457,404m2 로 B04 2차 유역과 정확히 일치
관 편집 재산정 0.12s, 관 3개 -> 세부유역 3개, 면적 보존
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
397 lines
9.9 KiB
CSS
397 lines
9.9 KiB
CSS
.b03-file {
|
|
min-height: calc(100vh - var(--app-header-height, 0px));
|
|
background: linear-gradient(
|
|
180deg,
|
|
var(--color-canvas, #ffffff) 0%,
|
|
var(--color-mist-violet, #edecff) 100%
|
|
);
|
|
padding: 0 var(--spacing-24) var(--spacing-48) var(--spacing-24); /* 상단 여백은 main-layout 마진으로 조정하므로 padding-top은 0 */
|
|
}
|
|
|
|
.b03-file__main-layout {
|
|
max-width: var(--page-max-width, 1200px);
|
|
margin: var(--spacing-40) auto 0 auto; /* 상단 구분선(헤더)과의 간격을 확실하게 40px 벌림 */
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-32);
|
|
}
|
|
|
|
.b03-file__control-panel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-20);
|
|
background: var(--color-canvas, #ffffff);
|
|
padding: var(--spacing-32);
|
|
border-radius: var(--radius-large, 24px); /* Wiza 24px 대형 둥글기 */
|
|
border: 1px solid var(--color-mist, #e6e2e3);
|
|
box-shadow: var(--shadow-lg);
|
|
}
|
|
|
|
.b03-file__cards-container-panel {
|
|
margin-top: 0; /* 단일 layout gap에 의해 제어되도록 설정 */
|
|
}
|
|
|
|
.b03-file__subtitle {
|
|
font-size: var(--text-body-sm, 14px);
|
|
color: var(--color-slate, #615e6e);
|
|
margin: 0 0 var(--spacing-8) 0;
|
|
}
|
|
|
|
.b03-file__native-input,
|
|
.b03-file__slot-input {
|
|
display: none;
|
|
}
|
|
|
|
/* Wiza 스타일 가이드의 Lavender Glow 그라데이션 및 soft purple border hover */
|
|
.b03-file__dropzone {
|
|
min-height: 160px;
|
|
padding: var(--spacing-32) var(--spacing-24);
|
|
border: 2px dashed var(--color-mist, #e6e2e3);
|
|
border-radius: var(--radius-large, 24px);
|
|
background: var(--color-paper, #f6f7fa);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: var(--spacing-8);
|
|
cursor: pointer;
|
|
text-align: center;
|
|
transition:
|
|
background var(--transition-base, 0.2s),
|
|
border-color var(--transition-base, 0.2s),
|
|
box-shadow var(--transition-base, 0.2s);
|
|
}
|
|
|
|
.b03-file__dropzone:hover {
|
|
background: var(--color-mist-violet, #edecff);
|
|
border-color: var(--color-royal-amethyst, #3e0079);
|
|
}
|
|
|
|
.b03-file__dropzone:focus-visible,
|
|
.b03-file__dropzone.is-dragging {
|
|
outline: none;
|
|
background: var(--color-mist-violet, #edecff);
|
|
border-color: var(--color-royal-amethyst, #3e0079);
|
|
box-shadow: 0 0 0 4px rgba(62, 0, 121, 0.15);
|
|
}
|
|
|
|
.b03-file__dropzone strong {
|
|
color: var(--color-deep-iris, #26114a);
|
|
font-size: var(--text-body, 16px);
|
|
font-weight: var(--font-weight-medium, 500);
|
|
}
|
|
|
|
.b03-file__dropzone span {
|
|
font-size: var(--text-body-sm, 14px);
|
|
color: var(--color-slate, #615e6e);
|
|
}
|
|
|
|
.b03-file__error {
|
|
min-height: var(--spacing-16);
|
|
color: var(--color-danger, #dc2626);
|
|
font-size: var(--text-body-sm, 14px);
|
|
margin: var(--spacing-8) 0;
|
|
}
|
|
|
|
.b03-file__resume {
|
|
display: none;
|
|
align-items: center;
|
|
gap: var(--spacing-8);
|
|
flex-wrap: wrap;
|
|
padding: var(--spacing-12) var(--spacing-16);
|
|
border: 1px solid var(--color-mist, #e6e2e3);
|
|
border-radius: var(--radius-cards, 8px);
|
|
background: var(--color-paper, #f6f7fa);
|
|
color: var(--color-deep-iris, #26114a);
|
|
font-size: var(--text-body-sm, 14px);
|
|
margin-top: var(--spacing-8);
|
|
}
|
|
|
|
.b03-file__resume.is-visible {
|
|
display: flex;
|
|
}
|
|
|
|
.b03-file__cards-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
gap: var(--spacing-32);
|
|
}
|
|
|
|
.b03-file__group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-20);
|
|
}
|
|
|
|
.b03-file__group-title {
|
|
font-size: var(--text-subheading, 24px);
|
|
color: var(--color-deep-iris, #26114a);
|
|
font-family: var(--font-britti-sans, inherit);
|
|
margin: 0 0 var(--spacing-8) 0;
|
|
}
|
|
|
|
.b03-file__group-content {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr); /* 2행 2열 구조 */
|
|
gap: var(--spacing-24);
|
|
}
|
|
|
|
.b03-file__group--route .b03-file__group-content {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.b03-file__group--route .b03-file__card {
|
|
border-color: var(--color-royal-amethyst, #3e0079);
|
|
background: var(--color-mist-violet, #edecff);
|
|
}
|
|
|
|
/* Wiza 8px radius 카드 */
|
|
.b03-file__card {
|
|
min-height: 220px;
|
|
padding: var(--spacing-24); /* 내부 전체 패딩 확대 */
|
|
border: 1px solid var(--color-mist, #e6e2e3);
|
|
border-radius: var(--radius-cards, 8px);
|
|
background: var(--color-canvas, #ffffff);
|
|
box-shadow: var(--shadow-sm);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-20);
|
|
transition:
|
|
transform var(--transition-base, 0.2s),
|
|
border-color var(--transition-base, 0.2s),
|
|
box-shadow var(--transition-base, 0.2s);
|
|
}
|
|
|
|
.b03-file__card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: var(--shadow-lg);
|
|
border-color: var(--color-royal-amethyst, #3e0079);
|
|
}
|
|
|
|
.b03-file__card--selected {
|
|
border-color: var(--color-royal-amethyst, #3e0079);
|
|
background: var(--color-paper, #f6f7fa);
|
|
}
|
|
|
|
.b03-file__card--uploading {
|
|
border-color: var(--color-royal-amethyst, #3e0079);
|
|
box-shadow: var(--shadow-lg-2);
|
|
}
|
|
|
|
.b03-file__card--completed {
|
|
border-color: #10b981; /* Success Green */
|
|
}
|
|
|
|
.b03-file__card--error {
|
|
border-color: var(--color-danger, #dc2626);
|
|
}
|
|
|
|
.b03-file__card-header {
|
|
display: grid;
|
|
grid-template-columns: auto 1fr auto auto; /* 아이콘 및 닫기 버튼 주변 확보 */
|
|
gap: var(--spacing-12);
|
|
align-items: center;
|
|
}
|
|
|
|
.b03-file__card-icon {
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: var(--radius-icons, 8px);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: var(--color-royal-amethyst, #3e0079);
|
|
background: var(--color-mist-violet, #edecff);
|
|
font-size: var(--text-body-sm, 14px);
|
|
margin-right: var(--spacing-8); /* 아이콘 우측 마진 추가 (아이콘 좌측 여유 확대 효과) */
|
|
}
|
|
|
|
.b03-file__card-heading {
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
margin-top: 2px; /* 제목 상단 여유 추가 */
|
|
}
|
|
|
|
.b03-file__card-label {
|
|
color: var(--color-deep-iris, #26114a);
|
|
font-size: var(--text-body-sm, 14px);
|
|
font-weight: var(--font-weight-medium, 500);
|
|
}
|
|
|
|
.b03-file__card-ext,
|
|
.b03-file__file-size,
|
|
.b03-file__progress-info {
|
|
color: var(--color-slate, #615e6e);
|
|
font-size: var(--text-caption, 12px);
|
|
}
|
|
|
|
.b03-file__card-badge-container {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-right: var(--spacing-4);
|
|
}
|
|
|
|
.b03-file__card-remove {
|
|
width: 24px;
|
|
height: 24px;
|
|
border: 1px solid var(--color-mist, #e6e2e3);
|
|
border-radius: var(--radius-buttons, 8px);
|
|
color: var(--color-slate, #615e6e);
|
|
background: var(--color-canvas, #ffffff);
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: var(--text-body-sm, 14px);
|
|
line-height: 1;
|
|
padding: 0;
|
|
margin-left: var(--spacing-8); /* 취소 버튼 좌측 여유 추가 (취소 버튼 우측 여유 확보) */
|
|
transition: all var(--transition-base, 0.2s);
|
|
}
|
|
|
|
.b03-file__card-remove:hover {
|
|
color: var(--color-danger, #dc2626);
|
|
border-color: var(--color-danger, #dc2626);
|
|
background: #fef2f2;
|
|
}
|
|
|
|
.b03-file__card-content {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-end;
|
|
gap: var(--spacing-16); /* 버튼 하단 및 내부 간격 확대 */
|
|
padding-bottom: var(--spacing-8); /* 파일 선택 버튼 하단 패딩 확보 */
|
|
}
|
|
|
|
.b03-file__card-select {
|
|
width: 100%;
|
|
min-height: 40px; /* 버튼 높이 증가 */
|
|
border: 1px solid var(--color-mist, #e6e2e3);
|
|
border-radius: var(--radius-buttons, 8px);
|
|
color: var(--color-deep-iris, #26114a);
|
|
background: var(--color-canvas, #ffffff);
|
|
font-weight: var(--font-weight-medium, 500);
|
|
font-size: var(--text-body-sm, 14px);
|
|
cursor: pointer;
|
|
margin-bottom: var(--spacing-8); /* 파일 선택 버튼 하단 마진 추가 */
|
|
transition:
|
|
background var(--transition-base, 0.2s),
|
|
border-color var(--transition-base, 0.2s);
|
|
}
|
|
|
|
.b03-file__file-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.b03-file__file-name {
|
|
color: var(--color-deep-iris, #26114a);
|
|
font-size: var(--text-body-sm, 14px);
|
|
font-weight: var(--font-weight-medium, 500);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.b03-file__progress-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-8);
|
|
}
|
|
|
|
.b03-file__progress-bar-container {
|
|
width: 100%;
|
|
height: 6px;
|
|
overflow: hidden;
|
|
border-radius: var(--radius-pills, 1440px);
|
|
background: var(--color-mist, #e6e2e3);
|
|
}
|
|
|
|
.b03-file__progress-bar {
|
|
width: 0;
|
|
height: 100%;
|
|
border-radius: var(--radius-pills, 1440px);
|
|
background: var(--color-royal-amethyst, #3e0079);
|
|
transition: width var(--transition-base, 0.2s);
|
|
}
|
|
|
|
.b03-file__progress-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
}
|
|
|
|
.b03-file__error-message {
|
|
display: none;
|
|
padding: var(--spacing-8) var(--spacing-12);
|
|
border-radius: var(--radius-cards, 8px);
|
|
color: var(--color-danger, #dc2626);
|
|
background: #fef2f2;
|
|
font-size: var(--text-caption, 12px);
|
|
border: 1px solid rgba(220, 38, 38, 0.15);
|
|
}
|
|
|
|
.b03-file__card--empty .b03-file__file-info,
|
|
.b03-file__card--empty .b03-file__progress-section,
|
|
.b03-file__card--empty .b03-file__error-message {
|
|
display: none;
|
|
}
|
|
|
|
.b03-file__card--selected .b03-file__progress-section {
|
|
display: none;
|
|
}
|
|
|
|
.b03-file__card--uploading .b03-file__card-select,
|
|
.b03-file__card--completed .b03-file__card-select {
|
|
display: none;
|
|
}
|
|
|
|
.b03-file__card--error .b03-file__error-message {
|
|
display: block;
|
|
}
|
|
|
|
.b03-file__card--error .b03-file__progress-section {
|
|
display: none;
|
|
}
|
|
|
|
.b03-file__results {
|
|
margin: var(--spacing-8) 0 0 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
border: 1px solid var(--color-mist, #e6e2e3);
|
|
border-radius: var(--radius-cards, 8px);
|
|
overflow: hidden;
|
|
background: var(--color-canvas, #ffffff);
|
|
}
|
|
|
|
.b03-file__results:empty {
|
|
display: none;
|
|
}
|
|
|
|
.b03-file__results li {
|
|
padding: var(--spacing-12) var(--spacing-16);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
border-bottom: 1px solid var(--color-mist, #e6e2e3);
|
|
color: var(--color-deep-iris, #26114a);
|
|
font-size: var(--text-body-sm, 14px);
|
|
}
|
|
|
|
.b03-file__results li:last-child {
|
|
border-bottom: 0;
|
|
}
|
|
|
|
@media (max-width: 720px) {
|
|
.b03-file {
|
|
padding: var(--spacing-16) var(--spacing-12);
|
|
}
|
|
|
|
.b03-file__group-content {
|
|
grid-template-columns: 1fr; /* 모바일에서는 1행 1열 구조 */
|
|
}
|
|
}
|