feat(B04): 유입 집중점에 세류 교차점(기본 관) 자리 추가
세류가 도로를 가로지르는 자리가 대개 그 노선에서 가장 큰 유역을 받는데, 그 자리는 구역 경계라 구역별 선정 규칙으로는 영영 뽑히지 않았다. 실측 프로젝트에서 전체 유역의 89%(409,210㎡)를 받는 지점이 마커에서 빠져 있었다. - 기본 관마다 관 ±최소간격 안에서 강도가 가장 큰 칸을 집중점으로 넣는다. 관 누가거리를 그대로 쓰지 않는 이유는 도로를 폭 4m로 굽는 과정에서 실제 유입 봉우리가 관에서 두어 걸음 옆에 놓이기 때문이다(관 149.73m, 봉우리 152m). - 이 항목은 구역 번호 -1로 구분한다. 구역별 보충 후보 선정 규칙은 그대로 둔다. - 관 자리와 같은 칸이 구역 쪽에서 다시 뽑혀 마커가 겹치는 것만 막았다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -360,6 +360,9 @@ def find_inflow_hotspots(
|
||||
) -> list[tuple[float, float, int, int]]:
|
||||
"""도로 위 **유입 집중점**을 뽑는다 — (누가거리 m, 유입면적 ㎡, 구역 번호, 구역 내 순위).
|
||||
|
||||
구역 번호 `-1`은 **기본 관(세류 교차점) 자리**를 뜻한다. 나머지는 그 구역에서 보충 후보로
|
||||
뽑은 자리다.
|
||||
|
||||
왜 필요한가(2026-08-01 사용자 지시): 기본 관은 국가 수치지형도의 세류선이 도로를 가로지르는
|
||||
자리라 근거가 확실하다. 그런데 관 **최대 간격**을 지키려면 세류가 없는 긴 구간에도 관을
|
||||
넣어야 하는데, 이때 등간격으로 기계적으로 꽂는 대신 **물이 실제로 많이 모이는 자리**를
|
||||
@@ -379,6 +382,11 @@ def find_inflow_hotspots(
|
||||
전부(전체 유역의 대부분)가 관에서 두어 걸음 떨어진 칸에서 다시 최대값으로 잡혀,
|
||||
정작 관이 없어 보충이 필요한 자리는 뽑히지 못한다. 노선 시·종점은 관이 아니므로
|
||||
물러나지 않는다.
|
||||
· **기본 관 자리 자체도 집중점으로 넣는다(구역 번호 −1).** 세류가 도로를 가로지르는
|
||||
자리가 대개 그 노선에서 가장 큰 유역을 받는데, 그 자리는 구역 경계라 위 규칙으로는
|
||||
영영 뽑히지 않는다(2026-08-01 사용자 지시). 위치는 관 누가거리 그대로가 아니라
|
||||
관 ±최소간격 안에서 강도가 가장 큰 칸으로 잡는다 — 도로를 폭으로 굽는 과정에서
|
||||
실제 유입 봉우리가 관에서 두어 걸음 옆에 놓이기 때문이다.
|
||||
· 강도 0인 칸은 뽑지 않는다(개수를 못 채워도 그대로 둔다).
|
||||
"""
|
||||
if curve.size == 0 or route_length_m <= 0:
|
||||
@@ -387,6 +395,22 @@ def find_inflow_hotspots(
|
||||
edges = sorted({0.0, float(route_length_m), *(float(p) for p in pipe_chainages)})
|
||||
exclusion = max(1, int(round(DRAINAGE_PIPE_MIN_SPACING_M)))
|
||||
hotspots: list[tuple[float, float, int, int]] = []
|
||||
|
||||
# ① 기본 관 자리 — 관마다 그 근처 봉우리 하나.
|
||||
for pipe_m in sorted({float(p) for p in pipe_chainages}):
|
||||
center = int(round(pipe_m))
|
||||
low = max(0, center - exclusion)
|
||||
high = min(curve.size - 1, center + exclusion)
|
||||
if high < low:
|
||||
continue
|
||||
window = curve[low : high + 1]
|
||||
best = int(np.argmax(window))
|
||||
if window[best] <= 0:
|
||||
continue
|
||||
hotspots.append((float(low + best), float(window[best]), -1, 0))
|
||||
# 관 자리와 겹치는 칸은 구역 쪽에서 다시 뽑지 않는다 — 같은 자리에 마커가 두 개 겹친다.
|
||||
taken = {int(chainage) for chainage, _, _, _ in hotspots}
|
||||
# ② 구역별 보충 후보 — 관과 관 사이에서 물이 많이 모이는 자리.
|
||||
for zone_index, (start_m, end_m) in enumerate(zip(edges, edges[1:])):
|
||||
span = end_m - start_m
|
||||
if span <= 0:
|
||||
@@ -401,6 +425,9 @@ def find_inflow_hotspots(
|
||||
if high < low:
|
||||
continue
|
||||
window = curve[low : high + 1].copy()
|
||||
for index in taken:
|
||||
if low <= index <= high:
|
||||
window[index - low] = 0.0
|
||||
for rank in range(wanted):
|
||||
best = int(np.argmax(window))
|
||||
if window[best] <= 0:
|
||||
|
||||
Reference in New Issue
Block a user