판정 기준은 성토측 자연 지반 경사(사용자 확정). 지반이 임계보다 가파르면 부어 놓은 흙이 쌓이지 않고 스스로 흘러내리므로 운반비를 세지 않는다. 계상 방식은 참고 도면을 근거로 정했다. 도면의 사토 balloon은 L=(운반거리) 없이 M.N=측점만 적는다 - 그 노선의 사토가 이미 운반비 없이 처리되고 있다는 뜻이고, 자연방토라는 이름을 안 썼을 뿐 처리 방식이 자연방토다. 그래서 새 항목을 만들지 않고 사토 안에서 가른다. 판정 통과분은 도면과 똑같이 M.N만, 실패분에만 "사토장=X㎥"를 더한다. 수량은 그대로 두고 성토로 흡수하지 않아 유토곡선 모양과 누가토량은 안 바뀐다. - config NATURAL_SPOIL_MIN_GROUND_SLOPE = 1/1.5 (rise/run, 약 33.7도) 신설. 유일한 정의처이며 프론트는 context로 받아 쓴다. 못 받으면 자연방토 없음(보수적) - 엔진 fill_ground_slope(): 노면 끝~성토 사면의 첫 지반 교차까지 평균 경사. 양쪽이 다 성토면 완만한 쪽을 택한다(보수적) - AreaSample/MassHaulPoint에 natural_spoil - 양 끝 측점이 둘 다 가능할 때만 참 - HaulResidual.natural_m3 - 잔량 구간에서 자연방토 가능한 길이 비율만큼 계상. 장거리 상쇄로 깎이면 안분·자연방토도 같은 비율로 감소 - HaulPlan.natural_spoil_m3 + 요약 칩 700줄 초과 2건 분리: _Engine_Areas.py(면적 적분), _UI_MassHaul_Settle.ts(안분·장비·상쇄· 직렬화). 의존은 한 방향(Balance -> Settle 함수, Settle -> Balance 타입만)이라 순환 없음. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
174 lines
7.0 KiB
Python
174 lines
7.0 KiB
Python
"""B06 종횡단 조회·확정 응답 검증 모델."""
|
|
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class SectionRegenerateRequest(BaseModel):
|
|
"""표시 옵션의 횡단 반폭 변경에 따른 종횡단 재생성 요청."""
|
|
|
|
cross_half_width_m: float = Field(..., gt=0)
|
|
|
|
|
|
class CompanyStandardProject(BaseModel):
|
|
"""같은 회사의 표준횡단 설정값 보유 프로젝트 항목(조회용)."""
|
|
|
|
project_id: str
|
|
name: str
|
|
|
|
|
|
class CompanyStandardListResponse(BaseModel):
|
|
"""같은 회사에서 설계값을 불러올 수 있는 프로젝트 목록."""
|
|
|
|
status: str = "success"
|
|
projects: list[CompanyStandardProject]
|
|
|
|
|
|
class CompanyStandardResponse(BaseModel):
|
|
"""특정 프로젝트의 표준횡단 설정값(미리보기·적용용, 회사 스코프)."""
|
|
|
|
status: str = "success"
|
|
project_id: str
|
|
standard_cross_section: dict[str, Any]
|
|
|
|
|
|
class CrossDesignRequest(BaseModel):
|
|
"""측점 하나의 표준횡단 설계(지반유형·단면유형) 지정 요청.
|
|
|
|
버튼 클릭 즉시 절·성토 단면적을 계산해 잠정치로 저장한다. 편절편성은 절토측이
|
|
측구측이라 ditch_side가 무시되고, 양절·양성에서만 배수 방향으로 사용된다.
|
|
"""
|
|
|
|
chainage_m: float = Field(..., ge=0)
|
|
ground_type: Literal["soil", "ripping_rock", "blasting_rock"]
|
|
section_mode: Literal["left_cut", "right_cut", "both_cut", "both_fill"]
|
|
ditch_side: Literal["left", "right"] | None = None
|
|
# 측구 형식: 일반(사다리꼴)/L형. L형은 암 구간에서만 허용된다.
|
|
ditch_type: Literal["standard", "l_type"] = "standard"
|
|
# 포장 중첩 여부 — 횡단경사·포장층만 포장 그룹 값으로 계산한다.
|
|
paved: bool = False
|
|
# 암 경계선 오프셋(m, 지면선 기준 하향 음수). 암 지반 2단계 절토 무릎 계산에 쓴다.
|
|
rock_boundary_offset_m: float | None = None
|
|
# 암 지반 2단계 경사(암반 경계 아래=암 경사, 위=토사 경사) 적용 여부. 토글로 해제 가능.
|
|
two_stage_slope: bool = True
|
|
# 측구 생성 여부. None=자동 판정(측구측 절토면만 생성), True/False=수동 override.
|
|
ditch_enabled: bool | None = None
|
|
# B06 설정 패널 편집값(STANDARD_CROSS_SECTION 형태). 요청값 → config 기본값 순.
|
|
standard_cross_section: dict[str, Any] | None = None
|
|
|
|
|
|
class CrossDesignResponse(BaseModel):
|
|
"""측점 표준횡단 설계 계산 결과(잠정치)."""
|
|
|
|
status: str = "success"
|
|
chainage_m: float
|
|
design: dict[str, Any]
|
|
|
|
|
|
class CrossSectionPatch(BaseModel):
|
|
"""확정 시 측점별 data.design에 병합할 프론트 세션 보관값."""
|
|
|
|
chainage_m: float = Field(..., ge=0)
|
|
# 암 경계선 오프셋(m, 지면선 기준 하향 음수 — 계획선 아님). 암 지반 측점만 의미 있다.
|
|
rock_boundary_offset_m: float | None = None
|
|
|
|
|
|
class SectionConfirmRequest(BaseModel):
|
|
"""종횡단 확정 요청. 확정 시 저장할 표준 횡단면 설정값(선택)을 함께 받는다."""
|
|
|
|
# B06 표준 횡단면 설정 패널 편집값(토사/암/포장). longitudinal_sections.data.options에 저장.
|
|
standard_cross_section: dict[str, Any] | None = None
|
|
# 측점별 세션 보관값(암 경계선 오프셋 등) — 확정 시점에 일괄 DB 병합.
|
|
cross_patches: list[CrossSectionPatch] | None = None
|
|
# 프론트가 계산한 유토곡선 결과. longitudinal_sections.data.mass_haul에 저장한다.
|
|
# 계산은 프론트 전담(네트워크 왕복 회피)이라 백엔드는 검산 없이 스냅샷으로 보관한다.
|
|
mass_haul: dict[str, Any] | None = None
|
|
|
|
|
|
class SectionConfirmResponse(BaseModel):
|
|
"""종횡단 확정 결과."""
|
|
|
|
status: str = "success"
|
|
project_id: str
|
|
route_id: int
|
|
confirmed: bool = True
|
|
|
|
|
|
class SectionOptionDefaults(BaseModel):
|
|
"""config에서 읽은 종횡단 생성 기본 옵션."""
|
|
|
|
station_interval_m: float
|
|
cross_half_width_m: float
|
|
cross_sample_interval_m: float
|
|
long_sample_interval_m: float
|
|
vertical_exaggeration: float
|
|
|
|
|
|
class EarthworkConversionFactor(BaseModel):
|
|
"""지반유형 하나의 토량환산계수(둘 다 자연상태 기준 배율).
|
|
|
|
loose = 흐트러진 상태 / 자연상태(팽창률), compacted = 다져진 상태 / 자연상태(다짐률).
|
|
정의처는 config.config_system.EARTHWORK_CONVERSION_FACTORS 한 곳뿐이다.
|
|
"""
|
|
|
|
loose: float = Field(..., gt=0)
|
|
compacted: float = Field(..., gt=0)
|
|
|
|
|
|
class HaulEquipmentLimit(BaseModel):
|
|
"""평균운반거리 상한 하나(m). max_distance_m=None이면 상한 없음(나머지를 전부 받는다).
|
|
|
|
정의처는 config.config_system.EARTHWORK_HAUL_EQUIPMENT_LIMITS_M 한 곳뿐이다.
|
|
프론트(유토곡선 토량 분배)는 이 값을 받아 쓰고 사본을 두지 않는다.
|
|
"""
|
|
|
|
key: str
|
|
max_distance_m: float | None = None
|
|
|
|
|
|
class SectionContextResponse(BaseModel):
|
|
"""B06 진입 시 필요한 확정 경로 컨텍스트와 기본 옵션."""
|
|
|
|
project_id: str
|
|
route_id: int | None = None
|
|
filter_key: str | None = None
|
|
method: str | None = None
|
|
smooth: bool | None = None
|
|
crs_epsg: int | None = None
|
|
defaults: SectionOptionDefaults
|
|
# 표준 횡단면 설정 패널(토사/암/포장) 기본값. config STANDARD_CROSS_SECTION 사본.
|
|
standard_cross_section: dict[str, Any] = Field(default_factory=dict)
|
|
# 암 경계선 기본 오프셋(m)과 상/하 제어 스텝(m).
|
|
rock_boundary_default_offset_m: float = -0.5
|
|
rock_boundary_step_m: float = 0.1
|
|
# 지반유형별 토량환산계수. 유토곡선 계산은 프론트가 이 값으로 수행한다.
|
|
earthwork_conversion: dict[str, EarthworkConversionFactor] = Field(default_factory=dict)
|
|
# 평균운반거리별 운반장비 경계. 유토곡선의 토량 분배가 이 값으로 장비를 고른다.
|
|
haul_equipment_limits: list[HaulEquipmentLimit] = Field(default_factory=list)
|
|
# 자연방토 판정 경사(rise/run). 성토측 자연 지반이 이보다 가파르면 흙이 스스로 흘러내려
|
|
# 운반비를 세지 않는다. 정의처는 config_system.NATURAL_SPOIL_MIN_GROUND_SLOPE 한 곳뿐이다.
|
|
natural_spoil_min_ground_slope: float | None = None
|
|
|
|
|
|
class SectionSummaryResponse(BaseModel):
|
|
"""종횡단 요약 조회 결과."""
|
|
|
|
status: str = "success"
|
|
project_id: str
|
|
route_id: int
|
|
longitudinal: dict[str, Any] | None = None
|
|
length_m: float | None = None
|
|
cross_section_count: int = 0
|
|
|
|
|
|
class SectionDetailResponse(BaseModel):
|
|
"""종단·횡단 SVG 렌더링에 필요한 원시 샘플 데이터."""
|
|
|
|
longitudinal: dict[str, Any]
|
|
cross_sections: list[dict[str, Any]]
|
|
# 유토곡선 balloon을 사용자가 끌어 옮긴 위치(띠 번호 -> [dx, dy], px).
|
|
# longitudinal_sections.data.mass_haul.balloon_offsets에 확정 시점에 저장된 값이며,
|
|
# 브라우저가 바뀌어도 같은 자리에 뜨도록 진입 시 프론트 캐시의 씨앗으로 내려보낸다.
|
|
balloon_offsets: dict[str, list[float]] | None = None
|