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), }, )