103 lines
3.1 KiB
Python
103 lines
3.1 KiB
Python
"""B05 경로 설계 요청·응답 검증 모델."""
|
|
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
|
|
from config.config_system import ROUTE_GRADE_CLASSES
|
|
|
|
|
|
class RoutePoint(BaseModel):
|
|
"""경로 제어점 (BP/CP/EP)."""
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
x: float
|
|
y: float
|
|
order: int | None = None
|
|
|
|
|
|
class CirclePoint(BaseModel):
|
|
"""회피/금지 원 (AP/FP)."""
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
x: float
|
|
y: float
|
|
radius_m: float = Field(gt=0)
|
|
|
|
|
|
class RouteSolveRequest(BaseModel):
|
|
"""경로 탐색 실행 요청."""
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
filter_key: str = Field(description="지면 필터 키 (grid_min_z/csf/pmf)")
|
|
method: str = Field(default="dtm", description="지표면 표현 (dtm/tin/nurbs/implicit/meshfree)")
|
|
smooth: bool = Field(default=False)
|
|
surface_model_id: int | None = Field(default=None, description="기반 지표면 모델 id")
|
|
algorithm: str = Field(default="dijkstra", description="경로 알고리즘 (dijkstra/ridge_valley)")
|
|
|
|
bp: RoutePoint
|
|
ep: RoutePoint
|
|
cp: list[RoutePoint] = Field(default_factory=list)
|
|
ap: list[CirclePoint] = Field(default_factory=list)
|
|
fp: list[CirclePoint] = Field(default_factory=list)
|
|
|
|
grade_class: str = Field(default="trunk")
|
|
paved: bool = Field(default=False)
|
|
min_curve_radius_m: float | None = None
|
|
max_uphill_grade: float | None = None
|
|
max_downhill_grade: float | None = None
|
|
weights: dict[str, float] | None = None
|
|
allow_avoid_pass_through: bool = Field(default=False)
|
|
|
|
@model_validator(mode="after")
|
|
def validate_choices(self) -> "RouteSolveRequest":
|
|
if self.grade_class not in ROUTE_GRADE_CLASSES:
|
|
raise ValueError(f"임도 등급은 {ROUTE_GRADE_CLASSES} 중 하나여야 합니다.")
|
|
if self.algorithm not in ("dijkstra", "ridge_valley"):
|
|
raise ValueError("경로 알고리즘은 dijkstra 또는 ridge_valley여야 합니다.")
|
|
return self
|
|
|
|
def points_data(self) -> dict[str, Any]:
|
|
return {
|
|
"bp": self.bp.model_dump(),
|
|
"ep": self.ep.model_dump(),
|
|
"cp": [p.model_dump() for p in self.cp],
|
|
"ap": [p.model_dump() for p in self.ap],
|
|
"fp": [p.model_dump() for p in self.fp],
|
|
}
|
|
|
|
def options(self) -> dict[str, Any]:
|
|
return {
|
|
"grade_class": self.grade_class,
|
|
"paved": self.paved,
|
|
"min_curve_radius_m": self.min_curve_radius_m,
|
|
"max_uphill_grade": self.max_uphill_grade,
|
|
"max_downhill_grade": self.max_downhill_grade,
|
|
"weights": self.weights,
|
|
"allow_avoid_pass_through": self.allow_avoid_pass_through,
|
|
}
|
|
|
|
|
|
class RouteSolveResponse(BaseModel):
|
|
"""경로 탐색 실행 결과."""
|
|
|
|
status: str = "success"
|
|
project_id: str
|
|
route_id: int
|
|
total_length_m: float
|
|
metrics: dict[str, Any]
|
|
required_points_ok: bool
|
|
route_data_path: str
|
|
|
|
|
|
class RouteConfirmResponse(BaseModel):
|
|
"""경로 확정 결과."""
|
|
|
|
status: str = "success"
|
|
project_id: str
|
|
route_id: int
|
|
confirmed: bool = True
|