diff --git a/geoarray/baseclasses.py b/geoarray/baseclasses.py index e5a60bd3383c711fb83b610d5b595d64404f7192..eebacb6dff305f37dd24865c6a8561a7f72beb30 100644 --- a/geoarray/baseclasses.py +++ b/geoarray/baseclasses.py @@ -52,14 +52,17 @@ __author__ = 'Daniel Scheffler' class GeoArray(object): + """ + This class creates a fast Python interface for geodata - either on disk or in memory. It can be instanced + with a file path or with a numpy array and the corresponding geoinformation. Instances can always be indexed + like normal numpy arrays, no matter if GeoArray has been instanced from file or from an in-memory array. + GeoArray provides a wide range of geo-related attributes belonging to the dataset as well as some functions for + quickly visualizing the data as a map, a simple image or an interactive image. + """ def __init__(self, path_or_array, geotransform=None, projection=None, bandnames=None, nodata=None, progress=True, q=False): # type: (Union[str, np.ndarray], tuple, str, list, float, bool, bool) -> None - """This class creates a fast Python interface for geodata - either on disk or in memory. It can be instanced with - a file path or with a numpy array and the corresponding geoinformation. Instances can always be indexed like - normal numpy arrays, no matter if GeoArray has been instanced from file or from an in-memory array. GeoArray - provides a wide range of geo-related attributes belonging to the dataset as well as some functions for quickly - visualizing the data as a map, a simple image or an interactive image. + """Get an instance of GeoArray. :param path_or_array: a numpy.ndarray or a valid file path :param geotransform: GDAL geotransform of the given array or file on disk @@ -354,7 +357,7 @@ class GeoArray(object): if not self.is_inmem: self.set_gdalDataset_meta() if self._nodata is None: - self._nodata = self.find_noDataVal() + self.find_noDataVal() if self._nodata == 'ambiguous': warnings.warn('Nodata value could not be clearly identified. It has been set to None.') self._nodata = None @@ -369,6 +372,9 @@ class GeoArray(object): # type: (Union[int, None]) -> None self._nodata = value + if self._metadata and value is not None: + self.metadata.global_meta.update({'data ignore value': str(value)}) + @property def mask_nodata(self): """ @@ -515,7 +521,7 @@ class GeoArray(object): if self._metadata is not None: return self._metadata else: - default = GDAL_Metadata(nbands=self.bands) + default = GDAL_Metadata(nbands=self.bands, nodata_allbands=self._nodata) self._metadata = default if not self.is_inmem: @@ -705,6 +711,7 @@ class GeoArray(object): else: nodata = None + self.nodata = nodata return nodata def set_gdalDataset_meta(self): @@ -736,12 +743,12 @@ class GeoArray(object): if 'nodata' not in self._initParams or self._initParams['nodata'] is None: band = ds.GetRasterBand(1) # FIXME this does not support different nodata values within the same file - self._nodata = band.GetNoDataValue() + self.nodata = band.GetNoDataValue() # set metadata attribute if self.is_inmem or not self.filePath: # metadata cannot be read from disk -> set it to the default - self._metadata = GDAL_Metadata(nbands=self.bands) + self._metadata = GDAL_Metadata(nbands=self.bands, nodata_allbands=self._nodata) else: self._metadata = GDAL_Metadata(filePath=self.filePath) @@ -977,27 +984,27 @@ class GeoArray(object): gdal.Unlink(out_path + '.aux.xml') elif self.metadata.all_meta: - # set global domain metadata - if self.metadata.global_meta: - ds_out.SetMetadata(dict((k, repr(v)) for k, v in self.metadata.global_meta.items())) + # set global domain metadata + if self.metadata.global_meta: + ds_out.SetMetadata(dict((k, repr(v)) for k, v in self.metadata.global_meta.items())) - if 'description' in envi_metadict: - ds_out.SetDescription(envi_metadict['description']) + if 'description' in envi_metadict: + ds_out.SetDescription(envi_metadict['description']) - # set band domain metadata - bandmeta_dict = self.metadata.to_DataFrame().astype(str).to_dict() + # set band domain metadata + bandmeta_dict = self.metadata.to_DataFrame().astype(str).to_dict() - for bidx in range(self.bands): - band = ds_out.GetRasterBand(bidx + 1) - bandmeta = bandmeta_dict[bidx] - # meta2write = dict((k, repr(v)) for k, v in self.metadata.band_meta.items() if v is not np.nan) - band.SetMetadata(bandmeta) + for bidx in range(self.bands): + band = ds_out.GetRasterBand(bidx + 1) + bandmeta = bandmeta_dict[bidx] + # meta2write = dict((k, repr(v)) for k, v in self.metadata.band_meta.items() if v is not np.nan) + band.SetMetadata(bandmeta) - if 'band_names' in envi_metadict: - band.SetDescription(self.metadata.band_meta['band_names'][bidx].strip()) + if 'band_names' in envi_metadict: + band.SetDescription(self.metadata.band_meta['band_names'][bidx].strip()) - band.FlushCache() - del band + band.FlushCache() + del band ds_out.FlushCache() del ds_out @@ -1507,7 +1514,7 @@ class GeoArray(object): def get_subset(self, xslice=None, yslice=None, zslice=None, return_GeoArray=True, reset_bandnames=False): # type: (slice, slice, slice, bool, bool) -> GeoArray - """Returns a new instance of GeoArray representing a subset of the initial one wit respect to given array position. + """Return a new GeoArray instance representing a subset of the initial one wit respect to given array position. :param xslice: a slice providing the X-position for the subset in the form slice(xstart, xend, xstep) :param yslice: a slice providing the Y-position for the subset in the form slice(ystart, yend, ystep) diff --git a/geoarray/metadata.py b/geoarray/metadata.py index 61985b5837bf5e8b83468c38d2bd5c274097d5f2..cb9fba6785931bbfbba904d2ffe5278b6703e602 100644 --- a/geoarray/metadata.py +++ b/geoarray/metadata.py @@ -4,6 +4,7 @@ import os from pprint import pformat from copy import deepcopy from typing import Union # noqa F401 # flake8 issue +from collections import OrderedDict from geopandas import GeoDataFrame, GeoSeries import numpy as np @@ -27,18 +28,22 @@ autohandled_meta = [ class GDAL_Metadata(object): - def __init__(self, filePath='', nbands=1): + def __init__(self, filePath='', nbands=1, nodata_allbands=None): # privates - self._global_meta = dict() - self._band_meta = dict() + self._global_meta = OrderedDict() + self._band_meta = OrderedDict() self.bands = nbands self.filePath = filePath self.fileFormat = '' + self.nodata_allbands = nodata_allbands if filePath: self.read_from_file(filePath) + if nodata_allbands is not None: + self.global_meta.update({'data ignore value': str(nodata_allbands)}) + @classmethod def from_file(cls, filePath): return GDAL_Metadata(filePath=filePath) @@ -73,8 +78,8 @@ class GDAL_Metadata(object): @band_meta.setter def band_meta(self, meta_dict): - if not isinstance(meta_dict, dict): - raise TypeError("Expected type 'dict', received '%s'." % type(meta_dict)) + if not isinstance(meta_dict, (dict, OrderedDict)): + raise TypeError("Expected type 'dict'/'OrderedDict', received '%s'." % type(meta_dict)) for k, v in meta_dict.items(): if not isinstance(v, list): @@ -83,11 +88,11 @@ class GDAL_Metadata(object): raise ValueError("The length of the given lists must be equal to the number of bands. " "Received a list with %d items for '%s'." % (len(v), k)) - self._band_meta = meta_dict # TODO convert strings to useful types + self._band_meta = OrderedDict(meta_dict) # TODO convert strings to useful types @property def all_meta(self): - all_meta = self.global_meta.copy() + all_meta = OrderedDict(self.global_meta.copy()) all_meta.update(self.band_meta) return all_meta @@ -190,8 +195,8 @@ class GDAL_Metadata(object): return 'Metadata: \n\n' + pformat(self.all_meta) def to_ENVI_metadict(self): - return dict(zip(self.all_meta.keys(), - [self._convert_param_to_ENVI_str(i) for i in self.all_meta.values()])) + return OrderedDict(zip(self.all_meta.keys(), + [self._convert_param_to_ENVI_str(i) for i in self.all_meta.values()])) def get_subset(self, bands2extract=None, keys2extract=None): # type: (Union[slice, list, np.ndarray], Union[str, list]) -> 'GDAL_Metadata' diff --git a/geoarray/version.py b/geoarray/version.py index db64b826e366ec5f5d3a14666225ba472a08402c..87b15281be310a3d7481bae3dbf5b21d6a9f06a0 100644 --- a/geoarray/version.py +++ b/geoarray/version.py @@ -1,2 +1,2 @@ -__version__ = '0.8.13' -__versionalias__ = '20190329.02' +__version__ = '0.8.14' +__versionalias__ = '20190329.03'