def save_image(self, img, filename=None, floating_point=False, **kwargs): """Save the image to the given *filename* in geotiff_ format. `floating_point` allows the saving of 'L' mode images in floating point format if set to True. .. _geotiff: http://trac.osgeo.org/geotiff/ """ raster = gdal.GetDriverByName("GTiff") filename = filename or self.get_filename(**img.info) # Update global GDAL options with these specific ones gdal_options = self.gdal_options.copy() for k in kwargs.keys(): if k in self.GDAL_OPTIONS: gdal_options[k] = kwargs[k] floating_point = floating_point if floating_point is not None else self.floating_point if "alpha" in kwargs: raise ValueError( "Keyword 'alpha' is automatically set and should not be specified" ) if floating_point: if img.mode != "L": raise ValueError( "Image must be in 'L' mode for floating point geotiff saving" ) if img.fill_value is None: LOG.warning( "Image with floats cannot be transparent, so setting fill_value to 0" ) fill_value = 0 datasets = [img.channels[0].astype(np.float64)] fill_value = img.fill_value or [0] gformat = gdal.GDT_Float64 opacity = 0 else: nbits = int(gdal_options.get("nbits", "8")) if nbits > 16: dtype = np.uint32 gformat = gdal.GDT_UInt32 elif nbits > 8: dtype = np.uint16 gformat = gdal.GDT_UInt16 else: dtype = np.uint8 gformat = gdal.GDT_Byte opacity = np.iinfo(dtype).max datasets, fill_value = img._finalize(dtype) LOG.debug("Saving to GeoTiff: %s", filename) g_opts = [ "{0}={1}".format(k.upper(), str(v)) for k, v in gdal_options.items() ] ensure_dir(filename) if img.mode == "L": if fill_value is not None: dst_ds = raster.Create(filename, img.width, img.height, 1, gformat, g_opts) else: g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, img.width, img.height, 2, gformat, g_opts) self._gdal_write_datasets(dst_ds, datasets, opacity, fill_value) elif img.mode == "LA": g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, img.width, img.height, 2, gformat, g_opts) self._gdal_write_datasets(dst_ds, datasets[:-1], datasets[1], fill_value) elif img.mode == "RGB": if fill_value is not None: dst_ds = raster.Create(filename, img.width, img.height, 3, gformat, g_opts) else: g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, img.width, img.height, 4, gformat, g_opts) self._gdal_write_datasets(dst_ds, datasets, opacity, fill_value) elif img.mode == "RGBA": g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, img.width, img.height, 4, gformat, g_opts) self._gdal_write_datasets(dst_ds, datasets[:-1], datasets[3], fill_value) else: raise NotImplementedError( "Saving to GeoTIFF using image mode %s is not implemented." % img.mode) # Create raster GeoTransform based on upper left corner and pixel # resolution ... if not overwritten by argument geotransform. if "area" not in img.info: LOG.warning("No 'area' metadata found in image") else: area = img.info["area"] try: geotransform = [ area.area_extent[0], area.pixel_size_x, 0, area.area_extent[3], 0, -area.pixel_size_y ] dst_ds.SetGeoTransform(geotransform) srs = osr.SpatialReference() srs.ImportFromProj4(area.proj4_string) srs.SetProjCS(area.proj_id) try: srs.SetWellKnownGeogCS(area.proj_dict['ellps']) except KeyError: pass try: # Check for epsg code. srs.ImportFromEPSG( int(area.proj_dict['init'].lower().split('epsg:')[1])) except (KeyError, IndexError): pass srs = srs.ExportToWkt() dst_ds.SetProjection(srs) except AttributeError: LOG.warning( "Can't save geographic information to geotiff, unsupported area type" ) tags = self.tags.copy() if "start_time" in img.info: tags.update({ 'TIFFTAG_DATETIME': img.info["start_time"].strftime("%Y:%m:%d %H:%M:%S") }) dst_ds.SetMetadata(tags, '') # Close the dataset dst_ds = None
def save_image(self, img, filename=None, dtype=None, fill_value=None, floating_point=None, compute=True, **kwargs): """Save the image to the given ``filename`` in geotiff_ format. Note for faster output and reduced memory usage the ``rasterio`` library must be installed. This writer currently falls back to using ``gdal`` directly, but that will be deprecated in the future. Args: img (xarray.DataArray): Data to save to geotiff. filename (str): Filename to save the image to. Defaults to ``filename`` passed during writer creation. Unlike the creation ``filename`` keyword argument, this filename does not get formatted with data attributes. dtype (numpy.dtype): Numpy data type to save the image as. Defaults to 8-bit unsigned integer (``np.uint8``). If the ``dtype`` argument is provided during writer creation then that will be used as the default. fill_value (int or float): Value to use where data values are NaN/null. If this is specified in the writer configuration file that value will be used as the default. floating_point (bool): Deprecated. Use ``dtype=np.float64`` instead. compute (bool): Compute dask arrays and save the image immediately. If ``False`` then the return value can be passed to :func:`~satpy.writers.compute_writer_results` to do the computation. This is useful when multiple images may share input calculations where dask can benefit from not repeating them multiple times. Defaults to ``True`` in the writer by itself, but is typically passed as ``False`` by callers where calculations can be combined. .. _geotiff: http://trac.osgeo.org/geotiff/ """ filename = filename or self.get_filename(**img.data.attrs) # Update global GDAL options with these specific ones gdal_options = self.gdal_options.copy() for k in kwargs.keys(): if k in self.GDAL_OPTIONS: gdal_options[k] = kwargs[k] if fill_value is None: # fall back to fill_value from configuration file fill_value = self.info.get('fill_value') if floating_point is not None: import warnings warnings.warn("'floating_point' is deprecated, use" "'dtype=np.float64' instead.", DeprecationWarning) dtype = np.float64 dtype = dtype if dtype is not None else self.dtype if dtype is None: dtype = np.uint8 if "alpha" in kwargs: raise ValueError( "Keyword 'alpha' is automatically set based on 'fill_value' " "and should not be specified") if np.issubdtype(dtype, np.floating): if img.mode != "L": raise ValueError("Image must be in 'L' mode for floating " "point geotiff saving") if fill_value is None: LOG.debug("Alpha band not supported for float geotiffs, " "setting fill value to 'NaN'") fill_value = np.nan try: import rasterio # noqa # we can use the faster rasterio-based save return img.save(filename, fformat='tif', fill_value=fill_value, dtype=dtype, compute=compute, **gdal_options) except ImportError: LOG.warning("Using legacy/slower geotiff save method, install " "'rasterio' for faster saving.") import warnings warnings.warn("Using legacy/slower geotiff save method with 'gdal'." "This will be deprecated in the future. Install " "'rasterio' for faster saving and future " "compatibility.", PendingDeprecationWarning) # Map numpy data types to GDAL data types NP2GDAL = { np.float32: gdal.GDT_Float32, np.float64: gdal.GDT_Float64, np.uint8: gdal.GDT_Byte, np.uint16: gdal.GDT_UInt16, np.uint32: gdal.GDT_UInt32, np.int16: gdal.GDT_Int16, np.int32: gdal.GDT_Int32, np.complex64: gdal.GDT_CFloat32, np.complex128: gdal.GDT_CFloat64, } # force to numpy dtype object dtype = np.dtype(dtype) gformat = NP2GDAL[dtype.type] gdal_options['nbits'] = int(gdal_options.get('nbits', dtype.itemsize * 8)) datasets, mode = img._finalize(fill_value=fill_value, dtype=dtype) LOG.debug("Saving to GeoTiff: %s", filename) g_opts = ["{0}={1}".format(k.upper(), str(v)) for k, v in gdal_options.items()] ensure_dir(filename) delayed = self._create_file(filename, img, gformat, g_opts, datasets, mode) if compute: return delayed.compute() return delayed
def save_image(self, img, filename=None, dtype=None, fill_value=None, floating_point=None, compute=True, **kwargs): """Save the image to the given *filename* in geotiff_ format. `floating_point` allows the saving of 'L' mode images in floating point format if set to True. .. _geotiff: http://trac.osgeo.org/geotiff/ """ filename = filename or self.get_filename(**img.data.attrs) # Update global GDAL options with these specific ones gdal_options = self.gdal_options.copy() for k in kwargs.keys(): if k in self.GDAL_OPTIONS: gdal_options[k] = kwargs[k] if floating_point is not None: import warnings warnings.warn("'floating_point' is deprecated, use" "'dtype=np.float64' instead.", DeprecationWarning) dtype = np.float64 dtype = dtype if dtype is not None else self.dtype if dtype is None: dtype = np.uint8 if "alpha" in kwargs: raise ValueError( "Keyword 'alpha' is automatically set based on 'fill_value' " "and should not be specified") if np.issubdtype(dtype, np.floating): if img.mode != "L": raise ValueError("Image must be in 'L' mode for floating " "point geotiff saving") if fill_value is None: LOG.debug("Alpha band not supported for float geotiffs, " "setting fill value to 'NaN'") fill_value = np.nan try: import rasterio # noqa # we can use the faster rasterio-based save return img.save(filename, fformat='tif', fill_value=fill_value, dtype=dtype, compute=compute, **gdal_options) except ImportError: LOG.warning("Using legacy/slower geotiff save method, install " "'rasterio' for faster saving.") # force to numpy dtype object dtype = np.dtype(dtype) gformat = NP2GDAL[dtype.type] gdal_options['nbits'] = int(gdal_options.get('nbits', dtype.itemsize * 8)) datasets, mode = img._finalize(fill_value=fill_value, dtype=dtype) LOG.debug("Saving to GeoTiff: %s", filename) g_opts = ["{0}={1}".format(k.upper(), str(v)) for k, v in gdal_options.items()] ensure_dir(filename) delayed = self._create_file(filename, img, gformat, g_opts, datasets, mode) if compute: return delayed.compute() return delayed
def save_image(self, img, filename=None, dtype=None, fill_value=None, floating_point=None, compute=True, **kwargs): """Save the image to the given *filename* in geotiff_ format. `floating_point` allows the saving of 'L' mode images in floating point format if set to True. .. _geotiff: http://trac.osgeo.org/geotiff/ """ filename = filename or self.get_filename(**img.data.attrs) # Update global GDAL options with these specific ones gdal_options = self.gdal_options.copy() for k in kwargs.keys(): if k in self.GDAL_OPTIONS: gdal_options[k] = kwargs[k] if floating_point is not None: import warnings warnings.warn( "'floating_point' is deprecated, use" "'dtype=np.float64' instead.", DeprecationWarning) dtype = np.float64 dtype = dtype if dtype is not None else self.dtype if dtype is None: dtype = np.uint8 if "alpha" in kwargs: raise ValueError( "Keyword 'alpha' is automatically set based on 'fill_value' " "and should not be specified") if np.issubdtype(dtype, np.floating): if img.mode != "L": raise ValueError("Image must be in 'L' mode for floating " "point geotiff saving") if fill_value is None: LOG.debug("Alpha band not supported for float geotiffs, " "setting fill value to 'NaN'") fill_value = np.nan try: import rasterio # noqa # we can use the faster rasterio-based save return img.save(filename, fformat='tif', fill_value=fill_value, dtype=dtype, compute=compute, **gdal_options) except ImportError: LOG.warning("Using legacy/slower geotiff save method, install " "'rasterio' for faster saving.") # force to numpy dtype object dtype = np.dtype(dtype) gformat = NP2GDAL[dtype.type] gdal_options['nbits'] = int( gdal_options.get('nbits', dtype.itemsize * 8)) datasets, mode = img._finalize(fill_value=fill_value, dtype=dtype) LOG.debug("Saving to GeoTiff: %s", filename) g_opts = [ "{0}={1}".format(k.upper(), str(v)) for k, v in gdal_options.items() ] ensure_dir(filename) delayed = self._create_file(filename, img, gformat, g_opts, datasets, mode) if compute: return delayed.compute() return delayed
def save_image(self, img, filename=None, floating_point=False, **kwargs): """Save the image to the given *filename* in geotiff_ format. `floating_point` allows the saving of 'L' mode images in floating point format if set to True. .. _geotiff: http://trac.osgeo.org/geotiff/ """ raster = gdal.GetDriverByName("GTiff") filename = filename or self.get_filename(**img.info) # Update global GDAL options with these specific ones gdal_options = self.gdal_options.copy() for k in kwargs.keys(): if k in self.GDAL_OPTIONS: gdal_options[k] = kwargs[k] floating_point = floating_point if floating_point is not None else self.floating_point if "alpha" in kwargs: raise ValueError( "Keyword 'alpha' is automatically set and should not be specified") if floating_point: if img.mode != "L": raise ValueError( "Image must be in 'L' mode for floating point geotiff saving") if img.fill_value is None: LOG.warning( "Image with floats cannot be transparent, so setting fill_value to 0") fill_value = 0 datasets = [img.datasets[0].astype(np.float64)] fill_value = img.fill_value or [0] gformat = gdal.GDT_Float64 opacity = 0 else: nbits = int(gdal_options.get("nbits", "8")) if nbits > 16: dtype = np.uint32 gformat = gdal.GDT_UInt32 elif nbits > 8: dtype = np.uint16 gformat = gdal.GDT_UInt16 else: dtype = np.uint8 gformat = gdal.GDT_Byte opacity = np.iinfo(dtype).max datasets, fill_value = img._finalize(dtype) LOG.debug("Saving to GeoTiff: %s", filename) g_opts = ["{0}={1}".format(k.upper(), str(v)) for k, v in self.gdal_options.items()] ensure_dir(filename) if img.mode == "L": if fill_value is not None: dst_ds = raster.Create(filename, img.width, img.height, 1, gformat, g_opts) else: g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, img.width, img.height, 2, gformat, g_opts) self._gdal_write_datasets(dst_ds, datasets, opacity, fill_value) elif img.mode == "LA": g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, img.width, img.height, 2, gformat, g_opts) self._gdal_write_datasets(dst_ds, datasets[:-1], datasets[1], fill_value) elif img.mode == "RGB": if fill_value is not None: dst_ds = raster.Create(filename, img.width, img.height, 3, gformat, g_opts) else: g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, img.width, img.height, 4, gformat, g_opts) self._gdal_write_datasets(dst_ds, datasets, opacity, fill_value) elif img.mode == "RGBA": g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, img.width, img.height, 4, gformat, g_opts) self._gdal_write_datasets(dst_ds, datasets[:-1], datasets[3], fill_value) else: raise NotImplementedError( "Saving to GeoTIFF using image mode %s is not implemented." % img.mode) # Create raster GeoTransform based on upper left corner and pixel # resolution ... if not overwritten by argument geotransform. if "area" not in img.info: LOG.warning("No 'area' metadata found in image") else: area = img.info["area"] try: geotransform = [area.area_extent[0], area.pixel_size_x, 0, area.area_extent[3], 0, -area.pixel_size_y] dst_ds.SetGeoTransform(geotransform) srs = osr.SpatialReference() srs.ImportFromProj4(area.proj4_string) srs.SetProjCS(area.proj_id) try: srs.SetWellKnownGeogCS(area.proj_dict['ellps']) except KeyError: pass try: # Check for epsg code. srs.ImportFromEPSG(int( area.proj_dict['init'].lower().split('epsg:')[1])) except (KeyError, IndexError): pass srs = srs.ExportToWkt() dst_ds.SetProjection(srs) except AttributeError: LOG.warning( "Can't save geographic information to geotiff, unsupported area type") tags = self.tags.copy() if "start_time" in img.info: tags.update({'TIFFTAG_DATETIME': img.info["start_time"].strftime( "%Y:%m:%d %H:%M:%S")}) dst_ds.SetMetadata(tags, '') # Close the dataset dst_ds = None