Beispiel #1
0
def test_transform_bounds_densify():
    # This transform is non-linear along the edges, so densification produces
    # a different result than otherwise
    src_crs = {'init': 'EPSG:4326'}
    dst_crs = {'init': 'EPSG:32610'}
    assert numpy.allclose(
        transform_bounds(
            src_crs,
            dst_crs,
            -120, 40, -80, 64,
            densify_pts=0
        ),
        (
            646695.227266598, 4432069.056898901,
            4201818.984205882, 7807592.187464975
        )
    )

    assert numpy.allclose(
        transform_bounds(
            src_crs,
            dst_crs,
            -120, 40, -80, 64,
            densify_pts=100
        ),
        (
            646695.2272665979, 4432069.056898901,
            4201818.984205882, 7807592.187464977
        )
    )
Beispiel #2
0
def test_transform_bounds_densify_out_of_bounds():
    with pytest.raises(ValueError):
        transform_bounds(
            {'init': 'EPSG:4326'},
            {'init': 'EPSG:32610'},
            -120, 40, -80, 64,
            densify_pts=-10
        )
Beispiel #3
0
def test_transform_bounds():
    """CRSError is raised."""
    left, bottom, right, top = (
        -11740727.544603072, 4852834.0517692715, -11584184.510675032,
        5009377.085697309)
    src_crs = 'EPSG:3857'
    dst_crs = {'proj': 'foobar'}
    with pytest.raises(CRSError):
        transform_bounds(src_crs, dst_crs, left, bottom, right, top)
Beispiel #4
0
def test_transform_bounds_densify_out_of_bounds():
    with pytest.raises(ValueError):
        transform_bounds(
            {"init": "epsg:4326"},
            {"init": "epsg:32610"},
            -120,
            40,
            -80,
            64,
            densify_pts=-10,
        )
Beispiel #5
0
def test_transform_bounds_densify():
    # This transform is non-linear along the edges, so densification produces
    # a different result than otherwise
    src_crs = {"init": "epsg:4326"}
    dst_crs = {"init": "epsg:2163"}
    assert np.allclose(
        transform_bounds(src_crs, dst_crs, -120, 40, -80, 64, densify_pts=0),
        (-1684649.41338, -350356.81377, 1684649.41338, 2234551.18559),
    )

    assert np.allclose(
        transform_bounds(src_crs, dst_crs, -120, 40, -80, 64, densify_pts=100),
        (-1684649.41338, -555777.79210, 1684649.41338, 2234551.18559),
    )
def generate_chunk_tasks(image_source, tile_dim):
    tasks = []
    zoom = image_source.zoom
    (min_col, max_col) = (image_source.tile_bounds[0], image_source.tile_bounds[2])
    (min_row, max_row) = (image_source.tile_bounds[1], image_source.tile_bounds[3])

    for tile_col in range(min_col, min(max_col + 1, 2**zoom)):
        for tile_row in range(min_row, min(max_row + 1, 2**zoom)):
            tile_bounds = mercantile.bounds(tile_col, tile_row, zoom)
            (wm_left, wm_bottom, wm_right, wm_top)  = warp.transform_bounds("EPSG:4326",
                                                                           "EPSG:3857",
                                                                            tile_bounds.west,
                                                                            tile_bounds.south,
                                                                            tile_bounds.east,
                                                                            tile_bounds.north)
            affine = transform.from_bounds(wm_left, wm_bottom, wm_right, wm_top, tile_dim, tile_dim)
            target_meta = { 
                "transform": affine[:6],
                "width": tile_dim,
                "height": tile_dim 
            }

            target = os.path.join(image_source.image_folder, "%d/%d/%d.tif" % (zoom, tile_col, tile_row))
            task = ChunkTask(source_uri=image_source.source_uri,
                             target_meta=target_meta,
                             target=target)

            tasks.append(task)

    return tasks
Beispiel #7
0
def test_transform_bounds__esri_wkt():
    left, bottom, right, top = \
        (-78.95864996545055, 23.564991210854686,
         -76.57492370013823, 25.550873767433984)
    dst_projection_string = (
        'PROJCS["USA_Contiguous_Albers_Equal_Area_Conic_USGS_version",'
        'GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",'
        'SPHEROID["GRS_1980",6378137.0,298.257222101]],'
        'PRIMEM["Greenwich",0.0],'
        'UNIT["Degree",0.0174532925199433]],'
        'PROJECTION["Albers"],'
        'PARAMETER["false_easting",0.0],'
        'PARAMETER["false_northing",0.0],'
        'PARAMETER["central_meridian",-96.0],'
        'PARAMETER["standard_parallel_1",29.5],'
        'PARAMETER["standard_parallel_2",45.5],'
        'PARAMETER["latitude_of_origin",23.0],'
        'UNIT["Meter",1.0],'
        'VERTCS["NAVD_1988",'
        'VDATUM["North_American_Vertical_Datum_1988"],'
        'PARAMETER["Vertical_Shift",0.0],'
        'PARAMETER["Direction",1.0],UNIT["Centimeter",0.01]]]')
    assert np.allclose(
        transform_bounds({"init": "epsg:4326"},
                         dst_projection_string,
                         left,
                         bottom,
                         right,
                         top),
        (
            1721263.7931814701,
            219684.49332178483,
            2002926.56696663,
            479360.16562217404),
    )
Beispiel #8
0
def test_transform_bounds_no_change():
    """Make sure that going from and to the same crs causes no change."""
    with rasterio.open('tests/data/RGB.byte.tif') as src:
        l, b, r, t = src.bounds
        assert np.allclose(
            transform_bounds(src.crs, src.crs, l, b, r, t),
            src.bounds
        )
Beispiel #9
0
def get_bounds(path):
    """ Retrun bounds in WGS84 system """

    with rasterio.drivers():
        src = rasterio.open(path)

        return transform_bounds(
                    src.crs,
                    {'init': 'EPSG:4326'},
                    *src.bounds)
Beispiel #10
0
def test_transform_bounds():
    with rasterio.open('tests/data/RGB.byte.tif') as src:
        l, b, r, t = src.bounds
        assert np.allclose(
            transform_bounds(src.crs, {'init': 'EPSG:4326'}, l, b, r, t),
            (
                -78.95864996545055, 23.564991210854686,
                -76.57492370013823, 25.550873767433984
            )
        )
Beispiel #11
0
    def clip(self):
        """ Clip images based on bounds provided
        Implementation is borrowed from
        https://github.com/brendan-ward/rasterio/blob/e3687ce0ccf8ad92844c16d913a6482d5142cf48/rasterio/rio/convert.py
        """

        self.output("Clipping", normal=True)

        # create new folder for clipped images
        path = check_create_folder(join(self.scene_path, 'clipped'))

        try:
            temp_bands = copy(self.bands)
            temp_bands.append('QA')
            for i, band in enumerate(temp_bands):
                band_name = self._get_full_filename(band)
                band_path = join(self.scene_path, band_name)

                self.output("Band %s" % band, normal=True, color='green', indent=1)
                with rasterio.open(band_path) as src:
                    bounds = transform_bounds(
                        {
                            'proj': 'longlat',
                            'ellps': 'WGS84',
                            'datum': 'WGS84',
                            'no_defs': True
                        },
                        src.crs,
                        *self.bounds
                    )

                    if disjoint_bounds(bounds, src.bounds):
                        bounds = adjust_bounding_box(src.bounds, bounds)

                    window = src.window(*bounds)

                    out_kwargs = src.meta.copy()
                    out_kwargs.update({
                        'driver': 'GTiff',
                        'height': window[0][1] - window[0][0],
                        'width': window[1][1] - window[1][0],
                        'transform': src.window_transform(window)
                    })

                    with rasterio.open(join(path, band_name), 'w', **out_kwargs) as out:
                        out.write(src.read(window=window))

            # Copy MTL to the clipped folder
            copyfile(join(self.scene_path, self.scene + '_MTL.txt'), join(path, self.scene + '_MTL.txt'))

            return path

        except IOError as e:
            exit(e.message, 1)
Beispiel #12
0
def test_transform_bounds():
    with rasterio.open("tests/data/RGB.byte.tif") as src:
        l, b, r, t = src.bounds
        assert np.allclose(
            transform_bounds(src.crs, {"init": "epsg:4326"}, l, b, r, t),
            (
                -78.95864996545055,
                23.564991210854686,
                -76.57492370013823,
                25.550873767433984,
            ),
        )
Beispiel #13
0
def tile(
    src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT],
    x: int,
    y: int,
    z: int,
    tilesize: int = 256,
    **kwargs,
) -> Tuple[numpy.ndarray, numpy.ndarray]:
    """
    Read mercator tile from an image.

    Attributes
    ----------
        src_dst : rasterio.io.DatasetReader
            rasterio.io.DatasetReader object
        x : int
            Mercator tile X index.
        y : int
            Mercator tile Y index.
        z : int
            Mercator tile ZOOM level.
        tilesize : int, optional
            Output tile size. Default is 256.
        kwargs : Any, optional
            Additional options to forward to part()

    Returns
    -------
        data : numpy ndarray
        mask: numpy array

    """
    warnings.warn(
        "'rio_tiler.reader.tile' will be deprecated in rio-tiler 2.0",
        DeprecationWarning,
    )

    bounds = transform_bounds(
        src_dst.crs, constants.WGS84_CRS, *src_dst.bounds, densify_pts=21
    )
    if not tile_exists(bounds, z, x, y):
        raise TileOutsideBounds(f"Tile {z}/{x}/{y} is outside image bounds")

    tile_bounds = mercantile.xy_bounds(mercantile.Tile(x=x, y=y, z=z))
    return part(
        src_dst,
        tile_bounds,
        tilesize,
        tilesize,
        dst_crs=constants.WEB_MERCATOR_CRS,
        **kwargs,
    )
