""" 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