示例#1
0
def test_array_bounds():
    with rasterio.open('tests/data/RGB.byte.tif') as src:
        w, s, e, n = src.bounds
        height = src.height
        width = src.width
        tr = transform.from_bounds(w, s, e, n, src.width, src.height)
    assert (w, s, e, n) == transform.array_bounds(height, width, tr)
def test_array_bounds():
    with rasterio.open('tests/data/RGB.byte.tif') as src:
        w, s, e, n = src.bounds
        height = src.height
        width = src.width
        tr = transform.from_bounds(w, s, e, n, src.width, src.height)
    assert (w, s, e, n) == transform.array_bounds(height, width, tr)
def plot_canopy(canopy_arr,
                canopy_transform,
                canopy_crs=None,
                num_steps=10,
                **subplots_kws):
    if canopy_crs is None:
        canopy_crs = settings.CRS

    # reproject the canopy array
    height, width = canopy_arr.shape[-2:]
    canopy_arr, canopy_transform = _reproject_raster(canopy_crs,
                                                     canopy_arr,
                                                     transform.array_bounds(
                                                         height, width,
                                                         canopy_transform),
                                                     canopy_transform,
                                                     dst_nodata=0)

    # prepare the plot
    fig, ax = plt.subplots(**subplots_kws)

    # use geopandas plotting (with alpha 0) just to set the extent
    gpd.GeoSeries([
        geometry.box(
            *transform.array_bounds(*canopy_arr.shape, canopy_transform))
    ]).plot(alpha=0, ax=ax)
    ctx.add_basemap(ax)

    # prepare a colormap for the trees
    tree_cmap = np.stack([[0, 0.5, 0, 0] for _ in range(num_steps)])
    # set alpha
    tree_cmap[:, -1] = np.linspace(0, 1, num_steps)
    # create new colormap
    tree_cmap = colors.ListedColormap(tree_cmap)

    # plot
    # if len(canopy_arr.shape) == 3:
    #     canopy_arr = canopy_arr
    plot.show(canopy_arr, transform=canopy_transform, ax=ax, cmap=tree_cmap)
    ax.set_axis_off()

    return ax
示例#4
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]]
    def align(self, base_layer, output_path=None):
        if self.rescale:
            with rasterio.open(base_layer) as src:
                crs = src.meta['crs']
                cell_size = src.meta['transform'][0]
            transform = self.calculate_default_transform(crs)[0]

        layer, meta = align_raster(base_layer, self.path, method=self.resample)
        self.layer = layer
        self.meta = meta
        self.bounds = array_bounds(meta['height'], meta['width'],
                                   meta['transform'])

        if self.rescale:
            self.layer[self.layer == self.meta['nodata']] = np.nan
            self.meta['nodata'] = np.nan
            factor = (cell_size**2) / (transform[0]**2)
            self.layer *= factor

        if output_path:
            self.save(output_path)