Beispiel #14
0
def get_zooms(
    src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT],
    ensure_global_max_zoom: bool = False,
    tilesize: int = 256,
) -> Tuple[int, int]:
    """
    Calculate raster min/max mercator zoom level.

    Parameters
    ----------
    src_dst: rasterio.io.DatasetReader
        Rasterio io.DatasetReader object
    ensure_global_max_zoom: bool, optional
        Apply latitude correction factor to ensure max_zoom equality for global
        datasets covering different latitudes (default: False).
    tilesize: int, optional
        Mercator tile size (default: 256).

    Returns
    -------
    min_zoom, max_zoom: Tuple
        Min/Max Mercator zoom levels.

    """
    bounds = transform_bounds(src_dst.crs,
                              constants.WGS84_CRS,
                              *src_dst.bounds,
                              densify_pts=21)
    center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2]
    lat = center[1] if ensure_global_max_zoom else 0

    dst_affine, w, h = calculate_default_transform(
        src_dst.crs,
        constants.WEB_MERCATOR_CRS,
        src_dst.width,
        src_dst.height,
        *src_dst.bounds,
    )

    mercator_resolution = max(abs(dst_affine[0]), abs(dst_affine[4]))

    # Correction factor for web-mercator projection latitude scale change
    latitude_correction_factor = math.cos(math.radians(lat))
    adjusted_resolution = mercator_resolution * latitude_correction_factor

    max_zoom = zoom_for_pixelsize(adjusted_resolution, tilesize=tilesize)

    overview_level = get_maximum_overview_level(w, h, minsize=tilesize)
    ovr_resolution = adjusted_resolution * (2**overview_level)
    min_zoom = zoom_for_pixelsize(ovr_resolution, tilesize=tilesize)

    return (min_zoom, max_zoom)
Beispiel #15
0
def tile(address,
         tile_x,
         tile_y,
         tile_z,
         indexes=None,
         tilesize=256,
         nodata=None):
    """
    Create mercator tile from any images.

    Attributes
    ----------
    address : str
        file url.
    tile_x : int
        Mercator tile X index.
    tile_y : int
        Mercator tile Y index.
    tile_z : int
        Mercator tile ZOOM level.
    indexes : tuple, int, optional (default: (1, 2, 3))
        Bands indexes for the RGB combination.
    tilesize : int, optional (default: 256)
        Output image size.
    nodata: int or float, optional
        Overwrite nodata value for mask creation.

    Returns
    -------
    data : numpy ndarray
    mask: numpy array

    """
    with rasterio.open(address) as src:
        wgs_bounds = transform_bounds(*[src.crs, 'epsg:4326'] +
                                      list(src.bounds),
                                      densify_pts=21)

        indexes = indexes if indexes is not None else src.indexes

        if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y):
            raise TileOutsideBounds(
                'Tile {}/{}/{} is outside image bounds'.format(
                    tile_z, tile_x, tile_y))

        mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z)
        tile_bounds = mercantile.xy_bounds(mercator_tile)
        return utils.tile_read(src,
                               tile_bounds,
                               tilesize,
                               indexes=indexes,
                               nodata=nodata)
Beispiel #16
0
    def get_sources(self, bounds, resolution):
        bounds, bounds_crs = bounds
        zoom = get_zoom(max(resolution))
        left, bottom, right, top = warp.transform_bounds(
            bounds_crs, WGS84_CRS, *bounds)

        if (self._bounds[0] <= left <= self._bounds[2]
                or self._bounds[0] <= right <= self._bounds[2]) and (
                    self._bounds[1] <= bottom <= self._bounds[3]
                    or self._bounds[1] <= top <= self._bounds[3]) and (
                        self._minzoom <= zoom <= self._maxzoom):
            yield Source(self._source, self._name, self._resolution, {}, {},
                         {"imagery": True})
Beispiel #17
0
def test_get_bounding_box():
    src_crs = CRS(init='epsg:4326')
    dst_crs = CRS(init='epsg:32718')
    src_bounds = dict(xmin=-73.309037, ymin=-40.665865,
                      xmax=-72.723835, ymax=-40.026434)

    gv = GeoVector.from_bounds(crs=src_crs, **src_bounds)
    bounds = transform_bounds(src_crs=src_crs, dst_crs=dst_crs,
                              left=src_bounds['xmin'], bottom=src_bounds['ymin'],
                              right=src_bounds['xmax'], top=src_bounds['ymax'])

    assert gv.get_bounding_box(dst_crs).almost_equals(
        GeoVector.from_bounds(*bounds, crs=dst_crs))
Beispiel #18
0
def metadata(sceneid, pmin=2, pmax=98, **kwargs):
    """
    Retrieve image bounds and band statistics.

    Attributes
    ----------
        sceneid : str
            Sentinel-2 sceneid.
        pmin : int, optional, (default: 2)
            Histogram minimum cut.
        pmax : int, optional, (default: 98)
            Histogram maximum cut.
        kwargs : optional
            These are passed to 'rio_tiler.sentinel2._sentinel_stats'
            e.g: histogram_bins=20'

    Returns
    -------
        out : dict
            Dictionary with image bounds and bands statistics.

    """
    scene_params = _sentinel_parse_scene_id(sceneid)
    path_prefix = os.path.join(scene_params["aws_bucket"],
                               scene_params["aws_prefix"])
    preview_file = os.path.join(path_prefix, scene_params["preview_file"])

    dst_crs = CRS({"init": "EPSG:4326"})
    with rasterio.open(preview_file) as src:
        bounds = transform_bounds(*[src.crs, dst_crs] + list(src.bounds),
                                  densify_pts=21)

    info = {"sceneid": sceneid}
    info["bounds"] = {"value": bounds, "crs": dst_crs.to_string()}

    addresses = [
        "{}/{}/B{}.jp2".format(path_prefix, scene_params["preview_prefix"],
                               band) for band in scene_params["bands"]
    ]
    _stats_worker = partial(_sentinel_stats,
                            percentiles=(pmin, pmax),
                            **kwargs)
    with futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
        responses = executor.map(_stats_worker, addresses)

    info["statistics"] = {
        b: v
        for b, d in zip(scene_params["bands"], responses)
        for k, v in d.items()
    }
    return info
Beispiel #19
0
def _get_warped_array(
    input_file=None, band_idx=None, dst_bounds=None, dst_shape=None,
    dst_affine=None, dst_crs=None, resampling="nearest"
):
    """Extract a numpy array from a raster file."""
    LOGGER.debug("read array using rasterio")
    with rasterio.open(input_file, "r") as src:
        if dst_crs == src.crs:
            src_left, src_bottom, src_right, src_top = dst_bounds
        else:
            # Return empty array if destination bounds don't intersect with
            # file bounds.
            file_bbox = box(*src.bounds)
            tile_bbox = reproject_geometry(
                box(*dst_bounds), src_crs=dst_crs, dst_crs=src.crs)
            if not file_bbox.intersects(tile_bbox):
                LOGGER.debug("file bounding box does not intersect with tile")
                return ma.MaskedArray(
                    data=ma.zeros(dst_shape, dtype=src.profile["dtype"]),
                    mask=ma.ones(dst_shape), fill_value=src.nodata)
            # Reproject tile bounds to source file SRS.
            src_left, src_bottom, src_right, src_top = transform_bounds(
                dst_crs, src.crs, *dst_bounds, densify_pts=21)

        if float('Inf') in (src_left, src_bottom, src_right, src_top):
            # Maybe not the best way to deal with it, but if bounding box
            # cannot be translated, it is assumed that data is emtpy
            LOGGER.debug("tile seems to be outside of input CRS bounds")
            return ma.MaskedArray(
                data=ma.zeros(dst_shape, dtype=src.profile["dtype"]),
                mask=ma.ones(dst_shape), fill_value=src.nodata)
        # Read data window.
        window = src.window(
            src_left, src_bottom, src_right, src_top, boundless=True)
        start = time.time()
        src_band = src.read(band_idx, window=window, boundless=True)
        LOGGER.debug("window read in %ss" % round(time.time() - start, 3))
        # Quick fix because None nodata is not allowed.
        nodataval = 0 if not src.nodata else src.nodata
        # Prepare reprojected array.
        dst_band = np.empty(dst_shape, src.dtypes[band_idx-1])
        # Run rasterio's reproject().
        start = time.time()
        reproject(
            src_band, dst_band, src_transform=src.window_transform(window),
            src_crs=src.crs, src_nodata=nodataval, dst_transform=dst_affine,
            dst_crs=dst_crs, dst_nodata=nodataval,
            resampling=RESAMPLING_METHODS[resampling])
        LOGGER.debug(
            "window reprojected in %ss" % round(time.time() - start, 3))
        return ma.MaskedArray(dst_band, mask=dst_band == nodataval)
Beispiel #20
0
        def __call__(self):
            for i, path in enumerate(input):
                with rasterio.open(path) as src:
                    bounds = src.bounds
                    if dst_crs:
                        bbox = transform_bounds(src.crs, dst_crs, *bounds)
                    elif projection == 'mercator':
                        bbox = transform_bounds(src.crs, {'init': 'epsg:3857'},
                                                *bounds)
                    elif projection == 'geographic':
                        bbox = transform_bounds(src.crs, {'init': 'epsg:4326'},
                                                *bounds)
                    else:
                        bbox = bounds

                if precision >= 0:
                    bbox = [round(b, precision) for b in bbox]

                yield {
                    'type': 'Feature',
                    'bbox': bbox,
                    'geometry': {
                        'type':
                        'Polygon',
                        'coordinates':
                        [[[bbox[0], bbox[1]], [bbox[2], bbox[1]],
                          [bbox[2], bbox[3]], [bbox[0], bbox[3]],
                          [bbox[0], bbox[1]]]]
                    },
                    'properties': {
                        'id': str(i),
                        'title': path,
                        'filename': os.path.basename(path)
                    }
                }

                self._xs.extend(bbox[::2])
                self._ys.extend(bbox[1::2])
Beispiel #21
0
def get_gz_scene_bounds(fn):
    assert _exists(fn), fn
    assert fn.endswith('.tar.gz')

    tf = tarfile.open(fn, 'r|gz')

    member = tf.next()

    while not member.name.lower().endswith('.tif'):
        member = tf.next()

    data = io.BytesIO(tf.extractfile(member).read())
    ds = MemoryFile(data).open()
    return transform_bounds(ds.crs, 'EPSG:4326', *ds.bounds)
