Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #28288 -- Allowed passing papsz options to GDALRaster initiation. #8616

Merged
merged 1 commit into from Jun 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions django/contrib/gis/gdal/raster/source.py
@@ -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
Expand Down Expand Up @@ -92,6 +92,16 @@ def __init__(self, ds_input, write=False):
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,
Expand All @@ -100,7 +110,7 @@ def __init__(self, ds_input, write=False):
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
Expand Down
66 changes: 51 additions & 15 deletions docs/ref/contrib/gis/gdal.txt
Expand Up @@ -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

Expand Down Expand Up @@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
3 changes: 3 additions & 0 deletions docs/releases/2.0.txt
Expand Up @@ -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`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
38 changes: 38 additions & 0 deletions tests/gis_tests/gdal_tests/test_raster.py
Expand Up @@ -310,6 +310,44 @@ def test_raster_info_accessor(self):
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({
Expand Down