Fixed #27421 -- Added shape, size, and offset controls to GDALRaster constructor.

Thanks Tim Graham for the review.
This commit is contained in:
Daniel Wiesmann 2016-11-11 12:09:38 +00:00 committed by Tim Graham
parent 501c993010
commit 2dc07da497
4 changed files with 128 additions and 12 deletions

View File

@ -108,9 +108,22 @@ class GDALRaster(GDALBase):
# Set band data if provided # Set band data if provided
for i, band_input in enumerate(ds_input.get('bands', [])): for i, band_input in enumerate(ds_input.get('bands', [])):
band = self.bands[i] band = self.bands[i]
band.data(band_input['data'])
if 'nodata_value' in band_input: if 'nodata_value' in band_input:
band.nodata_value = band_input['nodata_value'] band.nodata_value = band_input['nodata_value']
# Instantiate band filled with nodata values if only
# partial input data has been provided.
if band.nodata_value is not None and (
'data' not in band_input or
'size' in band_input or
'shape' in band_input):
band.data(data=(band.nodata_value,), shape=(1, 1))
# Set band data values from input.
band.data(
data=band_input.get('data'),
size=band_input.get('size'),
shape=band_input.get('shape'),
offset=band_input.get('offset'),
)
# Set SRID # Set SRID
self.srs = ds_input.get('srid') self.srs = ds_input.get('srid')
@ -339,16 +352,12 @@ class GDALRaster(GDALBase):
if 'datatype' not in ds_input: if 'datatype' not in ds_input:
ds_input['datatype'] = self.bands[0].datatype() ds_input['datatype'] = self.bands[0].datatype()
# Set the number of bands # Instantiate raster bands filled with nodata values.
ds_input['nr_of_bands'] = len(self.bands) ds_input['bands'] = [{'nodata_value': bnd.nodata_value} for bnd in self.bands]
# Create target raster # Create target raster
target = GDALRaster(ds_input, write=True) target = GDALRaster(ds_input, write=True)
# Copy nodata values to warped raster
for index, band in enumerate(self.bands):
target.bands[index].nodata_value = band.nodata_value
# Select resampling algorithm # Select resampling algorithm
algorithm = GDAL_RESAMPLE_ALGORITHMS[resampling] algorithm = GDAL_RESAMPLE_ALGORITHMS[resampling]

View File

@ -1117,16 +1117,39 @@ blue.
>>> rst = GDALRaster('/path/to/your/raster.tif', write=False) >>> rst = GDALRaster('/path/to/your/raster.tif', write=False)
>>> rst.name >>> rst.name
'/path/to/your/raster.tif' '/path/to/your/raster.tif'
>>> rst.width, rst.height # This file has 163 x 174 pixels >>> rst.width, rst.height # This file has 163 x 174 pixels
(163, 174) (163, 174)
>>> rst = GDALRaster({'srid': 4326, 'width': 1, 'height': 2, 'datatype': 1 >>> rst = GDALRaster({ # Creates an in-memory raster
... 'bands': [{'data': [0, 1]}]}) # Creates in-memory raster ... 'srid': 4326,
... 'width': 4,
... 'height': 4,
... 'datatype': 1,
... 'bands': [{
... 'data': (2, 3),
... 'offset': (1, 1),
... 'size': (2, 2),
... 'shape': (2, 1),
... 'nodata_value': 5,
... }]
... })
>>> rst.srs.srid >>> rst.srs.srid
4326 4326
>>> rst.width, rst.height >>> rst.width, rst.height
(1, 2) (4, 4)
>>> rst.bands[0].data() >>> rst.bands[0].data()
array([[0, 1]], dtype=int8) array([[5, 5, 5, 5],
[5, 2, 3, 5],
[5, 2, 3, 5],
[5, 5, 5, 5]], dtype=uint8)
.. versionchanged:: 1.11
Added the ability to pass the ``size``, ``shape``, and ``offset``
parameters when creating :class:`GDALRaster` objects. The parameters
can be passed through the ``ds_input`` dictionary. This allows to
finely control initial pixel values. The functionality is similar to
the :meth:`GDALBand.data()<django.contrib.gis.gdal.GDALBand.data>`
method.
.. attribute:: name .. attribute:: name

View File

@ -148,6 +148,9 @@ Minor features
* PostGIS migrations can now change field dimensions. * PostGIS migrations can now change field dimensions.
* Added the ability to pass the `size`, `shape`, and `offset` parameter when
creating :class:`~django.contrib.gis.gdal.GDALRaster` objects.
:mod:`django.contrib.messages` :mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -190,6 +190,64 @@ class GDALRasterTests(unittest.TestCase):
else: else:
self.assertEqual(restored_raster.bands[0].data(), self.rs.bands[0].data()) self.assertEqual(restored_raster.bands[0].data(), self.rs.bands[0].data())
def test_offset_size_and_shape_on_raster_creation(self):
rast = GDALRaster({
'datatype': 1,
'width': 4,
'height': 4,
'srid': 4326,
'bands': [{
'data': (1,),
'offset': (1, 1),
'size': (2, 2),
'shape': (1, 1),
'nodata_value': 2,
}],
})
# Get array from raster.
result = rast.bands[0].data()
if numpy:
result = result.flatten().tolist()
# Band data is equal to nodata value except on input block of ones.
self.assertEqual(
result,
[2, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 2]
)
def test_set_nodata_value_on_raster_creation(self):
# Create raster filled with nodata values.
rast = GDALRaster({
'datatype': 1,
'width': 2,
'height': 2,
'srid': 4326,
'bands': [{'nodata_value': 23}],
})
# Get array from raster.
result = rast.bands[0].data()
if numpy:
result = result.flatten().tolist()
# All band data is equal to nodata value.
self.assertEqual(result, [23, ] * 4)
def test_set_nodata_none_on_raster_creation(self):
if GDAL_VERSION < (2, 1):
self.skipTest("GDAL >= 2.1 is required for this test.")
# Create raster without data and without nodata value.
rast = GDALRaster({
'datatype': 1,
'width': 2,
'height': 2,
'srid': 4326,
'bands': [{'nodata_value': None}],
})
# Get array from raster.
result = rast.bands[0].data()
if numpy:
result = result.flatten().tolist()
# Band data is equal to zero becaues no nodata value has been specified.
self.assertEqual(result, [0] * 4)
def test_raster_warp(self): def test_raster_warp(self):
# Create in memory raster # Create in memory raster
source = GDALRaster({ source = GDALRaster({
@ -246,6 +304,29 @@ class GDALRasterTests(unittest.TestCase):
12.0, 13.0, 14.0, 15.0] 12.0, 13.0, 14.0, 15.0]
) )
def test_raster_warp_nodata_zone(self):
# Create in memory raster.
source = GDALRaster({
'datatype': 1,
'driver': 'MEM',
'width': 4,
'height': 4,
'srid': 3086,
'origin': (500000, 400000),
'scale': (100, -100),
'skew': (0, 0),
'bands': [{
'data': range(16),
'nodata_value': 23,
}],
})
# Warp raster onto a location that does not cover any pixels of the original.
result = source.warp({'origin': (200000, 200000)}).bands[0].data()
if numpy:
result = result.flatten().tolist()
# The result is an empty raster filled with the correct nodata value.
self.assertEqual(result, [23] * 16)
def test_raster_transform(self): def test_raster_transform(self):
if GDAL_VERSION < (1, 8, 1): if GDAL_VERSION < (1, 8, 1):
self.skipTest("GDAL >= 1.8.1 is required for this test") self.skipTest("GDAL >= 1.8.1 is required for this test")