Beispiel #22
0
def get_zoom(input):
    input = re.sub("s3://([^/]+)/", "http://\\1.s3.amazonaws.com/", input)
    with rasterio.Env():
        with rasterio.open(input) as src:
            # grab the lowest resolution dimension
            if src.crs.is_geographic:
                # convert from degrees to "meters" (close enough for our use, esp. since we're using "meters" too)
                bounds = transform_bounds(src.crs, "EPSG:3857", *src.bounds)
                resolution = max((bounds[2] - bounds[0]) / src.width, (bounds[3] - bounds[1]) / src.height)
            else:
                resolution = max((src.bounds.right - src.bounds.left) / src.width, (src.bounds.top - src.bounds.bottom) / src.height)

            return min(22, int(math.ceil(math.log((2 * math.pi * 6378137) /
                                                  (resolution * 256)) / math.log(2))))
Beispiel #23
0
def tile(sceneid, tile_x, tile_y, tile_z, rgb=(7, 6, 5), tilesize=256):
    """Create mercator tile from CBERS data.

    Attributes
    ----------

    sceneid : str
        CBERS sceneid.
    tile_x : int
        Mercator tile X index.
    tile_y : int
        Mercator tile Y index.
    tile_z : int
        Mercator tile ZOOM level.
    rgb : tuple, int, optional (default: ('5', '6', '7'))
        Bands index for the RGB combination.
    tilesize : int, optional (default: 256)
        Output image size.

    Returns
    -------
    data : numpy ndarray
    mask: numpy array
    """

    if not isinstance(rgb, tuple):
        rgb = tuple((rgb, ))

    scene_params = utils.cbers_parse_scene_id(sceneid)
    cbers_address = '{}/{}'.format(CBERS_BUCKET, scene_params['key'])

    with rasterio.open('{}/{}_BAND6.tif'.format(cbers_address, sceneid)) as src:
        wgs_bounds = transform_bounds(
            *[src.crs, 'epsg:4326'] + list(src.bounds), densify_pts=21)

    if not utils.tile_exists(wgs_bounds, tile_z, tile_x, tile_y):
        raise TileOutsideBounds('Tile {}/{}/{} is outside image bounds'.format(
            tile_z, tile_x, tile_y))

    mercator_tile = mercantile.Tile(x=tile_x, y=tile_y, z=tile_z)
    tile_bounds = mercantile.xy_bounds(mercator_tile)

    addresses = ['{}/{}_BAND{}.tif'.format(cbers_address, sceneid, band) for band in rgb]

    _tiler = partial(utils.tile_band_worker, bounds=tile_bounds, tilesize=tilesize, nodata=0)
    with futures.ThreadPoolExecutor(max_workers=3) as executor:
        data, masks = zip(*list(executor.map(_tiler, addresses)))
        mask = np.all(masks, axis=0).astype(np.uint8) * 255

    return np.concatenate(data), mask
Beispiel #24
0
def test_transform_bounds__ignore_inf():
    # Depending on the GDAL version we might get an exception or inf values
    try:
        assert not numpy.isinf(
            transform_bounds(
                "EPSG:4326",
                "ESRI:102036",
                -180.0,
                -90.0,
                180.0,
                0.0,
            )).any()
    except CPLE_BaseError:
        pass
Beispiel #25
0
def test_transform_bounds__antimeridian():
    transformed_bounds = transform_bounds(
        "EPSG:4167",
        "EPSG:3851",
        160.6,
        -55.95,
        -171.2,
        -25.88,
    )
    assert_almost_equal(
        transformed_bounds,
        (1722483.900174921, 5228058.6143420935, 4624385.494808555,
         8692574.544944234),
    )
    inverse_transformed_bounds = transform_bounds(
        "EPSG:3851",
        "EPSG:4167",
        *transformed_bounds,
    )
    assert_almost_equal(
        inverse_transformed_bounds,
        (153.2799922, -56.7471249, -162.1813873, -24.6148194),
    )
