示例#1
0
def sample(
        raster: rasterio.io.DatasetReader,
        coords: list[tuple[float, ...]]) -> list[tuple[float, float, float]]:
    """Sample a raster at given coordinates

    Given a list of coordinates, return a list of x,y,z triples with z coordinates sampled from an input raster

    Parameters:
        raster: raster dataset to sample
        coords: array of tuples containing coordinate pairs (x,y) or triples (x,y,z)

    Returns:
        array of tuples containing coordinate triples (x,y,z)
        
    """
    if len(coords[0]) == 3:
        logging.info('Input is a 3D geometry, z coordinate will be updated.')
        z = raster.sample([(x, y) for x, y, z in coords],
                          indexes=raster.indexes)
    else:
        z = raster.sample(coords, indexes=raster.indexes)

    result = [(vert[0], vert[1], vert_z) for vert, vert_z in zip(coords, z)]

    return result
示例#2
0
def resample_band(dataset: rio.io.DatasetReader,
                  target_resolution: float,
                  resampling_method: Resampling,
                  resampler: str) -> dict:
    """Resample a Band (2D NDArray)

    :param dataset: Input dataset
    :param target_resolution: Resolution of the output dataset: what you want to resample to
    :param resampling_method: Which method to use for resampling (only applicable to rasterio!)
    :param resampler: Which resampling function to use (e.g. Rasterio vs SciPy zoom)
    :return: Dictionary containing resampled data ("data") and the resampled profile ("profile")
    """

    # Calculate scale factor
    scaling = int(dataset.res[0]) / float(target_resolution)

    # Calculate profile and transfor elements
    profile = copy.deepcopy(dataset.profile)
    trans = copy.deepcopy(dataset.transform)
    transform = Affine(trans.a / scaling, trans.b, trans.c, trans.d, trans.e / scaling, trans.f)
    height = copy.deepcopy(dataset.height) * scaling
    width = copy.deepcopy(dataset.width) * scaling
    profile.update(
        res=(float(target_resolution), float(target_resolution)),
        transform=transform,
        height=height,
        width=width
    )

    # Resample data to target resolution
    if resampler is "rasterio":
        print("[INFO] Using Rasterio resampler...")
        resampled = dataset.read(
            out_shape=(
                dataset.count,
                int(height),
                int(width)
            ),
            resampling=resampling_method
        )
    else:
        print("[INFO] Using scipy zoom resampler...")
        print("[WARNING] Zoom resampler does not honor resampling methods! Use the Rasterio resampler for this!")
        raw_read = dataset.read()
        resampled = np.array(list(map(
            lambda layer: zoom(layer, scaling, order=0, mode='nearest'),
            raw_read
        )))

    # Create output dictionary
    output = {
        "data": resampled,
        "profile": profile
    }

    return output
示例#3
0
    def subdivide(self,
                  root: str = "",
                  name: str = "",
                  tif: rio.io.DatasetReader = (),
                  sub: int = 5) -> None:
        if sub is 0: return
        if type(tif) == tuple:
            arr, box, meta = tif
            #with open("./data_lookup.csv", "a") as csv:
            #    csv.write("|".join((root,"ROOT", repr(box))) +"\n")
        else:
            arr, box, meta = np.array(tif.read(1)), Box(tif.bounds), tif.meta

        width = meta["width"] = box.width / 2
        height = meta["height"] = box.height / 2
        sub_bound = {
            0: (0, 1, -1, 0),
            1: (1, 1, 0, 0),
            2: (0, 0, -1, -1),
            3: (1, 0, 0, -1)
        }
        sub_slice = {
            0: {
                "sx": slice(None, int(height)),
                "sy": slice(None, int(width))
            },
            1: {
                "sx": slice(None, int(height)),
                "sy": slice(int(width), None)
            },
            2: {
                "sx": slice(int(height), None),
                "sy": slice(None, int(width))
            },
            3: {
                "sx": slice(int(height), None),
                "sy": slice(int(width), None)
            }
        }
        for idx in range(4):
            sub_name = name + (str(idx) if name is "" else f"_{idx}")
            directory = f"./{root}/{sub_name}/"
            if not os.path.exists(directory): os.mkdir(directory)

            meta["transform"] = from_bounds(
                box.left + width * sub_bound[idx][0],
                box.bottom + height * sub_bound[idx][1],
                box.right + width * sub_bound[idx][2],
                box.top + height * sub_bound[idx][3], width, height)

            with rio.open(directory + f"{self.tif_type}.tif", "w+",
                          **meta) as sub_tif:
                sub_tif.write(arr[sub_slice[idx]["sx"], sub_slice[idx]["sy"]],
                              indexes=1)
                self.subdivide(root, sub_name, sub_tif, sub - 1)
                if sub > 1:
                    os.remove(directory + f"{self.tif_type}.tif")
                    os.rmdir(directory)
                    continue
