|
1 | 1 | """Types for geojson_pydantic models"""
|
2 | 2 |
|
3 |
| -from typing import TYPE_CHECKING, List, Tuple, Union |
| 3 | +from typing import TYPE_CHECKING, Any, Callable, Generator, List, Tuple, Union |
4 | 4 |
|
5 |
| -from pydantic import conlist |
| 5 | +from pydantic import ConstrainedList, conlist |
6 | 6 |
|
7 |
| -BBox = Union[ |
8 |
| - Tuple[float, float, float, float], # 2D bbox |
9 |
| - Tuple[float, float, float, float, float, float], # 3D bbox |
10 |
| -] |
| 7 | + |
| 8 | +class _BBoxBase(ConstrainedList): |
| 9 | + """Base Class with additional Validation for order.""" |
| 10 | + |
| 11 | + # This is needed because pydantic checks it rather than `item_type` |
| 12 | + __args__ = (float,) |
| 13 | + |
| 14 | + item_type = float |
| 15 | + |
| 16 | + @classmethod |
| 17 | + def __get_validators__(cls) -> Generator[Callable[..., Any], None, None]: |
| 18 | + """Yield the validators.""" |
| 19 | + yield from super().__get_validators__() |
| 20 | + yield cls.validate_bbox |
| 21 | + |
| 22 | + @classmethod |
| 23 | + def validate_bbox(cls, bbox: List[float]) -> List[float]: |
| 24 | + """Validate BBox values are ordered correctly.""" |
| 25 | + if not bbox: |
| 26 | + return bbox |
| 27 | + |
| 28 | + offset = len(bbox) // 2 |
| 29 | + errors: List[str] = [] |
| 30 | + # Check X |
| 31 | + if bbox[0] > bbox[offset]: |
| 32 | + errors.append(f"Min X ({bbox[0]}) must be <= Max X ({bbox[offset]}).") |
| 33 | + # Check Y |
| 34 | + if bbox[1] > bbox[1 + offset]: |
| 35 | + errors.append(f"Min Y ({bbox[1]}) must be <= Max Y ({bbox[1 + offset]}).") |
| 36 | + # If 3D, check Z values. |
| 37 | + if offset > 2 and bbox[2] > bbox[2 + offset]: |
| 38 | + errors.append(f"Min Z ({bbox[2]}) must be <= Max Z ({bbox[2 + offset]}).") |
| 39 | + |
| 40 | + if errors: |
| 41 | + raise ValueError("Invalid BBox. Error(s): " + " ".join(errors)) |
| 42 | + |
| 43 | + return bbox |
| 44 | + |
| 45 | + |
| 46 | +class BBox2D(_BBoxBase): |
| 47 | + """2D Bounding Box""" |
| 48 | + |
| 49 | + min_items = 4 |
| 50 | + max_items = 4 |
| 51 | + |
| 52 | + |
| 53 | +class BBox3D(_BBoxBase): |
| 54 | + """3D Bounding Box""" |
| 55 | + |
| 56 | + min_items = 6 |
| 57 | + max_items = 6 |
| 58 | + |
| 59 | + |
| 60 | +BBox = Union[BBox3D, BBox2D] |
11 | 61 | Position = Union[Tuple[float, float], Tuple[float, float, float]]
|
12 | 62 |
|
13 | 63 | # Coordinate arrays
|
|
0 commit comments