Esempio n. 1
0
    def fetch(self, grouped: VirtualDatasetBox,
              **load_settings: Dict[str, Any]) -> xarray.Dataset:
        """ Convert grouped datasets to `xarray.Dataset`. """

        load_keys = self._LOAD_KEYS - {'measurements'}
        merged = merge_search_terms(select_keys(self, load_keys),
                                    select_keys(load_settings, load_keys))

        product = grouped.product_definitions[self._product]

        if 'measurements' in self and 'measurements' in load_settings:
            for measurement in load_settings['measurements']:
                self._assert(
                    measurement in self['measurements'],
                    '{} not found in {}'.format(measurement, self._product))

        measurement_dicts = self.output_measurements(
            grouped.product_definitions, load_settings.get('measurements'))

        if grouped.load_natively:
            canonical_names = [
                product.canonical_measurement(measurement)
                for measurement in measurement_dicts
            ]
            dataset_geobox = geobox_union_conservative([
                native_geobox(ds,
                              measurements=canonical_names,
                              basis=merged.get('like'))
                for ds in grouped.box.sum().item()
            ])

            if grouped.geopolygon is not None:
                reproject_roi = compute_reproject_roi(
                    dataset_geobox,
                    GeoBox.from_geopolygon(
                        grouped.geopolygon,
                        crs=dataset_geobox.crs,
                        align=dataset_geobox.alignment,
                        resolution=dataset_geobox.resolution))

                self._assert(reproject_roi.is_st,
                             "native load is not axis-aligned")
                self._assert(numpy.isclose(reproject_roi.scale, 1.0),
                             "native load should not require scaling")

                geobox = dataset_geobox[reproject_roi.roi_src]
            else:
                geobox = dataset_geobox
        else:
            geobox = grouped.geobox

        result = Datacube.load_data(grouped.box,
                                    geobox,
                                    list(measurement_dicts.values()),
                                    fuse_func=merged.get('fuse_func'),
                                    dask_chunks=merged.get('dask_chunks'),
                                    resampling=merged.get(
                                        'resampling', 'nearest'))

        return result
Esempio n. 2
0
def compute_native_load_geobox(dst_geobox: GeoBox,
                               ds: Dataset,
                               band: str,
                               buffer: Optional[float] = None) -> GeoBox:
    """
    Compute area of interest for a given Dataset given query.

    Take native projection and resolution from ``ds, band`` pair and compute
    region in that projection that fully encloses footprint of the
    ``dst_geobox`` with some padding. Construct GeoBox that encloses that
    region fully with resolution/pixel alignment copied from supplied band.

    :param dst_geobox:
    :param ds: Sample dataset (only resolution and projection is used, not footprint)
    :param band: Reference band to use (resolution of output GeoBox will match resolution of this band)
    :param buffer: Buffer in units of CRS of ``ds`` (meters usually), default is 10 pixels worth
    """
    native: GeoBox = native_geobox(ds, basis=band)
    if buffer is None:
        buffer = 10 * cast(float, max(map(abs,
                                          native.resolution)))  # type: ignore

    assert native.crs is not None
    return GeoBox.from_geopolygon(
        dst_geobox.extent.to_crs(native.crs).buffer(buffer),
        crs=native.crs,
        resolution=native.resolution,
        align=native.alignment,
    )
Esempio n. 3
0
    def __call__(self, index, product, time, group_by) -> Tile:
        # Do for a specific poly whose boundary is known
        output_crs = CRS(self.storage['crs'])
        filtered_items = [
            'geopolygon', 'lon', 'lat', 'longitude', 'latitude', 'x', 'y'
        ]
        filtered_dict = {
            k: v
            for k, v in self.input_region.items() if k in filtered_items
        }
        if self.feature is not None:
            filtered_dict['geopolygon'] = self.feature.geopolygon
            geopoly = filtered_dict['geopolygon']
        else:
            geopoly = query_geopolygon(**self.input_region)

        dc = Datacube(index=index)
        datasets = dc.find_datasets(product=product,
                                    time=time,
                                    group_by=group_by,
                                    **filtered_dict)
        group_by = query_group_by(group_by=group_by)
        sources = dc.group_datasets(datasets, group_by)
        output_resolution = [
            self.storage['resolution'][dim] for dim in output_crs.dimensions
        ]
        geopoly = geopoly.to_crs(output_crs)
        geobox = GeoBox.from_geopolygon(geopoly, resolution=output_resolution)

        return Tile(sources, geobox)