示例#4
0
def _read_pixel_value(point: gpd.GeoSeries,
                      source: rio.io.DatasetReader) -> np.ndarray:
    """Reads raster value from an open rasterio dataset.

    Designed to be run using a `geodataframe.apply()` function.

    Args:
        point: a row from gdf.apply() or gdf.iterrows()
        source: an open rasterio data source

    Returns:
        values: 1-d n-length array with the pixel values of each raster band.
    """
    row, col = source.index(point.geometry.x, point.geometry.y)
    window = rio.windows.Window(col, row, 1, 1)
    values = source.read(window=window, boundless=True)
    return np.squeeze(values)
示例#5
0
def drape_geojson(
    features: Union[Iterable[Dict], fiona.Collection],
    raster: rasterio.io.DatasetReader,
    interpolate: bool = False,
) -> Generator[Dict, None, None]:
    """
    Drape with geojson as input, fiona uses geojson as interface.
    Parameters
    ----------
    features : Iterable[Dict], fiona.Collection
        vector as geojson
    raster : rasterio.io.DatasetReader
        rasterio reader of raster file
    interpolate : bool, default True
        choose to interpolate or not
        * if True, value will be interpolated from  nearest value
        * if False, value will be obtained from exact row and column

    Yields
    -------
    dict
        geojson

    """
    """
    Drape with geojson as input, fiona uses geojson as interface.
    :param features: geojson
    :param raster: rasterio dataset reader
    :param interpolate:
    :return:
    """
    dsm_array = raster.read(1)
    affine = raster.transform
    no_data = raster.nodatavals
    for i, feature in enumerate(features):
        draped_feature = feature.copy()
        geometry: Dict = feature["geometry"]
        geom_type: str = geometry["type"]

        draped_coordinates: Union[List[List[float]],
                                  List[List[List[float]]]] = []
        if geom_type == "Polygon":
            draped_coordinates = [
                list(
                    drape_coordinates(ring, dsm_array, affine, interpolate,
                                      no_data)) for ring in geometry
            ]

        elif geom_type == "LineString":
            draped_coordinates = list(
                drape_coordinates(geometry, dsm_array, affine, interpolate,
                                  no_data))
        else:
            raise ValueError("Unsupported geometry type")

        draped_feature["geometry"]["coordinates"] = draped_coordinates
        yield draped_feature
def load_raster_selection(raster_io: rasterio.io.DatasetReader,
                          geom_list: List[ShapelyGeom]) -> np.ndarray:
    '''
    rasterio allows loading of subselection of a tiff file, so 
    given a list of geometries and an open raster DatasetReader,
    loads in the values within the geom_list.
    This allows for loading of small selections from otherwise 
        huge tiff's
    '''

    if not isinstance(geom_list, List):
        geom_list = [geom_list]

    # Find the window around the geom_list
    window = rasterio.features.geometry_window(raster_io, geom_list)
    transform = raster_io.window_transform(window)

    # Perform the windowed read
    sub_data = raster_io.read(window=window)
    return sub_data, transform
