Example #1
0
    def polygon_chunk(self, size=64):
        stmt = (session.query(
            func.ST_Dump(Place.geom.cast(Geometry())).label('x')).filter_by(
                place_id=self.place_id).subquery())

        q = session.query(
            stmt.c.x.path[1],
            func.ST_Area(stmt.c.x.geom.cast(Geography)) / (1000 * 1000),
            func.Box2D(stmt.c.x.geom))

        for num, area, box2d in q:
            chunk_size = utils.calc_chunk_size(area, size=size)
            west, south, east, north = map(float, re_box.match(box2d).groups())
            for chunk in bbox_chunk((south, north, west, east), chunk_size):
                yield chunk
Example #2
0
def show_polygons(place_identifier):
    place = get_place(place_identifier)
    num = 0
    for chunk in place.polygon_chunk(size=64):
        num += 1
        print(chunk)

    print()
    print(num)

    return
    num = '(-?[0-9.]+)'
    re_box = re.compile(f'^BOX\({num} {num},{num} {num}\)$')

    # select ST_Dump(geom::geometry) as poly from place where osm_id=1543125
    stmt = (database.session.query(
        func.ST_Dump(Place.geom.cast(Geometry())).label('x')).filter_by(
            place_id=place.place_id).subquery())

    q = database.session.query(
        stmt.c.x.path[1],
        func.ST_Area(stmt.c.x.geom.cast(Geography)) / (1000 * 1000),
        func.Box2D(stmt.c.x.geom))
    print(q)

    for num, area, box2d in q:
        # west, south, east, north
        # BOX(135.8536855 20.2145811,136.3224209 20.6291059)

        size = wikidata_chunk_size(area)
        west, south, east, north = map(float, re_box.match(box2d).groups())
        bbox = (south, north, west, east)

        # print((num, area, size, box2d))

        for chunk in chunk_n(bbox, size):
            print(chunk)
Example #3
0
def get_collection_items(
    collection_id=None,
    roles=[],
    item_id=None,
    bbox=None,
    datetime=None,
    ids=None,
    collections=None,
    intersects=None,
    page=1,
    limit=10,
    query=None,
    **kwargs,
):
    """Retrieve a list of collection items based on filters.

    :param collection_id: Single Collection ID to include in the search for items.
                          Only Items in one of the provided Collection will be searched, defaults to None
    :type collection_id: str, optional
    :param item_id: item identifier, defaults to None
    :type item_id: str, optional
    :param bbox: bounding box for intersection [west, north, east, south], defaults to None
    :type bbox: list, optional
    :param datetime: Single date+time, or a range ('/' seperator), formatted to RFC 3339, section 5.6.
                     Use double dots '..' for open date ranges, defaults to None. If the start or end date of an image
                     generated by a temporal composition intersects the given datetime or range it will be included in the
                     result.
    :type datetime: str, optional
    :param ids: Array of Item ids to return. All other filter parameters that further restrict the
                number of search results are ignored, defaults to None
    :type ids: list, optional
    :param collections: Array of Collection IDs to include in the search for items.
                        Only Items in one of the provided Collections will be searched, defaults to None
    :type collections: list, optional
    :param intersects: Searches items by performing intersection between their geometry and provided GeoJSON geometry.
                       All GeoJSON geometry types must be supported., defaults to None
    :type intersects: dict, optional
    :param page: The page offset of results, defaults to 1
    :type page: int, optional
    :param limit: The maximum number of results to return (page size), defaults to 10
    :type limit: int, optional
    :return: list of collectio items
    :rtype: list
    """
    columns = [
        func.concat(Collection.name, "-",
                    Collection.version).label("collection"),
        Collection.collection_type,
        Collection._metadata.label("meta"),
        Item._metadata.label("item_meta"),
        Item.name.label("item"),
        Item.id,
        Item.collection_id,
        Item.start_date.label("start"),
        Item.end_date.label("end"),
        Item.assets,
        Item.created,
        Item.updated,
        cast(Item.cloud_cover, Float).label("cloud_cover"),
        func.ST_AsGeoJSON(Item.geom).label("geom"),
        func.Box2D(Item.geom).label("bbox"),
        Tile.name.label("tile"),
    ]

    where = [
        Collection.id == Item.collection_id,
        or_(Collection.is_public.is_(True),
            Collection.id.in_([int(r.split(":")[0]) for r in roles])),
    ]

    if ids is not None:
        where += [Item.name.in_(ids.split(","))]
    else:
        if collections is not None:
            where += [
                func.concat(Collection.name, "-",
                            Collection.version).in_(collections.split(","))
            ]
        elif collection_id is not None:
            where += [
                func.concat(Collection.name, "-",
                            Collection.version) == collection_id
            ]

        if item_id is not None:
            where += [Item.name.like(item_id)]

        if query:
            filters = create_query_filter(query)
            if filters:
                where += filters

        if intersects is not None:
            where += [
                func.ST_Intersects(func.ST_GeomFromGeoJSON(str(intersects)),
                                   Item.geom)
            ]
        elif bbox is not None:
            try:
                split_bbox = [float(x) for x in bbox.split(",")]
                if split_bbox[0] == split_bbox[2] or split_bbox[
                        1] == split_bbox[3]:
                    raise InvalidBoundingBoxError("")

                where += [
                    func.ST_Intersects(
                        func.ST_MakeEnvelope(
                            split_bbox[0],
                            split_bbox[1],
                            split_bbox[2],
                            split_bbox[3],
                            func.ST_SRID(Item.geom),
                        ),
                        Item.geom,
                    )
                ]
            except:
                raise (
                    InvalidBoundingBoxError(f"'{bbox}' is not a valid bbox."))

        if datetime is not None:
            date_filter = None
            if "/" in datetime:
                matches_open = ("..", "")
                time_start, time_end = datetime.split("/")
                if time_start in matches_open:  # open start
                    date_filter = [
                        or_(Item.start_date <= time_end,
                            Item.end_date <= time_end)
                    ]
                elif time_end in matches_open:  # open end
                    date_filter = [
                        or_(Item.start_date >= time_start,
                            Item.end_date >= time_start)
                    ]
                else:  # closed range
                    date_filter = [
                        or_(
                            and_(Item.start_date >= time_start,
                                 Item.start_date <= time_end),
                            and_(Item.end_date >= time_start,
                                 Item.end_date <= time_end),
                            and_(Item.start_date < time_start,
                                 Item.end_date > time_end),
                        )
                    ]
            else:
                date_filter = [
                    and_(Item.start_date <= datetime,
                         Item.end_date >= datetime)
                ]
            where += date_filter
    outer = [Item.tile_id == Tile.id]
    query = session.query(*columns).outerjoin(
        Tile, *outer).filter(*where).order_by(Item.start_date.desc(), Item.id)

    result = query.paginate(page=int(page),
                            per_page=int(limit),
                            error_out=False,
                            max_per_page=BDC_STAC_MAX_LIMIT)

    return result
