Fixed #28288 -- Allowed passing papsz options to GDALRaster initialization.

This commit is contained in:
Daniel Wiesmann 2017-06-08 15:51:19 +01:00 committed by Tim Graham
parent 0c3c37a376
commit fe5e34a295
4 changed files with 104 additions and 17 deletions

View File

@ -1,6 +1,6 @@
import json
import os
from ctypes import addressof, byref, c_double, c_void_p
from ctypes import addressof, byref, c_char_p, c_double, c_void_p
from django.contrib.gis.gdal.driver import Driver
from django.contrib.gis.gdal.error import GDALException
@ -92,6 +92,16 @@ class GDALRaster(GDALRasterBase):
if 'srid' not in ds_input:
raise GDALException('Specify srid for JSON or dict input.')
# Create null terminated gdal options array.
papsz_options = []
for key, val in ds_input.get('papsz_options', {}).items():
option = '{}={}'.format(key, val)
papsz_options.append(option.upper().encode())
papsz_options.append(None)
# Convert papszlist to ctypes array.
papsz_options = (c_char_p * len(papsz_options))(*papsz_options)
# Create GDAL Raster
self._ptr = capi.create_ds(
driver._ptr,
@ -100,7 +110,7 @@ class GDALRaster(GDALRasterBase):
ds_input['height'],
ds_input.get('nr_of_bands', len(ds_input.get('bands', []))),
ds_input.get('datatype', 6),
None
byref(papsz_options),
)
# Set band data if provided

View File

@ -1619,21 +1619,22 @@ the others are described below.
The following table describes all keys that can be set in the ``ds_input``
dictionary.
=============== ======== ==================================================
Key Default Usage
=============== ======== ==================================================
``srid`` required Mapped to the :attr:`~GDALRaster.srid` attribute
``width`` required Mapped to the :attr:`~GDALRaster.width` attribute
``height`` required Mapped to the :attr:`~GDALRaster.height` attribute
``driver`` ``MEM`` Mapped to the :attr:`~GDALRaster.driver` attribute
``name`` ``''`` See below
``origin`` ``0`` Mapped to the :attr:`~GDALRaster.origin` attribute
``scale`` ``0`` Mapped to the :attr:`~GDALRaster.scale` attribute
``skew`` ``0`` Mapped to the :attr:`~GDALRaster.width` attribute
``bands`` ``[]`` See below
``nr_of_bands`` ``0`` See below
``datatype`` ``6`` See below
=============== ======== ==================================================
================= ======== ==================================================
Key Default Usage
================= ======== ==================================================
``srid`` required Mapped to the :attr:`~GDALRaster.srid` attribute
``width`` required Mapped to the :attr:`~GDALRaster.width` attribute
``height`` required Mapped to the :attr:`~GDALRaster.height` attribute
``driver`` ``MEM`` Mapped to the :attr:`~GDALRaster.driver` attribute
``name`` ``''`` See below
``origin`` ``0`` Mapped to the :attr:`~GDALRaster.origin` attribute
``scale`` ``0`` Mapped to the :attr:`~GDALRaster.scale` attribute
``skew`` ``0`` Mapped to the :attr:`~GDALRaster.width` attribute
``bands`` ``[]`` See below
``nr_of_bands`` ``0`` See below
``datatype`` ``6`` See below
``papsz_options`` ``{}`` See below
================= ======== ==================================================
.. object:: name
@ -1673,6 +1674,41 @@ Key Default Usage
raster bands values are instantiated as an array of zeros and the "no
data" value is set to ``None``.
.. object:: papsz_options
.. versionadded:: 2.0
A dictionary with raster creation options. The key-value pairs of the
input dictionary are passed to the driver on creation of the raster.
The available options are driver-specific and are described in the
documentation of each driver.
The values in the dictionary are not case-sensitive and are automatically
converted to the correct string format upon creation.
The following example uses some of the options available for the
`GTiff driver`__. The result is a compressed signed byte raster with an
internal tiling scheme. The internal tiles have a block size of 23 by 23::
>>> GDALRaster({
... 'driver': 'GTiff',
... 'name': '/path/to/new/file.tif',
... 'srid': 4326,
... 'width': 255,
... 'height': 255,
... 'nr_of_bands': 1,
... 'papsz_options': {
... 'compress': 'packbits',
... 'pixeltype': 'signedbyte',
... 'tiled': 'yes',
... 'blockxsize': 23,
... 'blockysize': 23,
... }
... })
__ http://www.gdal.org/frmt_gtiff.html
The ``band_input`` dictionary
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -83,6 +83,9 @@ Minor features
:attr:`~django.contrib.gis.gdal.GDALRaster.info`, and
:attr:`~django.contrib.gis.gdal.GDALBand.metadata` attributes.
* Allowed passing driver-specific creation options to
:class:`~django.contrib.gis.gdal.GDALRaster` objects using ``papsz_options``.
:mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -310,6 +310,44 @@ class GDALRasterTests(SimpleTestCase):
info_ref = [line.strip() for line in gdalinfo.split('\n') if line.strip() != '']
self.assertEqual(info_dyn, info_ref)
def test_compressed_file_based_raster_creation(self):
rstfile = tempfile.NamedTemporaryFile(suffix='.tif')
# Make a compressed copy of an existing raster.
compressed = self.rs.warp({'papsz_options': {'compress': 'packbits'}, 'name': rstfile.name})
# Check physically if compression worked.
self.assertLess(os.path.getsize(compressed.name), os.path.getsize(self.rs.name))
if GDAL_VERSION > (1, 11):
# Create file-based raster with options from scratch.
compressed = GDALRaster({
'datatype': 1,
'driver': 'tif',
'name': rstfile.name,
'width': 40,
'height': 40,
'srid': 3086,
'origin': (500000, 400000),
'scale': (100, -100),
'skew': (0, 0),
'bands': [{
'data': range(40 ^ 2),
'nodata_value': 255,
}],
'papsz_options': {
'compress': 'packbits',
'pixeltype': 'signedbyte',
'blockxsize': 23,
'blockysize': 23,
}
})
# Check if options used on creation are stored in metadata.
# Reopening the raster ensures that all metadata has been written
# to the file.
compressed = GDALRaster(compressed.name)
self.assertEqual(compressed.metadata['IMAGE_STRUCTURE']['COMPRESSION'], 'PACKBITS',)
self.assertEqual(compressed.bands[0].metadata['IMAGE_STRUCTURE']['PIXELTYPE'], 'SIGNEDBYTE')
if GDAL_VERSION >= (2, 1):
self.assertIn('Block=40x23', compressed.info)
def test_raster_warp(self):
# Create in memory raster
source = GDALRaster({