Commit 6c4ac762 authored by Daniel Scheffler's avatar Daniel Scheffler
Browse files

Revised GeoArray.save() which fixes two GDAL warnings when writing cloud...


Revised GeoArray.save() which fixes two GDAL warnings when writing cloud optimized GeoTiff (COG) format.
Signed-off-by: Daniel Scheffler's avatarDaniel Scheffler <danschef@gfz-potsdam.de>
parent 9c0963af
Pipeline #26319 failed with stage
in 1 minute and 49 seconds
......@@ -2,6 +2,16 @@
History
=======
0.14.1 (coming soon)
--------------------
* Revised GeoArray.save() which fixes two GDAL warnings when writing cloud optimized GeoTiff (COG) format:
* *Warning 1*: This file used to have optimizations in its layout, but those have been, at least partly,
invalidated by later changes
* *Warning 2*: The IFD has been rewritten at the end of the file, which breaks COG layout.
0.14.0 (2021-07-26)
-------------------
......
......@@ -998,65 +998,51 @@ class GeoArray(object):
driver = gdal.GetDriverByName(fmt)
if driver is None:
raise Exception("'%s' is not a supported GDAL driver. Refer to www.gdal.org/formats_list.html for full "
"list of GDAL driver codes." % fmt)
raise Exception("'%s' is not a supported GDAL driver. Refer to https://gdal.org/drivers/raster/index.html "
"for full list of GDAL driver codes." % fmt)
if not os.path.isdir(os.path.dirname(out_path)):
os.makedirs(os.path.dirname(out_path))
envi_metadict = self.metadata.to_ENVI_metadict()
#####################
# write raster data #
#####################
###########################
# get source GDAL dataset #
###########################
if self.is_inmem:
# write in-memory data
ds_inmem = get_GDAL_ds_inmem(self.arr, # expects rows,columns,bands
self.geotransform, self.projection,
self._nodata) # avoid to compute the nodata value here, so use private attrib.
# write dataset
ds_out = driver.CreateCopy(out_path, ds_inmem,
options=creationOptions or []) # noqa
ds_src: gdal.Dataset
ds_out: gdal.Dataset
# # rows, columns, bands => bands, rows, columns
# out_arr = self.arr if self.ndim == 2 else np.swapaxes(np.swapaxes(self.arr, 0, 2), 1, 2)
# gdalnumeric.SaveArray(out_arr, out_path, format=fmt, prototype=ds_inmem) # expects bands,rows,columns
# ds_out = gdal.Open(out_path)
del ds_inmem
del ds_out
if self.is_inmem:
ds_src = get_GDAL_ds_inmem(self.arr, # expects rows,columns,bands
self.geotransform, self.projection,
self._nodata) # avoid to compute the nodata value here, so use private attrib.
else:
# transform linked image dataset (not in-memory) to target format
src_ds = gdal.Open(self.filePath)
if not src_ds:
raise Exception('Error reading file: ' + gdal.GetLastErrorMsg())
ds_src = gdal.Open(self.filePath)
# metadomains = {dom: src_ds.GetMetadata(dom) for dom in src_ds.GetMetadataDomainList()}
gdal.Translate(out_path, src_ds, format=fmt, creationOptions=creationOptions)
del src_ds
if not os.path.exists(out_path):
raise Exception(gdal.GetLastErrorMsg())
################
# set metadata #
################
# NOTE: The dataset has to be written BEFORE metadata are added. Otherwise, metadata are not written.
if not ds_src:
raise Exception('Error reading file: ' + gdal.GetLastErrorMsg())
ds_out = gdal.Open(out_path, gdal.GA_Update)
#########################################
# write output dataset and set metadata #
#########################################
try:
if not ds_out:
raise Exception('Error reading file: ' + gdal.GetLastErrorMsg())
# disable to write separate metadata XML files
os.environ['GDAL_PAM_ENABLED'] = 'NO'
# ENVI #
########
if fmt == 'ENVI':
# NOTE: The dataset has to be written BEFORE metadata are added. Otherwise, metadata are not written.
# write ds_src to disk and re-open it to add the metadata
gdal.Translate(out_path, ds_src, format=fmt, creationOptions=creationOptions)
del ds_src
ds_out = gdal.Open(out_path, gdal.GA_Update)
for bidx in range(self.bands):
band = ds_out.GetRasterBand(bidx + 1)
......@@ -1090,44 +1076,60 @@ class GeoArray(object):
ds_out.SetDescription(envi_metadict['description'])
ds_out.FlushCache()
gdal.Unlink(out_path + '.aux.xml')
# 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()))
else:
ds_out = ds_src
if 'description' in envi_metadict:
ds_out.SetDescription(envi_metadict['description'])
# set metadata
if self.metadata.all_meta:
# set band domain metadata
bandmeta_dict = self.metadata.to_DataFrame().astype(str).to_dict()
# 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()))
for bidx in range(self.bands):
band = ds_out.GetRasterBand(bidx + 1)
bandmeta = bandmeta_dict[bidx].copy()
if 'description' in envi_metadict:
ds_out.SetDescription(envi_metadict['description'])
# filter global metadata out
bandmeta = {k: v for k, v in bandmeta.items() if k not in self.metadata.global_meta}
# meta2write = dict((k, repr(v)) for k, v in self.metadata.band_meta.items() if v is not np.nan)
# set band domain metadata
bandmeta_dict = self.metadata.to_DataFrame().astype(str).to_dict()
if 'band_names' in bandmeta:
band.SetDescription(self.metadata.band_meta['band_names'][bidx].strip())
del bandmeta['band_names']
for bidx in range(self.bands):
band = ds_out.GetRasterBand(bidx + 1)
bandmeta = bandmeta_dict[bidx].copy()
if 'nodata' in bandmeta:
band.SetNoDataValue(self.metadata.band_meta['nodata'][bidx])
del bandmeta['nodata']
# filter global metadata out
bandmeta = {k: v for k, v in bandmeta.items() if k not in self.metadata.global_meta}
# meta2write = dict((k, repr(v)) for k, v in self.metadata.band_meta.items() if v is not np.nan)
if bandmeta:
band.SetMetadata(bandmeta)
if 'band_names' in bandmeta:
band.SetDescription(self.metadata.band_meta['band_names'][bidx].strip())
del bandmeta['band_names']
band.FlushCache()
del band
if 'nodata' in bandmeta:
band.SetNoDataValue(self.metadata.band_meta['nodata'][bidx])
del bandmeta['nodata']
ds_out.FlushCache()
if bandmeta:
band.SetMetadata(bandmeta)
band.FlushCache()
del band
ds_out.FlushCache()
# write ds_out to disk,
# -> writes the in-memory array or transforms the linked dataset into the target format
gdal.Translate(out_path, ds_out, format=fmt, creationOptions=creationOptions)
del ds_src
finally:
if 'GDAL_PAM_ENABLED' in os.environ:
del os.environ['GDAL_PAM_ENABLED']
if not os.path.exists(out_path):
raise Exception(gdal.GetLastErrorMsg())
del ds_out
def dump(self, out_path):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment