Skip to content

Commit 5935463

Browse files
committed
move add bbox shape to fn
1 parent 783e395 commit 5935463

File tree

4 files changed

+94
-89
lines changed

4 files changed

+94
-89
lines changed

sfeos_tools/sfeos_tools/cli.py

Lines changed: 7 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@
1414

1515
import click
1616

17-
from stac_fastapi.core.utilities import bbox2polygon
17+
from stac_fastapi.sfeos_helpers.database import add_bbox_shape_to_collection
1818
from stac_fastapi.sfeos_helpers.mappings import COLLECTIONS_INDEX
1919

2020
logging.basicConfig(level=logging.INFO)
2121
logger = logging.getLogger(__name__)
2222

2323

24-
async def add_bbox_shape_to_collection(client, collection_doc, backend):
25-
"""Add bbox_shape field to a single collection document.
24+
async def process_collection_bbox_shape(client, collection_doc, backend):
25+
"""Process a single collection document to add bbox_shape field.
2626
2727
Args:
2828
client: Elasticsearch/OpenSearch client
@@ -35,56 +35,12 @@ async def add_bbox_shape_to_collection(client, collection_doc, backend):
3535
collection = collection_doc["_source"]
3636
collection_id = collection.get("id", collection_doc["_id"])
3737

38-
# Check if bbox_shape already exists
39-
if "bbox_shape" in collection:
40-
logger.info(
41-
f"Collection '{collection_id}' already has bbox_shape field, skipping"
42-
)
43-
return False
44-
45-
# Check if collection has spatial extent
46-
if "extent" not in collection or "spatial" not in collection["extent"]:
47-
logger.warning(f"Collection '{collection_id}' has no spatial extent, skipping")
48-
return False
49-
50-
spatial_extent = collection["extent"]["spatial"]
51-
if "bbox" not in spatial_extent or not spatial_extent["bbox"]:
52-
logger.warning(
53-
f"Collection '{collection_id}' has no bbox in spatial extent, skipping"
54-
)
55-
return False
56-
57-
# Get the first bbox (collections can have multiple bboxes, but we use the first one)
58-
bbox = (
59-
spatial_extent["bbox"][0]
60-
if isinstance(spatial_extent["bbox"][0], list)
61-
else spatial_extent["bbox"]
62-
)
38+
# Use the shared function to add bbox_shape
39+
was_added = add_bbox_shape_to_collection(collection)
6340

64-
if len(bbox) < 4:
65-
logger.warning(
66-
f"Collection '{collection_id}': bbox has insufficient coordinates (length={len(bbox)}), expected at least 4"
67-
)
41+
if not was_added:
6842
return False
6943