示例#6
0
def reproject(source, destination=None, src_transform=None, gcps=None, rpcs=None,
              src_crs=None, src_nodata=None, dst_transform=None, dst_crs=None,
              dst_nodata=None, dst_resolution=None, src_alpha=0, dst_alpha=0,
              resampling=Resampling.nearest, num_threads=1,
              init_dest_nodata=True, warp_mem_limit=0, **kwargs):
    """Reproject a source raster to a destination raster.

    If the source and destination are ndarrays, coordinate reference
    system definitions and affine transformation parameters or ground
    control points (gcps) are required for reprojection.

    If the source and destination are rasterio Bands, shorthand for
    bands of datasets on disk, the coordinate reference systems and
    transforms or GCPs will be read from the appropriate datasets.

    Parameters
    ------------
    source: ndarray or Band
        The source is a 2 or 3-D ndarray, or a single or a multiple
        Rasterio Band object. The dimensionality of source
        and destination must match, i.e., for multiband reprojection
        the lengths of the first axes of the source and destination
        must be the same.
    destination: ndarray or Band, optional
        The destination is a 2 or 3-D ndarray, or a single or a multiple
        Rasterio Band object. The dimensionality of source
        and destination must match, i.e., for multiband reprojection
        the lengths of the first axes of the source and destination
        must be the same.
    src_transform: affine.Affine(), optional
        Source affine transformation. Required if source and
        destination are ndarrays. Will be derived from source if it is
        a rasterio Band. An error will be raised if this parameter is
        defined together with gcps.
    gcps: sequence of GroundControlPoint, optional
        Ground control points for the source. An error will be raised
        if this parameter is defined together with src_transform or rpcs.
    rpcs: RPC or dict, optional
        Rational polynomial coefficients for the source. An error will
        be raised if this parameter is defined together with src_transform
        or gcps.
    src_crs: CRS or dict, optional
        Source coordinate reference system, in rasterio dict format.
        Required if source and destination are ndarrays.
        Will be derived from source if it is a rasterio Band.
        Example: CRS({'init': 'EPSG:4326'})
    src_nodata: int or float, optional
        The source nodata value. Pixels with this value will not be
        used for interpolation. If not set, it will default to the
        nodata value of the source image if a masked ndarray or
        rasterio band, if available.
    dst_transform: affine.Affine(), optional
        Target affine transformation. Required if source and
        destination are ndarrays. Will be derived from target if it is
        a rasterio Band.
    dst_crs: CRS or dict, optional
        Target coordinate reference system. Required if source and
        destination are ndarrays. Will be derived from target if it
        is a rasterio Band.
    dst_nodata: int or float, optional
        The nodata value used to initialize the destination; it will
        remain in all areas not covered by the reprojected source.
        Defaults to the nodata value of the destination image (if set),
        the value of src_nodata, or 0 (GDAL default).
    dst_resolution: tuple (x resolution, y resolution) or float, optional
        Target resolution, in units of target coordinate reference
        system.
    src_alpha : int, optional
        Index of a band to use as the alpha band when warping.
    dst_alpha : int, optional
        Index of a band to use as the alpha band when warping.
    resampling: int, rasterio.enums.Resampling
        Resampling method to use.
        Default is :attr:`rasterio.enums.Resampling.nearest`.
        An exception will be raised for a method not supported by the running
        version of GDAL.
    num_threads : int, optional
        The number of warp worker threads. Default: 1.
    init_dest_nodata: bool
        Flag to specify initialization of nodata in destination;
        prevents overwrite of previous warps. Defaults to True.
    warp_mem_limit : int, optional
        The warp operation memory limit in MB. Larger values allow the
        warp operation to be carried out in fewer chunks. The amount of
        memory required to warp a 3-band uint8 2000 row x 2000 col
        raster to a destination of the same size is approximately
        56 MB. The default (0) means 64 MB with GDAL 2.2.
    kwargs:  dict, optional
        Additional arguments passed to transformation function.

    Returns
    ---------
    destination: ndarray or Band
        The transformed narray or Band.
    dst_transform: Affine
        THe affine transformation matrix of the destination.
    """

    # Only one type of georeferencing is permitted.
    if (src_transform and gcps) or (src_transform and rpcs) or (gcps and rpcs):
        raise ValueError("src_transform, gcps, and rpcs are mutually "
                         "exclusive parameters and may not be used together.")

    # Guard against invalid or unsupported resampling algorithms.
    try:
        if resampling == 7:
            raise ValueError("Gauss resampling is not supported")

        Resampling(resampling)

    except ValueError:
        raise ValueError(
            "resampling must be one of: {0}".format(", ".join(
                ['Resampling.{0}'.format(r.name) for r in
                 SUPPORTED_RESAMPLING])))

    if destination is None and dst_transform is not None:
        raise ValueError("Must provide destination if dst_transform is provided.")

    # calculate the destination transform if not provided
    if dst_transform is None and (destination is None or isinstance(destination, np.ndarray)):
        src_bounds = tuple([None] * 4)
        if isinstance(source, np.ndarray):
            if source.ndim == 3:
                src_count, src_height, src_width = source.shape
            else:
                src_count = 1
                src_height, src_width = source.shape

            # try to compute src_bounds if we don't have gcps
            if not (gcps or rpcs):
                src_bounds = array_bounds(src_height, src_width, src_transform)
        else:
            src_rdr, src_bidx, _, src_shape = source

            # dataset.bounds will return raster extents in pixel coordinates
            # if dataset does not have geotransform, which is not useful here.
            if not (src_rdr.transform.is_identity and src_rdr.crs is None):
                src_bounds = src_rdr.bounds

            src_crs = src_crs or src_rdr.crs

            if isinstance(src_bidx, int):
                src_bidx = [src_bidx]

            src_count = len(src_bidx)
            src_height, src_width = src_shape
            gcps = src_rdr.gcps[0] if src_rdr.gcps[0] else None

        dst_height = None
        dst_width = None
        dst_count = src_count
        if isinstance(destination, np.ndarray):
            if destination.ndim == 3:
                dst_count, dst_height, dst_width = destination.shape
            else:
                dst_count = 1
                dst_height, dst_width = destination.shape

        left, bottom, right, top = src_bounds
        dst_transform, dst_width, dst_height = calculate_default_transform(
            src_crs=src_crs, dst_crs=dst_crs, width=src_width, height=src_height,
            left=left, bottom=bottom, right=right, top=top,
            gcps=gcps, rpcs=rpcs, dst_width=dst_width, dst_height=dst_height,
            resolution=dst_resolution)

        if destination is None:
            destination = np.empty((int(dst_count), int(dst_height), int(dst_width)),
                                   dtype=source.dtype)

    # Call the function in our extension module.
    _reproject(
        source, destination, src_transform=src_transform, gcps=gcps, rpcs=rpcs,
        src_crs=src_crs, src_nodata=src_nodata, dst_transform=dst_transform,
        dst_crs=dst_crs, dst_nodata=dst_nodata, dst_alpha=dst_alpha,
        src_alpha=src_alpha, resampling=resampling,
        init_dest_nodata=init_dest_nodata, num_threads=num_threads,
        warp_mem_limit=warp_mem_limit, **kwargs)

    return destination, dst_transform