Beispiel #26
0
def _grid_datasets(datasets, bounds_override, grid_proj, grid_size):
    tiles = defaultdict(list)
    for dataset in datasets:
        dataset_proj = dataset.crs
        dataset_bounds = dataset.bounds
        bounds = bounds_override or BoundingBox(*transform_bounds(dataset_proj, grid_proj, *dataset_bounds))

        for y in range(int(bounds.bottom // grid_size[1]), int(bounds.top // grid_size[1]) + 1):
            for x in range(int(bounds.left // grid_size[0]), int(bounds.right // grid_size[0]) + 1):
                tile_index = (x, y)
                if _check_intersect(tile_index, grid_size, grid_proj, dataset_bounds, dataset_proj):
                    tiles[tile_index].append(dataset)

    return tiles
Beispiel #27
0
async def test_cog_read(infile, create_cog_reader):
    async with create_cog_reader(infile) as cog:
        with rasterio.open(infile) as ds:
            _, zoom = get_zooms(ds)
        centroid = Polygon.from_bounds(
            *transform_bounds(cog.epsg, "EPSG:4326", *cog.bounds)).centroid
        tile = mercantile.tile(centroid.x, centroid.y, zoom)

        tile_native_bounds = transform_bounds("EPSG:4326", cog.epsg,
                                              *mercantile.bounds(tile))

        arr = await cog.read(tile_native_bounds, (256, 256))

        with cogeo_reader(infile) as ds:
            rio_tile_arr, rio_tile_mask = ds.tile(tile.x,
                                                  tile.y,
                                                  tile.z,
                                                  tilesize=256,
                                                  resampling_method="bilinear")

        if cog.is_masked:
            tile_arr = np.ma.getdata(arr)
            tile_mask = np.ma.getmask(arr)

            # Make sure image data is the same
            assert pytest.approx(tile_arr - rio_tile_arr,
                                 1) == np.zeros(tile_arr.shape)

            # Make sure mask data is the same
            rio_mask_counts = np.unique(rio_tile_mask, return_counts=True)
            tile_mask_counts = np.unique(tile_mask, return_counts=True)
            assert len(rio_mask_counts[0]) == len(tile_mask_counts[0])
            assert (rio_mask_counts[1][0] *
                    cog.profile["count"] == tile_mask_counts[1][0])

        else:
            assert pytest.approx(arr - rio_tile_arr, 1) == np.zeros(arr.shape)
Beispiel #28
0
        def __call__(self):
            for i, path in enumerate(input):
                with rasterio.open(path) as src:
                    bounds = src.bounds
                    if dst_crs:
                        bbox = transform_bounds(src.crs,
                                                dst_crs, *bounds)
                    elif projection == 'mercator':
                        bbox = transform_bounds(src.crs,
                                                {'init': 'epsg:3857'}, *bounds)
                    elif projection == 'geographic':
                        bbox = transform_bounds(src.crs,
                                                {'init': 'epsg:4326'}, *bounds)
                    else:
                        bbox = bounds

                if precision >= 0:
                    bbox = [round(b, precision) for b in bbox]

                yield {
                    'type': 'Feature',
                    'bbox': bbox,
                    'geometry': {
                        'type': 'Polygon',
                        'coordinates': [[
                            [bbox[0], bbox[1]],
                            [bbox[2], bbox[1]],
                            [bbox[2], bbox[3]],
                            [bbox[0], bbox[3]],
                            [bbox[0], bbox[1]]]]},
                    'properties': {
                        'id': str(i),
                        'title': path,
                        'filename': os.path.basename(path)}}

                self._xs.extend(bbox[::2])
                self._ys.extend(bbox[1::2])
Beispiel #29
0
def test_cog_translate_web():
    """
    Test Web-Optimized COG.

    - Test COG size is a multiple of 256 (mercator tile size)
    - Test COG bounds are aligned with mercator grid at max zoom
    """
    tms = morecantile.tms.get("WebMercatorQuad")

    runner = CliRunner()
    with runner.isolated_filesystem():

        web_profile = cog_profiles.get("raw")
        web_profile.update({"blockxsize": 256, "blockysize": 256})
        config = dict(GDAL_TIFF_OVR_BLOCKSIZE="256")

        cog_translate(
            raster_path_web,
            "cogeo.tif",
            web_profile,
            quiet=True,
            web_optimized=True,
            config=config,
            aligned_levels=0,
        )
        with rasterio.open(raster_path_web) as src_dst:
            with rasterio.open("cogeo.tif") as out_dst:
                blocks = list(set(out_dst.block_shapes))
                assert len(blocks) == 1
                ts = blocks[0][0]
                assert not out_dst.width % ts
                assert not out_dst.height % ts
                _, max_zoom = get_zooms(out_dst)

                bounds = list(
                    transform_bounds(src_dst.crs,
                                     "epsg:4326",
                                     *src_dst.bounds,
                                     densify_pts=21))
                ulTile = tms.xy_bounds(tms.tile(bounds[0], bounds[3],
                                                max_zoom))
                assert out_dst.bounds.left == ulTile.left
                assert out_dst.bounds.top == ulTile.top

                lrTile = tms.xy_bounds(tms.tile(bounds[2], bounds[1],
                                                max_zoom))
                assert out_dst.bounds.right == lrTile.right
                assert round(out_dst.bounds.bottom,
                             6) == round(lrTile.bottom, 6)
Beispiel #30
0
def test_nonearthbody():
    """COGReader should work with non-earth dataset."""
    with pytest.warns(UserWarning):
        with COGReader(COG_EUROPA) as cog:
            assert cog.minzoom == 0
            assert cog.maxzoom == 24

    with pytest.warns(None) as warnings:
        with COGReader(COG_EUROPA) as cog:
            assert cog.info()
            assert len(warnings) == 2

            img = cog.read()
            assert numpy.array_equal(img.data, cog.dataset.read(indexes=(1, )))
            assert img.width == cog.dataset.width
            assert img.height == cog.dataset.height
            assert img.count == cog.dataset.count

            img = cog.preview()
            assert img.bounds == cog.bounds

            part = cog.part(cog.bounds, bounds_crs=cog.crs)
            assert part.bounds == cog.bounds

            lon = (cog.bounds[0] + cog.bounds[2]) / 2
            lat = (cog.bounds[1] + cog.bounds[3]) / 2
            assert cog.point(lon, lat, coord_crs=cog.crs)[0] is not None

    europa_crs = CRS.from_authority("ESRI", 104915)
    tms = TileMatrixSet.custom(
        crs=europa_crs,
        extent=europa_crs.area_of_use.bounds,
        matrix_scale=[2, 1],
    )
    with pytest.warns(None) as warnings:
        with COGReader(COG_EUROPA, tms=tms) as cog:
            assert cog.minzoom == 4
            assert cog.maxzoom == 6

            # Get Tile covering the UL corner
            bounds = transform_bounds(cog.crs, tms.rasterio_crs, *cog.bounds)
            t = tms._tile(bounds[0], bounds[1], cog.minzoom)
            img = cog.tile(t.x, t.y, t.z)

            assert img.height == 256
            assert img.width == 256
            assert img.crs == tms.rasterio_crs

            assert len(warnings) == 0
Beispiel #31
0
    def __enter__(self):
        """Support using with Context Managers."""
        self.dataset = self.dataset or rasterio.open(self.filepath)

        self.bounds = transform_bounds(self.dataset.crs,
                                       constants.WGS84_CRS,
                                       *self.dataset.bounds,
                                       densify_pts=21)
        if self.minzoom is None or self.maxzoom is None:
            self._get_zooms()

        if self.colormap is None:
            self._get_colormap()

        return self
Beispiel #32
0
def get_img_bounds(img, jsonFile, dst_crs=None):
    """Get the projected top left and bottom right coordinates of an image
      Parameters:
      img (ndarray): image to generate bounding coordinates for
      jsonFile (str): path to json file defining crs and image size
      dst_crs (str): epsg code for output crs
      Return:
      tpl: [[lat min, lon min],[lat max, lon max]]
      """
    # Get a single string w/ newlines from the IPython.utils.text.SList
    with open(jsonFile, ) as f:
        mixer = json.load(f)
    # mixer = json.loads(jsonText.nlstr)
    transform = mixer['projection']['affine']['doubleMatrix']
    print(transform)
    src_crs = CRS.from_string(mixer['projection']['crs'])
    print(src_crs)
    affine = rio.Affine(transform[0], transform[1], transform[2], transform[3],
                        transform[4], transform[5])
    H, W = [0, 0]

    if type(img) == np.ndarray:
        print('input image is numpy')
        H, W = img.shape
        print('image shape is ', H, W)
        bounds = array_bounds(H, W, affine)

    elif type(img) == str:
        print('input image is geotiff')
        with rio.open(img) as src:
            bounds = src.bounds
    # H, W = src.shape

    print(bounds)
    lon_min, lat_min, lon_max, lat_max = bounds
    # if we need to transform the bounds, such as for folium ('EPSG:3857')
    if dst_crs:
        dst_crs = CRS.from_string(dst_crs)
        out_bounds = transform_bounds(src_crs,
                                      dst_crs,
                                      left=lon_min,
                                      bottom=lat_min,
                                      right=lon_max,
                                      top=lat_max,
                                      densify_pts=21)
        lon_min, lat_min, lon_max, lat_max = out_bounds
        print(out_bounds)
    return [[lat_min, lon_min], [lat_max, lon_max]]
Beispiel #33
0
def _calculate_default_transform(src_crs: Union[Dict[str, str], str],
                                 _TARGET_CRS: Union[Dict[str, str], str],
                                 width: int, height: int,
                                 *bounds: Number) -> Tuple[Affine, int, int]:
    """A more stable version of GDAL's default transform.

    Ensures that the number of pixels along the image's shortest diagonal remains
    the same in both CRS, without enforcing square pixels.

    Bounds are in order (west, south, east, north).
    """
    from rasterio import warp, transform

    if len(bounds) != 4:
        raise ValueError('Bounds must contain 4 values')

    if src_crs is None:
        src_crs = _TARGET_CRS

    # transform image corners to target CRS
    dst_corner_sw, dst_corner_nw, dst_corner_se, dst_corner_ne = (list(
        zip(*warp.transform(src_crs, _TARGET_CRS,
                            [bounds[0], bounds[0], bounds[2], bounds[2]],
                            [bounds[1], bounds[3], bounds[1], bounds[3]]))))

    # determine inner bounding box of corners in target CRS
    dst_corner_bounds = [
        max(dst_corner_sw[0], dst_corner_nw[0]),
        max(dst_corner_sw[1], dst_corner_se[1]),
        min(dst_corner_se[0], dst_corner_ne[0]),
        min(dst_corner_nw[1], dst_corner_ne[1])
    ]

    # compute target resolution
    dst_corner_transform = transform.from_bounds(*dst_corner_bounds,
                                                 width=width,
                                                 height=height)
    target_res = (dst_corner_transform.a, dst_corner_transform.e)

    # get transform spanning whole bounds (not just projected corners)
    dst_bounds = warp.transform_bounds(src_crs, _TARGET_CRS, *bounds)
    dst_width = math.ceil((dst_bounds[2] - dst_bounds[0]) / target_res[0])
    dst_height = math.ceil((dst_bounds[1] - dst_bounds[3]) / target_res[1])
    dst_transform = transform.from_bounds(*dst_bounds,
                                          width=dst_width,
                                          height=dst_height)

    return dst_transform, dst_width, dst_height
Beispiel #34
0
    async def part(self,
                   bounds: Tuple[float, float, float, float],
                   bounds_crs: CRS = WGS84,
                   width: int = None,
                   height: int = None) -> np.ndarray:
        if bounds_crs != self.cog.epsg:
            bounds = transform_bounds(bounds_crs, CRS.from_epsg(self.cog.epsg),
                                      *bounds)

        if not height or not width:
            width = math.ceil(
                (bounds[2] - bounds[0]) / self.profile['transform'].a)
            height = math.ceil(
                (bounds[3] - bounds[1]) / -self.profile['transform'].e)

        arr = await self.cog.read(bounds=bounds, shape=(width, height))
        return arr
Beispiel #35
0
 def toRTM(self):
     # todo mask cloud
     srcList = [rio.open(i) for i in self.srcList]
     with rio.open(self.qaBand) as qaSrc:
         # srcList = [redset, greenset, blueset]
         meta = srcList[0].meta.copy()
         self.crs = meta['crs']
         meta.update({
             'COMPRESS': 'LZW',
             'dtype': 'uint8',
             'count': self.count,
             'nodata': 0
         })
         with rio.open(self.tempOutName, 'w', **meta) as dst:
             for ji, window in srcList[0].block_windows(1):
                 imBands = [src.read(1, window=window) for src in srcList]
                 im = np.stack(imBands)
                 imqa = qaSrc.read(1, window=window)
                 if self.maskcloud:
                     clearMask = np.zeros(imBands[0].shape, dtype=np.bool)
                     for clearValue in self.clearQaValues:
                         clearMask[imqa == clearValue] = True
                 _, rows, cols = im.shape
                 if self.pixel_sunangle:
                     bbox = rio.coords.BoundingBox(*transform_bounds(
                         self.crs, {'init': u'epsg:4326'}, *
                         srcList[0].window_bounds(window), 0))
                     E = sun_elevation(bbox, (rows, cols),
                                       self.date_collected,
                                       self.time_collected_utc).reshape(
                                           rows, cols, 1)
                 else:
                     E = np.array([self.E for i in range(self.count)])
                 if self.maskcloud:
                     mask = clearMask == False
                 else:
                     mask = imqa == 1
                     mask[imqa == 0] = True
                 rgb = reflectance(im, self.M, self.A, E).clip(
                     min=0, max=0.55) * (254 / 0.55) + 1
                 rgb[:, mask] = 0
                 dst.write(rgb.astype('uint8'),
                           list(range(1, self.count + 1)),
                           window=window)
     if self.maskcloud:
         qaSrc = None
Beispiel #36
0
def _get_info(src_path):
    with rasterio.open(src_path) as src_dst:
        bounds = transform_bounds(
            *[src_dst.crs, "epsg:4326"] + list(src_dst.bounds), densify_pts=21
        )
        center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2]
        minzoom, maxzoom = get_zooms(src_dst)

        def _get_name(ix):
            name = src_dst.descriptions[ix - 1]
            if not name:
                name = f"band{ix}"
            return name

        band_descriptions = [_get_name(ix) for ix in src_dst.indexes]

    return bounds, center, minzoom, maxzoom, band_descriptions
Beispiel #37
0
    async def info(self) -> COGInfo:
        wgs84_bounds = transform_bounds(CRS.from_epsg(self.cog.epsg),
                                        CRS.from_epsg(4326), *self.cog.bounds)
        mercator_resolution = max(self.profile["transform"][0],
                                  abs(self.profile["transform"][4]))
        max_zoom = zoom_for_pixelsize(mercator_resolution)
        min_zoom = zoom_for_pixelsize(
            mercator_resolution *
            max(self.profile["width"], self.profile["height"]) / 256)

        return COGInfo(
            min_zoom=min_zoom,
            max_zoom=max_zoom,
            bounds=list(wgs84_bounds),
            dtype=self.profile["dtype"],
            color_interp=self.profile["photometric"],
        )
Beispiel #38
0
    def _read_window(self, vrt: WarpedVRT, dst_window: Window) -> MaskedArray:
        """Read window of input raster."""
        dst_bounds: Bounds = bounds(dst_window, self.dst[self.default_format].transform)
        window = vrt.window(*dst_bounds)

        src_bounds = transform_bounds(
            self.dst[self.default_format].crs, self.src.crs, *dst_bounds
        )

        LOGGER.debug(
            f"Read {dst_window} for Tile {self.tile_id} - this corresponds to bounds {src_bounds} in source"
        )

        shape = (
            len(self.layer.input_bands),
            int(round(dst_window.height)),
            int(round(dst_window.width)),
        )

        try:

            return vrt.read(
                window=window,
                out_shape=shape,
                masked=True,
            )
        except rasterio.RasterioIOError as e:
            if "Access window out of range" in str(e) and (
                shape[1] == 1 or shape[2] == 1
            ):
                LOGGER.warning(
                    f"Access window out of range while reading {dst_window} for Tile {self.tile_id}. "
                    "This is most likely due to subpixel misalignment. "
                    "Returning empty array instead."
                )
                return np.ma.array(
                    data=np.zeros(shape=shape), mask=np.ones(shape=shape)
                )

            else:
                LOGGER.warning(
                    f"RasterioIO error while reading {dst_window} for Tile {self.tile_id}. "
                    "Will make attempt to retry."
                )
                raise
Beispiel #39
0
    def __init__(
        self,
        bounds,
        zoom=11,
        bounds_crs="EPSG:4326",
        dst_crs="EPSG:4326",
        resolution=None,
        cache_dir=None,
        resampling="bilinear",
    ):

        # create a temp folder for vrts
        self.tempfolder = tempfile.mkdtemp(prefix="terrain-tiles-")

        # zoom must be 1-15
        if type(zoom) != int or zoom < 1 or zoom > 15:
            raise ValueError("Zoom must be an integer from 1-15")

        # if cache is not specified
        if not cache_dir:
            # first look for environment variable
            if "TERRAINCACHE" in os.environ.keys():
                cache_dir = os.environ["TERRAINCACHE"]
            # then default to temp folder
            else:
                cache_dir = self.tempfolder

        self.cache_dir = cache_dir
        self.zoom = zoom
        self.bounds = bounds
        if bounds_crs != "EPSG:4326":
            self.bounds_ll = transform_bounds(bounds_crs, "EPSG:4326", *bounds)
        else:
            self.bounds_ll = bounds
        self.bounds_crs = bounds_crs

        self.url = "http://s3.amazonaws.com/elevation-tiles-prod/geotiff/"
        self.tiles = [t for t in mercantile.tiles(*self.bounds_ll, self.zoom)]
        self.files = []
        self.merged = False
        self.warped = False
        self.resolution = resolution
        self.dst_crs = dst_crs
        self.resampling = resampling
        self.cache()
Beispiel #40
0
    def _reproject_dst_window(self, dst_window: Window) -> Window:
        """Reproject window into same projection as source raster."""

        dst_bounds: Bounds = bounds(
            window=dst_window,
            transform=self.dst[self.default_format].transform,
            height=self.grid.blockysize,
            width=self.grid.blockxsize,
        )
        src_bounds: Bounds = transform_bounds(
            self.dst[self.default_format].crs, self.src.crs, *dst_bounds
        )

        src_window: Window = from_bounds(*src_bounds, transform=self.src.transform)
        LOGGER.debug(
            f"Source window for {dst_window} of tile {self.tile_id} is {src_window}"
        )
        return src_window
Beispiel #41
0
def wmts_handler(
    url: str = None,
    tile_format: str = "png",
    tile_scale: int = 1,
    title: str = "Cloud Optimizied GeoTIFF",
    **kwargs: Any,
) -> Tuple[str, str, str]:
    """Handle /wmts requests."""
    if tile_scale is not None and isinstance(tile_scale, str):
        tile_scale = int(tile_scale)

    # Remove QGIS arguments
    kwargs.pop("SERVICE", None)
    kwargs.pop("REQUEST", None)

    kwargs.update(dict(url=url))
    query_string = urllib.parse.urlencode(list(kwargs.items()))

    # & is an invalid character in XML
    query_string = query_string.replace("&", "&amp;")

    with rasterio.Env(aws_session):
        with rasterio.open(url) as src_dst:
            bounds = list(
                warp.transform_bounds(src_dst.crs,
                                      "epsg:4326",
                                      *src_dst.bounds,
                                      densify_pts=21))
            minzoom, maxzoom = get_zooms(src_dst)

    return (
        "OK",
        "application/xml",
        wmts_template(
            app.host,
            query_string,
            minzoom=minzoom,
            maxzoom=maxzoom,
            bounds=bounds,
            tile_scale=tile_scale,
            tile_format=tile_format,
            title=title,
        ),
    )
Beispiel #42
0
def get_zooms(src_dst, ensure_global_max_zoom=False, tilesize=256):
    """
    Calculate raster min/max mercator zoom level.

    Parameters
    ----------
    src_dst: rasterio.io.DatasetReader
        Rasterio io.DatasetReader object
    ensure_global_max_zoom: bool, optional
        Apply latitude correction factor to ensure max_zoom equality for global
        datasets covering different latitudes (default: False).
    tilesize: int, optional
        Mercator tile size (default: 256).

    Returns
    -------
    min_zoom, max_zoom: Tuple
        Min/Max Mercator zoom levels.

    """
    bounds = transform_bounds(src_dst.crs,
                              "epsg:5514",
                              *src_dst.bounds,
                              densify_pts=21)
    center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2]
    lat = center[1] if ensure_global_max_zoom else 0

    dst_affine, w, h = calculate_default_transform(src_dst.crs, "epsg:5514",
                                                   src_dst.width,
                                                   src_dst.height,
                                                   *src_dst.bounds)

    mercator_resolution = max(abs(dst_affine[0]), abs(dst_affine[4]))

    # Correction factor for web-mercator projection latitude scale change
    latitude_correction_factor = math.cos(math.radians(lat))
    adjusted_resolution = mercator_resolution * latitude_correction_factor

    max_zoom = zoom_for_pixelsize(adjusted_resolution, tilesize=tilesize)

    ovr_resolution = adjusted_resolution * max(h, w) / tilesize
    min_zoom = zoom_for_pixelsize(ovr_resolution, tilesize=tilesize)

    return (min_zoom, max_zoom)
    def __init__(self, uri, rgb=None, nodata=None, linear_stretch=None):
        self._uri = uri
        self._rgb = rgb
        self._nodata = nodata
        self._linear_stretch = linear_stretch
        self._meta = {}

        with get_source(self._uri) as src:
            self._bounds = warp.transform_bounds(src.crs, WGS84_CRS,
                                                 *src.bounds)
            self._resolution = get_resolution_in_meters(
                Bounds(src.bounds, src.crs), (src.height, src.width))
            approximate_zoom = get_zoom(max(self._resolution), op=math.ceil)

            global_min = src.get_tag_item("TIFFTAG_MINSAMPLEVALUE")
            global_max = src.get_tag_item("TIFFTAG_MAXSAMPLEVALUE")

            for band in xrange(0, src.count):
                self._meta["values"] = self._meta.get("values", {})
                self._meta["values"][band] = {}
                min_val = src.get_tag_item("STATISTICS_MINIMUM", bidx=band + 1)
                max_val = src.get_tag_item("STATISTICS_MAXIMUM", bidx=band + 1)
                mean_val = src.get_tag_item("STATISTICS_MEAN", bidx=band + 1)

                if min_val is not None:
                    self._meta["values"][band]["min"] = float(min_val)
                elif global_min is not None:
                    self._meta["values"][band]["min"] = float(global_min)

                if max_val is not None:
                    self._meta["values"][band]["max"] = float(max_val)
                elif global_max is not None:
                    self._meta["values"][band]["max"] = float(global_max)

                if mean_val is not None:
                    self._meta["values"][band]["mean"] = float(mean_val)

        self._center = [
            (self._bounds[0] + self.bounds[2]) / 2,
            (self._bounds[1] + self.bounds[3]) / 2,
            approximate_zoom - 3,
        ]
        self._maxzoom = approximate_zoom + 3
        self._minzoom = approximate_zoom - 10
def clip_from_wgs(input_path, output_path, bounds):
    """
        Clips a raster based on WGS coordinates.
        Heavily borrowed from: https://github.com/mapbox/rasterio/blob/master/rasterio/rio/clip.py
        :param input_path The path to the raster to clip out of
        :param output_path The path to place the clipped raster
        :param bounds (left, bottom, right, top) bounds in WGS 84 projection
    """

    wgs_crs = CRS({'init': 'epsg:4326'})  # WGS 84 Projection

    with rasterio.open(input_path, 'r') as src:

        window = src.window(*transform_bounds(wgs_crs, src.crs, *bounds))
        height = window[0][1] - window[0][0]
        width = window[1][1] - window[1][0]
        t = 2048    # Threshold, for if the user selects an area greater than 2048 pixels in size
                    # If so, select the center of the area at size of 2048 by 2048
        if width > t:
            if width % 2 != 0:
                width -= 1
            trim = int((width - t) / 2)
            width = t
            window = (window[0], (window[1][0] + trim, window[1][0] + t + trim))

        if height > t:
            if height % 2 != 0:
                height -= 1
            trim = int((height - t) / 2)
            height = t
            window = ((window[0][0] + trim, window[0][0] + t + trim), window[1])

        out_kwargs = src.meta.copy()
        out_kwargs.update({
            'height': height,
            'width': width,
            'transform': src.window_transform(window),
            'dtype': 'int32',   # SyncroSim needs a signed int32
            'nodata': 0
            })

        with rasterio.open(output_path, 'w', **out_kwargs) as dst:
            dst.write(src.read(1, window=window).astype('int32'), 1)
Beispiel #45
0
def test_crs_should_be_set(path_rgb_byte_tif, tmpdir, complex):

    """When ``dst_height``, ``dst_width``, and ``dst_transform`` are set
    :py:class:`rasterio.warp.WarpedVRT` calls ``GDALCreateWarpedVRT()``,
    which requires the caller to then set a projection with
    ``GDALSetProjection()``.

    Permalink to ``GDALCreateWarpedVRT()`` call:

        https://github.com/mapbox/rasterio/blob/1f759e5f67628f163ea2550d8926b91545245712/rasterio/_warp.pyx#L753

    """

    vrt_path = str(tmpdir.join("test_crs_should_be_set.vrt"))

    with rasterio.open(path_rgb_byte_tif) as src:

        dst_crs = "EPSG:4326"
        dst_height = dst_width = 10
        dst_bounds = transform_bounds(src.crs, dst_crs, *src.bounds)

        # Destination transform
        left, bottom, right, top = dst_bounds
        xres = (right - left) / dst_width
        yres = (top - bottom) / dst_height
        dst_transform = affine.Affine(xres, 0.0, left, 0.0, -yres, top)

        # The 'complex' test case hits the affected code path
        vrt_options = {"dst_crs": dst_crs}
        if complex:
            vrt_options.update(
                dst_crs=dst_crs,
                dst_height=dst_height,
                dst_width=dst_width,
                dst_transform=dst_transform,
                resampling=Resampling.nearest,
            )

        with WarpedVRT(src, **vrt_options) as vrt:
            rio_shutil.copy(vrt, vrt_path, driver="VRT")
        with rasterio.open(vrt_path) as src:
            assert src.crs
Beispiel #46
0
def test_windows(runner, path_rgb_byte_tif):
    result = runner.invoke(main_group, [
        'blocks', path_rgb_byte_tif])
    assert result.exit_code == 0

    fc = json.loads(result.output)

    with rasterio.open(path_rgb_byte_tif) as src:
        block_windows = tuple(src.block_windows())

        # Check the coordinates of the first output feature
        actual_first = fc['features'][0]
        expected_first = block_windows[0]
        bounds = src.window_bounds(expected_first[1])
        xmin, ymin, xmax, ymax = transform_bounds(src.crs, 'EPSG:4326', *bounds)
        coordinates = [[xmin, ymin], [xmin, ymax], [xmax, ymax], [xmax, ymin]]
        assert np.array_equal(
            actual_first['geometry']['coordinates'][0], coordinates)

        assert len(fc['features']) == len(block_windows)
        assert check_features_block_windows(fc['features'], src, bidx=1)
Beispiel #47
0
def test_windows_sequence(runner, path_rgb_byte_tif):
    result = runner.invoke(main_group, [
        'blocks',
        path_rgb_byte_tif,
        '--sequence'])
    assert result.exit_code == 0

    features = tuple(map(json.loads, result.output.splitlines()))

    with rasterio.open(path_rgb_byte_tif) as src:

        # Check the coordinates of the first output feature
        actual_first = features[0]
        expected_first = next(src.block_windows())
        bounds = src.window_bounds(expected_first[1])
        xmin, ymin, xmax, ymax = transform_bounds(src.crs, 'EPSG:4326', *bounds)
        coordinates = [[xmin, ymin], [xmin, ymax], [xmax, ymax], [xmax, ymin]]
        assert np.array_equal(
            actual_first['geometry']['coordinates'][0], coordinates)

        assert check_features_block_windows(features, src, bidx=1)
Beispiel #48
0
def _make_tiles(bbox, src_crs, minz, maxz):
    '''
    Given a bounding box, zoom range, and source crs,
    find all tiles that would intersect

    Parameters
    -----------
    bbox: list
        [w, s, e, n] bounds
    src_crs: str
        the source crs of the input bbox
    minz: int
        minumum zoom to find tiles for
    maxz: int
        maximum zoom to find tiles for

    Returns
    --------
    tiles: generator
        generator of [x, y, z] tiles that intersect
        the provided bounding box
    '''
    w, s, e, n = transform_bounds(*[src_crs, 'epsg:4326'] + bbox, densify_pts=0)

    EPSILON = 1.0e-10

    w += EPSILON
    s += EPSILON
    e -= EPSILON
    n -= EPSILON

    for z in range(minz, maxz + 1):
        for x, y in _tile_range(
            mercantile.tile(w, n, z),
            mercantile.tile(e, s, z)):

            yield [x, y, z]
Beispiel #49
0
def clip(ctx, files, output, bounds, like, driver, projection,
         creation_options):
    """Clips a raster using projected or geographic bounds.

    \b
      $ rio clip input.tif output.tif --bounds xmin ymin xmax ymax
      $ rio clip input.tif output.tif --like template.tif

    The values of --bounds are presumed to be from the coordinate
    reference system of the input dataset unless the --geographic option
    is used, in which case the values may be longitude and latitude
    bounds. Either JSON, for example "[west, south, east, north]", or
    plain text "west south east north" representations of a bounding box
    are acceptable.

    If using --like, bounds will automatically be transformed to match the
    coordinate reference system of the input.

    It can also be combined to read bounds of a feature dataset using Fiona:

    \b
      $ rio clip input.tif output.tif --bounds $(fio info features.shp --bounds)

    """
    from rasterio.warp import transform_bounds

    with ctx.obj['env']:

        output, files = resolve_inout(files=files, output=output)
        input = files[0]

        with rasterio.open(input) as src:
            if bounds:
                if projection == 'geographic':
                    bounds = transform_bounds('epsg:4326', src.crs, *bounds)
                if disjoint_bounds(bounds, src.bounds):
                    raise click.BadParameter('must overlap the extent of '
                                             'the input raster',
                                             param='--bounds',
                                             param_hint='--bounds')
            elif like:
                with rasterio.open(like) as template_ds:
                    bounds = template_ds.bounds
                    if template_ds.crs != src.crs:
                        bounds = transform_bounds(template_ds.crs, src.crs,
                                                  *bounds)

                    if disjoint_bounds(bounds, src.bounds):
                        raise click.BadParameter('must overlap the extent of '
                                                 'the input raster',
                                                 param='--like',
                                                 param_hint='--like')

            else:
                raise click.UsageError('--bounds or --like required')

            bounds_window = src.window(*bounds)
            bounds_window = bounds_window.intersection(
                Window(0, 0, src.width, src.height))

            # Get the window with integer height
            # and width that contains the bounds window.
            out_window = bounds_window.round_lengths(op='ceil')

            height = int(out_window.height)
            width = int(out_window.width)

            out_kwargs = src.meta.copy()
            out_kwargs.update({
                'driver': driver,
                'height': height,
                'width': width,
                'transform': src.window_transform(out_window)})
            out_kwargs.update(**creation_options)

            with rasterio.open(output, 'w', **out_kwargs) as out:
                out.write(src.read(window=out_window,
                                   out_shape=(src.count, height, width)))
Beispiel #50
0
def warp(ctx, input, output, driver, like, dst_crs, dimensions, bounds, res, resampling, threads, creation_options):
    """
    Warp a raster dataset.

    Currently, the output is always overwritten.  This will be changed in a
    later version.

    If a template raster is provided using the --like option, the coordinate
    reference system, affine transform, and dimensions of that raster will
    be used for the output.  In this case --dst-crs, --bounds, --res, and
    --dimensions options are ignored.

    \b
        $ rio warp input.tif output.tif --like template.tif

    The output coordinate reference system may be either a PROJ.4 or EPSG:nnnn
    string,

    \b
        --dst-crs EPSG:4326
        --dst-crs '+proj=longlat +ellps=WGS84 +datum=WGS84'

    or a JSON text-encoded PROJ.4 object.

    \b
        --dst-crs '{"proj": "utm", "zone": 18, ...}'

    If --dimensions are provided, --res and --bounds are ignored.  Resolution
    is calculated based on the relationship between the raster bounds in the
    target coordinate system and the dimensions, and may produce rectangular
    rather than square pixels.

    \b
        $ rio warp input.tif output.tif --dimensions 100 200 --dst-crs EPSG:4326

    If --bounds are provided, --res is required if --dst-crs is provided
    (defaults to source raster resolution otherwise).  Bounds are in the source
    coordinate reference system.

    \b
        $ rio warp input.tif output.tif --bounds -78 22 -76 24 --dst-crs \\
          EPSG:4326 --res 0.1
    """

    verbosity = (ctx.obj and ctx.obj.get("verbosity")) or 1
    resampling = getattr(RESAMPLING, resampling)  # get integer code for method

    if not len(res):
        # Click sets this as an empty tuple if not provided
        res = None
    else:
        # Expand one value to two if needed
        res = (res[0], res[0]) if len(res) == 1 else res

    with rasterio.drivers(CPL_DEBUG=verbosity > 2):
        with rasterio.open(input) as src:
            l, b, r, t = src.bounds
            out_kwargs = src.meta.copy()
            out_kwargs["driver"] = driver

            if like:
                with rasterio.open(like) as template_ds:
                    dst_crs = template_ds.crs
                    dst_transform = template_ds.affine
                    dst_height = template_ds.height
                    dst_width = template_ds.width

            elif dst_crs:
                try:
                    dst_crs = crs.from_string(dst_crs)
                except ValueError:
                    raise click.BadParameter("invalid crs format", param=dst_crs, param_hint=dst_crs)

                if dimensions:
                    # Calculate resolution appropriate for dimensions in target
                    dst_width, dst_height = dimensions
                    xmin, ymin, xmax, ymax = transform_bounds(src.crs, dst_crs, *src.bounds)
                    dst_transform = Affine(
                        (xmax - xmin) / float(dst_width), 0, xmin, 0, (ymin - ymax) / float(dst_height), ymax
                    )

                elif bounds:
                    if not res:
                        raise click.BadParameter("Required when using --bounds", param="res", param_hint="res")

                    xmin, ymin, xmax, ymax = transform_bounds(src.crs, dst_crs, *bounds)
                    dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)
                    dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)
                    dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)

                else:
                    dst_transform, dst_width, dst_height = calculate_default_transform(
                        src.crs, dst_crs, src.width, src.height, *src.bounds, resolution=res
                    )

            elif dimensions:
                # Same projection, different dimensions, calculate resolution
                dst_crs = src.crs
                dst_width, dst_height = dimensions
                dst_transform = Affine((r - l) / float(dst_width), 0, l, 0, (b - t) / float(dst_height), t)

            elif bounds:
                # Same projection, different dimensions and possibly different
                # resolution
                if not res:
                    res = (src.affine.a, -src.affine.e)

                dst_crs = src.crs
                xmin, ymin, xmax, ymax = bounds
                dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)
                dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)
                dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)

            elif res:
                # Same projection, different resolution
                dst_crs = src.crs
                dst_transform = Affine(res[0], 0, l, 0, -res[1], t)
                dst_width = max(int(ceil((r - l) / res[0])), 1)
                dst_height = max(int(ceil((t - b) / res[1])), 1)

            else:
                dst_crs = src.crs
                dst_transform = src.affine
                dst_width = src.width
                dst_height = src.height

            out_kwargs.update(
                {
                    "crs": dst_crs,
                    "transform": dst_transform,
                    "affine": dst_transform,
                    "width": dst_width,
                    "height": dst_height,
                }
            )

            out_kwargs.update(**creation_options)

            with rasterio.open(output, "w", **out_kwargs) as dst:
                for i in range(1, src.count + 1):

                    reproject(
                        source=rasterio.band(src, i),
                        destination=rasterio.band(dst, i),
                        src_transform=src.affine,
                        src_crs=src.crs,
                        # src_nodata=#TODO
                        dst_transform=out_kwargs["transform"],
                        dst_crs=out_kwargs["crs"],
                        # dst_nodata=#TODO
                        resampling=resampling,
                        num_threads=threads,
                    )
