From b0747e74b667d8d97af1d45a698203ce82410ef3 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 31 Jul 2026 20:04:26 +0900 Subject: [PATCH] =?UTF-8?q?fix(B05):=206/7/8=20=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=EC=82=B0=EC=B6=9C=EB=AC=BC=EB=8F=84=20=EC=98=81=EA=B5=AC?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=EC=86=8C=EC=97=90=20=EB=82=A8=EA=B8=B4?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 확인해 보니 7(2차 유역 외곽선)과 8(기본 관)은 저장되고 있었으나 6(흐름 강도 곡선)이 빠져 있었고, 관에 누가거리가 안 붙었다. - write_stage 의 레이어 항목이 (기하, 속성dict) 짝도 받도록 확장. 관 피처에 chainage_m / reason / stream_name 이 실린다. - 흐름 강도 곡선은 기하가 아니라 수치 곡선이라 GeoJSON 대신 02_flow_direction.npz 에 strength_chainage_m / strength_area_m2 로 담는다. - manifest 에 strength_points / strength_total_m2 요약 추가. 저장 확인 (실데이터) 01_primary_region.geojson 688KB kind: primary_region 1 / upstream 16 / downstream 860 / route 1 / grid_bbox 1 / basin_boundary 1 / pipe 1 pipe 속성 = chainage_m 149.73, reason stream 01_primary_region.npz 11KB 셀 마스크 + 확장 회차 02_flow_direction.npz 2353KB direction/reaches_road/analyzed/receiver/ burned/band_elevation + 강도 곡선 71점(합계 457,404m2, 최대 150m) manifest.json 3단계 요약 전부 Co-Authored-By: Claude Fable 5 --- .../B05_wf2_Route_Engine_Watershed_Export.py | 23 ++++++++++++------- .../B05_wf2_Route_Router_Drainage.py | 21 +++++++++++++++-- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/B05_wf2_Route/B05_wf2_Route_Engine_Watershed_Export.py b/B05_wf2_Route/B05_wf2_Route_Engine_Watershed_Export.py index 1b8ac236..babe06a2 100644 --- a/B05_wf2_Route/B05_wf2_Route_Engine_Watershed_Export.py +++ b/B05_wf2_Route/B05_wf2_Route_Engine_Watershed_Export.py @@ -44,13 +44,14 @@ def drainage_dir(stored_path: str) -> Path: def write_stage( stored_path: str, stage: str, - layers: dict[str, Sequence[BaseGeometry]], + layers: dict[str, Sequence[BaseGeometry | tuple[BaseGeometry, dict[str, Any]]]], properties: dict[str, Any], to_lonlat: LonLat, ) -> str | None: """한 단계의 기하 산출물을 GeoJSON으로 저장하고 매니페스트를 갱신한다. - `layers`는 {레이어이름: 사업지 CRS(m) 기하 목록}이며 피처 `kind` 속성이 된다. + `layers`는 {레이어이름: 사업지 CRS(m) 기하 목록}이며 레이어 이름이 피처 `kind` 속성이 + 된다. 기하 대신 `(기하, 속성dict)` 짝을 넣으면 그 속성이 피처에 함께 실린다. 좌표는 여기서 WGS84로 바꾼다 — 저장 파일은 어떤 도구로 열어도 바로 보여야 한다. """ prefix = STAGES.get(stage) @@ -60,12 +61,14 @@ def write_stage( features: list[dict[str, Any]] = [] counts: dict[str, int] = {} - for kind, geometries in layers.items(): - for index, geometry in enumerate(geometries): - feature = _to_feature(kind, index, geometry, to_lonlat) + for kind, entries in layers.items(): + for index, entry in enumerate(entries): + # 항목은 기하 하나이거나 (기하, 속성) 짝이다 — 관 누가거리처럼 붙일 값이 있을 때 쓴다. + geometry, extra = entry if isinstance(entry, tuple) else (entry, None) + feature = _to_feature(kind, index, geometry, to_lonlat, extra) if feature is not None: features.append(feature) - counts[kind] = len(geometries) + counts[kind] = len(entries) filename = f"{prefix}_{stage}.geojson" directory = drainage_dir(stored_path) @@ -90,14 +93,18 @@ def write_stage( def _to_feature( - kind: str, index: int, geometry: BaseGeometry, to_lonlat: LonLat + kind: str, + index: int, + geometry: BaseGeometry, + to_lonlat: LonLat, + extra: dict[str, Any] | None = None, ) -> dict[str, Any] | None: coordinates = _to_lonlat_coords(geometry, to_lonlat) if coordinates is None: return None return { "type": "Feature", - "properties": {"kind": kind, "index": index}, + "properties": {"kind": kind, "index": index, **(extra or {})}, "geometry": {"type": geometry.geom_type, "coordinates": coordinates}, } diff --git a/B05_wf2_Route/B05_wf2_Route_Router_Drainage.py b/B05_wf2_Route/B05_wf2_Route_Router_Drainage.py index 7a5c0345..6c5334d4 100644 --- a/B05_wf2_Route/B05_wf2_Route_Router_Drainage.py +++ b/B05_wf2_Route/B05_wf2_Route_Router_Drainage.py @@ -279,9 +279,19 @@ async def get_primary_region(project_id: UUID) -> dict[str, Any] | JSONResponse: "downstream": region.split.downstream, "route": [prepared["route_line"]], "grid_bbox": [_grid_bbox_polygon(spec)], - # ⑦ 2차 전체 배수유역 외곽선, ⑧ 기본 관 위치. + # ⑦ 2차 전체 배수유역 외곽선, ⑧ 기본 관 위치(누가거리·근거 포함). "basin_boundary": _boundary_geometry(preview.basin_boundary_xy), - "pipe": [Point(pipe.x, pipe.y) for pipe in preview.pipes], + "pipe": [ + ( + Point(pipe.x, pipe.y), + { + "chainage_m": round(pipe.chainage_m, 2), + "reason": pipe.reason, + "stream_name": pipe.stream_name, + }, + ) + for pipe in preview.pipes + ], }, { "radius_m": region.radius_m, @@ -331,6 +341,11 @@ def _write_stage_arrays(stored_path: str, preview: Any, domain: Any, spec: Any) arrays["burned"] = flow.burned.reshape(spec.n_rows, spec.n_cols) if preview.descent is not None: arrays["band_elevation"] = preview.descent.band_elevation + # ⑥ 흐름 강도 곡선 — 기하가 아니라 수치 곡선이라 GeoJSON이 아닌 여기에 함께 담는다. + if preview.strength_profile: + curve = np.asarray(preview.strength_profile, dtype=np.float64) + arrays["strength_chainage_m"] = curve[:, 0] + arrays["strength_area_m2"] = curve[:, 1] write_grid_arrays( stored_path, "flow_direction", @@ -346,6 +361,8 @@ def _write_stage_arrays(stored_path: str, preview: Any, domain: Any, spec: Any) "burned": 0 if flow.burned is None else int(flow.burned.sum()), "outer_seeds": flow.outer_seeds, "interior_seeds": flow.interior_seeds, + "strength_points": len(preview.strength_profile), + "strength_total_m2": round(sum(area for _, area in preview.strength_profile), 1), }, )