Module delta.extensions.sources.tiff
Block-aligned reading from multiple Geotiff files.
Functions
def write_tiff(output_path: str, data: numpy.ndarray = None, image: DeltaImage = None, nodata=None, metadata: dict = None, block_size=None, show_progress: bool = False)-
Write a numpy array to a file as a tiff.
Parameters
output_path:str- Filename to save tiff file to
data:numpy.ndarray- Image data to save.
image:delta_image.DeltaImage- Image data to save (specify one of this or data).
nodata:Any- Nodata value.
metadata:dict- Optional metadata to include.
block_size:Tuple[int]- Optionally override block size for writing.
show_progress:bool- Display command line progress bar.
Classes
class TiffImage (path, nodata_value=None)-
Images supported by GDAL.
Opens a geotiff for reading.
Parameters
paths:strorList[str]- Either a single filename or a list. For a list, the images are opened in order as a multi-band image, assumed to overlap.
nodata_value:dtypeofimage- Value representing no data.
Expand source code
class TiffImage(delta_image.DeltaImage): """Images supported by GDAL.""" def __init__(self, path, nodata_value=None): """ Opens a geotiff for reading. Parameters ---------- paths: str or List[str] Either a single filename or a list. For a list, the images are opened in order as a multi-band image, assumed to overlap. nodata_value: dtype of image Value representing no data. """ paths = self._prep(path) self._path = path self._paths = paths self._handles = [] for p in paths: if not os.path.exists(p): raise Exception('Image file does not exist: ' + p) result = gdal.Open(p) if result is None: raise Exception('Failed to open tiff file %s.' % (p)) self._handles.append(result) self._band_map = [] for i, h in enumerate(self._handles): if h.RasterXSize != self._handles[0].RasterXSize or h.RasterYSize != self._handles[0].RasterYSize: raise Exception('Images %s and %s have different sizes!' % (self._paths[0], self._paths[i])) for j in range(h.RasterCount): self._band_map.append((i, j + 1)) # gdal uses 1-based band indexing if nodata_value is None: temp = self._gdal_band(0).GetNoDataValue() super().__init__(temp) else: super().__init__(nodata_value) def __del__(self): self.close() def _prep(self, paths): #pylint:disable=no-self-use """ Prepare the file to be opened by other tools (unpack, etc). This can be overwritten by subclasses to, for example, unpack a zip file to a cache directory. Parameters ---------- paths: str or List[str] Paths passed to constructor Returns ------- Returns a list of underlying files to load instead of the original paths. """ if isinstance(paths, str): return [paths] return paths def __asert_open(self): if self._handles is None: raise IOError('Operating on an image that has been closed.') def close(self): """ Close the image. """ self._handles = None # gdal doesn't have a close function for some reason self._band_map = None self._paths = None def path(self): """ Returns the paths returned by `_prep`. """ return self._path def num_bands(self): self.__asert_open() return len(self._band_map) def size(self): self.__asert_open() return (self._handles[0].RasterYSize, self._handles[0].RasterXSize) def _read(self, roi, bands, buf=None): self.__asert_open() num_bands = len(bands) if bands else self.num_bands() if buf is None: buf = np.zeros(shape=(num_bands, roi.height(), roi.width()), dtype=self.dtype()) else: s = buf[0, :, :].shape if s != (roi.height(), roi.width()): raise IOError('Buffer shape should be (%d, %d) but is (%d, %d)!' % (roi.height(), roi.width(), s[0], s[1])) if bands: for i, b in enumerate(bands): band_handle = self._gdal_band(b) band_handle.ReadAsArray(yoff=roi.min_y, xoff=roi.min_x, win_ysize=roi.height(), win_xsize=roi.width(), buf_obj=buf[i, :, :]) else: cur_band = 0 for h in self._handles: h.ReadAsArray(yoff=roi.min_y, xoff=roi.min_x, ysize=roi.height(), xsize=roi.width(), buf_obj=buf[cur_band:cur_band + h.RasterCount, :, :]) cur_band += h.RasterCount return np.transpose(buf, [1, 2, 0]) def _gdal_band(self, band): (h, b) = self._band_map[band] ret = self._handles[h].GetRasterBand(b) assert ret return ret def _gdal_type(self, band=0): """ Returns the GDAL data type of the image. """ self.__asert_open() return self._gdal_band(band).DataType def dtype(self): self.__asert_open() dtype = self._gdal_type(0) if dtype in _GDAL_TO_NUMPY_TYPES: return _GDAL_TO_NUMPY_TYPES[dtype] raise Exception('Unrecognized gdal data type: ' + str(dtype)) def bytes_per_pixel(self, band=0): """ Returns ------- int: the number of bytes per pixel """ self.__asert_open() return gdal.GetDataTypeSize(self._gdal_type(band)) // 8 def block_size(self): """ Returns ------- (int, int): block height, block width """ self.__asert_open() band_handle = self._gdal_band(0) block_size = band_handle.GetBlockSize() return (block_size[1], block_size[0]) def metadata(self): self.__asert_open() data = dict() h = self._handles[0] data['projection'] = h.GetProjection() data['geotransform'] = h.GetGeoTransform() data['gcps'] = h.GetGCPs() data['gcpproj'] = h.GetGCPProjection() data['metadata'] = h.GetMetadata() data['spatial_ref'] = h.GetSpatialRef() return data def block_aligned_roi(self, desired_roi): self.__asert_open() bounds = rectangle.Rectangle(0, 0, width=self.width(), height=self.height()) if not bounds.contains_rect(desired_roi): raise Exception('desired_roi ' + str(desired_roi) + ' is outside the bounds of image with size' + str(self.size())) block_height, block_width = self.block_size() start_block_x = int(math.floor(desired_roi.min_x / block_width)) start_block_y = int(math.floor(desired_roi.min_y / block_height)) # Rect max is exclusive stop_block_x = int(math.floor((desired_roi.max_x-1) / block_width)) # The stops are inclusive stop_block_y = int(math.floor((desired_roi.max_y-1) / block_height)) start_x = start_block_x * block_width start_y = start_block_y * block_height w = (stop_block_x - start_block_x + 1) * block_width h = (stop_block_y - start_block_y + 1) * block_height # Restrict the output region to the bounding box of the image. # - Needed to handle images with partial tiles at the boundaries. ans = rectangle.Rectangle(start_x, start_y, width=w, height=h) bounds = rectangle.Rectangle(0, 0, width=self.width(), height=self.height()) return ans.get_intersection(bounds) def save(self, path, tile_size=None, nodata_value=None, show_progress=False): """ Save to file, with preprocessing applied. Parameters ---------- path: str Filename to save to. tile_size: (int, int) If specified, overwrite block size nodata_value: image dtype If specified, overwrite nodata value show_progress: bool Write progress bar to stdout """ write_tiff(path, image=self, nodata=nodata_value, block_size=tile_size, show_progress=show_progress)Ancestors
- DeltaImage
- abc.ABC
Subclasses
Methods
def block_size(self)-
Returns
(int, int): block height, block width
def bytes_per_pixel(self, band=0)-
Returns
int:- the number of bytes per pixel
def close(self)-
Close the image.
def path(self)-
Returns the paths returned by
_prep. def save(self, path, tile_size=None, nodata_value=None, show_progress=False)-
Save to file, with preprocessing applied.
Parameters
path:str- Filename to save to.
tile_size:(int, int)- If specified, overwrite block size
nodata_value:image dtype- If specified, overwrite nodata value
show_progress:bool- Write progress bar to stdout
Inherited members
class TiffWriter (filename)-
Write a geotiff to a file.
Expand source code
class TiffWriter(delta_image.DeltaImageWriter): """ Write a geotiff to a file. """ def __init__(self, filename): self._filename = filename self._tiff_w = None def initialize(self, size, numpy_dtype, metadata=None, nodata_value=None): assert (len(size) == 3), ('Error: len(size) of '+str(size)+' != 3') TILE_SIZE = 256 self._tiff_w = _TiffWriter(self._filename, size[0], size[1], num_bands=size[2], data_type=_numpy_dtype_to_gdal_type(numpy_dtype), metadata=metadata, nodata_value=nodata_value, tile_height=min(TILE_SIZE, size[0]), tile_width=min(TILE_SIZE, size[1])) def write(self, data, y, x): self._tiff_w.write_region(data, y, x) def close(self): if self._tiff_w is not None: self._tiff_w.close() def abort(self): self.close() try: os.remove(self._filename) except OSError: passAncestors
- DeltaImageWriter
- abc.ABC
Inherited members