Example #4
0
    def search_items(
        self,
        *,
        product_name: Optional[str] = None,
        time: Optional[Tuple[datetime, datetime]] = None,
        bbox: Tuple[float, float, float, float] = None,
        limit: int = 500,
        offset: int = 0,
        full_dataset: bool = False,
        dataset_ids: Sequence[UUID] = None,
        require_geometry=True,
        ordered=True,
    ) -> Generator[DatasetItem, None, None]:
        """
        Search datasets using Cubedash's spatial table

        Returned as DatasetItem records, with optional embedded full Datasets
        (if full_dataset==True)

        Returned results are always sorted by (center_time, id)
        """
        geom = func.ST_Transform(DATASET_SPATIAL.c.footprint, 4326)

        columns = [
            geom.label("geometry"),
            func.Box2D(geom).label("bbox"),
            # TODO: dataset label?
            DATASET_SPATIAL.c.region_code.label("region_code"),
            DATASET_SPATIAL.c.creation_time,
            DATASET_SPATIAL.c.center_time,
        ]

        # If fetching the whole dataset, we need to join the ODC dataset table.
        if full_dataset:
            query: Select = select(
                (*columns, *_utils.DATASET_SELECT_FIELDS)).select_from(
                    DATASET_SPATIAL.join(
                        ODC_DATASET,
                        onclause=ODC_DATASET.c.id == DATASET_SPATIAL.c.id))
        # Otherwise query purely from the spatial table.
        else:
            query: Select = select((*columns, DATASET_SPATIAL.c.id,
                                    DATASET_SPATIAL.c.dataset_type_ref
                                    )).select_from(DATASET_SPATIAL)

        if time:
            query = query.where(
                func.tstzrange(
                    _utils.default_utc(time[0]),
                    _utils.default_utc(time[1]),
                    "[]",
                    type_=TSTZRANGE,
                ).contains(DATASET_SPATIAL.c.center_time))

        if bbox:
            query = query.where(
                func.ST_Transform(DATASET_SPATIAL.c.footprint,
                                  4326).intersects(
                                      func.ST_MakeEnvelope(*bbox)))

        if product_name:
            query = query.where(DATASET_SPATIAL.c.dataset_type_ref == select(
                [ODC_DATASET_TYPE.c.id]).where(
                    ODC_DATASET_TYPE.c.name == product_name))

        if dataset_ids:
            query = query.where(DATASET_SPATIAL.c.id.in_(dataset_ids))

        if require_geometry:
            query = query.where(DATASET_SPATIAL.c.footprint != None)

        if ordered:
            query = query.order_by(DATASET_SPATIAL.c.center_time,
                                   DATASET_SPATIAL.c.id)

        query = query.limit(limit).offset(
            # TODO: Offset/limit isn't particularly efficient for paging...
            offset)

        for r in self._engine.execute(query):
            yield DatasetItem(
                dataset_id=r.id,
                bbox=_box2d_to_bbox(r.bbox) if r.bbox else None,
                product_name=self.index.products.get(r.dataset_type_ref).name,
                geometry=_get_shape(r.geometry),
                region_code=r.region_code,
                creation_time=r.creation_time,
                center_time=r.center_time,
                odc_dataset=(_utils.make_dataset_from_select_fields(
                    self.index, r) if full_dataset else None),
            )