示例#7
0
def get_file_bounds(filenames,
                    bounds_by='intersection',
                    crs=None,
                    res=None,
                    return_bounds=False):
    """
    Gets the union of all files

    Args:
        filenames (list): The file names to mosaic.
        bounds_by (Optional[str]): How to concatenate the output extent. Choices are ['intersection', 'union'].
        crs (Optional[crs]): The CRS to warp to.
        res (Optional[tuple]): The cell resolution to warp to.
        return_bounds (Optional[bool]): Whether to return the bounds tuple.

    Returns:
        transform, width, height
    """

    with rio.open(filenames[0]) as src:

        if not crs:
            crs = src.crs

        if not res:
            res = src.res

        # Transform the extent to the reference CRS
        bounds_left, bounds_bottom, bounds_right, bounds_top = transform_bounds(
            src.crs,
            crs,
            src.bounds.left,
            src.bounds.bottom,
            src.bounds.right,
            src.bounds.top,
            densify_pts=21)

    if bounds_by.lower() in ['union', 'intersection']:

        for fn in filenames[1:]:

            with rio.open(fn) as src:

                # Transform the extent to the reference CRS
                left, bottom, right, top = transform_bounds(src.crs,
                                                            crs,
                                                            src.bounds.left,
                                                            src.bounds.bottom,
                                                            src.bounds.right,
                                                            src.bounds.top,
                                                            densify_pts=21)

            # Update the mosaic bounds
            if bounds_by.lower() == 'union':

                bounds_left = min(bounds_left, left)
                bounds_bottom = min(bounds_bottom, bottom)
                bounds_right = max(bounds_right, right)
                bounds_top = max(bounds_top, top)

            elif bounds_by.lower() == 'intersection':

                bounds_left = max(bounds_left, left)
                bounds_bottom = max(bounds_bottom, bottom)
                bounds_right = min(bounds_right, right)
                bounds_top = min(bounds_top, top)

        # Align the cells
        bounds_transform, bounds_width, bounds_height = align_bounds(
            bounds_left, bounds_bottom, bounds_right, bounds_top, res)

    else:

        bounds_width = int((bounds_right - bounds_left) / abs(res[0]))
        bounds_height = int((bounds_top - bounds_bottom) / abs(res[1]))

        bounds_transform = from_bounds(bounds_left, bounds_bottom,
                                       bounds_right, bounds_top, bounds_width,
                                       bounds_height)

    if return_bounds:
        return array_bounds(bounds_height, bounds_width, bounds_transform)
    else:
        return bounds_transform, bounds_width, bounds_height