Esempio n. 4
0
    def __call__(self, product, time, group_by) -> Tile:
        # Do for a specific poly whose boundary is known
        output_crs = CRS(self.storage['crs'])
        filtered_item = [
            'geopolygon', 'lon', 'lat', 'longitude', 'latitude', 'x', 'y'
        ]
        filtered_dict = {
            k: v
            for k, v in filter(lambda t: t[0] in filtered_item,
                               self.input_region.items())
        }
        if 'feature_id' in self.input_region:
            filtered_dict['geopolygon'] = Geometry(
                self.input_region['geom_feat'],
                CRS(self.input_region['crs_txt']))
            geopoly = filtered_dict['geopolygon']
        else:
            geopoly = query_geopolygon(**self.input_region)
        datasets = self.dc.find_datasets(product=product,
                                         time=time,
                                         group_by=group_by,
                                         **filtered_dict)
        group_by = query_group_by(group_by=group_by)
        sources = self.dc.group_datasets(datasets, group_by)
        output_resolution = [
            self.storage['resolution'][dim] for dim in output_crs.dimensions
        ]
        geopoly = geopoly.to_crs(output_crs)
        geobox = GeoBox.from_geopolygon(geopoly, resolution=output_resolution)

        return Tile(sources, geobox)
def test_read_with_reproject(tmpdir):
    from datacube.testutils import mk_test_image
    from datacube.testutils.io import write_gtiff
    from pathlib import Path

    pp = Path(str(tmpdir))

    xx = mk_test_image(128, 64, nodata=None)
    assert (xx != -999).all()
    tile = AlbersGS.tile_geobox((17, -40))[:64, :128]

    mm = write_gtiff(pp / 'tst-read-with-reproject-128x64-int16.tif',
                     xx,
                     crs=str(tile.crs),
                     resolution=tile.resolution[::-1],
                     offset=tile.transform * (0, 0),
                     nodata=-999)
    assert mm.gbox == tile

    def _read(gbox,
              resampling='nearest',
              fallback_nodata=None,
              dst_nodata=-999):
        with RasterFileDataSource(mm.path, 1,
                                  nodata=fallback_nodata).open() as rdr:
            yy = np.full(gbox.shape, dst_nodata, dtype=rdr.dtype)
            roi = read_time_slice(rdr, yy, gbox, resampling, dst_nodata)
            return yy, roi

    gbox = gbx.pad(mm.gbox, 10)
    gbox = gbx.zoom_out(gbox, 0.873)
    yy, roi = _read(gbox)

    assert roi[0].start > 0 and roi[1].start > 0
    assert (yy[0] == -999).all()

    yy_expect, _ = rio_slurp(mm.path, gbox)
    np.testing.assert_array_equal(yy, yy_expect)

    gbox = gbx.zoom_out(mm.gbox[3:-3, 10:-10], 2.1)
    yy, roi = _read(gbox)

    assert roi_shape(roi) == gbox.shape
    assert not (yy == -999).any()

    gbox = GeoBox.from_geopolygon(mm.gbox.extent.to_crs(epsg3857).buffer(50),
                                  resolution=mm.gbox.resolution)

    assert gbox.extent.contains(mm.gbox.extent.to_crs(epsg3857))
    assert gbox.crs != mm.gbox.crs
    yy, roi = _read(gbox)
    assert roi[0].start > 0 and roi[1].start > 0
    assert (yy[0] == -999).all()

    gbox = gbx.zoom_out(gbox, 4)
    yy, roi = _read(gbox, resampling='average')
    nvalid = (yy != -999).sum()
    nempty = (yy == -999).sum()
    assert nvalid > nempty
