81 lines
3.2 KiB
Python
81 lines
3.2 KiB
Python
"""
|
|
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.
|
|
|
|
Thanks to Robert Coup for providing this functionality (see #4322).
|
|
"""
|
|
from django.db.models.query_utils import DeferredAttribute
|
|
from django.utils import six
|
|
|
|
|
|
class SpatialProxy(DeferredAttribute):
|
|
def __init__(self, klass, field):
|
|
"""
|
|
Proxy initializes on the given Geometry or Raster class (not an instance)
|
|
and the corresponding field.
|
|
"""
|
|
self._field = field
|
|
self._klass = klass
|
|
super(SpatialProxy, self).__init__(field.attname, klass)
|
|
|
|
def __get__(self, instance, cls=None):
|
|
"""
|
|
This accessor retrieves 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.
|
|
"""
|
|
if instance is None:
|
|
# Accessed on a class, not an instance
|
|
return self
|
|
|
|
# Getting the value of the field.
|
|
try:
|
|
geo_value = instance.__dict__[self._field.attname]
|
|
except KeyError:
|
|
geo_value = super(SpatialProxy, self).__get__(instance, cls)
|
|
|
|
if isinstance(geo_value, self._klass):
|
|
geo_obj = geo_value
|
|
elif (geo_value is None) or (geo_value == ''):
|
|
geo_obj = None
|
|
else:
|
|
# 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)
|
|
setattr(instance, self._field.attname, geo_obj)
|
|
return geo_obj
|
|
|
|
def __set__(self, instance, value):
|
|
"""
|
|
This accessor sets the proxied geometry or raster with the
|
|
corresponding class specified during initialization.
|
|
|
|
To set geometries, values of None, HEXEWKB, or WKT may be used.
|
|
To set rasters, JSON or dict values may be used.
|
|
"""
|
|
# The geographic type of the field.
|
|
gtype = self._field.geom_type
|
|
|
|
if gtype == 'RASTER' and (value is None or isinstance(value, six.string_types + (dict, self._klass))):
|
|
# 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.
|
|
if value.srid is None:
|
|
# Assigning the field SRID if the geometry has no SRID.
|
|
value.srid = self._field.srid
|
|
elif value is None or isinstance(value, six.string_types + (six.memoryview,)):
|
|
# Set geometries with None, WKT, HEX, or WKB
|
|
pass
|
|
else:
|
|
raise TypeError('Cannot set %s SpatialProxy (%s) with value of type: %s' % (
|
|
instance.__class__.__name__, gtype, type(value)))
|
|
|
|
# Setting the objects dictionary with the value, and returning.
|
|
instance.__dict__[self._field.attname] = value
|
|
return value
|