added covarage
This commit is contained in:
@@ -16,8 +16,9 @@ from rasterio.warp import (
|
|||||||
reproject,
|
reproject,
|
||||||
transform,
|
transform,
|
||||||
)
|
)
|
||||||
|
from pyproj import Transformer
|
||||||
from shapely.geometry import mapping, shape
|
from shapely.geometry import mapping, shape
|
||||||
from shapely.ops import unary_union
|
from shapely.ops import transform as shapely_transform, unary_union
|
||||||
|
|
||||||
from app.core.dem import _dem_files, _sample_dataset
|
from app.core.dem import _dem_files, _sample_dataset
|
||||||
from app.core.geo import GeoPoint
|
from app.core.geo import GeoPoint
|
||||||
@@ -122,6 +123,16 @@ def _run_gdal_viewshed(
|
|||||||
subprocess.run(command, check=True, capture_output=True, text=True)
|
subprocess.run(command, check=True, capture_output=True, text=True)
|
||||||
|
|
||||||
|
|
||||||
|
def _to_wgs84(geom, src_crs: CRS | None):
|
||||||
|
if src_crs is None:
|
||||||
|
return geom
|
||||||
|
crs_text = src_crs.to_string()
|
||||||
|
if crs_text in {"EPSG:4326", "OGC:CRS84", "WGS84"}:
|
||||||
|
return geom
|
||||||
|
transformer = Transformer.from_crs(src_crs, "EPSG:4326", always_xy=True)
|
||||||
|
return shapely_transform(transformer.transform, geom)
|
||||||
|
|
||||||
|
|
||||||
def _viewshed_geojson(viewshed_path: Path, observer: GeoPoint) -> dict[str, Any]:
|
def _viewshed_geojson(viewshed_path: Path, observer: GeoPoint) -> dict[str, Any]:
|
||||||
features: list[dict[str, Any]] = []
|
features: list[dict[str, Any]] = []
|
||||||
with rasterio.open(viewshed_path) as dataset:
|
with rasterio.open(viewshed_path) as dataset:
|
||||||
@@ -130,7 +141,7 @@ def _viewshed_geojson(viewshed_path: Path, observer: GeoPoint) -> dict[str, Any]
|
|||||||
for geom, value in shapes(mask, mask=mask.astype(bool), transform=dataset.transform):
|
for geom, value in shapes(mask, mask=mask.astype(bool), transform=dataset.transform):
|
||||||
if int(value) != 1:
|
if int(value) != 1:
|
||||||
continue
|
continue
|
||||||
polygon = shape(geom)
|
polygon = _to_wgs84(shape(geom), dataset.crs)
|
||||||
if polygon.is_empty:
|
if polygon.is_empty:
|
||||||
continue
|
continue
|
||||||
features.append(
|
features.append(
|
||||||
|
|||||||
Binary file not shown.
+48
-2
@@ -1,11 +1,49 @@
|
|||||||
from fastapi import APIRouter, HTTPException, status
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from fastapi import APIRouter, HTTPException, status
|
||||||
|
from fastapi.responses import FileResponse, JSONResponse
|
||||||
|
|
||||||
|
from app.config import get_settings
|
||||||
from app.models.jobs import JobResponse
|
from app.models.jobs import JobResponse
|
||||||
from app.services.jobs import get_job
|
from app.services.jobs import get_job, get_job_record
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
def _artifact_path(job_id: str) -> Path:
|
||||||
|
record = get_job_record(job_id)
|
||||||
|
if record is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail={"code": "JOB_NOT_FOUND", "detail": "Job not found"},
|
||||||
|
)
|
||||||
|
if record.get("status") != "done":
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_409_CONFLICT,
|
||||||
|
detail={"code": "JOB_NOT_READY", "detail": "Job is not finished yet"},
|
||||||
|
)
|
||||||
|
uri = (record.get("result") or {}).get("uri")
|
||||||
|
if not uri:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail={"code": "ARTIFACT_NOT_FOUND", "detail": "Job has no artifact URI"},
|
||||||
|
)
|
||||||
|
path = Path(uri).resolve()
|
||||||
|
root = get_settings().jobs_output_path.resolve()
|
||||||
|
if root not in path.parents and path != root:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail={"code": "ARTIFACT_FORBIDDEN", "detail": "Artifact path is outside jobs output"},
|
||||||
|
)
|
||||||
|
if not path.is_file():
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail={"code": "ARTIFACT_MISSING", "detail": f"Artifact file not found: {path.name}"},
|
||||||
|
)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{job_id}", response_model=JobResponse)
|
@router.get("/{job_id}", response_model=JobResponse)
|
||||||
def read(job_id: str) -> JobResponse:
|
def read(job_id: str) -> JobResponse:
|
||||||
job = get_job(job_id)
|
job = get_job(job_id)
|
||||||
@@ -15,3 +53,11 @@ def read(job_id: str) -> JobResponse:
|
|||||||
detail={"code": "JOB_NOT_FOUND", "detail": "Job not found"},
|
detail={"code": "JOB_NOT_FOUND", "detail": "Job not found"},
|
||||||
)
|
)
|
||||||
return job
|
return job
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{job_id}/artifact")
|
||||||
|
def read_artifact(job_id: str):
|
||||||
|
path = _artifact_path(job_id)
|
||||||
|
if path.suffix.lower() == ".json":
|
||||||
|
return JSONResponse(content=json.loads(path.read_text(encoding="utf-8")))
|
||||||
|
return FileResponse(path)
|
||||||
|
|||||||
Reference in New Issue
Block a user