Esempio n. 6
0
def compute_native_load_geobox(dst_geobox: GeoBox,
                               ds: Dataset,
                               band: str,
                               buffer: Optional[float] = None):

    native = native_geobox(ds, basis=band)
    if buffer is None:
        buffer = 10*max(map(abs, native.resolution))

    return GeoBox.from_geopolygon(dst_geobox.extent.to_crs(native.crs).buffer(buffer),
                                  crs=native.crs,
                                  resolution=native.resolution,
                                  align=native.alignment)
Esempio n. 7
0
def gbox_reproject(
    geobox: GeoBox,
    crs: SomeCRS,
    resolution: Optional[Tuple[int, int]] = None,
    pad: int = 0,
    pad_wh: Union[int, Tuple[int, int]] = 16,
) -> GeoBox:
    """
    Compute GeoBox in a given projection that fully encloses footprint of the source GeoBox.

    :param geobox: Source GeoBox

    :param crs: CRS of the output GeoBox

    :param resolution: Desired output resolution (defaults to source
                       resolution, if source and destination projections share
                       common units)

    :param pad: Padding in pixels of output GeoBox

    :param pad_wh: Expand output GeoBox some more such that (W%pad_wh) == 0 and (H%pad_wh) == 0
    """
    from datacube.utils.geometry import gbox

    crs = _norm_crs_or_error(crs)

    if resolution is None:
        if geobox.crs.units == crs.units:
            resolution = geobox.resolution
        else:
            raise NotImplementedError(
                "Source and destination projections have different units: have to supply desired output resolution"
            )

    out = GeoBox.from_geopolygon(geobox.extent, resolution, crs)
    if pad > 0:
        out = gbox.pad(out, pad)
    if pad_wh:
        if isinstance(pad_wh, int):
            pad_wh = max(1, pad_wh)
            pad_wh = (pad_wh, pad_wh)
        out = gbox.pad_wh(out, *pad_wh)

    return out
def test_can_paste():
    src = AlbersGS.tile_geobox((17, -40))

    def check_true(dst, **kwargs):
        ok, reason = can_paste(compute_reproject_roi(src, dst), **kwargs)
        if not ok:
            assert ok is True, reason

    def check_false(dst, **kwargs):
        ok, reason = can_paste(compute_reproject_roi(src, dst), **kwargs)
        if ok:
            assert ok is False, "Expected can_paste to return False, but got True"

    check_true(gbx.pad(src, 100))
    check_true(src[:10, :10])
    check_true(gbx.translate_pix(src, 0.1, 0.3), ttol=0.5)
    check_true(gbx.translate_pix(src, 3, -4))
    check_true(gbx.flipx(src))
    check_true(gbx.flipy(src))
    check_true(gbx.flipx(gbx.flipy(src)))
    check_true(gbx.zoom_out(src, 2))
    check_true(gbx.zoom_out(src, 4))
    check_true(gbx.zoom_out(src[:9, :9], 3))

    # Check False code paths
    dst = GeoBox.from_geopolygon(src.extent.to_crs(epsg3857).buffer(10),
                                 resolution=src.resolution)
    check_false(dst)  # non ST
    check_false(gbx.affine_transform_pix(src, Affine.rotation(1)))  # non ST

    check_false(gbx.zoom_out(src, 1.9))  # non integer scale
    check_false(gbx.affine_transform_pix(src, Affine.scale(1, 2)))  # sx != sy

    dst = gbx.translate_pix(src, -1, -1)
    dst = gbx.zoom_out(dst, 2)
    check_false(dst)  # src_roi doesn't align for scale

    check_false(dst, stol=0.7)  # src_roi/scale != dst_roi
    check_false(gbx.translate_pix(src, 0.3, 0.4))  # sub-pixel translation