Refs #25588 -- Fixed GDAL dependency in spatial lookups.

This commit is contained in:
Daniel Wiesmann 2016-05-27 17:43:17 +01:00
parent 0e7e47b5d7
commit 9bb1b4b7f6
2 changed files with 42 additions and 59 deletions

View File

@ -182,6 +182,25 @@ class BaseSpatialField(Field):
else: else:
return connection.ops.Adapter(self.get_prep_value(value)) return connection.ops.Adapter(self.get_prep_value(value))
def get_raster_prep_value(self, value, is_candidate):
"""
Return a GDALRaster if conversion is successful, otherwise return None.
"""
from django.contrib.gis.gdal import GDALRaster
if isinstance(value, GDALRaster):
return value
elif is_candidate:
try:
return GDALRaster(value)
except GDALException:
pass
elif isinstance(value, dict):
try:
return GDALRaster(value)
except GDALException:
raise ValueError("Couldn't create spatial object from lookup value '%s'." % value)
def get_prep_value(self, value): def get_prep_value(self, value):
""" """
Spatial lookup values are either a parameter that is (or may be Spatial lookup values are either a parameter that is (or may be
@ -190,9 +209,8 @@ class BaseSpatialField(Field):
geometry or raster value properly and preserves any other lookup geometry or raster value properly and preserves any other lookup
parameters. parameters.
""" """
from django.contrib.gis.gdal import GDALRaster
value = super(BaseSpatialField, self).get_prep_value(value) value = super(BaseSpatialField, self).get_prep_value(value)
# For IsValid lookups, boolean values are allowed. # For IsValid lookups, boolean values are allowed.
if isinstance(value, (Expression, bool)): if isinstance(value, (Expression, bool)):
return value return value
@ -205,23 +223,25 @@ class BaseSpatialField(Field):
# When the input is not a geometry or raster, attempt to construct one # When the input is not a geometry or raster, attempt to construct one
# from the given string input. # from the given string input.
if isinstance(obj, (Geometry, GDALRaster)): if isinstance(obj, Geometry):
pass pass
elif isinstance(obj, (bytes, six.string_types)) or hasattr(obj, '__geo_interface__'):
try:
obj = Geometry(obj)
except (GeometryException, GDALException):
try:
obj = GDALRaster(obj)
except GDALException:
raise ValueError("Couldn't create spatial object from lookup value '%s'." % obj)
elif isinstance(obj, dict):
try:
obj = GDALRaster(obj)
except GDALException:
raise ValueError("Couldn't create spatial object from lookup value '%s'." % obj)
else: else:
raise ValueError('Cannot use object with type %s for a spatial lookup parameter.' % type(obj).__name__) # Check if input is a candidate for conversion to raster or geometry.
is_candidate = isinstance(obj, (bytes, six.string_types)) or hasattr(obj, '__geo_interface__')
# With GDAL installed, try to convert the input to raster.
raster = False
if HAS_GDAL:
raster = self.get_raster_prep_value(obj, is_candidate)
if raster:
obj = raster
elif is_candidate:
try:
obj = Geometry(obj)
except (GeometryException, GDALException):
raise ValueError("Couldn't create spatial object from lookup value '%s'." % obj)
else:
raise ValueError('Cannot use object with type %s for a spatial lookup parameter.' % type(obj).__name__)
# Assigning the SRID value. # Assigning the SRID value.
obj.srid = self.get_srid(obj) obj.srid = self.get_srid(obj)
@ -295,48 +315,6 @@ class GeometryField(GeoSelectFormatMixin, BaseSpatialField):
""" """
return connection.ops.get_distance(self, value, lookup_type) return connection.ops.get_distance(self, value, lookup_type)
def get_prep_value(self, value):
"""
Spatial lookup values are either a parameter that is (or may be
converted to) a geometry, or a sequence of lookup values that
begins with a geometry. This routine will setup the geometry
value properly, and preserve any other lookup parameters before
returning to the caller.
"""
from django.contrib.gis.gdal import GDALRaster
value = super(GeometryField, self).get_prep_value(value)
if isinstance(value, (Expression, bool)):
return value
elif isinstance(value, (tuple, list)):
geom = value[0]
seq_value = True
else:
geom = value
seq_value = False
# When the input is not a GEOS geometry, attempt to construct one
# from the given string input.
if isinstance(geom, (Geometry, GDALRaster)):
pass
elif isinstance(geom, (bytes, six.string_types)) or hasattr(geom, '__geo_interface__'):
try:
geom = Geometry(geom)
except GeometryException:
raise ValueError('Could not create geometry from lookup value.')
else:
raise ValueError('Cannot use object with type %s for a geometry lookup parameter.' % type(geom).__name__)
# Assigning the SRID value.
geom.srid = self.get_srid(geom)
if seq_value:
lookup_val = [geom]
lookup_val.extend(value[1:])
return tuple(lookup_val)
else:
return geom
def from_db_value(self, value, expression, connection, context): def from_db_value(self, value, expression, connection, context):
if value: if value:
if not isinstance(value, Geometry): if not isinstance(value, Geometry):

View File

@ -1,6 +1,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from unittest import skipUnless
from django.contrib.gis.db.models import fields from django.contrib.gis.db.models import fields
from django.contrib.gis.gdal import HAS_GDAL
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.db import connection, migrations, models from django.db import connection, migrations, models
from django.db.migrations.migration import Migration from django.db.migrations.migration import Migration
@ -114,6 +117,7 @@ class OperationTests(TransactionTestCase):
self.assertSpatialIndexExists('gis_neighborhood', 'heatmap') self.assertSpatialIndexExists('gis_neighborhood', 'heatmap')
@skipIfDBFeature('supports_raster') @skipIfDBFeature('supports_raster')
@skipUnless(HAS_GDAL, 'A different error is raised if GDAL is not installed.')
def test_create_raster_model_on_db_without_raster_support(self): def test_create_raster_model_on_db_without_raster_support(self):
""" """
Test creating a model with a raster field on a db without raster support. Test creating a model with a raster field on a db without raster support.
@ -123,6 +127,7 @@ class OperationTests(TransactionTestCase):
self.set_up_test_model(True) self.set_up_test_model(True)
@skipIfDBFeature('supports_raster') @skipIfDBFeature('supports_raster')
@skipUnless(HAS_GDAL, 'A different error is raised if GDAL is not installed.')
def test_add_raster_field_on_db_without_raster_support(self): def test_add_raster_field_on_db_without_raster_support(self):
""" """
Test adding a raster field on a db without raster support. Test adding a raster field on a db without raster support.