Fixed #27421 -- Added shape, size, and offset controls to GDALRaster constructor.
Thanks Tim Graham for the review.
This commit is contained in:
parent
501c993010
commit
2dc07da497
|
@ -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]
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in New Issue