async def point(self, lon: float, lat: float, **kwargs: Any) -> List: """point""" coords = [lon, lat] if self.epsg != 4326: coords = [ pt[0] for pt in transform_coords( WGS84, CRS.from_epsg(self.epsg), [coords[0]], [coords[1]] ) ] ifd = self.ifds[0] geotransform = self.geotransform() invgt = ~geotransform # Transform request point to pixel coordinates relative to geotransform image_x, image_y = invgt * coords xtile = math.floor((image_x + 1e-6) / ifd.TileWidth.value) ytile = math.floor((image_y + 1e-6) / ifd.TileHeight.value) tile = await self.get_tile(xtile, ytile, 0) # Calculate index of pixel relative to the tile xindex = int(image_x % ifd.TileWidth.value) yindex = int(image_y % ifd.TileHeight.value) return tile[:, xindex, yindex].tolist()
def calculate_pixel_area(transform, width, height): """Calculates an approximate pixel area based on finding the UTM zone that contains the center latitude / longitude of the window. Parameters ---------- transform : Affine object width : int number of pixels in window height : int number of pixels in window Returns ------- area of a pixel (in meters) """ src_crs = "EPSG:4326" cell_x = transform.a cell_y = abs(transform.e) xmin = transform.c xmax = transform.c + ((width + 0) * cell_x) center_x = ((xmax - xmin) / 2) + xmin ymax = transform.f if transform.e < 0 else transform.f + cell_y * (height + 0) ymin = transform.f if transform.e > 0 else transform.f + transform.e * ( height + 0) center_y = ((ymax - ymin) / 2) + ymin ### to use UTM # UTM zones start numbered at 1 at -180 degrees, in 6 degree bands zone = int(round((center_x - -180) / 6.0)) + 1 dst_crs = "+proj=utm +zone={} +ellps=GRS80 +datum=NAD83 +units=m +no_defs".format( zone) ### to use a custom Albers # inset = (ymax - ymin) / 6.0 # lat1 = ymin + inset # lat2 = ymax - inset # dst_crs = "+proj=aea +lat_1={:.1f} +lat_2={:.1f} +lat_0={:.1f} +lon_0={:.1f} +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs".format( # lat1, lat2, center_y, center_x # ) xs = [center_x, center_x + cell_x] ys = [center_y, center_y + cell_y] (x1, x2), (y1, y2) = transform_coords(src_crs, dst_crs, xs, ys) area = abs(x1 - x2) * abs(y1 - y2) return area
async def point( self, coords: Tuple[float, float], coords_crs: CRS = WGS84, ) -> np.ndarray: if coords_crs != self.cog.epsg: coords = [ pt[0] for pt in transform_coords(coords_crs, CRS.from_epsg(self.cog.epsg), [coords[0]], [coords[1]]) ] arr = await self.cog.point(*coords) return arr
def point( src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT], coordinates: Tuple[float, float], indexes: Optional[Union[Sequence[int], int]] = None, coord_crs: CRS = constants.WGS84_CRS, masked: bool = True, nodata: Optional[Union[float, int, str]] = None, unscale: bool = False, resampling_method: Resampling = "nearest", vrt_options: Optional[Dict] = None, ) -> List: """ Read point value Attributes ---------- src_dst : rasterio.io.DatasetReader rasterio.io.DatasetReader object coordinates : tuple (X, Y) coordinates. indexes : list of ints or a single int, optional Band indexes coord_crs : rasterio.crs.CRS, optional (X, Y) coordinate system. Default is WGS84/EPSG:4326. nodata: int or float, optional unscale, bool, optional If True, apply scale and offset to the data. Default is set to False. masked : bool Whether to mask samples that fall outside the extent of the dataset. Default is set to True. vrt_options: dict, optional These will be passed to the rasterio.warp.WarpedVRT class. Returns ------- point : list List of pixel values per bands indexes. """ if isinstance(indexes, int): indexes = (indexes,) lon, lat = transform_coords( coord_crs, src_dst.crs, [coordinates[0]], [coordinates[1]] ) if not ( (src_dst.bounds[0] < lon[0] < src_dst.bounds[2]) and (src_dst.bounds[1] < lat[0] < src_dst.bounds[3]) ): raise PointOutsideBounds("Point is outside dataset bounds") indexes = indexes if indexes is not None else src_dst.indexes vrt_params: Dict[str, Any] = { "add_alpha": True, "resampling": Resampling[resampling_method], } nodata = nodata if nodata is not None else src_dst.nodata if nodata is not None: vrt_params.update({"nodata": nodata, "add_alpha": False, "src_nodata": nodata}) if has_alpha_band(src_dst): vrt_params.update({"add_alpha": False}) if vrt_options: vrt_params.update(vrt_options) with WarpedVRT(src_dst, **vrt_params) as vrt_dst: point_values = list( vrt_dst.sample([(lon[0], lat[0])], indexes=indexes, masked=masked) )[0] if unscale: point_values = point_values.astype("float32", casting="unsafe") numpy.multiply( point_values, vrt_dst.scales[0], out=point_values, casting="unsafe" ) numpy.add( point_values, vrt_dst.offsets[0], out=point_values, casting="unsafe" ) return point_values.tolist()
def point( src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT], coordinates: Tuple[float, float], indexes: Optional[Union[Sequence[int], int]] = None, coord_crs: CRS = constants.WGS84_CRS, masked: bool = True, nodata: Optional[Union[float, int, str]] = None, unscale: bool = False, resampling_method: Resampling = "nearest", vrt_options: Optional[Dict] = None, post_process: Optional[Callable[[numpy.ndarray, numpy.ndarray], Tuple[numpy.ndarray, numpy.ndarray]]] = None, ) -> List: """Read a pixel value for a point. Args: src_dst (rasterio.io.DatasetReader or rasterio.io.DatasetWriter or rasterio.vrt.WarpedVRT): Rasterio dataset. coordinates (tuple): Coordinates in form of (X, Y). indexes (sequence of int or int, optional): Band indexes. coord_crs (rasterio.crs.CRS, optional): Coordinate Reference System of the input coords. Defaults to `epsg:4326`. masked (bool): Mask samples that fall outside the extent of the dataset. Defaults to `True`. nodata (int or float, optional): Overwrite dataset internal nodata value. unscale (bool, optional): Apply 'scales' and 'offsets' on output data value. Defaults to `False`. resampling_method (rasterio.enums.Resampling, optional): Rasterio's resampling algorithm. Defaults to `nearest`. vrt_options (dict, optional): Options to be passed to the rasterio.warp.WarpedVRT class. post_process (callable, optional): Function to apply on output data and mask values. Returns: list: Pixel value per band indexes. """ if isinstance(indexes, int): indexes = (indexes, ) lon, lat = transform_coords(coord_crs, src_dst.crs, [coordinates[0]], [coordinates[1]]) if not ((src_dst.bounds[0] < lon[0] < src_dst.bounds[2]) and (src_dst.bounds[1] < lat[0] < src_dst.bounds[3])): raise PointOutsideBounds("Point is outside dataset bounds") indexes = indexes if indexes is not None else src_dst.indexes vrt_params: Dict[str, Any] = { "add_alpha": True, "resampling": Resampling[resampling_method], } nodata = nodata if nodata is not None else src_dst.nodata if nodata is not None: vrt_params.update({ "nodata": nodata, "add_alpha": False, "src_nodata": nodata }) if has_alpha_band(src_dst): vrt_params.update({"add_alpha": False}) if vrt_options: vrt_params.update(vrt_options) with WarpedVRT(src_dst, **vrt_params) as vrt_dst: values = list( vrt_dst.sample([(lon[0], lat[0])], indexes=indexes, masked=masked))[0] point_values = values.data mask = values.mask * 255 if masked else numpy.zeros(point_values.shape) if unscale: point_values = point_values.astype("float32", casting="unsafe") numpy.multiply(point_values, vrt_dst.scales[0], out=point_values, casting="unsafe") numpy.add(point_values, vrt_dst.offsets[0], out=point_values, casting="unsafe") if post_process: point_values, _ = post_process(point_values, mask) return point_values.tolist()