示例#8
0
def bounds_from_profile(profile):
    """ bounds from profile """
    a = profile['transform']
    h = profile['height']
    w = profile['width']
    return transform.array_bounds(h, w, a)
示例#9
0
 def get_array_bounds(self, window):
     return array_bounds(window.height, window.width,
                         self.get_window_transform(window))
示例#10
0
 def __iter__(self, chunk_size=None, overlap=None):
     for window in self.iter_windows(chunk_size, overlap):
         bounds = array_bounds(window.height, window.width,
                               self.get_window_transform(window))
         yield window, bounds
示例#11
0
DEMmin = resample( rast, dx, method=Resampling.min)
npDEM = np.copy(DEMavg['raster'])

## 2. Rasterize streams
arr_stream = vtorast( vect, cell_size = dx)['raster']

### 2i. expand streams to have same dimensions as DEM
arr_stream_expand = np.full_like(npDEM,0)
arr_stream_expand[0:arr_stream.shape[0],0:arr_stream.shape[1]] = arr_stream
arr_stream = np.copy(arr_stream_expand)

## 3. Set elevation to minimum cell elevation where stream cells exist 
npDEM[arr_stream> 0] = DEMmin['raster'][arr_stream> 0]

### 3.ii Read in basalt and sediment depth rasters
region = transform.array_bounds(*npDEM.shape,DEMavg['affine'])
dseds = resample(seds, dx, bounds=region, method=Resampling.average)  
dbasalt = resample(basalts, dx, bounds=region, method=Resampling.average)  
DEMbottom2 = npDEM - dseds['raster'] - dbasalt['raster']
DEMbottom1 = npDEM - dseds['raster']

