260710_1
This commit is contained in:
@@ -10,7 +10,7 @@ from uuid import UUID
|
||||
import aiomysql
|
||||
import numpy as np
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
|
||||
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
|
||||
from B04_wf1_Surface.B04_wf1_Surface_Engine import run_surface_analysis
|
||||
@@ -304,6 +304,15 @@ async def get_surface_point_cloud(
|
||||
else:
|
||||
sample = xyz
|
||||
|
||||
rgb_sample = None
|
||||
if "rgb" in structured:
|
||||
rgb_arr = np.asarray(structured["rgb"])
|
||||
if rgb_arr.ndim > 0 and len(rgb_arr) == point_count:
|
||||
if point_count > POINT_CLOUD_SAMPLE_LIMIT:
|
||||
rgb_sample = rgb_arr[indexes]
|
||||
else:
|
||||
rgb_sample = rgb_arr
|
||||
|
||||
return SurfacePointCloudSampleResponse(
|
||||
project_id=str(project_id),
|
||||
point_count=point_count,
|
||||
@@ -317,6 +326,7 @@ async def get_surface_point_cloud(
|
||||
"z_max": float(bounds[2, 1]),
|
||||
},
|
||||
points=sample.astype(float).tolist(),
|
||||
rgb=rgb_sample.astype(int).tolist() if rgb_sample is not None else None,
|
||||
)
|
||||
except LookupError as exc:
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": str(exc)})
|
||||
@@ -469,3 +479,161 @@ async def get_wf1_analysis_status(project_id: UUID) -> dict:
|
||||
status_code=500,
|
||||
content={"status": "error", "message": "분석 상태 조회 중 오류가 발생했습니다."},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{project_id}/surface/models/{model_id}/preview", response_model=None)
|
||||
async def get_surface_model_preview(
|
||||
project_id: UUID,
|
||||
model_id: int,
|
||||
smooth: bool = False,
|
||||
) -> FileResponse | JSONResponse:
|
||||
"""지표면 모델의 3D 프리뷰 파일(GLB/PLY)을 반환한다."""
|
||||
pool = get_db_pool()
|
||||
try:
|
||||
async with pool.acquire() as connection:
|
||||
stored_path = await get_project_storage_relative_path(connection, project_id)
|
||||
async with connection.cursor() as cursor:
|
||||
await cursor.execute(
|
||||
"""
|
||||
SELECT model_type, model_file_path
|
||||
FROM surface_models
|
||||
WHERE id = %s AND project_id = %s
|
||||
""",
|
||||
(model_id, str(project_id)),
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
if not row:
|
||||
return JSONResponse(
|
||||
status_code=404,
|
||||
content={"status": "error", "message": "해당 모델을 찾을 수 없습니다."},
|
||||
)
|
||||
model_type, model_file_path = row[0], row[1]
|
||||
|
||||
project_root = Path(resolve_stored_project_path(stored_path))
|
||||
if not model_file_path:
|
||||
return JSONResponse(
|
||||
status_code=404,
|
||||
content={"status": "error", "message": "모델 파일 경로가 없습니다."},
|
||||
)
|
||||
|
||||
model_path = project_root / model_file_path
|
||||
models_dir = model_path.parent
|
||||
stem = model_path.stem
|
||||
|
||||
ext = "ply" if model_type == "meshfree" else "glb"
|
||||
if smooth and model_type in ("dtm", "tin"):
|
||||
preview_filename = f"{stem}_smooth_preview.glb"
|
||||
else:
|
||||
preview_filename = f"{stem}_preview.{ext}"
|
||||
|
||||
preview_path = models_dir / preview_filename
|
||||
if not preview_path.is_file():
|
||||
return JSONResponse(
|
||||
status_code=404,
|
||||
content={
|
||||
"status": "error",
|
||||
"message": "프리뷰 파일이 생성되지 않았거나 존재하지 않습니다.",
|
||||
},
|
||||
)
|
||||
|
||||
media_type = "application/octet-stream"
|
||||
if ext == "glb":
|
||||
media_type = "model/gltf-binary"
|
||||
elif ext == "ply":
|
||||
media_type = "application/ply"
|
||||
|
||||
return FileResponse(preview_path, media_type=media_type, filename=preview_filename)
|
||||
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"지표면 모델 프리뷰 조회 실패: project_id=%s, model_id=%s", project_id, model_id
|
||||
)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={"status": "error", "message": "프리뷰 파일 조회 중 오류가 발생했습니다."},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{project_id}/surface/models/{model_id}/contour", response_model=None)
|
||||
async def get_surface_model_contour(
|
||||
project_id: UUID,
|
||||
model_id: int,
|
||||
interval: float = 5.0,
|
||||
smooth: bool = False,
|
||||
) -> FileResponse | JSONResponse:
|
||||
"""지표면 모델의 등고선 JSON 파일을 반환한다."""
|
||||
pool = get_db_pool()
|
||||
try:
|
||||
async with pool.acquire() as connection:
|
||||
stored_path = await get_project_storage_relative_path(connection, project_id)
|
||||
async with connection.cursor() as cursor:
|
||||
await cursor.execute(
|
||||
"""
|
||||
SELECT model_type, model_file_path
|
||||
FROM surface_models
|
||||
WHERE id = %s AND project_id = %s
|
||||
""",
|
||||
(model_id, str(project_id)),
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
if not row:
|
||||
return JSONResponse(
|
||||
status_code=404,
|
||||
content={"status": "error", "message": "해당 모델을 찾을 수 없습니다."},
|
||||
)
|
||||
model_type, model_file_path = row[0], row[1]
|
||||
|
||||
project_root = Path(resolve_stored_project_path(stored_path))
|
||||
if not model_file_path:
|
||||
return JSONResponse(
|
||||
status_code=404,
|
||||
content={"status": "error", "message": "모델 파일 경로가 없습니다."},
|
||||
)
|
||||
|
||||
model_path = project_root / model_file_path
|
||||
models_dir = model_path.parent
|
||||
stem = model_path.stem
|
||||
|
||||
parts = stem.split("_")
|
||||
if len(parts) >= 2:
|
||||
method = parts[0]
|
||||
filter_key = "_".join(parts[1:])
|
||||
else:
|
||||
method = model_type
|
||||
filter_key = "csf"
|
||||
|
||||
if smooth and method in ("dtm", "tin"):
|
||||
contour_filename = f"contour_{filter_key}_{method}_smooth_{interval}m.json"
|
||||
else:
|
||||
contour_filename = f"contour_{filter_key}_{method}_{interval}m.json"
|
||||
|
||||
contour_path = models_dir / contour_filename
|
||||
|
||||
if not contour_path.is_file():
|
||||
fallback_files = list(models_dir.glob(f"contour_{filter_key}_{method}*.json"))
|
||||
if smooth and method in ("dtm", "tin"):
|
||||
fallback_files = list(
|
||||
models_dir.glob(f"contour_{filter_key}_{method}_smooth_*.json")
|
||||
)
|
||||
if fallback_files:
|
||||
contour_path = fallback_files[0]
|
||||
|
||||
if not contour_path.is_file():
|
||||
return JSONResponse(
|
||||
status_code=404,
|
||||
content={
|
||||
"status": "error",
|
||||
"message": "등고선 파일이 생성되지 않았거나 존재하지 않습니다.",
|
||||
},
|
||||
)
|
||||
|
||||
return FileResponse(contour_path, media_type="application/json", filename=contour_filename)
|
||||
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"지표면 모델 등고선 조회 실패: project_id=%s, model_id=%s", project_id, model_id
|
||||
)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={"status": "error", "message": "등고선 파일 조회 중 오류가 발생했습니다."},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user