From 9317ad3175cb1d5235ae4ce8bbe095920553a2d3 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 7 Sep 2026 04:37:00 +0900 Subject: [PATCH] =?UTF-8?q?perf(B04):=20=ED=91=9C=EA=B3=A0=20=EB=B3=B4?= =?UTF-8?q?=EA=B0=84=20=EC=82=BC=EA=B0=81=EB=A7=9D=EC=9D=84=20=EB=AC=BC?= =?UTF-8?q?=EC=96=B4=EB=B3=BC=20=EC=9E=90=EB=A6=AC=20=EB=91=98=EB=A0=88?= =?UTF-8?q?=EB=A1=9C=20=EC=A2=81=ED=9E=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 노선 [확인] 이 293초였고 그중 285초가 배수유역 분석, 다시 그중 256초가 1차 영역 만들기였음. 범인은 ElevationSampler 하나임 — LinearNDInterpolator 가 들로네 삼각망을 첫 호출 때 만드는데 등고선 구름을 통째로(용화 317,348점) 넘겨 그 한 번이 256~1,481초로 뛰었음(편차 큼). 표고를 묻는 자리는 도로 교차 노드에 닿은 세류 조각 몇 개뿐임. 그 조각들만 먼저 골라 그 둘레 100m 안 등고선만 남기고 삼각망을 만듦. 여유 100m 는 등고선 재샘플 간격(5m)의 스무 배라 물어볼 점은 언제나 볼록껍질 안쪽이고 보간값이 그대로임. 실측(용화 5601e828) — 보간 구름 317,348 → 4,367점, 1차 영역 256.6s → 43.8s(측정 중 기기 부하가 9배였음, 준비 5.1s → 45.7s). 결과 완전 동일 — 1차 영역 266,697㎡ · 격자 545×835 · 268,456셀 · 노선 이탈 0m/1079m · 상류망 1 / 하류망 109 / 미연결 1153. pytest 409 passed. Co-Authored-By: Claude Opus 5 (1M context) --- .../B04_PreProcess_Engine_Watershed_Stream.py | 53 ++++++++++++++++--- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/B04_PreProcess/B04_PreProcess_Engine_Watershed_Stream.py b/B04_PreProcess/B04_PreProcess_Engine_Watershed_Stream.py index c6951a5f..3200c4ee 100644 --- a/B04_PreProcess/B04_PreProcess_Engine_Watershed_Stream.py +++ b/B04_PreProcess/B04_PreProcess_Engine_Watershed_Stream.py @@ -90,14 +90,25 @@ def split_streams_at_road( node_edges.setdefault(head, []).append(index) node_edges.setdefault(tail, []).append(index) - sampler = ElevationSampler(cloud) + # 표고를 묻는 조각은 **도로 교차 노드에 닿은 것뿐**이다(나머지는 확산으로만 정해진다). + # 그 조각들만 먼저 골라 두고, 그 둘레로 삼각망을 좁힌다 — 위 클래스 주석 참조. + seed_pieces = [ + (index, piece, touching) + for index, piece in enumerate(pieces) + if (touching := [node for node in ends[index] if node in crossing_nodes]) + ] + focus_xy = ( + np.concatenate( + [np.asarray(piece.coords, dtype=np.float64)[:, :2] for _index, piece, _n in seed_pieces] + ) + if seed_pieces + else None + ) + sampler = ElevationSampler(cloud, focus_xy=focus_xy) # 씨앗 조각 → 그 조각의 하류쪽 끝점(= 도로 교차 노드). 이 값이 물 흐름 방향의 기준이 된다. upper_seeds: dict[int, tuple[float, float]] = {} lower_seeds: dict[int, tuple[float, float]] = {} - for index, piece in enumerate(pieces): - touching = [node for node in ends[index] if node in crossing_nodes] - if not touching: - continue + for index, piece, touching in seed_pieces: heights = sampler.at(np.array(touching, dtype=np.float64)) crossing_node = touching[int(np.argmin(heights))] if _mean_elevation(piece, sampler) > float(np.min(heights)): @@ -210,16 +221,42 @@ class ElevationSampler: 최근접 등고선 정점만 쓰면 오차가 등고선 간격(주곡선 5m)만큼 나서, 계곡 교차점 표고가 실제보다 한 등고선 위로 잡히고 상류 조각이 통째로 하류로 오판된다. TIN 선형보간을 1차로 쓰고, TIN 밖(볼록껍질 외부)만 최근접 정점으로 메운다. + + **`focus_xy` 를 꼭 줄 것 — 노선 [확인] 이 3분 반이던 원인이 여기였다**(2026-09-07 실측). + `LinearNDInterpolator` 는 들로네 삼각망을 **첫 호출 때** 만드는데, 등고선 구름을 통째로 + (용화 317,348점) 넘기면 그 한 번이 **256~1,481초**로 뛴다(같은 입력에도 편차가 큼). + 쓰는 곳은 세류 조각 위 몇 점뿐이므로, 물어볼 자리 둘레만 남기면 삼각망이 작아져 + **1초 아래**가 된다. 남기는 여유(`margin_m`)는 등고선 재샘플 간격(5m)의 스무 배라 + 물어볼 점은 언제나 볼록껍질 한참 안쪽에 있고 보간값도 그대로다. """ - def __init__(self, cloud: ContourCloud) -> None: + def __init__( + self, + cloud: ContourCloud, + focus_xy: np.ndarray | None = None, + margin_m: float = 100.0, + ) -> None: self._z = cloud.z if cloud.is_empty: self._interpolator = None self._tree = None return - self._interpolator = LinearNDInterpolator(cloud.xy, cloud.z) - self._tree = cKDTree(cloud.xy) + xy = np.asarray(cloud.xy, dtype=np.float64) + z = np.asarray(cloud.z, dtype=np.float64) + if focus_xy is not None and len(focus_xy): + focus = np.asarray(focus_xy, dtype=np.float64) + near = cKDTree(focus).query(xy, workers=-1)[0] <= margin_m + if near.any(): + xy, z = xy[near], z[near] + logger.info( + "배수유역: 표고 보간 구름을 물어볼 자리 %.0fm 안으로 줄임 — %d → %d점", + margin_m, + len(cloud.z), + len(z), + ) + self._z = z + self._interpolator = LinearNDInterpolator(xy, z) + self._tree = cKDTree(xy) def at(self, xy: np.ndarray) -> np.ndarray: """(N, 2) 좌표의 표고 (N,)."""