#%%
### 3.i. Plot up these DEMs
# mask for plotting river elevations only
mask = np.zeros(npDEM.shape,dtype=bool) 
mask[ arr_stream == 0 ] = True 
cmin = -500
cmax = np.ceil(npDEM.max()/100)*100
fig,ax = plt.subplots(2,3,figsize=(15,10))
ax=ax.ravel()
im = ax[0].imshow(DEMavg['raster'],cmap='terrain',vmin=cmin,vmax=cmax)
示例#12
0
def warp_ds(ds,
            src_crs,
            new_cell_size=None,
            dst_crs=None,
            var_name='rain',
            xname='longitude',
            yname='latitude',
            tname='time',
            resampling_alg=gdal.GRA_CubicSpline):
    from tqdm import tqdm
    # todo: not sure what happens if the order of co-ordinates in the source xarray
    # does not match the order expected here (time, latitude, longitude)
    x_size, y_size = ds.dims[xname], ds.dims[yname]
    if not new_cell_size:
        new_cell_size = (x_size, y_size)
    if not dst_crs:
        dst_crs = src_crs
    time_size = ds.dims[tname]

    ds_src_transform = get_transform(ds, x_coords=xname, y_coords=yname)
    west, south, east, north = array_bounds(height=y_size,
                                            width=x_size,
                                            transform=ds_src_transform)
    src_ds = create_mem_src(x_size, y_size, ds_src_transform, src_crs)
    dst_transform, dst_width, dst_height = calculate_default_transform(
        src_crs=src_crs,
        dst_crs=dst_crs,
        width=x_size,
        height=y_size,
        left=west,
        bottom=south,
        right=east,
        top=north,
        resolution=(new_cell_size, new_cell_size))
    out_bounds = array_bounds(height=dst_height,
                              width=dst_width,
                              transform=dst_transform)

    wrapopts = gdal.WarpOptions(xRes=new_cell_size,
                                yRes=new_cell_size,
                                srcSRS=src_crs.to_wkt(),
                                dstSRS=dst_crs.to_wkt(),
                                outputBounds=out_bounds,
                                resampleAlg=resampling_alg,
                                dstNodata=np.nan)
    new_xs, new_ys = return_coords(dst_transform, dst_width, dst_height)
    time_values = times = ds.indexes[tname]
    warped_data = np.zeros((time_size, dst_height, dst_width))
    for ti, tvalue in tqdm(list(enumerate(ds.indexes[tname]))):
        data = ds[var_name][ti, ...].values.copy()
        src_ds.GetRasterBand(1).WriteArray(data)
        tb = src_ds.GetRasterBand(1)
        gdal.FillNodata(tb,
                        maskBand=None,
                        maxSearchDist=6,
                        smoothingIterations=0)
        _ = tb.ReadAsArray()
        warp_ras = gdal.Warp(r"/vsimem/wrap_singletimestamp.tiff",
                             src_ds,
                             options=wrapopts)
        warped_slice = warp_ras.GetRasterBand(1).ReadAsArray().copy()
        if warped_slice.shape != (dst_height, dst_width):
            raise ValueError(warped_slice.shape, (dst_height, dst_width))
        warped_data[ti, ...] = warped_slice
        del warp_ras
    warped_ds = xr.DataArray(warped_data,
                             coords=[times, new_ys, new_xs],
                             dims=[tname, yname, xname])
    return warped_ds