Beispiel #51
0
def clip(
        ctx,
        files,
        output,
        bounds,
        like,
        driver,
        creation_options):
    """Clips a raster using bounds input directly or from a template raster.

    \b
      $ rio clip input.tif output.tif --bounds xmin ymin xmax ymax
      $ rio clip input.tif output.tif --like template.tif

    If using --bounds, values must be in coordinate reference system of input.
    If using --like, bounds will automatically be transformed to match the
    coordinate reference system of the input.

    It can also be combined to read bounds of a feature dataset using Fiona:

    \b
      $ rio clip input.tif output.tif --bounds $(fio info features.shp --bounds)

    """

    from rasterio.warp import transform_bounds

    verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1

    with rasterio.drivers(CPL_DEBUG=verbosity > 2):

        output, files = resolve_inout(files=files, output=output)
        input = files[0]

        with rasterio.open(input) as src:
            if bounds:
                if disjoint_bounds(bounds, src.bounds):
                    raise click.BadParameter('must overlap the extent of '
                                             'the input raster',
                                             param='--bounds',
                                             param_hint='--bounds')
            elif like:
                with rasterio.open(like) as template_ds:
                    bounds = template_ds.bounds
                    if template_ds.crs != src.crs:
                        bounds = transform_bounds(template_ds.crs, src.crs,
                                                  *bounds)

                    if disjoint_bounds(bounds, src.bounds):
                        raise click.BadParameter('must overlap the extent of '
                                                 'the input raster',
                                                 param='--like',
                                                 param_hint='--like')

            else:
                raise click.UsageError('--bounds or --like required')

            window = src.window(*bounds)

            out_kwargs = src.meta.copy()
            out_kwargs.update({
                'driver': driver,
                'height': window[0][1] - window[0][0],
                'width': window[1][1] - window[1][0],
                'transform': src.window_transform(window)
            })
            out_kwargs.update(**creation_options)

            with rasterio.open(output, 'w', **out_kwargs) as out:
                out.write(src.read(window=window))
