Fixed #30489 -- Fixed RasterField deserialization with pixeltype flags.
Thanks Ivor Bosloper for the original patch.
This commit is contained in:
parent
769cee5252
commit
7e15795bf0
|
@ -42,5 +42,11 @@ STRUCT_SIZE = {
|
||||||
'd': 8, # Double
|
'd': 8, # Double
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Pixel type specifies type of pixel values in a band. Storage flag specifies
|
||||||
|
# whether the band data is stored as part of the datum or is to be found on the
|
||||||
|
# server's filesystem. There are currently 11 supported pixel value types, so 4
|
||||||
|
# bits are enough to account for all. Reserve the upper 4 bits for generic
|
||||||
|
# flags.
|
||||||
# See https://trac.osgeo.org/postgis/wiki/WKTRaster/RFC/RFC1_V0SerialFormat#Pixeltypeandstorageflag
|
# See https://trac.osgeo.org/postgis/wiki/WKTRaster/RFC/RFC1_V0SerialFormat#Pixeltypeandstorageflag
|
||||||
|
BANDTYPE_PIXTYPE_MASK = 0x0F
|
||||||
BANDTYPE_FLAG_HASNODATA = 1 << 6
|
BANDTYPE_FLAG_HASNODATA = 1 << 6
|
||||||
|
|
|
@ -3,8 +3,8 @@ import struct
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
BANDTYPE_FLAG_HASNODATA, GDAL_TO_POSTGIS, GDAL_TO_STRUCT,
|
BANDTYPE_FLAG_HASNODATA, BANDTYPE_PIXTYPE_MASK, GDAL_TO_POSTGIS,
|
||||||
POSTGIS_HEADER_STRUCTURE, POSTGIS_TO_GDAL, STRUCT_SIZE,
|
GDAL_TO_STRUCT, POSTGIS_HEADER_STRUCTURE, POSTGIS_TO_GDAL, STRUCT_SIZE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,13 +45,9 @@ def from_pgraster(data):
|
||||||
pixeltypes = []
|
pixeltypes = []
|
||||||
while data:
|
while data:
|
||||||
# Get pixel type for this band
|
# Get pixel type for this band
|
||||||
pixeltype, data = chunk(data, 2)
|
pixeltype_with_flags, data = chunk(data, 2)
|
||||||
pixeltype = unpack('B', pixeltype)[0]
|
pixeltype_with_flags = unpack('B', pixeltype_with_flags)[0]
|
||||||
|
pixeltype = pixeltype_with_flags & BANDTYPE_PIXTYPE_MASK
|
||||||
# Remove nodata byte from band nodata value if it exists.
|
|
||||||
has_nodata = pixeltype & BANDTYPE_FLAG_HASNODATA
|
|
||||||
if has_nodata:
|
|
||||||
pixeltype &= ~BANDTYPE_FLAG_HASNODATA
|
|
||||||
|
|
||||||
# Convert datatype from PostGIS to GDAL & get pack type and size
|
# Convert datatype from PostGIS to GDAL & get pack type and size
|
||||||
pixeltype = POSTGIS_TO_GDAL[pixeltype]
|
pixeltype = POSTGIS_TO_GDAL[pixeltype]
|
||||||
|
@ -68,8 +64,8 @@ def from_pgraster(data):
|
||||||
band, data = chunk(data, pack_size * header[10] * header[11])
|
band, data = chunk(data, pack_size * header[10] * header[11])
|
||||||
band_result = {'data': bytes.fromhex(band)}
|
band_result = {'data': bytes.fromhex(band)}
|
||||||
|
|
||||||
# If the nodata flag is True, set the nodata value.
|
# Set the nodata value if the nodata flag is set.
|
||||||
if has_nodata:
|
if pixeltype_with_flags & BANDTYPE_FLAG_HASNODATA:
|
||||||
band_result['nodata_value'] = nodata
|
band_result['nodata_value'] = nodata
|
||||||
|
|
||||||
# Append band data to band list
|
# Append band data to band list
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django.contrib.gis.geos import GEOSGeometry
|
||||||
from django.contrib.gis.measure import D
|
from django.contrib.gis.measure import D
|
||||||
from django.contrib.gis.shortcuts import numpy
|
from django.contrib.gis.shortcuts import numpy
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
from django.db.models import Q
|
from django.db.models import F, Func, Q
|
||||||
from django.test import TransactionTestCase, skipUnlessDBFeature
|
from django.test import TransactionTestCase, skipUnlessDBFeature
|
||||||
from django.test.utils import CaptureQueriesContext
|
from django.test.utils import CaptureQueriesContext
|
||||||
|
|
||||||
|
@ -51,6 +51,26 @@ class RasterFieldTest(TransactionTestCase):
|
||||||
qs = RasterModel.objects.all()
|
qs = RasterModel.objects.all()
|
||||||
qs[0].rast.bands[0].data()
|
qs[0].rast.bands[0].data()
|
||||||
|
|
||||||
|
def test_deserialize_with_pixeltype_flags(self):
|
||||||
|
no_data = 3
|
||||||
|
rast = GDALRaster({
|
||||||
|
'srid': 4326,
|
||||||
|
'origin': [0, 0],
|
||||||
|
'scale': [-1, 1],
|
||||||
|
'skew': [0, 0],
|
||||||
|
'width': 1,
|
||||||
|
'height': 1,
|
||||||
|
'nr_of_bands': 1,
|
||||||
|
'bands': [{'data': [no_data], 'nodata_value': no_data}],
|
||||||
|
})
|
||||||
|
r = RasterModel.objects.create(rast=rast)
|
||||||
|
RasterModel.objects.filter(pk=r.pk).update(
|
||||||
|
rast=Func(F('rast'), function='ST_SetBandIsNoData'),
|
||||||
|
)
|
||||||
|
r.refresh_from_db()
|
||||||
|
self.assertEquals(r.rast.bands[0].data(), [[no_data]])
|
||||||
|
self.assertEquals(r.rast.bands[0].nodata_value, no_data)
|
||||||
|
|
||||||
def test_model_creation(self):
|
def test_model_creation(self):
|
||||||
"""
|
"""
|
||||||
Test RasterField through a test model.
|
Test RasterField through a test model.
|
||||||
|
|
Loading…
Reference in New Issue