Skip to content

request

stac_fastapi.extensions.core.collection_search.request

Request models for the Collection-Search extension.

BaseCollectionSearchGetRequest

Bases: APIRequest, DatetimeMixin

Basics additional Collection-Search parameters for the GET request.

Source code in stac_fastapi/extensions/core/collection_search/request.py
23
24
25
26
27
28
29
30
31
32
33
34
@attr.s
class BaseCollectionSearchGetRequest(APIRequest, DatetimeMixin):
    """Basics additional Collection-Search parameters for the GET request."""

    bbox: Optional[BBox] = attr.ib(default=None, converter=_bbox_converter)
    datetime: DateTimeQueryType = attr.ib(default=None, validator=_validate_datetime)
    limit: Annotated[
        Optional[Limit],
        Query(
            description="Limits the number of results that are included in each page of the response."  # noqa: E501
        ),
    ] = attr.ib(default=10)

BaseCollectionSearchPostRequest

Bases: BaseModel

Collection-Search POST model.

Source code in stac_fastapi/extensions/core/collection_search/request.py
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
class BaseCollectionSearchPostRequest(BaseModel):
    """Collection-Search POST model."""

    bbox: Optional[BBox] = Field(
        default=None,
        description="Only return items intersecting this bounding box. Mutually exclusive with **intersects**.",  # noqa: E501
        json_schema_extra={
            "example": [-175.05, -85.05, 175.05, 85.05],
        },
    )
    datetime: Optional[str] = Field(
        default=None,
        description="""Only return items that have a temporal property that intersects this value.\n
Either a date-time or an interval, open or closed. Date and time expressions adhere to RFC 3339. Open intervals are expressed using double-dots.""",  # noqa: E501
        json_schema_extra={
            "examples": {
                "datetime": {"value": "2018-02-12T23:20:50Z"},
                "closed-interval": {"value": "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z"},
                "open-interval-from": {"value": "2018-02-12T00:00:00Z/.."},
                "open-interval-to": {"value": "../2018-03-18T12:31:12Z"},
            },
        },
    )
    limit: Optional[Limit] = Field(
        10,
        description="Limits the number of results that are included in each page of the response (capped to 10_000).",  # noqa: E501
    )

    # Private properties to store the parsed datetime values.
    # Not part of the model schema.
    _start_date: Optional[dt] = PrivateAttr(default=None)
    _end_date: Optional[dt] = PrivateAttr(default=None)

    # Properties to return the private values
    @property
    def start_date(self) -> Optional[dt]:
        """start date."""
        return self._start_date

    @property
    def end_date(self) -> Optional[dt]:
        """end date."""
        return self._end_date

    @field_validator("bbox")
    @classmethod
    def validate_bbox(cls, v: BBox) -> BBox:
        """validate bbox."""
        if v:
            # Validate order
            if len(v) == 4:
                xmin, ymin, xmax, ymax = cast(Tuple[int, int, int, int], v)
            else:
                xmin, ymin, min_elev, xmax, ymax, max_elev = cast(
                    Tuple[int, int, int, int, int, int], v
                )
                if max_elev < min_elev:
                    raise ValueError(
                        "Maximum elevation must greater than minimum elevation"
                    )
            # Validate against WGS84
            if xmin < -180 or ymin < -90 or xmax > 180 or ymax > 90:
                raise ValueError("Bounding box must be within (-180, -90, 180, 90)")

            if ymax < ymin:
                raise ValueError(
                    "Maximum longitude must be greater than minimum longitude"
                )

        return v

    @field_validator("datetime", mode="after")
    @classmethod
    def validate_datetime(
        cls, value: Optional[str], info: ValidationInfo
    ) -> Optional[str]:
        """validate datetime."""
        # Split on "/" and replace no value or ".." with None
        if value is None:
            return value
        values = [v if v and v != ".." else None for v in value.split("/")]

        # If there are more than 2 dates, it's invalid
        if len(values) > 2:
            raise ValueError(
                """Invalid datetime range. Too many values. """
                """Must match format: {begin_date}/{end_date}"""
            )

        # If there is only one date, duplicate to use for both start and end dates
        if len(values) == 1:
            values = [values[0], values[0]]

        # Cast because pylance gets confused by the type adapter and annotated type
        dates = cast(
            List[Optional[dt]],
            [
                # Use the type adapter to validate the datetime strings,
                # strict is necessary due to pydantic issues #8736 and #8762
                SearchDatetime.validate_strings(v, strict=True) if v else None
                for v in values
            ],
        )

        # If there is a start and end date,
        # check that the start date is before the end date
        if dates[0] and dates[1] and dates[0] > dates[1]:
            raise ValueError(
                "Invalid datetime range. Begin date after end date. "
                "Must match format: {begin_date}/{end_date}"
            )

        # Store the parsed dates
        info.data["_start_date"] = dates[0]
        info.data["_end_date"] = dates[1]

        # Return the original string value
        return value

end_date property

end_date: Optional[datetime]

end date.

start_date property

start_date: Optional[datetime]

start date.

validate_bbox classmethod

validate_bbox(v: BBox) -> BBox

validate bbox.

Source code in stac_fastapi/extensions/core/collection_search/request.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
@field_validator("bbox")
@classmethod
def validate_bbox(cls, v: BBox) -> BBox:
    """validate bbox."""
    if v:
        # Validate order
        if len(v) == 4:
            xmin, ymin, xmax, ymax = cast(Tuple[int, int, int, int], v)
        else:
            xmin, ymin, min_elev, xmax, ymax, max_elev = cast(
                Tuple[int, int, int, int, int, int], v
            )
            if max_elev < min_elev:
                raise ValueError(
                    "Maximum elevation must greater than minimum elevation"
                )
        # Validate against WGS84
        if xmin < -180 or ymin < -90 or xmax > 180 or ymax > 90:
            raise ValueError("Bounding box must be within (-180, -90, 180, 90)")

        if ymax < ymin:
            raise ValueError(
                "Maximum longitude must be greater than minimum longitude"
            )

    return v

validate_datetime classmethod

validate_datetime(value: Optional[str], info: ValidationInfo) -> Optional[str]

validate datetime.

Source code in stac_fastapi/extensions/core/collection_search/request.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
@field_validator("datetime", mode="after")
@classmethod
def validate_datetime(
    cls, value: Optional[str], info: ValidationInfo
) -> Optional[str]:
    """validate datetime."""
    # Split on "/" and replace no value or ".." with None
    if value is None:
        return value
    values = [v if v and v != ".." else None for v in value.split("/")]

    # If there are more than 2 dates, it's invalid
    if len(values) > 2:
        raise ValueError(
            """Invalid datetime range. Too many values. """
            """Must match format: {begin_date}/{end_date}"""
        )

    # If there is only one date, duplicate to use for both start and end dates
    if len(values) == 1:
        values = [values[0], values[0]]

    # Cast because pylance gets confused by the type adapter and annotated type
    dates = cast(
        List[Optional[dt]],
        [
            # Use the type adapter to validate the datetime strings,
            # strict is necessary due to pydantic issues #8736 and #8762
            SearchDatetime.validate_strings(v, strict=True) if v else None
            for v in values
        ],
    )

    # If there is a start and end date,
    # check that the start date is before the end date
    if dates[0] and dates[1] and dates[0] > dates[1]:
        raise ValueError(
            "Invalid datetime range. Begin date after end date. "
            "Must match format: {begin_date}/{end_date}"
        )

    # Store the parsed dates
    info.data["_start_date"] = dates[0]
    info.data["_end_date"] = dates[1]

    # Return the original string value
    return value