Beispiel #52
0
def test_transform_bounds_identity():
    """Confirm fix of #1411"""
    bounds = (12978395.906596646, 146759.09430753812, 12983287.876406897, 151651.06411778927)
    assert transform_bounds("+init=epsg:3857", "+init=epsg:3857", *bounds) == bounds
Beispiel #53
0
def warp(ctx, files, output, driver, like, dst_crs, dimensions, src_bounds,
         x_dst_bounds, bounds, res, resampling, threads, force_overwrite,
         creation_options):
    """
    Warp a raster dataset.

    If a template raster is provided using the --like option, the
    coordinate reference system, affine transform, and dimensions of
    that raster will be used for the output.  In this case --dst-crs,
    --bounds, --res, and --dimensions options are ignored.

    \b
        $ rio warp input.tif output.tif --like template.tif

    The output coordinate reference system may be either a PROJ.4 or
    EPSG:nnnn string,

    \b
        --dst-crs EPSG:4326
        --dst-crs '+proj=longlat +ellps=WGS84 +datum=WGS84'

    or a JSON text-encoded PROJ.4 object.

    \b
        --dst-crs '{"proj": "utm", "zone": 18, ...}'

    If --dimensions are provided, --res and --bounds are ignored.
    Resolution is calculated based on the relationship between the
    raster bounds in the target coordinate system and the dimensions,
    and may produce rectangular rather than square pixels.

    \b
        $ rio warp input.tif output.tif --dimensions 100 200 \\
        > --dst-crs EPSG:4326

    If --bounds are provided, --res is required if --dst-crs is provided
    (defaults to source raster resolution otherwise).

    \b
        $ rio warp input.tif output.tif \\
        > --bounds -78 22 -76 24 --res 0.1 --dst-crs EPSG:4326

    """

    verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1
    logger = logging.getLogger('rio')

    output, files = resolve_inout(
        files=files, output=output, force_overwrite=force_overwrite)

    resampling = Resampling[resampling]  # get integer code for method

    if not len(res):
        # Click sets this as an empty tuple if not provided
        res = None
    else:
        # Expand one value to two if needed
        res = (res[0], res[0]) if len(res) == 1 else res

    with rasterio.drivers(CPL_DEBUG=verbosity > 2):
        with rasterio.open(files[0]) as src:
            l, b, r, t = src.bounds
            out_kwargs = src.meta.copy()
            out_kwargs['driver'] = driver

            # Sort out the bounds options.
            src_bounds = bounds or src_bounds
            dst_bounds = x_dst_bounds
            if src_bounds and dst_bounds:
                raise click.BadParameter(
                    "Source and destination bounds may not be specified "
                    "simultaneously.")

            if like:
                with rasterio.open(like) as template_ds:
                    dst_crs = template_ds.crs
                    dst_transform = template_ds.affine
                    dst_height = template_ds.height
                    dst_width = template_ds.width

            elif dst_crs:
                try:
                    dst_crs = crs.from_string(dst_crs)
                except ValueError:
                    raise click.BadParameter("invalid crs format.",
                                             param=dst_crs, param_hint=dst_crs)

                if dimensions:
                    # Calculate resolution appropriate for dimensions
                    # in target.
                    dst_width, dst_height = dimensions
                    xmin, ymin, xmax, ymax = transform_bounds(src.crs, dst_crs,
                                                              *src.bounds)
                    dst_transform = Affine(
                        (xmax - xmin) / float(dst_width),
                        0, xmin, 0,
                        (ymin - ymax) / float(dst_height),
                        ymax
                    )

                elif src_bounds or dst_bounds:
                    if not res:
                        raise click.BadParameter(
                            "Required when using --bounds.",
                            param='res', param_hint='res')

                    if src_bounds:
                        xmin, ymin, xmax, ymax = transform_bounds(
                            src.crs, dst_crs, *src_bounds)
                    else:
                        xmin, ymin, xmax, ymax = dst_bounds

                    dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)
                    dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)
                    dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)

                else:
                    dst_transform, dst_width, dst_height = calculate_default_transform(
                        src.crs, dst_crs, src.width, src.height, *src.bounds,
                        resolution=res)

            elif dimensions:
                # Same projection, different dimensions, calculate resolution.
                dst_crs = src.crs
                dst_width, dst_height = dimensions
                dst_transform = Affine(
                    (r - l) / float(dst_width),
                    0, l, 0,
                    (b - t) / float(dst_height),
                    t
                )

            elif src_bounds or dst_bounds:
                # Same projection, different dimensions and possibly
                # different resolution.
                if not res:
                    res = (src.affine.a, -src.affine.e)

                dst_crs = src.crs
                xmin, ymin, xmax, ymax = (src_bounds or dst_bounds)
                dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)
                dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)
                dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)

            elif res:
                # Same projection, different resolution.
                dst_crs = src.crs
                dst_transform = Affine(res[0], 0, l, 0, -res[1], t)
                dst_width = max(int(ceil((r - l) / res[0])), 1)
                dst_height = max(int(ceil((t - b) / res[1])), 1)

            else:
                dst_crs = src.crs
                dst_transform = src.affine
                dst_width = src.width
                dst_height = src.height

            # When the bounds option is misused, extreme values of
            # destination width and height may result.
            if (dst_width < 0 or dst_height < 0 or
                    dst_width > MAX_OUTPUT_WIDTH or
                    dst_height > MAX_OUTPUT_HEIGHT):
                raise click.BadParameter(
                    "Invalid output dimensions: {0}.".format(
                        (dst_width, dst_height)))

            out_kwargs.update({
                'crs': dst_crs,
                'transform': dst_transform,
                'affine': dst_transform,
                'width': dst_width,
                'height': dst_height
            })

            out_kwargs.update(**creation_options)

            with rasterio.open(output, 'w', **out_kwargs) as dst:
                for i in range(1, src.count + 1):

                    reproject(
                        source=rasterio.band(src, i),
                        destination=rasterio.band(dst, i),
                        src_transform=src.affine,
                        src_crs=src.crs,
                        # src_nodata=#TODO
                        dst_transform=out_kwargs['transform'],
                        dst_crs=out_kwargs['crs'],
                        # dst_nodata=#TODO
                        resampling=resampling,
                        num_threads=threads)