示例#7
0
    def _extract_data(self, image: rasterio.io.DatasetReader,
                      fragment: Fragment) -> (np.ndarray, dict):
        """
        The operation of spiting the images and copying its geo reference is carried out using a sliding window
        approach, where fragment specifies which part of the original image is to be processed

        :param image:
        :param fragment: the split1 size
        :return:
        """
        split_image = image.read(window=fragment.position)

        kwargs_split_image = image.meta.copy()
        kwargs_split_image.update({
            "height":
            self.split_size[0],
            "width":
            self.split_size[1],
            "transform":
            image.window_transform(fragment.position),
        })

        return split_image, kwargs_split_image
示例#8
0
def drape_shapely(
    geometry: Union[Polygon, LineString],
    raster: rasterio.io.DatasetReader,
    interpolate: bool = False,
) -> Union[Polygon, LineString]:
    """
    Drape with shapely geometry as input
    Parameters
    ----------
    geometry : shapely polygon, shapely linestring
        vector data as shapely object, currently only support polygon or linestring
    raster : rasterio.io.DatasetReader
        rasterio reader of raster file
    interpolate : bool, default True
        choose to interpolate or not
        * if True, value will be interpolated from  nearest value
        * if False, value will be obtained from exact row and column

    Returns
    -------
    shapely.Polygon or shapely.LineString

    """

    dsm_array = raster.read(1)
    affine = raster.transform
    no_data = raster.nodatavals
    if geometry.type == "Polygon":
        draped_exterior = list(
            drape_coordinates(geometry.exterior.coords, dsm_array, affine,
                              interpolate, no_data))
        draped_interiors = [
            list(
                drape_coordinates(interior.coords, dsm_array, affine,
                                  interpolate, no_data))
            for interior in geometry.interiors
        ]

        return Polygon(draped_exterior, draped_interiors)
    elif geometry.type == "LineString":
        return LineString(
            list(
                drape_coordinates(geometry.coords, dsm_array, affine,
                                  interpolate, no_data)))
    else:
        raise ValueError("Unsupported geometry type")
示例#9
0
def padded_read(fp: rasterio.io.DatasetReader,
                region: PixelWindow,
                band: Optional[int] = None,
                pad_value: Union[int, Sequence[int]] = 0) -> np.ndarray:
    """Read the specified region from the rasterio dataset.  If any part of the region is out of bounds of the dataset, that part is padded
    with the padding value.  If band is supplied, only that band is read, otherwise all bands are read."""
    available_region = pixel_window_intersect(datafile_pixel_window(fp),
                                              region)
    dat = fp.read(indexes=band, window=available_region)
    # pad_dataset_to_window assumes we are dealing with multiple bands, and it would be tricky to change that.
    # instead, we just add-then-remove an extra level in the case that we are dealing with a single band
    if band:
        dat = dat[None]  # add an extra dimension
    dat = pad_dataset_to_window(dat, available_region, region, pad_value)
    if band:
        dat = dat[0]
    return dat
示例#10
0
def raster_to_geodataframe(
    raster_data: np.ndarray,
    transform: affine.Affine,
    window: rasterio.windows.Window,
    raster_io: rasterio.io.DatasetReader,
) -> gpd.GeoDataFrame:
    """
    Takes in raster data as a numpy array and converts it to a geo df, then
    writes that to file
    """
    if raster_data.ndim == 3:
        assert raster_data.shape[
            0] == 1, "ERROR - can't handle multichannel yet"
        raster_data = raster_data[0]

    # To convert when our raster_data is from a windowed
    # read we need to go from the windowed raster_data
    # coords -> orig dataset coords -> x,y spatial
    data_h, data_w = raster_data.shape
    all_geoms = []
    data = []
    for h in range(data_h):
        for w in range(data_w):
            # Convert to the coord sys of the raster_io dataset
            abs_h = h + window.row_off
            abs_w = w + window.col_off

            # And use its xy method now
            # abs_x1, abs_y1 = raster_io.xy(abs_h, abs_w)
            # abs_x0, abs_y0 = raster_io.xy(abs_h-1, abs_w-1)
            p_ul = Point(raster_io.xy(abs_h, abs_w, offset='ul'))
            p_lr = Point(raster_io.xy(abs_h, abs_w, offset='lr'))
            geom = MultiPoint([p_ul, p_lr]).envelope
            all_geoms.append(geom)
            data.append(raster_data[h, w])
    return gpd.GeoDataFrame.from_dict({'geometry': all_geoms, 'pop': data})
