|
5 | 5 | from base64 import urlsafe_b64decode, urlsafe_b64encode |
6 | 6 | from collections.abc import Iterable |
7 | 7 | from copy import deepcopy |
| 8 | +from dataclasses import dataclass |
8 | 9 | from typing import Any, Dict, List, Optional, Tuple, Type |
9 | 10 |
|
10 | 11 | import attr |
|
74 | 75 | logger = logging.getLogger(__name__) |
75 | 76 |
|
76 | 77 |
|
| 78 | +@dataclass |
| 79 | +class GeoShapeNode: |
| 80 | + geometry: "GeometryNode" |
| 81 | + |
| 82 | + |
| 83 | +@dataclass |
| 84 | +class GeometryNode: |
| 85 | + shape: "ShapeNode" |
| 86 | + relation: str |
| 87 | + |
| 88 | + |
| 89 | +@dataclass |
| 90 | +class ShapeNode: |
| 91 | + type: str |
| 92 | + coordinates: List[List[List[float]]] |
| 93 | + |
| 94 | + |
| 95 | +def analyze_bbox_query(node) -> dict: |
| 96 | + """Analyze and build bbox query""" |
| 97 | + if isinstance(node, GeoShapeNode): |
| 98 | + return analyze_bbox_query(node.geometry) |
| 99 | + elif isinstance(node, GeometryNode): |
| 100 | + shape_filter = analyze_bbox_query(node.shape) |
| 101 | + return {"shape": shape_filter, "relation": node.relation} |
| 102 | + elif isinstance(node, ShapeNode): |
| 103 | + if node.type == "polygon": |
| 104 | + return {"type": node.type, "coordinates": node.coordinates} |
| 105 | + return {} |
| 106 | + |
| 107 | + |
77 | 108 | async def create_index_templates() -> None: |
78 | 109 | """ |
79 | 110 | Create index templates for the Collection and Item indices. |
@@ -595,34 +626,15 @@ def apply_datetime_filter( |
595 | 626 |
|
596 | 627 | @staticmethod |
597 | 628 | def apply_bbox_filter(search: Search, bbox: List): |
598 | | - """Filter search results based on bounding box. |
| 629 | + """Filter search results based on bounding box using AST analysis pattern.""" |
| 630 | + polygon_coordinates = bbox2polygon(*bbox) |
599 | 631 |
|
600 | | - Args: |
601 | | - search (Search): The search object to apply the filter to. |
602 | | - bbox (List): The bounding box coordinates, represented as a list of four values [minx, miny, maxx, maxy]. |
603 | | -
|
604 | | - Returns: |
605 | | - search (Search): The search object with the bounding box filter applied. |
| 632 | + shape_node = ShapeNode(type="polygon", coordinates=polygon_coordinates) |
| 633 | + geometry_node = GeometryNode(shape=shape_node, relation="intersects") |
| 634 | + geo_shape_node = GeoShapeNode(geometry=geometry_node) |
606 | 635 |
|
607 | | - Notes: |
608 | | - The bounding box is transformed into a polygon using the `bbox2polygon` function and |
609 | | - a geo_shape filter is added to the search object, set to intersect with the specified polygon. |
610 | | - """ |
611 | | - return search.filter( |
612 | | - Q( |
613 | | - { |
614 | | - "geo_shape": { |
615 | | - "geometry": { |
616 | | - "shape": { |
617 | | - "type": "polygon", |
618 | | - "coordinates": bbox2polygon(*bbox), |
619 | | - }, |
620 | | - "relation": "intersects", |
621 | | - } |
622 | | - } |
623 | | - } |
624 | | - ) |
625 | | - ) |
| 636 | + bbox_query_dict = analyze_bbox_query(geo_shape_node) |
| 637 | + return search.filter(Q("geo_shape", geometry=bbox_query_dict)) |
626 | 638 |
|
627 | 639 | @staticmethod |
628 | 640 | def apply_intersects_filter( |
|
0 commit comments