2008-08-06 02:13:06 +08:00
|
|
|
"""
|
2015-06-19 23:46:03 +08:00
|
|
|
The SpatialProxy object allows for lazy-geometries and lazy-rasters. The proxy
|
|
|
|
uses Python descriptors for instantiating and setting Geometry or Raster
|
|
|
|
objects corresponding to geographic model fields.
|
2008-08-06 02:13:06 +08:00
|
|
|
|
2010-02-25 05:20:02 +08:00
|
|
|
Thanks to Robert Coup for providing this functionality (see #4322).
|
2008-08-06 02:13:06 +08:00
|
|
|
"""
|
2016-02-02 17:33:09 +08:00
|
|
|
from django.db.models.query_utils import DeferredAttribute
|
2008-08-06 02:13:06 +08:00
|
|
|
|
2013-11-03 01:18:46 +08:00
|
|
|
|
2016-02-02 17:33:09 +08:00
|
|
|
class SpatialProxy(DeferredAttribute):
|
2010-02-25 05:20:02 +08:00
|
|
|
def __init__(self, klass, field):
|
2008-08-06 02:13:06 +08:00
|
|
|
"""
|
2017-01-25 04:31:57 +08:00
|
|
|
Initialize on the given Geometry or Raster class (not an instance)
|
2015-06-19 23:46:03 +08:00
|
|
|
and the corresponding field.
|
2008-08-06 02:13:06 +08:00
|
|
|
"""
|
2010-02-25 05:20:02 +08:00
|
|
|
self._field = field
|
2008-08-06 02:13:06 +08:00
|
|
|
self._klass = klass
|
2017-01-21 21:13:44 +08:00
|
|
|
super().__init__(field.attname, klass)
|
2010-02-25 05:20:02 +08:00
|
|
|
|
2015-10-26 23:31:16 +08:00
|
|
|
def __get__(self, instance, cls=None):
|
2008-08-06 02:13:06 +08:00
|
|
|
"""
|
2017-01-25 04:31:57 +08:00
|
|
|
Retrieve the geometry or raster, initializing it using the
|
|
|
|
corresponding class specified during initialization and the value of
|
|
|
|
the field. Currently, GEOS or OGR geometries as well as GDALRasters are
|
|
|
|
supported.
|
2008-08-06 02:13:06 +08:00
|
|
|
"""
|
2015-10-26 23:31:16 +08:00
|
|
|
if instance is None:
|
2010-02-25 05:20:02 +08:00
|
|
|
# Accessed on a class, not an instance
|
|
|
|
return self
|
|
|
|
|
2008-08-06 02:13:06 +08:00
|
|
|
# Getting the value of the field.
|
2016-02-02 17:33:09 +08:00
|
|
|
try:
|
|
|
|
geo_value = instance.__dict__[self._field.attname]
|
|
|
|
except KeyError:
|
2017-01-21 21:13:44 +08:00
|
|
|
geo_value = super().__get__(instance, cls)
|
2010-02-25 05:20:02 +08:00
|
|
|
|
2015-06-19 23:46:03 +08:00
|
|
|
if isinstance(geo_value, self._klass):
|
|
|
|
geo_obj = geo_value
|
|
|
|
elif (geo_value is None) or (geo_value == ''):
|
|
|
|
geo_obj = None
|
2010-02-25 05:20:02 +08:00
|
|
|
else:
|
2015-06-19 23:46:03 +08:00
|
|
|
# Otherwise, a geometry or raster object is built using the field's
|
|
|
|
# contents, and the model's corresponding attribute is set.
|
|
|
|
geo_obj = self._klass(geo_value)
|
2015-10-26 23:31:16 +08:00
|
|
|
setattr(instance, self._field.attname, geo_obj)
|
2015-06-19 23:46:03 +08:00
|
|
|
return geo_obj
|
2010-02-25 05:20:02 +08:00
|
|
|
|
2015-10-26 23:31:16 +08:00
|
|
|
def __set__(self, instance, value):
|
2008-08-06 02:13:06 +08:00
|
|
|
"""
|
2017-01-25 04:31:57 +08:00
|
|
|
Retrieve the proxied geometry or raster with the corresponding class
|
|
|
|
specified during initialization.
|
2015-06-19 23:46:03 +08:00
|
|
|
|
2017-01-25 04:31:57 +08:00
|
|
|
To set geometries, use values of None, HEXEWKB, or WKT.
|
|
|
|
To set rasters, use JSON or dict values.
|
2008-08-06 02:13:06 +08:00
|
|
|
"""
|
2015-06-19 23:46:03 +08:00
|
|
|
# The geographic type of the field.
|
2009-03-31 01:15:49 +08:00
|
|
|
gtype = self._field.geom_type
|
2010-02-25 05:20:02 +08:00
|
|
|
|
2016-12-29 23:27:49 +08:00
|
|
|
if gtype == 'RASTER' and (value is None or isinstance(value, (str, dict, self._klass))):
|
2015-06-19 23:46:03 +08:00
|
|
|
# For raster fields, assure input is None or a string, dict, or
|
|
|
|
# raster instance.
|
|
|
|
pass
|
|
|
|
elif isinstance(value, self._klass) and (str(value.geom_type).upper() == gtype or gtype == 'GEOMETRY'):
|
|
|
|
# The geometry type must match that of the field -- unless the
|
|
|
|
# general GeometryField is used.
|
2013-10-17 16:17:41 +08:00
|
|
|
if value.srid is None:
|
2015-06-19 23:46:03 +08:00
|
|
|
# Assigning the field SRID if the geometry has no SRID.
|
2013-10-17 16:17:41 +08:00
|
|
|
value.srid = self._field.srid
|
2016-12-29 23:27:49 +08:00
|
|
|
elif value is None or isinstance(value, (str, memoryview)):
|
2015-06-19 23:46:03 +08:00
|
|
|
# Set geometries with None, WKT, HEX, or WKB
|
2008-08-06 02:13:06 +08:00
|
|
|
pass
|
|
|
|
else:
|
2015-06-19 23:46:03 +08:00
|
|
|
raise TypeError('Cannot set %s SpatialProxy (%s) with value of type: %s' % (
|
2015-10-26 23:31:16 +08:00
|
|
|
instance.__class__.__name__, gtype, type(value)))
|
2008-08-06 02:13:06 +08:00
|
|
|
|
|
|
|
# Setting the objects dictionary with the value, and returning.
|
2015-10-26 23:31:16 +08:00
|
|
|
instance.__dict__[self._field.attname] = value
|
2010-02-25 05:20:02 +08:00
|
|
|
return value
|