示例#11
0
def sample_from_rasterio(raster: rasterio.io.DatasetReader,
                         point_x: Union[float, int, list, np.ndarray],
                         point_y: Union[float, int, list, np.ndarray],
                         sample_outside_extent: bool = True) -> Union[list, float]:
    """Sampling the value of a rasterio object at a given point within the extent of the raster

    Parameters
    __________

        raster : rasterio.io.DatasetReader
            Rasterio Object containing the height information

        point_x : list, np.ndarray, float, int
            Object containing the x coordinates of a point or points at which the array value is obtained

        point_y : list, np.ndarray, float, int
            Object containing the y coordinates of a point or points at which the array value is obtained

        sample_outside_extent : bool
            Allow sampling outside the extent of the rasterio object, default is True

    Returns
    _______

        sample : list, float
            Value/s of the raster at the provided position/s

    """

    # Checking that the raster is a rasterio object
    if not isinstance(raster, rasterio.io.DatasetReader):
        raise TypeError('Raster must be provided as rasterio object')

    # Checking if the point coordinates are stored as a list
    if not isinstance(point_x, (list, np.ndarray, float, int)):
        raise TypeError('Point_x must be of type list or np.ndarray')

    # Checking if the point coordinates are stored as a list
    if not isinstance(point_y, (list, np.ndarray, float, int)):
        raise TypeError('Point_y must be of type list or np.ndarray')

    # Checking the length of the point list
    if not isinstance(point_x, (float, int)) and not isinstance(point_y, (float, int)):
        if len(point_x) != len(point_y):
            raise ValueError('Length of both point lists/arrays must be equal')

    # Checking that all elements of the point list are of type int or float
    if isinstance(point_x, (list, np.ndarray)):
        if not all(isinstance(n, (int, float, np.int32, np.float64)) for n in point_x):
            raise TypeError('Point values must be of type int or float')

    # Checking that all elements of the point list are of type int or float
    if isinstance(point_y, (list, np.ndarray)):
        if not all(isinstance(n, (int, float, np.int32, np.float64)) for n in point_y):
            raise TypeError('Point values must be of type int or float')

    # Checking that sample_outside_extent is of type bool
    if not isinstance(sample_outside_extent, bool):
        raise TypeError('Sample_outside_extent argument must be of type bool')

    # If sample_outside extent is true, a nodata value will be assigned
    if not sample_outside_extent:
        # Checking if the point is located within the provided raster extent
        if isinstance(point_x, (list, np.ndarray)):
            if any(x < raster.bounds[0] for x in point_x) or any(x > raster.bounds[2] for x in point_x):
                raise ValueError('One or multiple points are located outside of the extent')
        if isinstance(point_y, (list, np.ndarray)):
            if any(y < raster.bounds[1] for y in point_y) or any(y > raster.bounds[3] for y in point_y):
                raise ValueError('One or multiple points are located outside of the extent')

        # Checking if the point is located within the provided raster extent
        if isinstance(point_x, (float, int)):
            if point_x < raster.bounds[0] or point_x > raster.bounds[2]:
                raise ValueError('One or multiple points are located outside of the extent')
        if isinstance(point_y, (float, int)):
            if point_y < raster.bounds[1] or point_y > raster.bounds[3]:
                raise ValueError('One or multiple points are located outside of the extent')

    # Converting lists of coordinates to np.ndarrays
    if isinstance(point_x, list) and isinstance(point_y, list):
        point_x = np.array(point_x)
        point_y = np.array(point_y)

    # Converting points into array
    coordinates = np.array([point_x, point_y]).T

    if isinstance(point_x, (float, int)) and isinstance(point_y, (float, int)):

        sample = float(list(next(raster.sample([coordinates])))[0])
    else:
        # Sampling from the raster using list comprehension
        sample = [float(z[0]) for z in raster.sample(coordinates)]

    return sample