260722_2
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
기하 규칙 (사용자 확정 사항):
|
||||
- 변화점은 **기준 측점 위에만** 놓인다 → 곡선 중심이 측점 수직선상에 있다.
|
||||
- 종단곡선은 변화점 대칭 배치이며 좌우에 직선 구간이 반드시 남는다.
|
||||
- 곡선 기본 길이 L = 측점간격 × `curve_length_ratio`, 반경 R = L / |A| 로 파생.
|
||||
- 곡선 기본 **반경** R = 측점간격 × `curve_radius_ratio`, 길이 L = R × |A| 로 파생.
|
||||
|
||||
수치 최적화(직선 분할 DP·표고 결정)는 [[B05_wf2_Route_Engine_Grade_Solver]]가,
|
||||
기준 해석과 오케스트레이션은 [[B05_wf2_Route_Engine_Grade]]가 담당한다. 이 모듈은
|
||||
@@ -37,8 +37,7 @@ class AlignmentPolicy:
|
||||
"""계획선 선형·편집 정책. config 기본값 위에 노선별 해석값을 얹는다."""
|
||||
|
||||
station_interval_m: float
|
||||
curve_length_ratio: float
|
||||
curve_length_min_m: float
|
||||
curve_radius_ratio: float
|
||||
curve_tangent_max_ratio: float
|
||||
curve_skip_legal_exception: bool
|
||||
balance_tolerance_percent: float
|
||||
@@ -62,8 +61,7 @@ class AlignmentPolicy:
|
||||
config = FOREST_ROAD_PROFILE_ALIGNMENT
|
||||
return cls(
|
||||
station_interval_m=float(station_interval_m),
|
||||
curve_length_ratio=float(config["curve_length_ratio"]),
|
||||
curve_length_min_m=float(config["curve_length_min_m"]),
|
||||
curve_radius_ratio=float(config["curve_radius_ratio"]),
|
||||
curve_tangent_max_ratio=float(config["curve_tangent_max_ratio"]),
|
||||
curve_skip_legal_exception=bool(config["curve_skip_legal_exception"]),
|
||||
balance_tolerance_percent=float(config["balance_tolerance_percent"]),
|
||||
@@ -82,11 +80,8 @@ class AlignmentPolicy:
|
||||
config = FOREST_ROAD_PROFILE_ALIGNMENT
|
||||
return cls(
|
||||
station_interval_m=float(payload.get("station_interval_m") or 20.0),
|
||||
curve_length_ratio=float(
|
||||
payload.get("curve_length_ratio", config["curve_length_ratio"])
|
||||
),
|
||||
curve_length_min_m=float(
|
||||
payload.get("curve_length_min_m", config["curve_length_min_m"])
|
||||
curve_radius_ratio=float(
|
||||
payload.get("curve_radius_ratio", config["curve_radius_ratio"])
|
||||
),
|
||||
curve_tangent_max_ratio=float(
|
||||
payload.get("curve_tangent_max_ratio", config["curve_tangent_max_ratio"])
|
||||
@@ -109,19 +104,22 @@ class AlignmentPolicy:
|
||||
)
|
||||
|
||||
@property
|
||||
def default_curve_length_m(self) -> float:
|
||||
"""측점간격 기준 기본 종단곡선 길이."""
|
||||
return max(self.curve_length_min_m, self.station_interval_m * self.curve_length_ratio)
|
||||
def default_curve_radius_m(self) -> float:
|
||||
"""측점간격 기준 기본 종단곡선 반경.
|
||||
|
||||
R을 1차 값으로 고정해야 계획고를 편집해도 곡선 반경이 흔들리지 않는다.
|
||||
곡선 길이는 L = R × |대수차| 로 따라 움직인다.
|
||||
"""
|
||||
return max(1e-6, self.station_interval_m * self.curve_radius_ratio)
|
||||
|
||||
def as_dict(self) -> dict[str, Any]:
|
||||
"""프론트엔드가 동일 기하를 재현하는 데 필요한 상수 묶음."""
|
||||
return {
|
||||
"station_interval_m": self.station_interval_m,
|
||||
"curve_length_ratio": self.curve_length_ratio,
|
||||
"curve_length_min_m": self.curve_length_min_m,
|
||||
"curve_radius_ratio": self.curve_radius_ratio,
|
||||
"curve_tangent_max_ratio": self.curve_tangent_max_ratio,
|
||||
"curve_skip_legal_exception": self.curve_skip_legal_exception,
|
||||
"default_curve_length_m": self.default_curve_length_m,
|
||||
"default_curve_radius_m": self.default_curve_radius_m,
|
||||
"balance_tolerance_percent": self.balance_tolerance_percent,
|
||||
"edit_step_m": self.edit_step_m,
|
||||
"grade_violation_policy": self.grade_violation_policy,
|
||||
@@ -174,14 +172,17 @@ def build_curves(
|
||||
pvi_s: np.ndarray,
|
||||
pvi_z: np.ndarray,
|
||||
policy: AlignmentPolicy,
|
||||
curve_lengths: dict[str, float] | None = None,
|
||||
curve_radii: dict[str, float] | None = None,
|
||||
) -> tuple[list[dict[str, Any]], list[str]]:
|
||||
"""각 변화점에 대칭 종단곡선을 삽입하고 곡선 제원을 만든다.
|
||||
|
||||
**반경 R이 1차 값**이고 곡선길이는 L = R × |대수차| 로 따라온다. 계획고를 편집하면
|
||||
대수차가 바뀌므로 L은 변하지만 R은 지정한 값 그대로 유지된다.
|
||||
|
||||
곡선 반쪽 길이는 짧은 쪽 인접 직선의 `curve_tangent_max_ratio` 이내로 제한해
|
||||
좌우에 직선이 반드시 남게 한다(사용자가 R을 키워도 곡선끼리 겹치지 않는다).
|
||||
좌우에 직선이 반드시 남게 한다(R을 크게 넣어도 곡선끼리 겹치지 않는다).
|
||||
"""
|
||||
overrides = curve_lengths or {}
|
||||
overrides = curve_radii or {}
|
||||
spans = pvi_s[1:] - pvi_s[:-1]
|
||||
grades = (pvi_z[1:] - pvi_z[:-1]) / np.where(spans > 0, spans, 1.0)
|
||||
curves: list[dict[str, Any]] = []
|
||||
@@ -201,11 +202,12 @@ def build_curves(
|
||||
omitted = skip_allowed and policy.curve_skip_legal_exception
|
||||
requested = overrides.get(key)
|
||||
try:
|
||||
length = float(requested) if requested is not None else policy.default_curve_length_m
|
||||
radius = float(requested) if requested is not None else policy.default_curve_radius_m
|
||||
except (TypeError, ValueError):
|
||||
length = policy.default_curve_length_m
|
||||
if not np.isfinite(length) or length <= 0:
|
||||
length = policy.default_curve_length_m
|
||||
radius = policy.default_curve_radius_m
|
||||
if not np.isfinite(radius) or radius <= 0:
|
||||
radius = policy.default_curve_radius_m
|
||||
length = radius * abs(delta)
|
||||
half_limit = (
|
||||
min(float(spans[index - 1]), float(spans[index])) * policy.curve_tangent_max_ratio
|
||||
)
|
||||
@@ -214,8 +216,8 @@ def build_curves(
|
||||
continue
|
||||
if length / 2.0 - half > 1e-6:
|
||||
warnings.append(
|
||||
f"chainage {chainage:.1f}m: 인접 직선이 짧아 종단곡선 길이를 "
|
||||
f"{half * 2.0:.1f}m로 줄였습니다."
|
||||
f"chainage {chainage:.1f}m: 인접 직선이 짧아 종단곡선 반경을 "
|
||||
f"{half * 2.0 / abs(delta):.0f}m(길이 {half * 2.0:.1f}m)로 줄였습니다."
|
||||
)
|
||||
length = half * 2.0
|
||||
curves.append(
|
||||
@@ -353,14 +355,14 @@ def build_alignment(
|
||||
for key, value in (edits.get("station_offsets") or {}).items()
|
||||
if value is not None
|
||||
}
|
||||
curve_lengths = {
|
||||
curve_radii = {
|
||||
str(key): float(value)
|
||||
for key, value in (edits.get("curve_lengths") or {}).items()
|
||||
for key, value in (edits.get("curve_radii") or {}).items()
|
||||
if value is not None
|
||||
}
|
||||
|
||||
pvi_s, pvi_z, sources = resolve_pvi(base_s, base_z, station_offsets)
|
||||
curves, warnings = build_curves(pvi_s, pvi_z, policy, curve_lengths)
|
||||
curves, warnings = build_curves(pvi_s, pvi_z, policy, curve_radii)
|
||||
segments = _segments(pvi_s, pvi_z)
|
||||
plan = evaluate(pvi_s, pvi_z, curves, chainage)
|
||||
difference = plan - ground
|
||||
@@ -420,7 +422,7 @@ def build_alignment(
|
||||
{"chainage_m": round(float(s), 6), "elevation_m": round(float(z), 6)}
|
||||
for s, z in zip(base_s, base_z)
|
||||
],
|
||||
"edits": {"station_offsets": station_offsets, "curve_lengths": curve_lengths},
|
||||
"edits": {"station_offsets": station_offsets, "curve_radii": curve_radii},
|
||||
"pvi": pvi_rows,
|
||||
"segments": segments,
|
||||
"curves": [
|
||||
|
||||
Reference in New Issue
Block a user