Skip to content

Commit 0a85d07

Browse files
committed
feat(DataMart): add AdminAreaOfInterest to TCL by driver
1 parent d9bbce7 commit 0a85d07

File tree

3 files changed

+78
-14
lines changed

3 files changed

+78
-14
lines changed

app/crud/geostore.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from sqlalchemy.sql.elements import Label, TextClause
1010

1111
from app.application import db
12+
from app.crud.tasks import create_or_update_task
1213
from app.errors import (
1314
BadAdminSourceException,
1415
BadAdminVersionException,
@@ -190,15 +191,15 @@ async def get_first_row(sql: Select):
190191
return row
191192

192193

193-
async def get_gadm_geostore(
194+
async def build_gadm_geostore(
194195
admin_provider: str,
195196
admin_version: str,
196197
adm_level: int,
197198
simplify: float | None,
198199
country_id: str,
199200
region_id: str | None = None,
200201
subregion_id: str | None = None,
201-
) -> AdminGeostoreResponse:
202+
) -> AdminGeostore:
202203
dv: Tuple[str, str] = await admin_params_to_dataset_version(
203204
admin_provider, admin_version
204205
)
@@ -281,7 +282,7 @@ async def get_gadm_geostore(
281282
"GeoJSON is None, try reducing or eliminating simplification."
282283
)
283284

284-
geostore: AdminGeostore = await form_admin_geostore(
285+
return await form_admin_geostore(
285286
adm_level=adm_level,
286287
admin_version=admin_version,
287288
area=float(row.gfw_area__ha),
@@ -293,6 +294,26 @@ async def get_gadm_geostore(
293294
simplify=simplify,
294295
)
295296

297+
298+
async def get_gadm_geostore(
299+
admin_provider: str,
300+
admin_version: str,
301+
adm_level: int,
302+
simplify: float | None,
303+
country_id: str,
304+
region_id: str | None = None,
305+
subregion_id: str | None = None,
306+
) -> AdminGeostoreResponse:
307+
geostore: AdminGeostore = await build_gadm_geostore(
308+
admin_provider=admin_provider,
309+
admin_version=admin_version,
310+
adm_level=adm_level,
311+
simplify=simplify,
312+
country_id=country_id,
313+
region_id=region_id,
314+
subregion_id=subregion_id,
315+
)
316+
296317
return AdminGeostoreResponse(data=geostore)
297318

298319

app/models/pydantic/datamart.py

+27-3
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,45 @@
88
from app.models.pydantic.responses import Response
99

1010
from .base import StrictBaseModel
11+
from ...crud.geostore import build_gadm_geostore
12+
1113

1214
class AreaOfInterest(StrictBaseModel, ABC):
1315
@abstractmethod
14-
def get_geostore_id(self) -> UUID:
16+
async def get_geostore_id(self) -> UUID:
1517
"""Return the unique identifier for the area of interest."""
1618
pass
1719

1820

1921
class GeostoreAreaOfInterest(AreaOfInterest):
2022
geostore_id:UUID = Field(..., title="Geostore ID")
2123

22-
def get_geostore_id(self) -> UUID:
24+
async def get_geostore_id(self) -> UUID:
2325
return self.geostore_id
2426

2527

28+
class AdminAreaOfInterest(AreaOfInterest):
29+
country: str = Field(..., title="ISO Country Code")
30+
region: Optional[str] = Field(None, title="Region")
31+
subregion: Optional[str] = Field(None, title="Subregion")
32+
provider: str = Field('gadm', title="Administrative Boundary Provider")
33+
version: str = Field('4.1', title="Administrative Boundary Version")
34+
35+
36+
async def get_geostore_id(self) -> UUID:
37+
admin_level = sum(1 for field in (self.country, self.region, self.subregion) if field is not None) - 1
38+
geostore = await build_gadm_geostore(
39+
admin_provider=self.provider,
40+
admin_version=self.version,
41+
adm_level=admin_level,
42+
simplify=None,
43+
country_id=self.country,
44+
region_id=self.region,
45+
subregion_id=self.subregion,
46+
)
47+
return UUID(geostore.id)
48+
49+
2650
class AnalysisStatus(str, Enum):
2751
saved = "saved"
2852
pending = "pending"
@@ -57,7 +81,7 @@ class DataMartResourceLinkResponse(Response):
5781

5882

5983
class TreeCoverLossByDriverIn(StrictBaseModel):
60-
aoi: Union[GeostoreAreaOfInterest]
84+
aoi: Union[GeostoreAreaOfInterest, AdminAreaOfInterest]
6185
canopy_cover: int = 30
6286
dataset_version: Dict[str, str] = {}
6387

app/routes/datamart/land.py

+27-8
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
DataMartResourceLinkResponse,
2929
TreeCoverLossByDriver,
3030
TreeCoverLossByDriverIn,
31-
TreeCoverLossByDriverResponse, AreaOfInterest, GeostoreAreaOfInterest,
31+
TreeCoverLossByDriverResponse,
32+
AreaOfInterest,
33+
GeostoreAreaOfInterest,
34+
AdminAreaOfInterest,
3235
)
3336
from app.settings.globals import API_URL
3437
from app.tasks.datamart.land import (
@@ -63,8 +66,22 @@ def _parse_dataset_versions(request: Request) -> Dict[str, str]:
6366

6467

6568
def _parse_area_of_interest(request: Request) -> AreaOfInterest:
66-
aoi_info = request.query_params.getlist('aoi[geostore_id]');
67-
return GeostoreAreaOfInterest(geostore_id=aoi_info[0])
69+
if 'aoi[geostore_id]' in request.query_params:
70+
return GeostoreAreaOfInterest(geostore_id=request.query_params['aoi[geostore_id]'])
71+
72+
# Otherwise, check if the request contains admin area information
73+
if 'aoi[country]' in request.query_params:
74+
return AdminAreaOfInterest(
75+
country=request.query_params['aoi[country]'],
76+
region=request.query_params.get('aoi[region]'),
77+
subregion=request.query_params.get('aoi[subregion]'),
78+
provider=request.query_params.get('aoi[provider]'),
79+
version=request.query_params.get('aoi[version]'),
80+
)
81+
82+
# If neither type is provided, raise an error
83+
raise HTTPException(status_code=422, detail="Invalid Area of Interest parameters")
84+
6885

6986
@router.get(
7087
"/tree_cover_loss_by_driver",
@@ -87,7 +104,7 @@ def _parse_area_of_interest(request: Request) -> AreaOfInterest:
87104
"schema": {
88105
"oneOf": [
89106
{"$ref": "#/components/schemas/GeostoreAreaOfInterest"},
90-
{"$ref": "#/components/schemas/WdpaAreaOfInterest"},
107+
{"$ref": "#/components/schemas/AdminAreaOfInterest"},
91108
]
92109
}
93110
},
@@ -120,8 +137,9 @@ async def tree_cover_loss_by_driver_search(
120137
api_key: APIKey = Depends(get_api_key),
121138
):
122139
"""Search if a resource exists for a given geostore and canopy cover."""
140+
geostore_id = await aoi.get_geostore_id()
123141
resource_id = _get_resource_id(
124-
"tree_cover_loss_by_driver", aoi.get_geostore_id(), canopy_cover, dataset_versions
142+
"tree_cover_loss_by_driver", geostore_id, canopy_cover, dataset_versions
125143
)
126144

127145
# check if it exists
@@ -174,10 +192,11 @@ async def tree_cover_loss_by_driver_post(
174192
):
175193
"""Create new tree cover loss by drivers resource for a given geostore and
176194
canopy cover."""
195+
geostore_id = await data.aoi.get_geostore_id()
177196

178197
# check geostore is valid
179198
try:
180-
await get_geostore(data.aoi.get_geostore_id(), GeostoreOrigin.rw)
199+
await get_geostore(geostore_id, GeostoreOrigin.rw)
181200
except HTTPException:
182201
raise HTTPException(
183202
status_code=422,
@@ -190,7 +209,7 @@ async def tree_cover_loss_by_driver_post(
190209
dataset_version = DEFAULT_LAND_DATASET_VERSIONS | data.dataset_version
191210
resource_id = _get_resource_id(
192211
"tree_cover_loss_by_driver",
193-
data.aoi.get_geostore_id(),
212+
geostore_id,
194213
data.canopy_cover,
195214
dataset_version,
196215
)
@@ -200,7 +219,7 @@ async def tree_cover_loss_by_driver_post(
200219
background_tasks.add_task(
201220
compute_tree_cover_loss_by_driver,
202221
resource_id,
203-
data.aoi.get_geostore_id(),
222+
geostore_id,
204223
data.canopy_cover,
205224
dataset_version,
206225
)

0 commit comments

Comments
 (0)