|
1 | 1 | """Serializers.""" |
2 | 2 |
|
3 | 3 | import abc |
| 4 | +import logging |
4 | 5 | from copy import deepcopy |
5 | 6 | from typing import Any, List, Optional |
6 | 7 |
|
|
9 | 10 |
|
10 | 11 | from stac_fastapi.core.datetime_utils import now_to_rfc3339_str |
11 | 12 | from stac_fastapi.core.models.links import CollectionLinks |
12 | | -from stac_fastapi.core.utilities import get_bool_env |
| 13 | +from stac_fastapi.core.utilities import bbox2polygon, get_bool_env |
13 | 14 | from stac_fastapi.types import stac as stac_types |
14 | 15 | from stac_fastapi.types.links import ItemLinks, resolve_links |
15 | 16 |
|
| 17 | +logger = logging.getLogger(__name__) |
| 18 | + |
16 | 19 |
|
17 | 20 | @attr.s |
18 | 21 | class Serializer(abc.ABC): |
@@ -141,6 +144,53 @@ def stac_to_db( |
141 | 144 | collection.get("links", []), str(request.base_url) |
142 | 145 | ) |
143 | 146 |
|
| 147 | + # Convert bbox to bbox_shape for geospatial queries |
| 148 | + if "extent" in collection and "spatial" in collection["extent"]: |
| 149 | + spatial_extent = collection["extent"]["spatial"] |
| 150 | + if "bbox" in spatial_extent and spatial_extent["bbox"]: |
| 151 | + # Get the first bbox (collections can have multiple bboxes, but we use the first one) |
| 152 | + bbox = ( |
| 153 | + spatial_extent["bbox"][0] |
| 154 | + if isinstance(spatial_extent["bbox"][0], list) |
| 155 | + else spatial_extent["bbox"] |
| 156 | + ) |
| 157 | + collection_id = collection.get("id", "unknown") |
| 158 | + logger.debug( |
| 159 | + f"Converting bbox to bbox_shape for collection '{collection_id}': bbox={bbox}" |
| 160 | + ) |
| 161 | + |
| 162 | + if len(bbox) >= 4: |
| 163 | + # Extract 2D coordinates (bbox can be 2D [minx, miny, maxx, maxy] or 3D [minx, miny, minz, maxx, maxy, maxz]) |
| 164 | + # For 2D polygon, we only need the x,y coordinates and discard altitude (z) values |
| 165 | + minx, miny = bbox[0], bbox[1] |
| 166 | + if len(bbox) == 4: |
| 167 | + # 2D bbox: [minx, miny, maxx, maxy] |
| 168 | + maxx, maxy = bbox[2], bbox[3] |
| 169 | + logger.debug( |
| 170 | + f"Collection '{collection_id}': Processing 2D bbox" |
| 171 | + ) |
| 172 | + else: |
| 173 | + # 3D bbox: [minx, miny, minz, maxx, maxy, maxz] |
| 174 | + # Extract indices 3,4 for maxx,maxy - discarding altitude at indices 2 (minz) and 5 (maxz) |
| 175 | + maxx, maxy = bbox[3], bbox[4] |
| 176 | + logger.debug( |
| 177 | + f"Collection '{collection_id}': Processing 3D bbox, discarding altitude values at indices 2 and 5" |
| 178 | + ) |
| 179 | + |
| 180 | + # Convert bbox to GeoJSON polygon |
| 181 | + bbox_polygon_coords = bbox2polygon(minx, miny, maxx, maxy) |
| 182 | + collection["bbox_shape"] = { |
| 183 | + "type": "Polygon", |
| 184 | + "coordinates": bbox_polygon_coords, |
| 185 | + } |
| 186 | + logger.info( |
| 187 | + f"Collection '{collection_id}': Created bbox_shape from bbox [{minx}, {miny}, {maxx}, {maxy}]" |
| 188 | + ) |
| 189 | + else: |
| 190 | + logger.warning( |
| 191 | + f"Collection '{collection_id}': bbox has insufficient coordinates (length={len(bbox)}), expected at least 4" |
| 192 | + ) |
| 193 | + |
144 | 194 | if get_bool_env("STAC_INDEX_ASSETS"): |
145 | 195 | collection["assets"] = [ |
146 | 196 | {"es_key": k, **v} for k, v in collection.get("assets", {}).items() |
@@ -168,6 +218,9 @@ def db_to_stac( |
168 | 218 | # Avoid modifying the input dict in-place ... doing so breaks some tests |
169 | 219 | collection = deepcopy(collection) |
170 | 220 |
|
| 221 | + # Remove internal bbox_shape field (not part of STAC spec) |
| 222 | + collection.pop("bbox_shape", None) |
| 223 | + |
171 | 224 | # Set defaults |
172 | 225 | collection_id = collection.get("id") |
173 | 226 | collection.setdefault("type", "Collection") |
|
0 commit comments