Example #5
0
def get_collection_items(collection_id=None,
                         roles=[],
                         item_id=None,
                         bbox=None,
                         time=None,
                         ids=None,
                         collections=None,
                         cubes=None,
                         intersects=None,
                         page=1,
                         limit=10,
                         query=None,
                         **kwargs):
    """Retrieve a list of collection items based on filters.

    :param collection_id: Single Collection ID to include in the search for items.
                          Only Items in one of the provided Collection will be searched, defaults to None
    :type collection_id: str, optional
    :param item_id: item identifier, defaults to None
    :type item_id: str, optional
    :param bbox: bounding box for intersection [west, north, east, south], defaults to None
    :type bbox: list, optional
    :param time: Single date+time, or a range ('/' seperator), formatted to RFC 3339, section 5.6, defaults to None
    :type time: str, optional
    :param ids: Array of Item ids to return. All other filter parameters that further restrict the
                number of search results are ignored, defaults to None
    :type ids: list, optional
    :param collections: Array of Collection IDs to include in the search for items.
                        Only Items in one of the provided Collections will be searched, defaults to None
    :type collections: list, optional
    :param cubes: Bool indicating if only cubes should be returned, defaults to None
    :type cubes: bool, optional
    :param intersects: Searches items by performing intersection between their geometry and provided GeoJSON geometry.
                       All GeoJSON geometry types must be supported., defaults to None
    :type intersects: dict, optional
    :param page: The page offset of results, defaults to 1
    :type page: int, optional
    :param limit: The maximum number of results to return (page size), defaults to 10
    :type limit: int, optional
    :return: list of collectio items
    :rtype: list
    """
    columns = [
        Collection.name.label('collection'),
        Item.name.label('item'),
        Item.start_date.label('start'),
        Item.end_date.label('end'), Item.assets,
        func.ST_AsGeoJSON(Item.geom).label('geom'),
        func.Box2D(Item.geom).label('bbox'),
        Tile.name.label('tile')
    ]

    where = [
        Collection.id == Item.collection_id, Item.tile_id == Tile.id,
        or_(Collection.is_public.is_(True),
            Collection.id.in_([int(r.split(':')[0]) for r in roles]))
    ]

    if ids is not None:
        where += [Item.id.in_(ids.split(','))]
    elif item_id is not None:
        where += [Item.id.like(item_id)]
    else:
        if collections is not None:
            where += [Collection.name.in_(collections.split(','))]
        elif collection_id is not None:
            where += [Collection.name.like(collection_id)]

        if intersects is not None:
            where += [
                func.ST_Intersects(func.ST_GeomFromGeoJSON(str(intersects)),
                                   Item.geom)
            ]

        if query:
            filters = create_query_filter(query)
            if filters:
                where += filters

        if bbox is not None:
            try:
                split_bbox = [float(x) for x in bbox.split(',')]

                where += [
                    func.ST_Intersects(
                        func.ST_MakeEnvelope(split_bbox[0], split_bbox[1],
                                             split_bbox[2], split_bbox[3],
                                             func.ST_SRID(Item.geom)),
                        Item.geom)
                ]
            except:
                raise (
                    InvalidBoundingBoxError(f"'{bbox}' is not a valid bbox.'"))

        if time is not None:
            if "/" in time:
                time_start, time_end = time.split("/")
                time_end = datetime.fromisoformat(time_end)
                where += [
                    or_(Item.end_date <= time_end, Item.start_date <= time_end)
                ]
            else:
                time_start = datetime.fromisoformat(time)
            where += [
                or_(Item.start_date >= time_start, Item.end_date >= time_start)
            ]

    query = session.query(*columns).filter(*where).order_by(
        Item.start_date.desc())

    result = query.paginate(page=int(page),
                            per_page=int(limit),
                            error_out=False,
                            max_per_page=int(BDC_STAC_MAX_LIMIT))

    return result