Beispiel #54
0
def process_tile(tile):
    """Process a single MBTiles tile

    Parameters
    ----------
    tile : mercantile.Tile

    Returns
    -------

    tile : mercantile.Tile
        The input tile.
    bytes : bytearray
        Image bytes corresponding to the tile.

    """
    global base_kwds, resampling, src

    # Get the bounds of the tile.
    ulx, uly = mercantile.xy(
        *mercantile.ul(tile.x, tile.y, tile.z))
    lrx, lry = mercantile.xy(
        *mercantile.ul(tile.x + 1, tile.y + 1, tile.z))

    kwds = base_kwds.copy()
    kwds['transform'] = transform_from_bounds(ulx, lry, lrx, uly,
                                              kwds['width'], kwds['height'])
    src_nodata = kwds.pop('src_nodata', None)
    dst_nodata = kwds.pop('dst_nodata', None)

    warnings.simplefilter('ignore')

    with MemoryFile() as memfile:

        with memfile.open(**kwds) as tmp:

            # determine window of source raster corresponding to the tile
            # image, with small buffer at edges
            try:
                west, south, east, north = transform_bounds(TILES_CRS, src.crs, ulx, lry, lrx, uly)
                tile_window = window_from_bounds(west, south, east, north, transform=src.transform)
                adjusted_tile_window = Window(
                    tile_window.col_off - 1, tile_window.row_off - 1,
                    tile_window.width + 2, tile_window.height + 2)
                tile_window = adjusted_tile_window.round_offsets().round_shape()

                # if no data in window, skip processing the tile
                if not src.read_masks(1, window=tile_window).any():
                    return tile, None

            except ValueError:
                log.info("Tile %r will not be skipped, even if empty. This is harmless.", tile)

            reproject(rasterio.band(src, tmp.indexes),
                      rasterio.band(tmp, tmp.indexes),
                      src_nodata=src_nodata,
                      dst_nodata=dst_nodata,
                      num_threads=1,
                      resampling=resampling)

        return tile, memfile.read()