def polygonize_pred(
        image_pred_uint8_bin,
        image_crs: str,
        image_transform,
        classname: str = None,
        output_basefilepath: Optional[Path] = None,
        prediction_cleanup_params: dict = None,
        border_pixels_to_ignore: int = 0) -> Optional[gpd.GeoDataFrame]:

    # Polygonize result
    try:
        # Returns a list of tupples with (geometry, value)
        polygonized_records = list(rio_features.shapes(
                image_pred_uint8_bin, mask=image_pred_uint8_bin, transform=image_transform))

        # If nothing found, we can return
        if len(polygonized_records) == 0:
            return None

        # Convert shapes to geopandas geodataframe 
        geoms = []
        for geom, _ in polygonized_records:
            geoms.append(sh_geom.shape(geom))   
        geoms_gdf = gpd.GeoDataFrame(geoms, columns=['geometry'])
        geoms_gdf.crs = image_crs

        # Calculate the bounds of the image in projected coordinates
        image_shape = image_pred_uint8_bin.shape
        image_width = image_shape[0]
        image_height = image_shape[1]
        image_bounds = rio_transform.array_bounds(
                image_height, image_width, image_transform)
        x_pixsize = get_pixelsize_x(image_transform)
        y_pixsize = get_pixelsize_y(image_transform)
        border_bounds = (image_bounds[0]+border_pixels_to_ignore*x_pixsize,
                         image_bounds[1]+border_pixels_to_ignore*y_pixsize,
                         image_bounds[2]-border_pixels_to_ignore*x_pixsize,
                         image_bounds[3]-border_pixels_to_ignore*y_pixsize)
        
        # Calculate the tolerance as half the diagonal of the square formed 
        # by the min pixel size, rounded up in centimeter
        if prediction_cleanup_params is not None:
            # If a simplify is asked... 
            if 'simplify_algorithm' in prediction_cleanup_params:
                # Define the bounds of the image as linestring, so points on this 
                # border are preserved during the simplify
                border_polygon = sh_geom.box(*border_bounds)
                assert border_polygon.exterior is not None
                border_lines = sh_geom.LineString(border_polygon.exterior.coords)
                geoms_gdf.geometry = geoms_gdf.geometry.apply(
                        lambda geom: gfo_vector_util.simplify_ext(
                                geometry=geom, 
                                algorithm=prediction_cleanup_params['simplify_algorithm'],
                                tolerance=prediction_cleanup_params['simplify_tolerance'], 
                                keep_points_on=border_lines))
                
                # Remove geom rows that became empty after simplify + explode
                geoms_gdf = geoms_gdf[~geoms_gdf.geometry.is_empty] 
                geoms_gdf = geoms_gdf[~geoms_gdf.geometry.isna()]  
                if len(geoms_gdf) == 0:
                    return None
                #geoms_gdf.reset_index(drop=True, inplace=True)
                geoms_gdf = geoms_gdf.explode(ignore_index=True) # type: ignore
                #geoms_gdf.reset_index(drop=True, inplace=True)

        # Now we can calculate the "onborder" property
        geoms_gdf = vector_util.calc_onborder(geoms_gdf, border_bounds) # type: ignore

        # Add the classname if provided and area
        if classname is not None:
            geoms_gdf['classname'] = classname
        geoms_gdf['area'] = geoms_gdf.geometry.area

        # Write the geoms to file
        if output_basefilepath is not None:
            geom_filepath = Path(f"{str(output_basefilepath)}_pred_cleaned_2.geojson")
            geofile.to_file(geoms_gdf, geom_filepath, index=False)
        
        return geoms_gdf
            
    except Exception as ex:
        message = f"Exception while polygonizing to file {output_basefilepath}"
        raise Exception(message) from ex
示例#14
0
    crs = rasterio.crs.CRS.from_string( crs )
    gcps = "-gcp 0 0 -164.510605 57.652809 -gcp 0 298 -172.422668 70.599640 -gcp 298 0 -139.489395 57.652809 -gcp 298 298 -131.577332 70.599640"

    # resolution is a real hacky way to do it and not correct
    res = np.mean([np.diff(lon).min(), np.diff(lon).max()])
    affine = rasterio.transform.from_origin( lon.data.min(), lat.data.max(), res, -res ) # upper left pixel
    time, levels, height, width = out_ds[variable].shape
    meta = {'res':(res, res), 'affine':affine, 'height':height, 'width':width, 'count':1, 'dtype':'float32', 'driver':'GTiff', 'compress':'lzw', 'crs':crs }

    # subset to one of the 29 'levels' no idea what these are...
    levelint = 0
    out_ds_level = out_ds[variable].isel( level=levelint )

    # # warp to 3338
    with rasterio.drivers( CHECK_WITH_INVERT_PROJ=True ): # constrain output to legit warp extent -- may want to remove...
        src_bounds = array_bounds( width, height, affine )
        dst_crs = {'init':'epsg:3338'}
        # Calculate the ideal dimensions and transformation in the new crs
        dst_affine, dst_width, dst_height = calculate_default_transform(
                    crs, dst_crs, width, height, *src_bounds)

        for band in range(time):
            print( 'reprojecting: {}'.format(band) )
            # in/out arrays
            cur_arr = out_ds_level[ band, ... ].data
            out_arr = np.empty_like( cur_arr )

            # reproject
            reproject( cur_arr, out_arr, src_transform=affine, src_crs=crs, src_nodata=-1,
                    dst_transform=dst_affine, dst_crs=dst_crs, dst_nodata=-9999, resampling=RESAMPLING.bilinear,
                    SOURCE_EXTRA=1000, num_threads=4 )