70-
# Extract 2D coordinates (bbox can be 2D [minx, miny, maxx, maxy] or 3D [minx, miny, minz, maxx, maxy, maxz])
71-
# For 2D polygon, we only need the x,y coordinates and discard altitude (z) values
72-
minx, miny = bbox[0], bbox[1]
73-
if len(bbox) == 4:
74-
# 2D bbox: [minx, miny, maxx, maxy]
75-
maxx, maxy = bbox[2], bbox[3]
76-
else:
77-
# 3D bbox: [minx, miny, minz, maxx, maxy, maxz]
78-
# Extract indices 3,4 for maxx,maxy - discarding altitude at indices 2 (minz) and 5 (maxz)
79-
maxx, maxy = bbox[3], bbox[4]
80-
81-
# Convert bbox to GeoJSON polygon
82-
bbox_polygon_coords = bbox2polygon(minx, miny, maxx, maxy)
83-
collection["bbox_shape"] = {
84-
"type": "Polygon",
85-
"coordinates": bbox_polygon_coords,
86-
}
87-
8844
# Update the collection in the database
8945
if backend == "elasticsearch":
9046
await client.index(
@@ -154,7 +110,7 @@ async def run_add_bbox_shape(backend):
154110
skipped_count = 0
155111

156112
for hit in response["hits"]["hits"]:
157-
was_updated = await add_bbox_shape_to_collection(client, hit, backend)
113+
was_updated = await process_collection_bbox_shape(client, hit, backend)
158114
if was_updated:
159115
updated_count += 1
160116
else:

stac_fastapi/core/stac_fastapi/core/serializers.py

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from stac_fastapi.core.datetime_utils import now_to_rfc3339_str
1212
from stac_fastapi.core.models.links import CollectionLinks
13-
from stac_fastapi.core.utilities import bbox2polygon, get_bool_env
13+
from stac_fastapi.core.utilities import get_bool_env
1414
from stac_fastapi.types import stac as stac_types
1515
from stac_fastapi.types.links import ItemLinks, resolve_links
1616

@@ -139,45 +139,15 @@ def stac_to_db(
139139
Returns:
140140
stac_types.Collection: The database-ready STAC Collection object.
141141
"""
142+
from stac_fastapi.sfeos_helpers.database import add_bbox_shape_to_collection
143+
142144
collection = deepcopy(collection)
143145
collection["links"] = resolve_links(
144146
collection.get("links", []), str(request.base_url)
145147
)
146148

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-
159-
if len(bbox) >= 4:
160-
# Extract 2D coordinates (bbox can be 2D [minx, miny, maxx, maxy] or 3D [minx, miny, minz, maxx, maxy, maxz])
161-
# For 2D polygon, we only need the x,y coordinates and discard altitude (z) values
162-
minx, miny = bbox[0], bbox[1]
163-
if len(bbox) == 4:
164-
# 2D bbox: [minx, miny, maxx, maxy]
165-
maxx, maxy = bbox[2], bbox[3]
166-
else:
167-
# 3D bbox: [minx, miny, minz, maxx, maxy, maxz]
168-
# Extract indices 3,4 for maxx,maxy - discarding altitude at indices 2 (minz) and 5 (maxz)
169-
maxx, maxy = bbox[3], bbox[4]
170-
171-
# Convert bbox to GeoJSON polygon
172-
bbox_polygon_coords = bbox2polygon(minx, miny, maxx, maxy)
173-
collection["bbox_shape"] = {
174-
"type": "Polygon",
175-
"coordinates": bbox_polygon_coords,
176-
}
177-
else:
178-
logger.warning(
179-
f"Collection '{collection_id}': bbox has insufficient coordinates (length={len(bbox)}), expected at least 4"
180-
)
149+
# Convert bbox to bbox_shape for geospatial queries (ES/OS specific)
150+
add_bbox_shape_to_collection(collection)
181151

182152
if get_bool_env("STAC_INDEX_ASSETS"):
183153
collection["assets"] = [

stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
apply_intersects_filter_shared,
4949
populate_sort_shared,
5050
)
51-
from .utils import get_bool_env, validate_refresh
51+
from .utils import add_bbox_shape_to_collection, get_bool_env, validate_refresh
5252

5353
__all__ = [
5454
# Index operations
@@ -72,6 +72,7 @@
7272
# Utility functions
7373
"validate_refresh",
7474
"get_bool_env",
75+
"add_bbox_shape_to_collection",
7576
# Datetime utilities
7677
"return_date",
7778
"extract_date",

stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,94 @@
55
"""
66

77
import logging
8-
from typing import Dict, List, Union
8+
from typing import Any, Dict, List, Union
99

10-
from stac_fastapi.core.utilities import get_bool_env
10+
from stac_fastapi.core.utilities import bbox2polygon, get_bool_env
1111
from stac_fastapi.extensions.core.transaction.request import (
1212
PatchAddReplaceTest,
1313
PatchOperation,
1414
PatchRemove,
1515
)
1616
from stac_fastapi.sfeos_helpers.models.patch import ElasticPath, ESCommandSet
1717

18+
logger = logging.getLogger(__name__)
19+
20+
21+
def add_bbox_shape_to_collection(collection: Dict[str, Any]) -> bool:
22+
"""Add bbox_shape field to a collection document for spatial queries.
23+
24+
This function extracts the bounding box from a collection's spatial extent
25+
and converts it to a GeoJSON polygon shape that can be used for geospatial
26+
queries in Elasticsearch/OpenSearch.
27+
28+
Args:
29+
collection: Collection document dictionary to modify in-place.
30+
31+
Returns:
32+
bool: True if bbox_shape was added, False if it was skipped (already exists,
33+
no spatial extent, or invalid bbox).
34+
35+
Notes:
36+
- Modifies the collection dictionary in-place by adding a 'bbox_shape' field
37+
- Handles both 2D [minx, miny, maxx, maxy] and 3D [minx, miny, minz, maxx, maxy, maxz] bboxes
38+
- Uses the first bbox if multiple are present in the collection
39+
- Logs warnings for collections with invalid or missing bbox data
40+
"""
41+
collection_id = collection.get("id", "unknown")
42+
43+
# Check if bbox_shape already exists
44+
if "bbox_shape" in collection:
45+
logger.debug(
46+
f"Collection '{collection_id}' already has bbox_shape field, skipping"
47+
)
48+
return False
49+
50+
# Check if collection has spatial extent
51+
if "extent" not in collection or "spatial" not in collection["extent"]:
52+
logger.warning(f"Collection '{collection_id}' has no spatial extent, skipping")
53+
return False
54+
55+
spatial_extent = collection["extent"]["spatial"]
56+
if "bbox" not in spatial_extent or not spatial_extent["bbox"]:
57+
logger.warning(
58+
f"Collection '{collection_id}' has no bbox in spatial extent, skipping"
59+
)
60+
return False
61+
62+
# Get the first bbox (collections can have multiple bboxes, but we use the first one)
63+
bbox = (
64+
spatial_extent["bbox"][0]
65+
if isinstance(spatial_extent["bbox"][0], list)
66+
else spatial_extent["bbox"]
67+
)
68+
69+
if len(bbox) < 4:
70+
logger.warning(
71+
f"Collection '{collection_id}': bbox has insufficient coordinates (length={len(bbox)}), expected at least 4"
72+
)
73+
return False
74+
75+
# Extract 2D coordinates (bbox can be 2D [minx, miny, maxx, maxy] or 3D [minx, miny, minz, maxx, maxy, maxz])
76+
# For 2D polygon, we only need the x,y coordinates and discard altitude (z) values
77+
minx, miny = bbox[0], bbox[1]
78+
if len(bbox) == 4:
79+
# 2D bbox: [minx, miny, maxx, maxy]
80+
maxx, maxy = bbox[2], bbox[3]
81+
else:
82+
# 3D bbox: [minx, miny, minz, maxx, maxy, maxz]
83+
# Extract indices 3,4 for maxx,maxy - discarding altitude at indices 2 (minz) and 5 (maxz)
84+
maxx, maxy = bbox[3], bbox[4]
85+
86+
# Convert bbox to GeoJSON polygon
87+
bbox_polygon_coords = bbox2polygon(minx, miny, maxx, maxy)
88+
collection["bbox_shape"] = {
89+
"type": "Polygon",
90+
"coordinates": bbox_polygon_coords,
91+
}
92+
93+
logger.debug(f"Collection '{collection_id}': Added bbox_shape field")
94+
return True
95+
1896

1997
def validate_refresh(value: Union[str, bool]) -> str:
2098
"""

0 commit comments

Comments
 (0)