- 흐름 방향장 열쇠 · 짓기에서 노선 최저점 하한 뺌 — 프로젝트마다 지형 저장본 한 종류 · 초안 [업데이트] 때 다시 짓지 않음 - expand_by_red_boundary 안 — 저장 큰 방향장 전체에서 도로 도달 한 번 판정 → 「도로로 오는 셀 + 확장 폭 띠 ∪ 1차 영역」으로 자름 · 큰 격자 가장자리에 닿으면 옛 회차 확장 - 끊기 확인 = 한 번 셈 앞 · [계측] 줄(한 번 셈 · 점 구름) - build_contour_cloud — 하한 없는 구름을 피처 목록 · 자르는 범위 열쇠로 2 벌 보관 · 하한은 점 표고로 거름(옛 결과와 점 · 차례 같음) - test_347_1 열쇠 시험 — 하한 인자 없어짐에 맞춰 고침 - 지킴 시험 test_520_6 — 합성 비탈에서 한 번 셈 = 회차 확장(도로로 오는 셀 · 도로 셀 귀속) · 하한 달라도 방향장 한 번만 지음 - 실데이터 대조(검증_sub2 · 검증_랩탑메인 · 검증_sub3) — 도로로 오는 셀 · 귀속 · 관별 면적 12 개 차이 0 · 초안 8.0~9.3 s → 0.8~2.5 s Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
89 lines
3.5 KiB
Python
89 lines
3.5 KiB
Python
"""520-6 배수유역 확장 한 번 셈 · 노선 무관 방향장 저장본.
|
|
|
|
- 한 번 셈(큰 방향장 전체 판정 → 도로로 오는 셀 + 띠 ∪ 1차 영역으로 자름) = 회차 확장과 도로로
|
|
오는 셀 · 도로 셀 귀속이 셀마다 같음(합성 등고선 · 능선까지 비탈).
|
|
- 방향장 열쇠 · 짓기가 노선 최저점 하한과 무관 — 하한이 달라도 저장본 하나를 읽기만.
|
|
"""
|
|
|
|
import numpy as np
|
|
from shapely.geometry import LineString
|
|
|
|
from B04_PreProcess import B04_PreProcess_Engine_Watershed_Expand as expand
|
|
from B04_PreProcess.B04_PreProcess_Engine_Watershed_Flow import trace_flow
|
|
from B04_PreProcess.B04_PreProcess_Engine_Watershed_Grid import GridSpec
|
|
|
|
|
|
def _ridge_contours():
|
|
"""도로 북쪽 비탈 — y = −60 능선까지 오르고 그 밖 · 도로 남쪽은 내려감(곧은 등고선)."""
|
|
features = []
|
|
for y in range(-250, 0, 5):
|
|
level = 290.0 - abs(y + 60.0)
|
|
features.append(
|
|
{
|
|
"geometry": {"type": "LineString", "coordinates": [[0.0, y], [300.0, y]]},
|
|
"properties": {"elevation": level},
|
|
}
|
|
)
|
|
return features
|
|
|
|
|
|
def _case():
|
|
spec = GridSpec(x_min=110.0, y_max=-130.0, cell_m=1.0, n_rows=40, n_cols=80)
|
|
domain = np.zeros((40, 80), bool)
|
|
domain[5:35, 5:75] = True
|
|
route = LineString([(120.0, -150.0), (180.0, -150.0)])
|
|
return spec, domain, route
|
|
|
|
|
|
def _run(monkeypatch, once, floor=None, store=None):
|
|
monkeypatch.setattr(expand, "DESCENT_SPARE_M", 60.0)
|
|
if not once:
|
|
monkeypatch.setattr(expand, "_expand_once", lambda *args: None)
|
|
spec, domain, route = _case()
|
|
return expand.expand_by_red_boundary(
|
|
spec, domain, _ridge_contours(), route, [], floor, band_m=10.0, descent_store=store
|
|
)
|
|
|
|
|
|
def _world(analysis):
|
|
"""도로로 오는 셀 → {(x, y): 도로 셀 누가거리}."""
|
|
spec = analysis.spec
|
|
routing = trace_flow(analysis.terrain, analysis.road)
|
|
red = analysis.flow.reaches_road & analysis.flow.analyzed
|
|
chainage = analysis.road.chainage
|
|
out = {}
|
|
for index in np.flatnonzero(red):
|
|
slot = routing.road_slot[index]
|
|
x = spec.x_min + (index % spec.n_cols) * spec.cell_m
|
|
y = spec.y_max - (index // spec.n_cols) * spec.cell_m
|
|
out[(round(x, 3), round(y, 3))] = None if slot < 0 else round(float(chainage[slot]), 3)
|
|
return out
|
|
|
|
|
|
def test_once_matches_round_expansion(monkeypatch):
|
|
rounds = _run(monkeypatch, once=False)
|
|
monkeypatch.undo()
|
|
once = _run(monkeypatch, once=True)
|
|
assert rounds.rounds >= 2 and rounds.closed # 회차 확장이 실제로 여러 번 넓힘
|
|
assert once.closed and once.rounds == 1 and once.added_cells > 0
|
|
old, new = _world(rounds.analysis), _world(once.analysis)
|
|
assert len(old) > 3_000
|
|
assert old == new
|
|
|
|
|
|
def test_descent_key_and_build_ignore_floor(tmp_path, monkeypatch):
|
|
built = []
|
|
real = expand.build_contour_descent
|
|
|
|
def counting(*args, **kwargs):
|
|
built.append(args[2:] + tuple(kwargs.values()))
|
|
return real(*args, **kwargs)
|
|
|
|
monkeypatch.setattr(expand, "build_contour_descent", counting)
|
|
store = (tmp_path, "도엽-1")
|
|
low = _run(monkeypatch, once=True, floor=None, store=store)
|
|
high = _run(monkeypatch, once=True, floor=230.0, store=store)
|
|
assert len(built) == 1 and built[0] == () # 하한 없이 한 번만 지음 · 두 번째는 읽기만
|
|
assert len(list(tmp_path.glob("descent_*.npz"))) == 1
|
|
assert _world(low.analysis) == _world(high.analysis)
|