feat(D03 겹): 노선분석 지도 올린 임도망도 · 산사태취약지역 겹 — 시군구 SHP 올리기 · 시도 표 점 + 지번 필지 테두리 (PLAN 584-3 · 요구 443)
- 임도망도: 지도 겹 줄에 올리기 단추(zip 하나 · shp · shx · dbf · prj 묶음 · 100 MB · 넘으면 413) → 서버가 읽어 받은 범위로 자름(prj 없으면 EPSG:5179 · cpg 없으면 cp949) · 원본 = 프로젝트 D03_PlanAnalysis/임도망도.zip · 자른 선 = 파생 저장본 · 못 읽거나 범위 안 선 0 = 400 · 옛 것 그대로 - 범례: 「임도 짐작」(주황 점선) · 「올린 임도망도」(진한 주황 실선) 구분 · 짐작 겹 그대로 - 산사태취약지역: data.go.kr 시도 표 넷(경북 · 충남 · 광주 · 경남)을 resources/data_landslide_zones 에 한 벌(머리 = 이름 · 받은 날 · 기준일 · 출처) · 시도마다 다른 열 이름은 별칭으로 · 위경도 = 받은 범위 안 점 · 지번(시군구 · 읍면동 · 리 · 산 · 본번 · 부번) = 지적 필지 테두리만 · 사업지 시도에 표 없으면 범례 「이 시도 자료 없음」 · 지적 못 받으면 점만 - 두 겹 처음 끔 · 바탕 캔버스 · 색 --draw-map-forest-road-file · landslide-zone · 아이콘 alertOctagon · 고르기 창 pickFiles(부품 상자 ui_template_drop) - 범례 · 겹별 수 · 받은 자료 꼴을 D03_PlanAnalysis_UI_LayerLegend.ts 로 옮김(700줄 한도) - test_553_4: 겹 목록에 forestRoadFile · landslideZones 더함(새 겹 · 기대 목록 갱신) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,8 +4,9 @@
|
||||
- `POST …/plan/terrain` — 사업 전체 지형 한 벌 받기(뒤에서 · 누를 때만) · 끝나면 `surface_models` 행
|
||||
- `GET …/plan/progress` — 일의 진행률
|
||||
- `GET …/plan/layers/sheet?key=` · `…/cadastral` · `…/raster?kind=slope|hillshade|landslide` ·
|
||||
`…/zones` · `…/soils` · `…/bands` · `…/lines?kind=` · `…/zero-order` — 지도 겹
|
||||
(PLAN 569-1 · 581-1 · 581-2 · 584-1)
|
||||
`…/zones` · `…/soils` · `…/bands` · `…/lines?kind=` · `…/zero-order` ·
|
||||
`…/forest-roads`(GET · POST 올리기) · `…/landslide-zones` — 지도 겹
|
||||
(PLAN 569-1 · 581-1 · 581-2 · 584-1 · 584-3)
|
||||
|
||||
범위 = 전체 시점 · 종점(D02 정본 `load_base`) + 그은 점(`extra`) 상자 + 여유.
|
||||
노선 정본(손 그은 선)은
|
||||
@@ -21,7 +22,7 @@ from pathlib import Path
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Request, Response
|
||||
from fastapi import APIRouter, File, HTTPException, Request, Response, UploadFile
|
||||
from pydantic import BaseModel
|
||||
|
||||
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
|
||||
@@ -29,7 +30,9 @@ from common_util.common_util_http_cache import cached_file_response
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
from config.config_db import get_db_pool
|
||||
from D02_PlanBase import D02_PlanBase_Router as base
|
||||
from D03_PlanAnalysis import D03_PlanAnalysis_ForestRoad as forest_road
|
||||
from D03_PlanAnalysis import D03_PlanAnalysis_Landslide as landslide
|
||||
from D03_PlanAnalysis import D03_PlanAnalysis_LandslideZone as landslide_zone
|
||||
from D03_PlanAnalysis import D03_PlanAnalysis_Layers as layers
|
||||
from D03_PlanAnalysis import D03_PlanAnalysis_Terrain as terrain
|
||||
from D03_PlanAnalysis import D03_PlanAnalysis_ZeroOrder as zero_order
|
||||
@@ -291,3 +294,58 @@ async def plan_layer_zero_order(
|
||||
except FileNotFoundError as error:
|
||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
||||
return {"status": "success", **value}
|
||||
|
||||
|
||||
@router.get("/{project_id}/plan/layers/forest-roads", response_model=None)
|
||||
async def plan_layer_forest_roads(project_id: UUID) -> dict[str, Any]:
|
||||
"""올린 임도망도(받은 범위로 자른 선 · 가닥 · 길이) — 올린 적 없으면 404."""
|
||||
root, epsg, box = await _layer_root(project_id)
|
||||
try:
|
||||
value = await asyncio.to_thread(forest_road.road_layer, root, box, epsg)
|
||||
except FileNotFoundError as error:
|
||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
||||
return {"status": "success", **value}
|
||||
|
||||
|
||||
@router.post("/{project_id}/plan/layers/forest-roads", response_model=None)
|
||||
async def plan_layer_forest_roads_upload(
|
||||
project_id: UUID, files: list[UploadFile] = File(...)
|
||||
) -> dict[str, Any]:
|
||||
"""임도망도 SHP 올리기(zip 하나 · shp · shx · dbf · prj 묶음 · 합계 100 MB) — 못 읽거나
|
||||
받은 범위 안 선이 없으면 400(옛 것 그대로) · 넘치면 413."""
|
||||
root, epsg, box = await _layer_root(project_id)
|
||||
got: list[tuple[str, bytes]] = []
|
||||
total = 0
|
||||
for upload in files:
|
||||
chunks = []
|
||||
while chunk := await upload.read(1024 * 1024):
|
||||
total += len(chunk)
|
||||
if total > forest_road.MAX_BYTES:
|
||||
raise HTTPException(
|
||||
status_code=413,
|
||||
detail=f"파일이 너무 큽니다 — 모두 {forest_road.MAX_BYTES // 2**20} MB 까지",
|
||||
)
|
||||
chunks.append(chunk)
|
||||
got.append((upload.filename or "", b"".join(chunks)))
|
||||
if not total:
|
||||
raise HTTPException(status_code=400, detail="빈 파일입니다.")
|
||||
try:
|
||||
value = await asyncio.to_thread(forest_road.save_roads, root, got, box, epsg)
|
||||
except ValueError as error:
|
||||
raise HTTPException(status_code=400, detail=str(error)) from error
|
||||
return {"status": "success", **value}
|
||||
|
||||
|
||||
@router.get("/{project_id}/plan/layers/landslide-zones", response_model=None)
|
||||
async def plan_layer_landslide_zones(project_id: UUID) -> dict[str, Any]:
|
||||
"""산사태취약지역(시도 표 · 점 + 지번 맞는 필지 테두리 · 표 없는 시도) — 지적도 못 받으면
|
||||
점만."""
|
||||
root, epsg, box = await _layer_root(project_id)
|
||||
processed = root / terrain.STAGE / "processed"
|
||||
try:
|
||||
got = await asyncio.to_thread(layers.ensure_cadastral, processed, box, epsg)
|
||||
cadastral = got["path"]
|
||||
except RuntimeError:
|
||||
cadastral = None
|
||||
value = await asyncio.to_thread(landslide_zone.zone_layer, root, cadastral, box, epsg)
|
||||
return {"status": "success", **value}
|
||||
|
||||
Reference in New Issue
Block a user