Fix raster upload metadata mapping
This commit is contained in:
@@ -145,6 +145,43 @@ class DatasetService:
|
|||||||
crs_assumed=metadata_json.get("crs_assumed"),
|
crs_assumed=metadata_json.get("crs_assumed"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_raster_bounds_json(metadata_json: dict[str, Any]) -> dict[str, float] | None:
|
||||||
|
existing = metadata_json.get("bounds_json")
|
||||||
|
if isinstance(existing, dict):
|
||||||
|
return existing
|
||||||
|
bounds = metadata_json.get("bounds")
|
||||||
|
if isinstance(bounds, (list, tuple)) and len(bounds) == 4:
|
||||||
|
return {
|
||||||
|
"minx": float(bounds[0]),
|
||||||
|
"miny": float(bounds[1]),
|
||||||
|
"maxx": float(bounds[2]),
|
||||||
|
"maxy": float(bounds[3]),
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_raster_resolution_json(metadata_json: dict[str, Any]) -> dict[str, float] | None:
|
||||||
|
existing = metadata_json.get("resolution_json")
|
||||||
|
if isinstance(existing, dict):
|
||||||
|
return existing
|
||||||
|
resolution = metadata_json.get("resolution")
|
||||||
|
if isinstance(resolution, (list, tuple)) and len(resolution) >= 2:
|
||||||
|
return {"x": float(resolution[0]), "y": float(resolution[1])}
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_raster_bands_json(metadata_json: dict[str, Any]) -> dict[str, Any] | None:
|
||||||
|
existing = metadata_json.get("bands_json")
|
||||||
|
if isinstance(existing, dict):
|
||||||
|
return existing
|
||||||
|
bands_json: dict[str, Any] = {}
|
||||||
|
if metadata_json.get("band_count") is not None:
|
||||||
|
bands_json["band_count"] = int(metadata_json["band_count"])
|
||||||
|
if metadata_json.get("dtype") is not None:
|
||||||
|
bands_json["dtype"] = metadata_json["dtype"]
|
||||||
|
return bands_json or None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def upload_dataset(
|
async def upload_dataset(
|
||||||
db: Session,
|
db: Session,
|
||||||
@@ -222,6 +259,14 @@ class DatasetService:
|
|||||||
StorageService.remove_dataset_file(storage_info["storage_path"])
|
StorageService.remove_dataset_file(storage_info["storage_path"])
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
bounds_json = metadata.get("bounds_json") if isinstance(metadata, dict) else None
|
||||||
|
resolution_json = metadata.get("resolution_json") if isinstance(metadata, dict) else None
|
||||||
|
bands_json = metadata.get("bands_json") if isinstance(metadata, dict) else None
|
||||||
|
if canonical_type == "raster" and isinstance(metadata, dict):
|
||||||
|
bounds_json = DatasetService._extract_raster_bounds_json(metadata)
|
||||||
|
resolution_json = DatasetService._extract_raster_resolution_json(metadata)
|
||||||
|
bands_json = DatasetService._extract_raster_bands_json(metadata)
|
||||||
|
|
||||||
dataset = Dataset(
|
dataset = Dataset(
|
||||||
id=dataset_id,
|
id=dataset_id,
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
@@ -242,9 +287,9 @@ class DatasetService:
|
|||||||
size_bytes=storage_info["size_bytes"],
|
size_bytes=storage_info["size_bytes"],
|
||||||
checksum_sha256=storage_info["checksum_sha256"],
|
checksum_sha256=storage_info["checksum_sha256"],
|
||||||
crs=metadata.get("crs") if isinstance(metadata, dict) else None,
|
crs=metadata.get("crs") if isinstance(metadata, dict) else None,
|
||||||
bounds_json=metadata.get("bounds_json") if isinstance(metadata, dict) else None,
|
bounds_json=bounds_json,
|
||||||
resolution_json=metadata.get("resolution_json") if isinstance(metadata, dict) else None,
|
resolution_json=resolution_json,
|
||||||
bands_json=metadata.get("bands_json") if isinstance(metadata, dict) else None,
|
bands_json=bands_json,
|
||||||
metadata_json=metadata,
|
metadata_json=metadata,
|
||||||
status=status,
|
status=status,
|
||||||
)
|
)
|
||||||
@@ -317,11 +362,19 @@ class DatasetService:
|
|||||||
dataset.status = "failed"
|
dataset.status = "failed"
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
bounds_json = metadata.get("bounds_json") if isinstance(metadata, dict) else dataset.bounds_json
|
||||||
|
resolution_json = metadata.get("resolution_json") if isinstance(metadata, dict) else dataset.resolution_json
|
||||||
|
bands_json = metadata.get("bands_json") if isinstance(metadata, dict) else dataset.bands_json
|
||||||
|
if DatasetService._is_raster_type(dataset.dataset_type) and isinstance(metadata, dict):
|
||||||
|
bounds_json = DatasetService._extract_raster_bounds_json(metadata)
|
||||||
|
resolution_json = DatasetService._extract_raster_resolution_json(metadata)
|
||||||
|
bands_json = DatasetService._extract_raster_bands_json(metadata)
|
||||||
|
|
||||||
dataset.crs = metadata.get("crs") if isinstance(metadata, dict) else dataset.crs
|
dataset.crs = metadata.get("crs") if isinstance(metadata, dict) else dataset.crs
|
||||||
dataset.bounds_json = metadata.get("bounds_json") if isinstance(metadata, dict) else dataset.bounds_json
|
dataset.bounds_json = bounds_json
|
||||||
dataset.metadata_json = metadata
|
dataset.metadata_json = metadata
|
||||||
dataset.resolution_json = metadata.get("resolution_json") if isinstance(metadata, dict) else dataset.resolution_json
|
dataset.resolution_json = resolution_json
|
||||||
dataset.bands_json = metadata.get("bands_json") if isinstance(metadata, dict) else dataset.bands_json
|
dataset.bands_json = bands_json
|
||||||
|
|
||||||
db.add(dataset)
|
db.add(dataset)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from app.models import Project
|
||||||
|
from app.services.dataset_service import DatasetService
|
||||||
|
|
||||||
|
|
||||||
|
class FakeUploadFile:
|
||||||
|
filename = "real-orthophoto.tif"
|
||||||
|
content_type = "image/tiff"
|
||||||
|
|
||||||
|
async def read(self) -> bytes:
|
||||||
|
return b"fake-raster"
|
||||||
|
|
||||||
|
|
||||||
|
class FakeSession:
|
||||||
|
def __init__(self, project_id):
|
||||||
|
self.project_id = project_id
|
||||||
|
self.added = []
|
||||||
|
|
||||||
|
def get(self, model, item_id):
|
||||||
|
if model is Project and item_id == self.project_id:
|
||||||
|
return SimpleNamespace(id=item_id)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add(self, item):
|
||||||
|
self.added.append(item)
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def refresh(self, _item):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def test_raster_upload_maps_metadata_bounds_resolution_and_bands(monkeypatch) -> None:
|
||||||
|
project_id = uuid4()
|
||||||
|
db = FakeSession(project_id)
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.services.dataset_service.StorageService.persist_dataset_file",
|
||||||
|
lambda **_: {
|
||||||
|
"storage_path": "/tmp/real-orthophoto.tif",
|
||||||
|
"original_filename": "real-orthophoto.tif",
|
||||||
|
"stored_filename": "real-orthophoto.tif",
|
||||||
|
"content_type": "image/tiff",
|
||||||
|
"size_bytes": 11,
|
||||||
|
"checksum_sha256": "checksum",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.services.dataset_service.extract_raster_metadata",
|
||||||
|
lambda _path: {
|
||||||
|
"driver": "GTiff",
|
||||||
|
"crs": "EPSG:31370",
|
||||||
|
"bounds": [193277.5, 205708.2, 193777.5, 206208.2],
|
||||||
|
"resolution": [0.9765625, 0.9765625],
|
||||||
|
"dtype": ["uint8", "uint8", "uint8"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
response = asyncio.run(
|
||||||
|
DatasetService.upload_dataset(
|
||||||
|
db=db,
|
||||||
|
project_id=project_id,
|
||||||
|
file=FakeUploadFile(),
|
||||||
|
dataset_type="raster",
|
||||||
|
source="user_upload",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status == "ready"
|
||||||
|
assert response.crs == "EPSG:31370"
|
||||||
|
assert response.bounds_json == {
|
||||||
|
"minx": 193277.5,
|
||||||
|
"miny": 205708.2,
|
||||||
|
"maxx": 193777.5,
|
||||||
|
"maxy": 206208.2,
|
||||||
|
}
|
||||||
|
assert db.added[0].bounds_json == response.bounds_json
|
||||||
|
assert db.added[0].resolution_json == {"x": 0.9765625, "y": 0.9765625}
|
||||||
|
assert db.added[0].bands_json == {"dtype": ["uint8", "uint8", "uint8"]}
|
||||||
Reference in New Issue
Block a user