예제 #1
0
    def _get_raster_tile(self, path: str, *,
                         upsampling_method: str,
                         downsampling_method: str,
                         bounds: Tuple[float, float, float, float] = None,
                         tile_size: Tuple[int, int] = (256, 256),
                         preserve_values: bool = False) -> np.ma.MaskedArray:
        """Load a raster dataset from a file through rasterio.

        Heavily inspired by mapbox/rio-tiler
        """
        import rasterio
        from rasterio import transform, windows, warp
        from rasterio.vrt import WarpedVRT
        from affine import Affine

        dst_bounds: Tuple[float, float, float, float]

        if preserve_values:
            upsampling_enum = downsampling_enum = self._get_resampling_enum('nearest')
        else:
            upsampling_enum = self._get_resampling_enum(upsampling_method)
            downsampling_enum = self._get_resampling_enum(downsampling_method)

        with contextlib.ExitStack() as es:
            es.enter_context(rasterio.Env(**self._RIO_ENV_KEYS))
            try:
                with trace('open_dataset'):
                    src = es.enter_context(rasterio.open(path))
            except OSError:
                raise IOError('error while reading file {}'.format(path))

            # compute suggested resolution and bounds in target CRS
            dst_transform, _, _ = self._calculate_default_transform(
                src.crs, self._TARGET_CRS, src.width, src.height, *src.bounds
            )
            dst_res = (abs(dst_transform.a), abs(dst_transform.e))
            dst_bounds = warp.transform_bounds(src.crs, self._TARGET_CRS, *src.bounds)

            if bounds is None:
                bounds = dst_bounds

            # pad tile bounds to prevent interpolation artefacts
            num_pad_pixels = 2

            # compute tile VRT shape and transform
            dst_width = max(1, round((bounds[2] - bounds[0]) / dst_res[0]))
            dst_height = max(1, round((bounds[3] - bounds[1]) / dst_res[1]))
            vrt_transform = (
                transform.from_bounds(*bounds, width=dst_width, height=dst_height)
                * Affine.translation(-num_pad_pixels, -num_pad_pixels)
            )
            vrt_height, vrt_width = dst_height + 2 * num_pad_pixels, dst_width + 2 * num_pad_pixels

            # remove padding in output
            out_window = windows.Window(
                col_off=num_pad_pixels, row_off=num_pad_pixels, width=dst_width, height=dst_height
            )

            # construct VRT
            vrt = es.enter_context(
                WarpedVRT(
                    src, crs=self._TARGET_CRS, resampling=upsampling_enum, add_alpha=True,
                    transform=vrt_transform, width=vrt_width, height=vrt_height
                )
            )

            # prevent loads of very sparse data
            out_window_bounds = windows.bounds(out_window, vrt_transform)
            cover_ratio = (
                (dst_bounds[2] - dst_bounds[0]) / (out_window_bounds[2] - out_window_bounds[0])
                * (dst_bounds[3] - dst_bounds[1]) / (out_window_bounds[3] - out_window_bounds[1])
            )

            if cover_ratio < 0.01:
                raise exceptions.TileOutOfBoundsError('dataset covers less than 1% of tile')

            # determine whether we are upsampling or downsampling
            pixel_ratio = min(out_window.width / tile_size[1], out_window.height / tile_size[0])
            if pixel_ratio < 1:
                resampling_enum = upsampling_enum
            else:
                resampling_enum = downsampling_enum

            # read data
            with warnings.catch_warnings(), trace('read_from_vrt'):
                warnings.filterwarnings('ignore', message='invalid value encountered.*')
                tile_data = vrt.read(
                    1, resampling=resampling_enum, window=out_window, out_shape=tile_size
                )
                # read alpha mask
                mask_idx = src.count + 1
                mask = vrt.read(mask_idx, window=out_window, out_shape=tile_size) == 0
                if src.nodata is not None:
                    mask |= tile_data == src.nodata

        return np.ma.masked_array(tile_data, mask=mask)