Beispiel #55
0
def warp(ctx, files, output, driver, like, dst_crs, dimensions, src_bounds,
         dst_bounds, res, resampling, src_nodata, dst_nodata, threads,
         check_invert_proj, overwrite, creation_options,
         target_aligned_pixels):
    """
    Warp a raster dataset.

    If a template raster is provided using the --like option, the
    coordinate reference system, affine transform, and dimensions of
    that raster will be used for the output.  In this case --dst-crs,
    --bounds, --res, and --dimensions options are not applicable and
    an exception will be raised.

    \b
        $ rio warp input.tif output.tif --like template.tif

    The output coordinate reference system may be either a PROJ.4 or
    EPSG:nnnn string,

    \b
        --dst-crs EPSG:4326
        --dst-crs '+proj=longlat +ellps=WGS84 +datum=WGS84'

    or a JSON text-encoded PROJ.4 object.

    \b
        --dst-crs '{"proj": "utm", "zone": 18, ...}'

    If --dimensions are provided, --res and --bounds are not applicable and an
    exception will be raised.
    Resolution is calculated based on the relationship between the
    raster bounds in the target coordinate system and the dimensions,
    and may produce rectangular rather than square pixels.

    \b
        $ rio warp input.tif output.tif --dimensions 100 200 \\
        > --dst-crs EPSG:4326

    If --bounds are provided, --res is required if --dst-crs is provided
    (defaults to source raster resolution otherwise).

    \b
        $ rio warp input.tif output.tif \\
        > --bounds -78 22 -76 24 --res 0.1 --dst-crs EPSG:4326

    """
    output, files = resolve_inout(
        files=files, output=output, overwrite=overwrite)

    resampling = Resampling[resampling]  # get integer code for method

    if not len(res):
        # Click sets this as an empty tuple if not provided
        res = None
    else:
        # Expand one value to two if needed
        res = (res[0], res[0]) if len(res) == 1 else res

    if target_aligned_pixels:
        if not res:
            raise click.BadParameter(
                '--target-aligned-pixels requires a specified resolution')
        if src_bounds or dst_bounds:
            raise click.BadParameter(
                '--target-aligned-pixels cannot be used with '
                '--src-bounds or --dst-bounds')

    # Check invalid parameter combinations
    if like:
        invalid_combos = (dimensions, dst_bounds, dst_crs, res)
        if any(p for p in invalid_combos if p is not None):
            raise click.BadParameter(
                "--like cannot be used with any of --dimensions, --bounds, "
                "--dst-crs, or --res")

    elif dimensions:
        invalid_combos = (dst_bounds, res)
        if any(p for p in invalid_combos if p is not None):
            raise click.BadParameter(
                "--dimensions cannot be used with --bounds or --res")

    with ctx.obj['env']:
        setenv(CHECK_WITH_INVERT_PROJ=check_invert_proj)

        with rasterio.open(files[0]) as src:
            l, b, r, t = src.bounds
            out_kwargs = src.profile.copy()
            out_kwargs['driver'] = driver

            # Sort out the bounds options.
            if src_bounds and dst_bounds:
                raise click.BadParameter(
                    "--src-bounds and destination --bounds may not be "
                    "specified simultaneously.")

            if like:
                with rasterio.open(like) as template_ds:
                    dst_crs = template_ds.crs
                    dst_transform = template_ds.transform
                    dst_height = template_ds.height
                    dst_width = template_ds.width

            elif dst_crs is not None:
                try:
                    dst_crs = CRS.from_string(dst_crs)
                except ValueError as err:
                    raise click.BadParameter(
                        str(err), param='dst_crs', param_hint='dst_crs')

                if dimensions:
                    # Calculate resolution appropriate for dimensions
                    # in target.
                    dst_width, dst_height = dimensions
                    try:
                        xmin, ymin, xmax, ymax = transform_bounds(
                            src.crs, dst_crs, *src.bounds)
                    except CRSError as err:
                        raise click.BadParameter(
                            str(err), param='dst_crs', param_hint='dst_crs')
                    dst_transform = Affine(
                        (xmax - xmin) / float(dst_width),
                        0, xmin, 0,
                        (ymin - ymax) / float(dst_height),
                        ymax
                    )

                elif src_bounds or dst_bounds:
                    if not res:
                        raise click.BadParameter(
                            "Required when using --bounds.",
                            param='res', param_hint='res')

                    if src_bounds:
                        try:
                            xmin, ymin, xmax, ymax = transform_bounds(
                                src.crs, dst_crs, *src_bounds)
                        except CRSError as err:
                            raise click.BadParameter(
                                str(err), param='dst_crs',
                                param_hint='dst_crs')
                    else:
                        xmin, ymin, xmax, ymax = dst_bounds

                    dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)
                    dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)
                    dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)

                else:
                    try:
                        if src.transform.is_identity and src.gcps:
                            src_crs = src.gcps[1]
                            kwargs = {'gcps': src.gcps[0]}
                        else:
                            src_crs = src.crs
                            kwargs = src.bounds._asdict()
                        dst_transform, dst_width, dst_height = calcdt(
                            src_crs, dst_crs, src.width, src.height,
                            resolution=res, **kwargs)
                    except CRSError as err:
                        raise click.BadParameter(
                            str(err), param='dst_crs', param_hint='dst_crs')

            elif dimensions:
                # Same projection, different dimensions, calculate resolution.
                dst_crs = src.crs
                dst_width, dst_height = dimensions
                dst_transform = Affine(
                    (r - l) / float(dst_width),
                    0, l, 0,
                    (b - t) / float(dst_height),
                    t
                )

            elif src_bounds or dst_bounds:
                # Same projection, different dimensions and possibly
                # different resolution.
                if not res:
                    res = (src.transform.a, -src.transform.e)

                dst_crs = src.crs
                xmin, ymin, xmax, ymax = (src_bounds or dst_bounds)
                dst_transform = Affine(res[0], 0, xmin, 0, -res[1], ymax)
                dst_width = max(int(ceil((xmax - xmin) / res[0])), 1)
                dst_height = max(int(ceil((ymax - ymin) / res[1])), 1)

            elif res:
                # Same projection, different resolution.
                dst_crs = src.crs
                dst_transform = Affine(res[0], 0, l, 0, -res[1], t)
                dst_width = max(int(ceil((r - l) / res[0])), 1)
                dst_height = max(int(ceil((t - b) / res[1])), 1)

            else:
                dst_crs = src.crs
                dst_transform = src.transform
                dst_width = src.width
                dst_height = src.height

            if target_aligned_pixels:
                dst_transform, dst_width, dst_height = aligned_target(dst_transform, dst_width, dst_height, res)

            # If src_nodata is not None, update the dst metadata NODATA
            # value to src_nodata (will be overridden by dst_nodata if it is not None
            if src_nodata is not None:
                # Update the dst nodata value
                out_kwargs.update({
                    'nodata': src_nodata
                })

            # Validate a manually set destination NODATA value
            # against the input datatype.
            if dst_nodata is not None:
                if src_nodata is None and src.meta['nodata'] is None:
                    raise click.BadParameter(
                        "--src-nodata must be provided because dst-nodata is not None")
                else:
                    # Update the dst nodata value
                    out_kwargs.update({'nodata': dst_nodata})

            # When the bounds option is misused, extreme values of
            # destination width and height may result.
            if (dst_width < 0 or dst_height < 0 or
                    dst_width > MAX_OUTPUT_WIDTH or
                    dst_height > MAX_OUTPUT_HEIGHT):
                raise click.BadParameter(
                    "Invalid output dimensions: {0}.".format(
                        (dst_width, dst_height)))

            out_kwargs.update({
                'crs': dst_crs,
                'transform': dst_transform,
                'width': dst_width,
                'height': dst_height
            })

            # Adjust block size if necessary.
            if ('blockxsize' in out_kwargs and
                    dst_width < out_kwargs['blockxsize']):
                del out_kwargs['blockxsize']
            if ('blockysize' in out_kwargs and
                    dst_height < out_kwargs['blockysize']):
                del out_kwargs['blockysize']

            out_kwargs.update(**creation_options)

            with rasterio.open(output, 'w', **out_kwargs) as dst:
                reproject(
                    source=rasterio.band(src, list(range(1, src.count + 1))),
                    destination=rasterio.band(
                        dst, list(range(1, src.count + 1))),
                    src_transform=src.transform,
                    src_crs=src.crs,
                    src_nodata=src_nodata,
                    dst_transform=out_kwargs['transform'],
                    dst_crs=out_kwargs['crs'],
                    dst_nodata=dst_nodata,
                    resampling=resampling,
                    num_threads=threads)
Beispiel #56
0
def test_transform_bounds_src_crs_none():
    with pytest.raises(CRSError):
        transform_bounds(None, WGS84_crs, 0, 0, 0, 0)
Beispiel #57
0
def test_transform_bounds_dst_crs_none():
    with pytest.raises(CRSError):
        transform_bounds(WGS84_crs, None, 0, 0, 0, 0)
Beispiel #58
0
 def _normalize_bounds(self, bounds):
     if self._geographic:
         bounds = transform_bounds(self._src.crs, 'EPSG:4326', *bounds)
     if self._precision >= 0:
         bounds = (round(v, self._precision) for v in bounds)
     return bounds