Skip to content

Commit

Permalink
Merge 8dcaa3b into d95c944
Browse files Browse the repository at this point in the history
  • Loading branch information
ch-k committed Sep 26, 2016
2 parents d95c944 + 8dcaa3b commit a034d51
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 9 deletions.
1 change: 1 addition & 0 deletions mpop/imageo/formats/writer_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@
WR_OPT_NBITS = 'nbits'
WR_OPT_COMPRESSION = 'compression'
WR_OPT_BLOCKSIZE = 'blocksize'
WR_OPT_FILL_VALUE_SUBST = 'fill_value_subst'
18 changes: 16 additions & 2 deletions mpop/imageo/geo_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,11 @@ def save(self, filename, compression=6,
writer_options[write_opts.WR_OPT_NBITS] = tags.get('NBITS')

if fformat.lower() in ('tif', 'tiff'):
kwargs = kwargs or {}
kwargs['writer_options'] = writer_options
return self.geotiff_save(filename, compression, tags,
gdal_options, blocksize, **kwargs)
gdal_options, blocksize,
**kwargs)
try:
# Let image.pil_save it ?
Image.save(self, filename, compression, fformat=fformat)
Expand Down Expand Up @@ -162,14 +165,18 @@ def _gdal_write_channels(self, dst_ds, channels, opacity, fill_value):
def geotiff_save(self, filename, compression=6,
tags=None, gdal_options=None,
blocksize=0, geotransform=None,
spatialref=None, floating_point=False):
spatialref=None, floating_point=False,
writer_options=None):
"""Save the image to the given *filename* in geotiff_ format, with the
*compression* level in [0, 9]. 0 means not compressed. The *tags*
argument is a dict of tags to include in the image (as metadata). By
default it uses the 'area' instance to generate geotransform and
spatialref information, this can be overwritten by the arguments
*geotransform* and *spatialref*. *floating_point* allows the saving of
'L' mode images in floating point format if set to True.
When argument *writer_options* is not none and entry 'fill_value_subst'
is included, its numeric value will be used to substitute image data
that would be equal to the fill_value (used to replace masked data).
.. _geotiff: http://trac.osgeo.org/geotiff/
"""
Expand All @@ -178,6 +185,7 @@ def geotiff_save(self, filename, compression=6,
raster = gdal.GetDriverByName("GTiff")

tags = tags or {}
writer_options = writer_options or {}

if floating_point:
if self.mode != "L":
Expand Down Expand Up @@ -205,6 +213,12 @@ def geotiff_save(self, filename, compression=6,
opacity = np.iinfo(dtype).max
channels, fill_value = self._finalize(dtype)

fill_value_subst = writer_options.get(
write_opts.WR_OPT_FILL_VALUE_SUBST, None)
if fill_value is not None and fill_value_subst is not None:
for i, chan in enumerate(channels):
np.place(chan, chan == fill_value[i], int(fill_value_subst))

logger.debug("Saving to GeoTiff.")

if tags is not None:
Expand Down
140 changes: 133 additions & 7 deletions mpop/tests/test_geo_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
sys.modules['pyresample'] = MagicMock()

import mpop.imageo.geo_image as geo_image
import mpop.imageo.formats.writer_options as writer_opts


class TestGeoImage(unittest.TestCase):
"""Class for testing pp.geo_image.
Expand All @@ -62,27 +64,49 @@ def test_save(self, mock_save):
"""

self.img.save("test.tif", compression=0)
mock_save.assert_called_once_with("test.tif", 0, {}, None, 256)
mock_save.assert_called_once_with("test.tif", 0, {}, None, 256,
writer_options={'blocksize': 256,
'compression': 0})
mock_save.reset_mock()
self.img.save("test.tif", compression=9)
mock_save.assert_called_once_with("test.tif", 9, {}, None, 256)
mock_save.assert_called_once_with("test.tif", 9, {}, None, 256,
writer_options={'blocksize': 256,
'compression': 9})
mock_save.reset_mock()
self.img.save("test.tif", compression=9, floating_point=True)
mock_save.assert_called_once_with("test.tif", 9, {}, None, 256,
floating_point=True)
floating_point=True,
writer_options={'blocksize': 256,
'compression': 9})

mock_save.reset_mock()
self.img.save("test.tif", compression=9, tags={"NBITS": 20})
mock_save.assert_called_once_with("test.tif", 9, {"NBITS": 20},
None, 256)
None, 256,
writer_options={'blocksize': 256,
'nbits': 20,
'compression': 9})
mock_save.reset_mock()
self.img.save("test.tif", writer_options={"compression":9})
mock_save.assert_called_once_with("test.tif", 9, {}, None, 256)

mock_save.assert_called_once_with("test.tif", 9, {}, None, 256,
writer_options={'blocksize': 256,
'compression': 9})