예제 #2
0
def get_raster_tile(
        path: str,
        *,
        reprojection_method: str = "nearest",
        resampling_method: str = "nearest",
        tile_bounds: Tuple[float, float, float, float] = None,
        tile_size: Tuple[int, int] = (256, 256),
        preserve_values: bool = False,
        target_crs: str = 'epsg:3857',
        rio_env_options: Dict[str, Any] = None) -> np.ma.MaskedArray:
    """Load a raster dataset from a file through rasterio.

    Heavily inspired by mapbox/rio-tiler
    """
    import rasterio
    from rasterio import transform, windows, warp
    from rasterio.vrt import WarpedVRT
    from affine import Affine

    dst_bounds: Tuple[float, float, float, float]

    if rio_env_options is None:
        rio_env_options = {}

    if preserve_values:
        reproject_enum = resampling_enum = get_resampling_enum('nearest')
    else:
        reproject_enum = get_resampling_enum(reprojection_method)
        resampling_enum = get_resampling_enum(resampling_method)

    with contextlib.ExitStack() as es:
        es.enter_context(rasterio.Env(**rio_env_options))
        try:
            with trace('open_dataset'):
                src = es.enter_context(rasterio.open(path))
        except OSError:
            raise IOError('error while reading file {}'.format(path))

        # compute buonds in target CRS
        dst_bounds = warp.transform_bounds(src.crs, target_crs, *src.bounds)

        if tile_bounds is None:
            tile_bounds = dst_bounds

        # prevent loads of very sparse data
        cover_ratio = ((dst_bounds[2] - dst_bounds[0]) /
                       (tile_bounds[2] - tile_bounds[0]) *
                       (dst_bounds[3] - dst_bounds[1]) /
                       (tile_bounds[3] - tile_bounds[1]))

        if cover_ratio < 0.01:
            raise exceptions.TileOutOfBoundsError(
                'dataset covers less than 1% of tile')

        # compute suggested resolution in target CRS
        dst_transform, _, _ = warp.calculate_default_transform(
            src.crs, target_crs, src.width, src.height, *src.bounds)
        dst_res = (abs(dst_transform.a), abs(dst_transform.e))

        # in some cases (e.g. at extreme latitudes), the default transform
        # suggests very coarse resolutions - in this case, fall back to native tile res
        tile_transform = transform.from_bounds(*tile_bounds, *tile_size)
        tile_res = (abs(tile_transform.a), abs(tile_transform.e))

        if tile_res[0] < dst_res[0] or tile_res[1] < dst_res[1]:
            dst_res = tile_res
            resampling_enum = get_resampling_enum('nearest')

        # pad tile bounds to prevent interpolation artefacts
        num_pad_pixels = 2

        # compute tile VRT shape and transform
        dst_width = max(1, round(
            (tile_bounds[2] - tile_bounds[0]) / dst_res[0]))
        dst_height = max(1,
                         round((tile_bounds[3] - tile_bounds[1]) / dst_res[1]))
        vrt_transform = (transform.from_bounds(
            *tile_bounds, width=dst_width, height=dst_height) *
                         Affine.translation(-num_pad_pixels, -num_pad_pixels))
        vrt_height, vrt_width = dst_height + 2 * num_pad_pixels, dst_width + 2 * num_pad_pixels

        # remove padding in output
        out_window = windows.Window(col_off=num_pad_pixels,
                                    row_off=num_pad_pixels,
                                    width=dst_width,
                                    height=dst_height)

        # construct VRT
        vrt = es.enter_context(
            WarpedVRT(src,
                      crs=target_crs,
                      resampling=reproject_enum,
                      transform=vrt_transform,
                      width=vrt_width,
                      height=vrt_height,
                      add_alpha=not has_alpha_band(src)))

        # read data
        with warnings.catch_warnings(), trace('read_from_vrt'):
            warnings.filterwarnings('ignore',
                                    message='invalid value encountered.*')
            tile_data = vrt.read(1,
                                 resampling=resampling_enum,
                                 window=out_window,
                                 out_shape=tile_size)

            # assemble alpha mask
            mask_idx = vrt.count
            mask = vrt.read(mask_idx, window=out_window,
                            out_shape=tile_size) == 0

            if src.nodata is not None:
                mask |= tile_data == src.nodata

    return np.ma.masked_array(tile_data, mask=mask)