mock_save.reset_mock()
self.img.save("test.tif", writer_options={"compression":9, "nbits":16})
mock_save.assert_called_once_with("test.tif", 9, {"NBITS": 16},
None, 256)
None, 256,
writer_options={'blocksize': 256,
'nbits': 16,
'compression': 9})

mock_save.reset_mock()
self.img.save("test.tif", writer_options={"fill_value_subst": 1})
mock_save.assert_called_once_with("test.tif", 6, {}, None, 256,
writer_options={'blocksize': 256,
'compression': 6,
'fill_value_subst': 1})


with patch.object(geo_image.Image, 'save') as mock_isave:
self.img.save("test.png")
Expand Down Expand Up @@ -440,6 +464,108 @@ def test_save_geotiff_geotransform(self, mock_write_channels, gtbn, gui32, gui16
dst_ds.SetMetadata.assert_called_once_with(time_tag, '')


@patch('osgeo.osr.SpatialReference')
@patch('mpop.projector.get_area_def')
@patch('osgeo.gdal.GDT_Float64')
@patch('osgeo.gdal.GDT_Byte')
@patch('osgeo.gdal.GDT_UInt16')
@patch('osgeo.gdal.GDT_UInt32')
@patch('osgeo.gdal.GetDriverByName')
@patch.object(geo_image.GeoImage, '_gdal_write_channels')
def test_save_geotiff_fill_value(self, mock_write_channels, gtbn, gui32, gui16, gby, gf, gad, spaceref):
"""Save to geotiff format.
"""

# source image data, masked data but only zeros
self.data = np.ma.zeros((512, 512), dtype=np.uint8)
self.data.mask = np.zeros(self.data .shape, dtype=bool)
self.data.mask[0,0] = True

self.img = geo_image.GeoImage(self.data,
area="euro",
time_slot=self.time_slot)
self.img.fill_value = [0]

raster = gtbn.return_value

self.img.geotiff_save("test.tif", 0, None, {"BLA": "09"}, 256)
gtbn.assert_called_once_with("GTiff")

raster.Create.assert_called_once_with("test.tif",
self.data.shape[0],
self.data.shape[1],
1,
gby,
["BLA=09",
'TILED=YES',
'BLOCKXSIZE=256',
'BLOCKYSIZE=256'])
dst_ds = raster.Create.return_value

self.assertEquals(mock_write_channels.call_count, 1)
self.assertEquals(mock_write_channels.call_args[0][0], dst_ds)
self.assertEquals(mock_write_channels.call_args[0][2], 255)
self.assertTrue(mock_write_channels.call_args[0][3], self.img.fill_value)
self.assertTrue(np.all(mock_write_channels.call_args[0][1]
== self.data))


@patch('osgeo.osr.SpatialReference')
@patch('mpop.projector.get_area_def')
@patch('osgeo.gdal.GDT_Float64')
@patch('osgeo.gdal.GDT_Byte')
@patch('osgeo.gdal.GDT_UInt16')
@patch('osgeo.gdal.GDT_UInt32')
@patch('osgeo.gdal.GetDriverByName')
@patch.object(geo_image.GeoImage, '_gdal_write_channels')
def test_save_geotiff_fill_value_subst(self, mock_write_channels, gtbn, gui32, gui16, gby, gf, gad, spaceref):
"""Save to geotiff format.
"""

# source image data, masked data but only zeros
self.data = np.ma.zeros((512, 512), dtype=np.uint8)
self.data.mask = np.zeros(self.data .shape, dtype=bool)
self.data.mask[0,0] = True

self.img = geo_image.GeoImage(self.data,
area="euro",
time_slot=self.time_slot)
self.img.fill_value = [0]

# not masked zeros should be replaced by ones
fill_value_substitution = 1

data_with_subst = np.ma.copy(self.data)
np.place(data_with_subst, self.data == self.img.fill_value[0], 1)

raster = gtbn.return_value

self.img.geotiff_save("test.tif", 0, None, {"BLA": "09"}, 256,
writer_options={writer_opts.WR_OPT_FILL_VALUE_SUBST: fill_value_substitution})

gtbn.assert_called_once_with("GTiff")

raster.Create.assert_called_once_with("test.tif",
self.data.shape[0],
self.data.shape[1],
1,
gby,
["BLA=09",
'TILED=YES',
'BLOCKXSIZE=256',
'BLOCKYSIZE=256'])
dst_ds = raster.Create.return_value

self.assertEquals(mock_write_channels.call_count, 1)
self.assertEquals(mock_write_channels.call_args[0][0], dst_ds)
self.assertEquals(mock_write_channels.call_args[0][2], 255)
self.assertTrue(mock_write_channels.call_args[0][3], self.img.fill_value)

# all zeros should be replaced by ones
self.assertTrue(np.all(mock_write_channels.call_args[0][1]
== data_with_subst))


def suite():
"""The test suite for test_geo_image.
"""
Expand Down

0 comments on commit a034d51

Please sign in to comment.