summaryrefslogtreecommitdiff
path: root/lib/python2.7/site-packages/django/contrib/gis/db/models
diff options
context:
space:
mode:
authorttt2017-05-13 00:29:47 +0530
committerttt2017-05-13 00:29:47 +0530
commit4336f5f06f61de30ae3fa54650fce63a9d5ef5be (patch)
tree23b4ee9b8e8f24bf732acf2f7ad22ed50cdd5670 /lib/python2.7/site-packages/django/contrib/gis/db/models
downloadSBHS-2018-Rpi-4336f5f06f61de30ae3fa54650fce63a9d5ef5be.tar.gz
SBHS-2018-Rpi-4336f5f06f61de30ae3fa54650fce63a9d5ef5be.tar.bz2
SBHS-2018-Rpi-4336f5f06f61de30ae3fa54650fce63a9d5ef5be.zip
added all server files
Diffstat (limited to 'lib/python2.7/site-packages/django/contrib/gis/db/models')
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/__init__.py14
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/aggregates.py16
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/fields.py305
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/manager.py103
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/proxy.py66
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/query.py784
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/sql/__init__.py3
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/sql/aggregates.py62
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/sql/compiler.py313
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/sql/conversion.py27
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/sql/query.py121
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/models/sql/where.py91
12 files changed, 1905 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/__init__.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/__init__.py
new file mode 100644
index 0000000..e36aa36
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/__init__.py
@@ -0,0 +1,14 @@
+# Want to get everything from the 'normal' models package.
+from django.db.models import *
+
+# Geographic aggregate functions
+from django.contrib.gis.db.models.aggregates import *
+
+# The GeoManager
+from django.contrib.gis.db.models.manager import GeoManager
+
+# The geographic-enabled fields.
+from django.contrib.gis.db.models.fields import (
+ GeometryField, PointField, LineStringField, PolygonField,
+ MultiPointField, MultiLineStringField, MultiPolygonField,
+ GeometryCollectionField)
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/aggregates.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/aggregates.py
new file mode 100644
index 0000000..d0fc6d3
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/aggregates.py
@@ -0,0 +1,16 @@
+from django.db.models import Aggregate
+
+class Collect(Aggregate):
+ name = 'Collect'
+
+class Extent(Aggregate):
+ name = 'Extent'
+
+class Extent3D(Aggregate):
+ name = 'Extent3D'
+
+class MakeLine(Aggregate):
+ name = 'MakeLine'
+
+class Union(Aggregate):
+ name = 'Union'
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/fields.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/fields.py
new file mode 100644
index 0000000..2e221b7
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/fields.py
@@ -0,0 +1,305 @@
+from django.db.models.fields import Field
+from django.db.models.sql.expressions import SQLEvaluator
+from django.utils.translation import ugettext_lazy as _
+from django.contrib.gis import forms
+from django.contrib.gis.db.models.proxy import GeometryProxy
+from django.contrib.gis.geometry.backend import Geometry, GeometryException
+from django.utils import six
+
+# Local cache of the spatial_ref_sys table, which holds SRID data for each
+# spatial database alias. This cache exists so that the database isn't queried
+# for SRID info each time a distance query is constructed.
+_srid_cache = {}
+
+def get_srid_info(srid, connection):
+ """
+ Returns the units, unit name, and spheroid WKT associated with the
+ given SRID from the `spatial_ref_sys` (or equivalent) spatial database
+ table for the given database connection. These results are cached.
+ """
+ global _srid_cache
+
+ try:
+ # The SpatialRefSys model for the spatial backend.
+ SpatialRefSys = connection.ops.spatial_ref_sys()
+ except NotImplementedError:
+ # No `spatial_ref_sys` table in spatial backend (e.g., MySQL).
+ return None, None, None
+
+ if not connection.alias in _srid_cache:
+ # Initialize SRID dictionary for database if it doesn't exist.
+ _srid_cache[connection.alias] = {}
+
+ if not srid in _srid_cache[connection.alias]:
+ # Use `SpatialRefSys` model to query for spatial reference info.
+ sr = SpatialRefSys.objects.using(connection.alias).get(srid=srid)
+ units, units_name = sr.units
+ spheroid = SpatialRefSys.get_spheroid(sr.wkt)
+ _srid_cache[connection.alias][srid] = (units, units_name, spheroid)
+
+ return _srid_cache[connection.alias][srid]
+
+class GeometryField(Field):
+ "The base GIS field -- maps to the OpenGIS Specification Geometry type."
+
+ # The OpenGIS Geometry name.
+ geom_type = 'GEOMETRY'
+ form_class = forms.GeometryField
+
+ # Geodetic units.
+ geodetic_units = ('Decimal Degree', 'degree')
+
+ description = _("The base GIS field -- maps to the OpenGIS Specification Geometry type.")
+
+ def __init__(self, verbose_name=None, srid=4326, spatial_index=True, dim=2,
+ geography=False, **kwargs):
+ """
+ The initialization function for geometry fields. Takes the following
+ as keyword arguments:
+
+ srid:
+ The spatial reference system identifier, an OGC standard.
+ Defaults to 4326 (WGS84).
+
+ spatial_index:
+ Indicates whether to create a spatial index. Defaults to True.
+ Set this instead of 'db_index' for geographic fields since index
+ creation is different for geometry columns.
+
+ dim:
+ The number of dimensions for this geometry. Defaults to 2.
+
+ extent:
+ Customize the extent, in a 4-tuple of WGS 84 coordinates, for the
+ geometry field entry in the `USER_SDO_GEOM_METADATA` table. Defaults
+ to (-180.0, -90.0, 180.0, 90.0).
+
+ tolerance:
+ Define the tolerance, in meters, to use for the geometry field
+ entry in the `USER_SDO_GEOM_METADATA` table. Defaults to 0.05.
+ """
+
+ # Setting the index flag with the value of the `spatial_index` keyword.
+ self.spatial_index = spatial_index
+
+ # Setting the SRID and getting the units. Unit information must be
+ # easily available in the field instance for distance queries.
+ self.srid = srid
+
+ # Setting the dimension of the geometry field.
+ self.dim = dim
+
+ # Setting the verbose_name keyword argument with the positional
+ # first parameter, so this works like normal fields.
+ kwargs['verbose_name'] = verbose_name
+
+ # Is this a geography rather than a geometry column?
+ self.geography = geography
+
+ # Oracle-specific private attributes for creating the entry in
+ # `USER_SDO_GEOM_METADATA`
+ self._extent = kwargs.pop('extent', (-180.0, -90.0, 180.0, 90.0))
+ self._tolerance = kwargs.pop('tolerance', 0.05)
+
+ super(GeometryField, self).__init__(**kwargs)
+
+ # The following functions are used to get the units, their name, and
+ # the spheroid corresponding to the SRID of the GeometryField.
+ def _get_srid_info(self, connection):
+ # Get attributes from `get_srid_info`.
+ self._units, self._units_name, self._spheroid = get_srid_info(self.srid, connection)
+
+ def spheroid(self, connection):
+ if not hasattr(self, '_spheroid'):
+ self._get_srid_info(connection)
+ return self._spheroid
+
+ def units(self, connection):
+ if not hasattr(self, '_units'):
+ self._get_srid_info(connection)
+ return self._units
+
+ def units_name(self, connection):
+ if not hasattr(self, '_units_name'):
+ self._get_srid_info(connection)
+ return self._units_name
+
+ ### Routines specific to GeometryField ###
+ def geodetic(self, connection):
+ """
+ Returns true if this field's SRID corresponds with a coordinate
+ system that uses non-projected units (e.g., latitude/longitude).
+ """
+ return self.units_name(connection) in self.geodetic_units
+
+ def get_distance(self, value, lookup_type, connection):
+ """
+ Returns a distance number in units of the field. For example, if
+ `D(km=1)` was passed in and the units of the field were in meters,
+ then 1000 would be returned.
+ """
+ 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.
+ """
+ if isinstance(value, SQLEvaluator):
+ 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):
+ 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 get_srid(self, geom):
+ """
+ Returns the default SRID for the given geometry, taking into account
+ the SRID set for the field. For example, if the input geometry
+ has no SRID, then that of the field will be returned.
+ """
+ gsrid = geom.srid # SRID of given geometry.
+ if gsrid is None or self.srid == -1 or (gsrid == -1 and self.srid != -1):
+ return self.srid
+ else:
+ return gsrid
+
+ ### Routines overloaded from Field ###
+ def contribute_to_class(self, cls, name):
+ super(GeometryField, self).contribute_to_class(cls, name)
+
+ # Setup for lazy-instantiated Geometry object.
+ setattr(cls, self.attname, GeometryProxy(Geometry, self))
+
+ def db_type(self, connection):
+ return connection.ops.geo_db_type(self)
+
+ def formfield(self, **kwargs):
+ defaults = {'form_class' : self.form_class,
+ 'geom_type' : self.geom_type,
+ 'srid' : self.srid,
+ }
+ defaults.update(kwargs)
+ if (self.dim > 2 and not 'widget' in kwargs and
+ not getattr(defaults['form_class'].widget, 'supports_3d', False)):
+ defaults['widget'] = forms.Textarea
+ return super(GeometryField, self).formfield(**defaults)
+
+ def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):
+ """
+ Prepare for the database lookup, and return any spatial parameters
+ necessary for the query. This includes wrapping any geometry
+ parameters with a backend-specific adapter and formatting any distance
+ parameters into the correct units for the coordinate system of the
+ field.
+ """
+ if lookup_type in connection.ops.gis_terms:
+ # special case for isnull lookup
+ if lookup_type == 'isnull':
+ return []
+
+ # Populating the parameters list, and wrapping the Geometry
+ # with the Adapter of the spatial backend.
+ if isinstance(value, (tuple, list)):
+ params = [connection.ops.Adapter(value[0])]
+ if lookup_type in connection.ops.distance_functions:
+ # Getting the distance parameter in the units of the field.
+ params += self.get_distance(value[1:], lookup_type, connection)
+ elif lookup_type in connection.ops.truncate_params:
+ # Lookup is one where SQL parameters aren't needed from the
+ # given lookup value.
+ pass
+ else:
+ params += value[1:]
+ elif isinstance(value, SQLEvaluator):
+ params = []
+ else:
+ params = [connection.ops.Adapter(value)]
+
+ return params
+ else:
+ raise ValueError('%s is not a valid spatial lookup for %s.' %
+ (lookup_type, self.__class__.__name__))
+
+ def get_prep_lookup(self, lookup_type, value):
+ if lookup_type == 'isnull':
+ return bool(value)
+ else:
+ return self.get_prep_value(value)
+
+ def get_db_prep_save(self, value, connection):
+ "Prepares the value for saving in the database."
+ if value is None:
+ return None
+ else:
+ return connection.ops.Adapter(self.get_prep_value(value))
+
+ def get_placeholder(self, value, connection):
+ """
+ Returns the placeholder for the geometry column for the
+ given value.
+ """
+ return connection.ops.get_geom_placeholder(self, value)
+
+# The OpenGIS Geometry Type Fields
+class PointField(GeometryField):
+ geom_type = 'POINT'
+ form_class = forms.PointField
+ description = _("Point")
+
+class LineStringField(GeometryField):
+ geom_type = 'LINESTRING'
+ form_class = forms.LineStringField
+ description = _("Line string")
+
+class PolygonField(GeometryField):
+ geom_type = 'POLYGON'
+ form_class = forms.PolygonField
+ description = _("Polygon")
+
+class MultiPointField(GeometryField):
+ geom_type = 'MULTIPOINT'
+ form_class = forms.MultiPointField
+ description = _("Multi-point")
+
+class MultiLineStringField(GeometryField):
+ geom_type = 'MULTILINESTRING'
+ form_class = forms.MultiLineStringField
+ description = _("Multi-line string")
+
+class MultiPolygonField(GeometryField):
+ geom_type = 'MULTIPOLYGON'
+ form_class = forms.MultiPolygonField
+ description = _("Multi polygon")
+
+class GeometryCollectionField(GeometryField):
+ geom_type = 'GEOMETRYCOLLECTION'
+ form_class = forms.GeometryCollectionField
+ description = _("Geometry collection")
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/manager.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/manager.py
new file mode 100644
index 0000000..aa57e3a
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/manager.py
@@ -0,0 +1,103 @@
+from django.db.models.manager import Manager
+from django.contrib.gis.db.models.query import GeoQuerySet
+
+class GeoManager(Manager):
+ "Overrides Manager to return Geographic QuerySets."
+
+ # This manager should be used for queries on related fields
+ # so that geometry columns on Oracle and MySQL are selected
+ # properly.
+ use_for_related_fields = True
+
+ def get_queryset(self):
+ return GeoQuerySet(self.model, using=self._db)
+
+ def area(self, *args, **kwargs):
+ return self.get_queryset().area(*args, **kwargs)
+
+ def centroid(self, *args, **kwargs):
+ return self.get_queryset().centroid(*args, **kwargs)
+
+ def collect(self, *args, **kwargs):
+ return self.get_queryset().collect(*args, **kwargs)
+
+ def difference(self, *args, **kwargs):
+ return self.get_queryset().difference(*args, **kwargs)
+
+ def distance(self, *args, **kwargs):
+ return self.get_queryset().distance(*args, **kwargs)
+
+ def envelope(self, *args, **kwargs):
+ return self.get_queryset().envelope(*args, **kwargs)
+
+ def extent(self, *args, **kwargs):
+ return self.get_queryset().extent(*args, **kwargs)
+
+ def extent3d(self, *args, **kwargs):
+ return self.get_queryset().extent3d(*args, **kwargs)
+
+ def force_rhr(self, *args, **kwargs):
+ return self.get_queryset().force_rhr(*args, **kwargs)
+
+ def geohash(self, *args, **kwargs):
+ return self.get_queryset().geohash(*args, **kwargs)
+
+ def geojson(self, *args, **kwargs):
+ return self.get_queryset().geojson(*args, **kwargs)
+
+ def gml(self, *args, **kwargs):
+ return self.get_queryset().gml(*args, **kwargs)
+
+ def intersection(self, *args, **kwargs):
+ return self.get_queryset().intersection(*args, **kwargs)
+
+ def kml(self, *args, **kwargs):
+ return self.get_queryset().kml(*args, **kwargs)
+
+ def length(self, *args, **kwargs):
+ return self.get_queryset().length(*args, **kwargs)
+
+ def make_line(self, *args, **kwargs):
+ return self.get_queryset().make_line(*args, **kwargs)
+
+ def mem_size(self, *args, **kwargs):
+ return self.get_queryset().mem_size(*args, **kwargs)
+
+ def num_geom(self, *args, **kwargs):
+ return self.get_queryset().num_geom(*args, **kwargs)
+
+ def num_points(self, *args, **kwargs):
+ return self.get_queryset().num_points(*args, **kwargs)
+
+ def perimeter(self, *args, **kwargs):
+ return self.get_queryset().perimeter(*args, **kwargs)
+
+ def point_on_surface(self, *args, **kwargs):
+ return self.get_queryset().point_on_surface(*args, **kwargs)
+
+ def reverse_geom(self, *args, **kwargs):
+ return self.get_queryset().reverse_geom(*args, **kwargs)
+
+ def scale(self, *args, **kwargs):
+ return self.get_queryset().scale(*args, **kwargs)
+
+ def snap_to_grid(self, *args, **kwargs):
+ return self.get_queryset().snap_to_grid(*args, **kwargs)
+
+ def svg(self, *args, **kwargs):
+ return self.get_queryset().svg(*args, **kwargs)
+
+ def sym_difference(self, *args, **kwargs):
+ return self.get_queryset().sym_difference(*args, **kwargs)
+
+ def transform(self, *args, **kwargs):
+ return self.get_queryset().transform(*args, **kwargs)
+
+ def translate(self, *args, **kwargs):
+ return self.get_queryset().translate(*args, **kwargs)
+
+ def union(self, *args, **kwargs):
+ return self.get_queryset().union(*args, **kwargs)
+
+ def unionagg(self, *args, **kwargs):
+ return self.get_queryset().unionagg(*args, **kwargs)
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/proxy.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/proxy.py
new file mode 100644
index 0000000..1fdc503
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/proxy.py
@@ -0,0 +1,66 @@
+"""
+The GeometryProxy object, allows for lazy-geometries. The proxy uses
+Python descriptors for instantiating and setting Geometry objects
+corresponding to geographic model fields.
+
+Thanks to Robert Coup for providing this functionality (see #4322).
+"""
+from django.contrib.gis import memoryview
+from django.utils import six
+
+class GeometryProxy(object):
+ def __init__(self, klass, field):
+ """
+ Proxy initializes on the given Geometry class (not an instance) and
+ the GeometryField.
+ """
+ self._field = field
+ self._klass = klass
+
+ def __get__(self, obj, type=None):
+ """
+ This accessor retrieves the geometry, initializing it using the geometry
+ class specified during initialization and the HEXEWKB value of the field.
+ Currently, only GEOS or OGR geometries are supported.
+ """
+ if obj is None:
+ # Accessed on a class, not an instance
+ return self
+
+ # Getting the value of the field.
+ geom_value = obj.__dict__[self._field.attname]
+
+ if isinstance(geom_value, self._klass):
+ geom = geom_value
+ elif (geom_value is None) or (geom_value==''):
+ geom = None
+ else:
+ # Otherwise, a Geometry object is built using the field's contents,
+ # and the model's corresponding attribute is set.
+ geom = self._klass(geom_value)
+ setattr(obj, self._field.attname, geom)
+ return geom
+
+ def __set__(self, obj, value):
+ """
+ This accessor sets the proxied geometry with the geometry class
+ specified during initialization. Values of None, HEXEWKB, or WKT may
+ be used to set the geometry as well.
+ """
+ # The OGC Geometry type of the field.
+ gtype = self._field.geom_type
+
+ # The geometry type must match that of the field -- unless the
+ # general GeometryField is used.
+ if isinstance(value, self._klass) and (str(value.geom_type).upper() == gtype or gtype == 'GEOMETRY'):
+ # Assigning the SRID to the geometry.
+ if value.srid is None: value.srid = self._field.srid
+ elif value is None or isinstance(value, six.string_types + (memoryview,)):
+ # Set with None, WKT, HEX, or WKB
+ pass
+ else:
+ raise TypeError('cannot set %s GeometryProxy with value of type: %s' % (obj.__class__.__name__, type(value)))
+
+ # Setting the objects dictionary with the value, and returning.
+ obj.__dict__[self._field.attname] = value
+ return value
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/query.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/query.py
new file mode 100644
index 0000000..c89912b
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/query.py
@@ -0,0 +1,784 @@
+from django.db import connections
+from django.db.models.query import QuerySet, ValuesQuerySet, ValuesListQuerySet
+
+from django.contrib.gis import memoryview
+from django.contrib.gis.db.models import aggregates
+from django.contrib.gis.db.models.fields import get_srid_info, PointField, LineStringField
+from django.contrib.gis.db.models.sql import AreaField, DistanceField, GeomField, GeoQuery
+from django.contrib.gis.geometry.backend import Geometry
+from django.contrib.gis.measure import Area, Distance
+
+from django.utils import six
+
+
+class GeoQuerySet(QuerySet):
+ "The Geographic QuerySet."
+
+ ### Methods overloaded from QuerySet ###
+ def __init__(self, model=None, query=None, using=None):
+ super(GeoQuerySet, self).__init__(model=model, query=query, using=using)
+ self.query = query or GeoQuery(self.model)
+
+ def values(self, *fields):
+ return self._clone(klass=GeoValuesQuerySet, setup=True, _fields=fields)
+
+ def values_list(self, *fields, **kwargs):
+ flat = kwargs.pop('flat', False)
+ if kwargs:
+ raise TypeError('Unexpected keyword arguments to values_list: %s'
+ % (list(kwargs),))
+ if flat and len(fields) > 1:
+ raise TypeError("'flat' is not valid when values_list is called with more than one field.")
+ return self._clone(klass=GeoValuesListQuerySet, setup=True, flat=flat,
+ _fields=fields)
+
+ ### GeoQuerySet Methods ###
+ def area(self, tolerance=0.05, **kwargs):
+ """
+ Returns the area of the geographic field in an `area` attribute on
+ each element of this GeoQuerySet.
+ """
+ # Peforming setup here rather than in `_spatial_attribute` so that
+ # we can get the units for `AreaField`.
+ procedure_args, geo_field = self._spatial_setup('area', field_name=kwargs.get('field_name', None))
+ s = {'procedure_args' : procedure_args,
+ 'geo_field' : geo_field,
+ 'setup' : False,
+ }
+ connection = connections[self.db]
+ backend = connection.ops
+ if backend.oracle:
+ s['procedure_fmt'] = '%(geo_col)s,%(tolerance)s'
+ s['procedure_args']['tolerance'] = tolerance
+ s['select_field'] = AreaField('sq_m') # Oracle returns area in units of meters.
+ elif backend.postgis or backend.spatialite:
+ if backend.geography:
+ # Geography fields support area calculation, returns square meters.
+ s['select_field'] = AreaField('sq_m')
+ elif not geo_field.geodetic(connection):
+ # Getting the area units of the geographic field.
+ s['select_field'] = AreaField(Area.unit_attname(geo_field.units_name(connection)))
+ else:
+ # TODO: Do we want to support raw number areas for geodetic fields?
+ raise Exception('Area on geodetic coordinate systems not supported.')
+ return self._spatial_attribute('area', s, **kwargs)
+
+ def centroid(self, **kwargs):
+ """
+ Returns the centroid of the geographic field in a `centroid`
+ attribute on each element of this GeoQuerySet.
+ """
+ return self._geom_attribute('centroid', **kwargs)
+
+ def collect(self, **kwargs):
+ """
+ Performs an aggregate collect operation on the given geometry field.
+ This is analagous to a union operation, but much faster because
+ boundaries are not dissolved.
+ """
+ return self._spatial_aggregate(aggregates.Collect, **kwargs)
+
+ def difference(self, geom, **kwargs):
+ """
+ Returns the spatial difference of the geographic field in a `difference`
+ attribute on each element of this GeoQuerySet.
+ """
+ return self._geomset_attribute('difference', geom, **kwargs)
+
+ def distance(self, geom, **kwargs):
+ """
+ Returns the distance from the given geographic field name to the
+ given geometry in a `distance` attribute on each element of the
+ GeoQuerySet.
+
+ Keyword Arguments:
+ `spheroid` => If the geometry field is geodetic and PostGIS is
+ the spatial database, then the more accurate
+ spheroid calculation will be used instead of the
+ quicker sphere calculation.
+
+ `tolerance` => Used only for Oracle. The tolerance is
+ in meters -- a default of 5 centimeters (0.05)
+ is used.
+ """
+ return self._distance_attribute('distance', geom, **kwargs)
+
+ def envelope(self, **kwargs):
+ """
+ Returns a Geometry representing the bounding box of the
+ Geometry field in an `envelope` attribute on each element of
+ the GeoQuerySet.
+ """
+ return self._geom_attribute('envelope', **kwargs)
+
+ def extent(self, **kwargs):
+ """
+ Returns the extent (aggregate) of the features in the GeoQuerySet. The
+ extent will be returned as a 4-tuple, consisting of (xmin, ymin, xmax, ymax).
+ """
+ return self._spatial_aggregate(aggregates.Extent, **kwargs)
+
+ def extent3d(self, **kwargs):
+ """
+ Returns the aggregate extent, in 3D, of the features in the
+ GeoQuerySet. It is returned as a 6-tuple, comprising:
+ (xmin, ymin, zmin, xmax, ymax, zmax).
+ """
+ return self._spatial_aggregate(aggregates.Extent3D, **kwargs)
+
+ def force_rhr(self, **kwargs):
+ """
+ Returns a modified version of the Polygon/MultiPolygon in which
+ all of the vertices follow the Right-Hand-Rule. By default,
+ this is attached as the `force_rhr` attribute on each element
+ of the GeoQuerySet.
+ """
+ return self._geom_attribute('force_rhr', **kwargs)
+
+ def geojson(self, precision=8, crs=False, bbox=False, **kwargs):
+ """
+ Returns a GeoJSON representation of the geomtry field in a `geojson`
+ attribute on each element of the GeoQuerySet.
+
+ The `crs` and `bbox` keywords may be set to True if the users wants
+ the coordinate reference system and the bounding box to be included
+ in the GeoJSON representation of the geometry.
+ """
+ backend = connections[self.db].ops
+ if not backend.geojson:
+ raise NotImplementedError('Only PostGIS 1.3.4+ and SpatiaLite 3.0+ '
+ 'support GeoJSON serialization.')
+
+ if not isinstance(precision, six.integer_types):
+ raise TypeError('Precision keyword must be set with an integer.')
+
+ # Setting the options flag -- which depends on which version of
+ # PostGIS we're using. SpatiaLite only uses the first group of options.
+ if backend.spatial_version >= (1, 4, 0):
+ options = 0
+ if crs and bbox: options = 3
+ elif bbox: options = 1
+ elif crs: options = 2
+ else:
+ options = 0
+ if crs and bbox: options = 3
+ elif crs: options = 1
+ elif bbox: options = 2
+ s = {'desc' : 'GeoJSON',
+ 'procedure_args' : {'precision' : precision, 'options' : options},
+ 'procedure_fmt' : '%(geo_col)s,%(precision)s,%(options)s',
+ }
+ return self._spatial_attribute('geojson', s, **kwargs)
+
+ def geohash(self, precision=20, **kwargs):
+ """
+ Returns a GeoHash representation of the given field in a `geohash`
+ attribute on each element of the GeoQuerySet.
+
+ The `precision` keyword may be used to custom the number of
+ _characters_ used in the output GeoHash, the default is 20.
+ """
+ s = {'desc' : 'GeoHash',
+ 'procedure_args': {'precision': precision},
+ 'procedure_fmt': '%(geo_col)s,%(precision)s',
+ }
+ return self._spatial_attribute('geohash', s, **kwargs)
+
+ def gml(self, precision=8, version=2, **kwargs):
+ """
+ Returns GML representation of the given field in a `gml` attribute
+ on each element of the GeoQuerySet.
+ """
+ backend = connections[self.db].ops
+ s = {'desc' : 'GML', 'procedure_args' : {'precision' : precision}}
+ if backend.postgis:
+ # PostGIS AsGML() aggregate function parameter order depends on the
+ # version -- uggh.
+ if backend.spatial_version > (1, 3, 1):
+ s['procedure_fmt'] = '%(version)s,%(geo_col)s,%(precision)s'
+ else:
+ s['procedure_fmt'] = '%(geo_col)s,%(precision)s,%(version)s'
+ s['procedure_args'] = {'precision' : precision, 'version' : version}
+
+ return self._spatial_attribute('gml', s, **kwargs)
+
+ def intersection(self, geom, **kwargs):
+ """
+ Returns the spatial intersection of the Geometry field in
+ an `intersection` attribute on each element of this
+ GeoQuerySet.
+ """
+ return self._geomset_attribute('intersection', geom, **kwargs)
+
+ def kml(self, **kwargs):
+ """
+ Returns KML representation of the geometry field in a `kml`
+ attribute on each element of this GeoQuerySet.
+ """
+ s = {'desc' : 'KML',
+ 'procedure_fmt' : '%(geo_col)s,%(precision)s',
+ 'procedure_args' : {'precision' : kwargs.pop('precision', 8)},
+ }
+ return self._spatial_attribute('kml', s, **kwargs)
+
+ def length(self, **kwargs):
+ """
+ Returns the length of the geometry field as a `Distance` object
+ stored in a `length` attribute on each element of this GeoQuerySet.
+ """
+ return self._distance_attribute('length', None, **kwargs)
+
+ def make_line(self, **kwargs):
+ """
+ Creates a linestring from all of the PointField geometries in the
+ this GeoQuerySet and returns it. This is a spatial aggregate
+ method, and thus returns a geometry rather than a GeoQuerySet.
+ """
+ return self._spatial_aggregate(aggregates.MakeLine, geo_field_type=PointField, **kwargs)
+
+ def mem_size(self, **kwargs):
+ """
+ Returns the memory size (number of bytes) that the geometry field takes
+ in a `mem_size` attribute on each element of this GeoQuerySet.
+ """
+ return self._spatial_attribute('mem_size', {}, **kwargs)
+
+ def num_geom(self, **kwargs):
+ """
+ Returns the number of geometries if the field is a
+ GeometryCollection or Multi* Field in a `num_geom`
+ attribute on each element of this GeoQuerySet; otherwise
+ the sets with None.
+ """
+ return self._spatial_attribute('num_geom', {}, **kwargs)
+
+ def num_points(self, **kwargs):
+ """
+ Returns the number of points in the first linestring in the
+ Geometry field in a `num_points` attribute on each element of
+ this GeoQuerySet; otherwise sets with None.
+ """
+ return self._spatial_attribute('num_points', {}, **kwargs)
+
+ def perimeter(self, **kwargs):
+ """
+ Returns the perimeter of the geometry field as a `Distance` object
+ stored in a `perimeter` attribute on each element of this GeoQuerySet.
+ """
+ return self._distance_attribute('perimeter', None, **kwargs)
+
+ def point_on_surface(self, **kwargs):
+ """
+ Returns a Point geometry guaranteed to lie on the surface of the
+ Geometry field in a `point_on_surface` attribute on each element
+ of this GeoQuerySet; otherwise sets with None.
+ """
+ return self._geom_attribute('point_on_surface', **kwargs)
+
+ def reverse_geom(self, **kwargs):
+ """
+ Reverses the coordinate order of the geometry, and attaches as a
+ `reverse` attribute on each element of this GeoQuerySet.
+ """
+ s = {'select_field' : GeomField(),}
+ kwargs.setdefault('model_att', 'reverse_geom')
+ if connections[self.db].ops.oracle:
+ s['geo_field_type'] = LineStringField
+ return self._spatial_attribute('reverse', s, **kwargs)
+
+ def scale(self, x, y, z=0.0, **kwargs):
+ """
+ Scales the geometry to a new size by multiplying the ordinates
+ with the given x,y,z scale factors.
+ """
+ if connections[self.db].ops.spatialite:
+ if z != 0.0:
+ raise NotImplementedError('SpatiaLite does not support 3D scaling.')
+ s = {'procedure_fmt' : '%(geo_col)s,%(x)s,%(y)s',
+ 'procedure_args' : {'x' : x, 'y' : y},
+ 'select_field' : GeomField(),
+ }
+ else:
+ s = {'procedure_fmt' : '%(geo_col)s,%(x)s,%(y)s,%(z)s',
+ 'procedure_args' : {'x' : x, 'y' : y, 'z' : z},
+ 'select_field' : GeomField(),
+ }
+ return self._spatial_attribute('scale', s, **kwargs)
+
+ def snap_to_grid(self, *args, **kwargs):
+ """
+ Snap all points of the input geometry to the grid. How the
+ geometry is snapped to the grid depends on how many arguments
+ were given:
+ - 1 argument : A single size to snap both the X and Y grids to.
+ - 2 arguments: X and Y sizes to snap the grid to.
+ - 4 arguments: X, Y sizes and the X, Y origins.
+ """
+ if False in [isinstance(arg, (float,) + six.integer_types) for arg in args]:
+ raise TypeError('Size argument(s) for the grid must be a float or integer values.')
+
+ nargs = len(args)
+ if nargs == 1:
+ size = args[0]
+ procedure_fmt = '%(geo_col)s,%(size)s'
+ procedure_args = {'size' : size}
+ elif nargs == 2:
+ xsize, ysize = args
+ procedure_fmt = '%(geo_col)s,%(xsize)s,%(ysize)s'
+ procedure_args = {'xsize' : xsize, 'ysize' : ysize}
+ elif nargs == 4:
+ xsize, ysize, xorigin, yorigin = args
+ procedure_fmt = '%(geo_col)s,%(xorigin)s,%(yorigin)s,%(xsize)s,%(ysize)s'
+ procedure_args = {'xsize' : xsize, 'ysize' : ysize,
+ 'xorigin' : xorigin, 'yorigin' : yorigin}
+ else:
+ raise ValueError('Must provide 1, 2, or 4 arguments to `snap_to_grid`.')
+
+ s = {'procedure_fmt' : procedure_fmt,
+ 'procedure_args' : procedure_args,
+ 'select_field' : GeomField(),
+ }
+
+ return self._spatial_attribute('snap_to_grid', s, **kwargs)
+
+ def svg(self, relative=False, precision=8, **kwargs):
+ """
+ Returns SVG representation of the geographic field in a `svg`
+ attribute on each element of this GeoQuerySet.
+
+ Keyword Arguments:
+ `relative` => If set to True, this will evaluate the path in
+ terms of relative moves (rather than absolute).
+
+ `precision` => May be used to set the maximum number of decimal
+ digits used in output (defaults to 8).
+ """
+ relative = int(bool(relative))
+ if not isinstance(precision, six.integer_types):
+ raise TypeError('SVG precision keyword argument must be an integer.')
+ s = {'desc' : 'SVG',
+ 'procedure_fmt' : '%(geo_col)s,%(rel)s,%(precision)s',
+ 'procedure_args' : {'rel' : relative,
+ 'precision' : precision,
+ }
+ }
+ return self._spatial_attribute('svg', s, **kwargs)
+
+ def sym_difference(self, geom, **kwargs):
+ """
+ Returns the symmetric difference of the geographic field in a
+ `sym_difference` attribute on each element of this GeoQuerySet.
+ """
+ return self._geomset_attribute('sym_difference', geom, **kwargs)
+
+ def translate(self, x, y, z=0.0, **kwargs):
+ """
+ Translates the geometry to a new location using the given numeric
+ parameters as offsets.
+ """
+ if connections[self.db].ops.spatialite:
+ if z != 0.0:
+ raise NotImplementedError('SpatiaLite does not support 3D translation.')
+ s = {'procedure_fmt' : '%(geo_col)s,%(x)s,%(y)s',
+ 'procedure_args' : {'x' : x, 'y' : y},
+ 'select_field' : GeomField(),
+ }
+ else:
+ s = {'procedure_fmt' : '%(geo_col)s,%(x)s,%(y)s,%(z)s',
+ 'procedure_args' : {'x' : x, 'y' : y, 'z' : z},
+ 'select_field' : GeomField(),
+ }
+ return self._spatial_attribute('translate', s, **kwargs)
+
+ def transform(self, srid=4326, **kwargs):
+ """
+ Transforms the given geometry field to the given SRID. If no SRID is
+ provided, the transformation will default to using 4326 (WGS84).
+ """
+ if not isinstance(srid, six.integer_types):
+ raise TypeError('An integer SRID must be provided.')
+ field_name = kwargs.get('field_name', None)
+ tmp, geo_field = self._spatial_setup('transform', field_name=field_name)
+
+ # Getting the selection SQL for the given geographic field.
+ field_col = self._geocol_select(geo_field, field_name)
+
+ # Why cascading substitutions? Because spatial backends like
+ # Oracle and MySQL already require a function call to convert to text, thus
+ # when there's also a transformation we need to cascade the substitutions.
+ # For example, 'SDO_UTIL.TO_WKTGEOMETRY(SDO_CS.TRANSFORM( ... )'
+ geo_col = self.query.custom_select.get(geo_field, field_col)
+
+ # Setting the key for the field's column with the custom SELECT SQL to
+ # override the geometry column returned from the database.
+ custom_sel = '%s(%s, %s)' % (connections[self.db].ops.transform, geo_col, srid)
+ # TODO: Should we have this as an alias?
+ # custom_sel = '(%s(%s, %s)) AS %s' % (SpatialBackend.transform, geo_col, srid, qn(geo_field.name))
+ self.query.transformed_srid = srid # So other GeoQuerySet methods
+ self.query.custom_select[geo_field] = custom_sel
+ return self._clone()
+
+ def union(self, geom, **kwargs):
+ """
+ Returns the union of the geographic field with the given
+ Geometry in a `union` attribute on each element of this GeoQuerySet.
+ """
+ return self._geomset_attribute('union', geom, **kwargs)
+
+ def unionagg(self, **kwargs):
+ """
+ Performs an aggregate union on the given geometry field. Returns
+ None if the GeoQuerySet is empty. The `tolerance` keyword is for
+ Oracle backends only.
+ """
+ return self._spatial_aggregate(aggregates.Union, **kwargs)
+
+ ### Private API -- Abstracted DRY routines. ###
+ def _spatial_setup(self, att, desc=None, field_name=None, geo_field_type=None):
+ """
+ Performs set up for executing the spatial function.
+ """
+ # Does the spatial backend support this?
+ connection = connections[self.db]
+ func = getattr(connection.ops, att, False)
+ if desc is None: desc = att
+ if not func:
+ raise NotImplementedError('%s stored procedure not available on '
+ 'the %s backend.' %
+ (desc, connection.ops.name))
+
+ # Initializing the procedure arguments.
+ procedure_args = {'function' : func}
+
+ # Is there a geographic field in the model to perform this
+ # operation on?
+ geo_field = self.query._geo_field(field_name)
+ if not geo_field:
+ raise TypeError('%s output only available on GeometryFields.' % func)
+
+ # If the `geo_field_type` keyword was used, then enforce that
+ # type limitation.
+ if not geo_field_type is None and not isinstance(geo_field, geo_field_type):
+ raise TypeError('"%s" stored procedures may only be called on %ss.' % (func, geo_field_type.__name__))
+
+ # Setting the procedure args.
+ procedure_args['geo_col'] = self._geocol_select(geo_field, field_name)
+
+ return procedure_args, geo_field
+
+ def _spatial_aggregate(self, aggregate, field_name=None,
+ geo_field_type=None, tolerance=0.05):
+ """
+ DRY routine for calling aggregate spatial stored procedures and
+ returning their result to the caller of the function.
+ """
+ # Getting the field the geographic aggregate will be called on.
+ geo_field = self.query._geo_field(field_name)
+ if not geo_field:
+ raise TypeError('%s aggregate only available on GeometryFields.' % aggregate.name)
+
+ # Checking if there are any geo field type limitations on this
+ # aggregate (e.g. ST_Makeline only operates on PointFields).
+ if not geo_field_type is None and not isinstance(geo_field, geo_field_type):
+ raise TypeError('%s aggregate may only be called on %ss.' % (aggregate.name, geo_field_type.__name__))
+
+ # Getting the string expression of the field name, as this is the
+ # argument taken by `Aggregate` objects.
+ agg_col = field_name or geo_field.name
+
+ # Adding any keyword parameters for the Aggregate object. Oracle backends
+ # in particular need an additional `tolerance` parameter.
+ agg_kwargs = {}
+ if connections[self.db].ops.oracle: agg_kwargs['tolerance'] = tolerance
+
+ # Calling the QuerySet.aggregate, and returning only the value of the aggregate.
+ return self.aggregate(geoagg=aggregate(agg_col, **agg_kwargs))['geoagg']
+
+ def _spatial_attribute(self, att, settings, field_name=None, model_att=None):
+ """
+ DRY routine for calling a spatial stored procedure on a geometry column
+ and attaching its output as an attribute of the model.
+
+ Arguments:
+ att:
+ The name of the spatial attribute that holds the spatial
+ SQL function to call.
+
+ settings:
+ Dictonary of internal settings to customize for the spatial procedure.
+
+ Public Keyword Arguments:
+
+ field_name:
+ The name of the geographic field to call the spatial
+ function on. May also be a lookup to a geometry field
+ as part of a foreign key relation.
+
+ model_att:
+ The name of the model attribute to attach the output of
+ the spatial function to.
+ """
+ # Default settings.
+ settings.setdefault('desc', None)
+ settings.setdefault('geom_args', ())
+ settings.setdefault('geom_field', None)
+ settings.setdefault('procedure_args', {})
+ settings.setdefault('procedure_fmt', '%(geo_col)s')
+ settings.setdefault('select_params', [])
+
+ connection = connections[self.db]
+ backend = connection.ops
+
+ # Performing setup for the spatial column, unless told not to.
+ if settings.get('setup', True):
+ default_args, geo_field = self._spatial_setup(att, desc=settings['desc'], field_name=field_name,
+ geo_field_type=settings.get('geo_field_type', None))
+ for k, v in six.iteritems(default_args): settings['procedure_args'].setdefault(k, v)
+ else:
+ geo_field = settings['geo_field']
+
+ # The attribute to attach to the model.
+ if not isinstance(model_att, six.string_types): model_att = att
+
+ # Special handling for any argument that is a geometry.
+ for name in settings['geom_args']:
+ # Using the field's get_placeholder() routine to get any needed
+ # transformation SQL.
+ geom = geo_field.get_prep_value(settings['procedure_args'][name])
+ params = geo_field.get_db_prep_lookup('contains', geom, connection=connection)
+ geom_placeholder = geo_field.get_placeholder(geom, connection)
+
+ # Replacing the procedure format with that of any needed
+ # transformation SQL.
+ old_fmt = '%%(%s)s' % name
+ new_fmt = geom_placeholder % '%%s'
+ settings['procedure_fmt'] = settings['procedure_fmt'].replace(old_fmt, new_fmt)
+ settings['select_params'].extend(params)
+
+ # Getting the format for the stored procedure.
+ fmt = '%%(function)s(%s)' % settings['procedure_fmt']
+
+ # If the result of this function needs to be converted.
+ if settings.get('select_field', False):
+ sel_fld = settings['select_field']
+ if isinstance(sel_fld, GeomField) and backend.select:
+ self.query.custom_select[model_att] = backend.select
+ if connection.ops.oracle:
+ sel_fld.empty_strings_allowed = False
+ self.query.extra_select_fields[model_att] = sel_fld
+
+ # Finally, setting the extra selection attribute with
+ # the format string expanded with the stored procedure
+ # arguments.
+ return self.extra(select={model_att : fmt % settings['procedure_args']},
+ select_params=settings['select_params'])
+
+ def _distance_attribute(self, func, geom=None, tolerance=0.05, spheroid=False, **kwargs):
+ """
+ DRY routine for GeoQuerySet distance attribute routines.
+ """
+ # Setting up the distance procedure arguments.
+ procedure_args, geo_field = self._spatial_setup(func, field_name=kwargs.get('field_name', None))
+
+ # If geodetic defaulting distance attribute to meters (Oracle and
+ # PostGIS spherical distances return meters). Otherwise, use the
+ # units of the geometry field.
+ connection = connections[self.db]
+ geodetic = geo_field.geodetic(connection)
+ geography = geo_field.geography
+
+ if geodetic:
+ dist_att = 'm'
+ else:
+ dist_att = Distance.unit_attname(geo_field.units_name(connection))
+
+ # Shortcut booleans for what distance function we're using and
+ # whether the geometry field is 3D.
+ distance = func == 'distance'
+ length = func == 'length'
+ perimeter = func == 'perimeter'
+ if not (distance or length or perimeter):
+ raise ValueError('Unknown distance function: %s' % func)
+ geom_3d = geo_field.dim == 3
+
+ # The field's get_db_prep_lookup() is used to get any
+ # extra distance parameters. Here we set up the
+ # parameters that will be passed in to field's function.
+ lookup_params = [geom or 'POINT (0 0)', 0]
+
+ # Getting the spatial backend operations.
+ backend = connection.ops
+
+ # If the spheroid calculation is desired, either by the `spheroid`
+ # keyword or when calculating the length of geodetic field, make
+ # sure the 'spheroid' distance setting string is passed in so we
+ # get the correct spatial stored procedure.
+ if spheroid or (backend.postgis and geodetic and
+ (not geography) and length):
+ lookup_params.append('spheroid')
+ lookup_params = geo_field.get_prep_value(lookup_params)
+ params = geo_field.get_db_prep_lookup('distance_lte', lookup_params, connection=connection)
+
+ # The `geom_args` flag is set to true if a geometry parameter was
+ # passed in.
+ geom_args = bool(geom)
+
+ if backend.oracle:
+ if distance:
+ procedure_fmt = '%(geo_col)s,%(geom)s,%(tolerance)s'
+ elif length or perimeter:
+ procedure_fmt = '%(geo_col)s,%(tolerance)s'
+ procedure_args['tolerance'] = tolerance
+ else:
+ # Getting whether this field is in units of degrees since the field may have
+ # been transformed via the `transform` GeoQuerySet method.
+ if self.query.transformed_srid:
+ u, unit_name, s = get_srid_info(self.query.transformed_srid, connection)
+ geodetic = unit_name in geo_field.geodetic_units
+
+ if backend.spatialite and geodetic:
+ raise ValueError('SQLite does not support linear distance calculations on geodetic coordinate systems.')
+
+ if distance:
+ if self.query.transformed_srid:
+ # Setting the `geom_args` flag to false because we want to handle
+ # transformation SQL here, rather than the way done by default
+ # (which will transform to the original SRID of the field rather
+ # than to what was transformed to).
+ geom_args = False
+ procedure_fmt = '%s(%%(geo_col)s, %s)' % (backend.transform, self.query.transformed_srid)
+ if geom.srid is None or geom.srid == self.query.transformed_srid:
+ # If the geom parameter srid is None, it is assumed the coordinates
+ # are in the transformed units. A placeholder is used for the
+ # geometry parameter. `GeomFromText` constructor is also needed
+ # to wrap geom placeholder for SpatiaLite.
+ if backend.spatialite:
+ procedure_fmt += ', %s(%%%%s, %s)' % (backend.from_text, self.query.transformed_srid)
+ else:
+ procedure_fmt += ', %%s'
+ else:
+ # We need to transform the geom to the srid specified in `transform()`,
+ # so wrapping the geometry placeholder in transformation SQL.
+ # SpatiaLite also needs geometry placeholder wrapped in `GeomFromText`
+ # constructor.
+ if backend.spatialite:
+ procedure_fmt += ', %s(%s(%%%%s, %s), %s)' % (backend.transform, backend.from_text,
+ geom.srid, self.query.transformed_srid)
+ else:
+ procedure_fmt += ', %s(%%%%s, %s)' % (backend.transform, self.query.transformed_srid)
+ else:
+ # `transform()` was not used on this GeoQuerySet.
+ procedure_fmt = '%(geo_col)s,%(geom)s'
+
+ if not geography and geodetic:
+ # Spherical distance calculation is needed (because the geographic
+ # field is geodetic). However, the PostGIS ST_distance_sphere/spheroid()
+ # procedures may only do queries from point columns to point geometries
+ # some error checking is required.
+ if not backend.geography:
+ if not isinstance(geo_field, PointField):
+ raise ValueError('Spherical distance calculation only supported on PointFields.')
+ if not str(Geometry(memoryview(params[0].ewkb)).geom_type) == 'Point':
+ raise ValueError('Spherical distance calculation only supported with Point Geometry parameters')
+ # The `function` procedure argument needs to be set differently for
+ # geodetic distance calculations.
+ if spheroid:
+ # Call to distance_spheroid() requires spheroid param as well.
+ procedure_fmt += ",'%(spheroid)s'"
+ procedure_args.update({'function' : backend.distance_spheroid, 'spheroid' : params[1]})
+ else:
+ procedure_args.update({'function' : backend.distance_sphere})
+ elif length or perimeter:
+ procedure_fmt = '%(geo_col)s'
+ if not geography and geodetic and length:
+ # There's no `length_sphere`, and `length_spheroid` also
+ # works on 3D geometries.
+ procedure_fmt += ",'%(spheroid)s'"
+ procedure_args.update({'function' : backend.length_spheroid, 'spheroid' : params[1]})
+ elif geom_3d and backend.postgis:
+ # Use 3D variants of perimeter and length routines on PostGIS.
+ if perimeter:
+ procedure_args.update({'function' : backend.perimeter3d})
+ elif length:
+ procedure_args.update({'function' : backend.length3d})
+
+ # Setting up the settings for `_spatial_attribute`.
+ s = {'select_field' : DistanceField(dist_att),
+ 'setup' : False,
+ 'geo_field' : geo_field,
+ 'procedure_args' : procedure_args,
+ 'procedure_fmt' : procedure_fmt,
+ }
+ if geom_args:
+ s['geom_args'] = ('geom',)
+ s['procedure_args']['geom'] = geom
+ elif geom:
+ # The geometry is passed in as a parameter because we handled
+ # transformation conditions in this routine.
+ s['select_params'] = [backend.Adapter(geom)]
+ return self._spatial_attribute(func, s, **kwargs)
+
+ def _geom_attribute(self, func, tolerance=0.05, **kwargs):
+ """
+ DRY routine for setting up a GeoQuerySet method that attaches a
+ Geometry attribute (e.g., `centroid`, `point_on_surface`).
+ """
+ s = {'select_field' : GeomField(),}
+ if connections[self.db].ops.oracle:
+ s['procedure_fmt'] = '%(geo_col)s,%(tolerance)s'
+ s['procedure_args'] = {'tolerance' : tolerance}
+ return self._spatial_attribute(func, s, **kwargs)
+
+ def _geomset_attribute(self, func, geom, tolerance=0.05, **kwargs):
+ """
+ DRY routine for setting up a GeoQuerySet method that attaches a
+ Geometry attribute and takes a Geoemtry parameter. This is used
+ for geometry set-like operations (e.g., intersection, difference,
+ union, sym_difference).
+ """
+ s = {'geom_args' : ('geom',),
+ 'select_field' : GeomField(),
+ 'procedure_fmt' : '%(geo_col)s,%(geom)s',
+ 'procedure_args' : {'geom' : geom},
+ }
+ if connections[self.db].ops.oracle:
+ s['procedure_fmt'] += ',%(tolerance)s'
+ s['procedure_args']['tolerance'] = tolerance
+ return self._spatial_attribute(func, s, **kwargs)
+
+ def _geocol_select(self, geo_field, field_name):
+ """
+ Helper routine for constructing the SQL to select the geographic
+ column. Takes into account if the geographic field is in a
+ ForeignKey relation to the current model.
+ """
+ opts = self.model._meta
+ if not geo_field in opts.fields:
+ # Is this operation going to be on a related geographic field?
+ # If so, it'll have to be added to the select related information
+ # (e.g., if 'location__point' was given as the field name).
+ self.query.add_select_related([field_name])
+ compiler = self.query.get_compiler(self.db)
+ compiler.pre_sql_setup()
+ for (rel_table, rel_col), field in self.query.related_select_cols:
+ if field == geo_field:
+ return compiler._field_column(geo_field, rel_table)
+ raise ValueError("%r not in self.query.related_select_cols" % geo_field)
+ elif not geo_field in opts.local_fields:
+ # This geographic field is inherited from another model, so we have to
+ # use the db table for the _parent_ model instead.
+ tmp_fld, parent_model, direct, m2m = opts.get_field_by_name(geo_field.name)
+ return self.query.get_compiler(self.db)._field_column(geo_field, parent_model._meta.db_table)
+ else:
+ return self.query.get_compiler(self.db)._field_column(geo_field)
+
+class GeoValuesQuerySet(ValuesQuerySet):
+ def __init__(self, *args, **kwargs):
+ super(GeoValuesQuerySet, self).__init__(*args, **kwargs)
+ # This flag tells `resolve_columns` to run the values through
+ # `convert_values`. This ensures that Geometry objects instead
+ # of string values are returned with `values()` or `values_list()`.
+ self.query.geo_values = True
+
+class GeoValuesListQuerySet(GeoValuesQuerySet, ValuesListQuerySet):
+ pass
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/__init__.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/__init__.py
new file mode 100644
index 0000000..38d9507
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/__init__.py
@@ -0,0 +1,3 @@
+from django.contrib.gis.db.models.sql.conversion import AreaField, DistanceField, GeomField
+from django.contrib.gis.db.models.sql.query import GeoQuery
+from django.contrib.gis.db.models.sql.where import GeoWhereNode
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/aggregates.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/aggregates.py
new file mode 100644
index 0000000..ae848c0
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/aggregates.py
@@ -0,0 +1,62 @@
+from django.db.models.sql.aggregates import *
+from django.contrib.gis.db.models.fields import GeometryField
+
+class GeoAggregate(Aggregate):
+ # Default SQL template for spatial aggregates.
+ sql_template = '%(function)s(%(field)s)'
+
+ # Conversion class, if necessary.
+ conversion_class = None
+
+ # Flags for indicating the type of the aggregate.
+ is_extent = False
+
+ def __init__(self, col, source=None, is_summary=False, tolerance=0.05, **extra):
+ super(GeoAggregate, self).__init__(col, source, is_summary, **extra)
+
+ # Required by some Oracle aggregates.
+ self.tolerance = tolerance
+
+ # Can't use geographic aggregates on non-geometry fields.
+ if not isinstance(self.source, GeometryField):
+ raise ValueError('Geospatial aggregates only allowed on geometry fields.')
+
+ def as_sql(self, qn, connection):
+ "Return the aggregate, rendered as SQL with parameters."
+
+ if connection.ops.oracle:
+ self.extra['tolerance'] = self.tolerance
+
+ params = []
+
+ if hasattr(self.col, 'as_sql'):
+ field_name, params = self.col.as_sql(qn, connection)
+ elif isinstance(self.col, (list, tuple)):
+ field_name = '.'.join([qn(c) for c in self.col])
+ else:
+ field_name = self.col
+
+ sql_template, sql_function = connection.ops.spatial_aggregate_sql(self)
+
+ substitutions = {
+ 'function': sql_function,
+ 'field': field_name
+ }
+ substitutions.update(self.extra)
+
+ return sql_template % substitutions, params
+
+class Collect(GeoAggregate):
+ pass
+
+class Extent(GeoAggregate):
+ is_extent = '2D'
+
+class Extent3D(GeoAggregate):
+ is_extent = '3D'
+
+class MakeLine(GeoAggregate):
+ pass
+
+class Union(GeoAggregate):
+ pass
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/compiler.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/compiler.py
new file mode 100644
index 0000000..3fc9c17
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/compiler.py
@@ -0,0 +1,313 @@
+import datetime
+
+from django.conf import settings
+from django.db.backends.util import truncate_name, typecast_date, typecast_timestamp
+from django.db.models.sql import compiler
+from django.db.models.sql.constants import MULTI
+from django.utils import six
+from django.utils.six.moves import zip, zip_longest
+from django.utils import timezone
+
+SQLCompiler = compiler.SQLCompiler
+
+class GeoSQLCompiler(compiler.SQLCompiler):
+
+ def get_columns(self, with_aliases=False):
+ """
+ Return the list of columns to use in the select statement. If no
+ columns have been specified, returns all columns relating to fields in
+ the model.
+
+ If 'with_aliases' is true, any column names that are duplicated
+ (without the table names) are given unique aliases. This is needed in
+ some cases to avoid ambiguitity with nested queries.
+
+ This routine is overridden from Query to handle customized selection of
+ geometry columns.
+ """
+ qn = self.quote_name_unless_alias
+ qn2 = self.connection.ops.quote_name
+ result = ['(%s) AS %s' % (self.get_extra_select_format(alias) % col[0], qn2(alias))
+ for alias, col in six.iteritems(self.query.extra_select)]
+ params = []
+ aliases = set(self.query.extra_select.keys())
+ if with_aliases:
+ col_aliases = aliases.copy()
+ else:
+ col_aliases = set()
+ if self.query.select:
+ only_load = self.deferred_to_columns()
+ # This loop customized for GeoQuery.
+ for col, field in self.query.select:
+ if isinstance(col, (list, tuple)):
+ alias, column = col
+ table = self.query.alias_map[alias].table_name
+ if table in only_load and column not in only_load[table]:
+ continue
+ r = self.get_field_select(field, alias, column)
+ if with_aliases:
+ if col[1] in col_aliases:
+ c_alias = 'Col%d' % len(col_aliases)
+ result.append('%s AS %s' % (r, c_alias))
+ aliases.add(c_alias)
+ col_aliases.add(c_alias)
+ else:
+ result.append('%s AS %s' % (r, qn2(col[1])))
+ aliases.add(r)
+ col_aliases.add(col[1])
+ else:
+ result.append(r)
+ aliases.add(r)
+ col_aliases.add(col[1])
+ else:
+ col_sql, col_params = col.as_sql(qn, self.connection)
+ result.append(col_sql)
+ params.extend(col_params)
+
+ if hasattr(col, 'alias'):
+ aliases.add(col.alias)
+ col_aliases.add(col.alias)
+
+ elif self.query.default_cols:
+ cols, new_aliases = self.get_default_columns(with_aliases,
+ col_aliases)
+ result.extend(cols)
+ aliases.update(new_aliases)
+
+ max_name_length = self.connection.ops.max_name_length()
+ for alias, aggregate in self.query.aggregate_select.items():
+ agg_sql, agg_params = aggregate.as_sql(qn, self.connection)
+ if alias is None:
+ result.append(agg_sql)
+ else:
+ result.append('%s AS %s' % (agg_sql, qn(truncate_name(alias, max_name_length))))
+ params.extend(agg_params)
+
+ # This loop customized for GeoQuery.
+ for (table, col), field in self.query.related_select_cols:
+ r = self.get_field_select(field, table, col)
+ if with_aliases and col in col_aliases:
+ c_alias = 'Col%d' % len(col_aliases)
+ result.append('%s AS %s' % (r, c_alias))
+ aliases.add(c_alias)
+ col_aliases.add(c_alias)
+ else:
+ result.append(r)
+ aliases.add(r)
+ col_aliases.add(col)
+
+ self._select_aliases = aliases
+ return result, params
+
+ def get_default_columns(self, with_aliases=False, col_aliases=None,
+ start_alias=None, opts=None, as_pairs=False, from_parent=None):
+ """
+ Computes the default columns for selecting every field in the base
+ model. Will sometimes be called to pull in related models (e.g. via
+ select_related), in which case "opts" and "start_alias" will be given
+ to provide a starting point for the traversal.
+
+ Returns a list of strings, quoted appropriately for use in SQL
+ directly, as well as a set of aliases used in the select statement (if
+ 'as_pairs' is True, returns a list of (alias, col_name) pairs instead
+ of strings as the first component and None as the second component).
+
+ This routine is overridden from Query to handle customized selection of
+ geometry columns.
+ """
+ result = []
+ if opts is None:
+ opts = self.query.get_meta()
+ aliases = set()
+ only_load = self.deferred_to_columns()
+ seen = self.query.included_inherited_models.copy()
+ if start_alias:
+ seen[None] = start_alias
+ for field, model in opts.get_concrete_fields_with_model():
+ if from_parent and model is not None and issubclass(from_parent, model):
+ # Avoid loading data for already loaded parents.
+ continue
+ alias = self.query.join_parent_model(opts, model, start_alias, seen)
+ table = self.query.alias_map[alias].table_name
+ if table in only_load and field.column not in only_load[table]:
+ continue
+ if as_pairs:
+ result.append((alias, field))
+ aliases.add(alias)
+ continue
+ # This part of the function is customized for GeoQuery. We
+ # see if there was any custom selection specified in the
+ # dictionary, and set up the selection format appropriately.
+ field_sel = self.get_field_select(field, alias)
+ if with_aliases and field.column in col_aliases:
+ c_alias = 'Col%d' % len(col_aliases)
+ result.append('%s AS %s' % (field_sel, c_alias))
+ col_aliases.add(c_alias)
+ aliases.add(c_alias)
+ else:
+ r = field_sel
+ result.append(r)
+ aliases.add(r)
+ if with_aliases:
+ col_aliases.add(field.column)
+ return result, aliases
+
+ def resolve_columns(self, row, fields=()):
+ """
+ This routine is necessary so that distances and geometries returned
+ from extra selection SQL get resolved appropriately into Python
+ objects.
+ """
+ values = []
+ aliases = list(self.query.extra_select)
+
+ # Have to set a starting row number offset that is used for
+ # determining the correct starting row index -- needed for
+ # doing pagination with Oracle.
+ rn_offset = 0
+ if self.connection.ops.oracle:
+ if self.query.high_mark is not None or self.query.low_mark: rn_offset = 1
+ index_start = rn_offset + len(aliases)
+
+ # Converting any extra selection values (e.g., geometries and
+ # distance objects added by GeoQuerySet methods).
+ values = [self.query.convert_values(v,
+ self.query.extra_select_fields.get(a, None),
+ self.connection)
+ for v, a in zip(row[rn_offset:index_start], aliases)]
+ if self.connection.ops.oracle or getattr(self.query, 'geo_values', False):
+ # We resolve the rest of the columns if we're on Oracle or if
+ # the `geo_values` attribute is defined.
+ for value, field in zip_longest(row[index_start:], fields):
+ values.append(self.query.convert_values(value, field, self.connection))
+ else:
+ values.extend(row[index_start:])
+ return tuple(values)
+
+ #### Routines unique to GeoQuery ####
+ def get_extra_select_format(self, alias):
+ sel_fmt = '%s'
+ if hasattr(self.query, 'custom_select') and alias in self.query.custom_select:
+ sel_fmt = sel_fmt % self.query.custom_select[alias]
+ return sel_fmt
+
+ def get_field_select(self, field, alias=None, column=None):
+ """
+ Returns the SELECT SQL string for the given field. Figures out
+ if any custom selection SQL is needed for the column The `alias`
+ keyword may be used to manually specify the database table where
+ the column exists, if not in the model associated with this
+ `GeoQuery`. Similarly, `column` may be used to specify the exact
+ column name, rather than using the `column` attribute on `field`.
+ """
+ sel_fmt = self.get_select_format(field)
+ if field in self.query.custom_select:
+ field_sel = sel_fmt % self.query.custom_select[field]
+ else:
+ field_sel = sel_fmt % self._field_column(field, alias, column)
+ return field_sel
+
+ def get_select_format(self, fld):
+ """
+ Returns the selection format string, depending on the requirements
+ of the spatial backend. For example, Oracle and MySQL require custom
+ selection formats in order to retrieve geometries in OGC WKT. For all
+ other fields a simple '%s' format string is returned.
+ """
+ if self.connection.ops.select and hasattr(fld, 'geom_type'):
+ # This allows operations to be done on fields in the SELECT,
+ # overriding their values -- used by the Oracle and MySQL
+ # spatial backends to get database values as WKT, and by the
+ # `transform` method.
+ sel_fmt = self.connection.ops.select
+
+ # Because WKT doesn't contain spatial reference information,
+ # the SRID is prefixed to the returned WKT to ensure that the
+ # transformed geometries have an SRID different than that of the
+ # field -- this is only used by `transform` for Oracle and
+ # SpatiaLite backends.
+ if self.query.transformed_srid and ( self.connection.ops.oracle or
+ self.connection.ops.spatialite ):
+ sel_fmt = "'SRID=%d;'||%s" % (self.query.transformed_srid, sel_fmt)
+ else:
+ sel_fmt = '%s'
+ return sel_fmt
+
+ # Private API utilities, subject to change.
+ def _field_column(self, field, table_alias=None, column=None):
+ """
+ Helper function that returns the database column for the given field.
+ The table and column are returned (quoted) in the proper format, e.g.,
+ `"geoapp_city"."point"`. If `table_alias` is not specified, the
+ database table associated with the model of this `GeoQuery` will be
+ used. If `column` is specified, it will be used instead of the value
+ in `field.column`.
+ """
+ if table_alias is None: table_alias = self.query.get_meta().db_table
+ return "%s.%s" % (self.quote_name_unless_alias(table_alias),
+ self.connection.ops.quote_name(column or field.column))
+
+class SQLInsertCompiler(compiler.SQLInsertCompiler, GeoSQLCompiler):
+ pass
+
+class SQLDeleteCompiler(compiler.SQLDeleteCompiler, GeoSQLCompiler):
+ pass
+
+class SQLUpdateCompiler(compiler.SQLUpdateCompiler, GeoSQLCompiler):
+ pass
+
+class SQLAggregateCompiler(compiler.SQLAggregateCompiler, GeoSQLCompiler):
+ pass
+
+class SQLDateCompiler(compiler.SQLDateCompiler, GeoSQLCompiler):
+ """
+ This is overridden for GeoDjango to properly cast date columns, since
+ `GeoQuery.resolve_columns` is used for spatial values.
+ See #14648, #16757.
+ """
+ def results_iter(self):
+ if self.connection.ops.oracle:
+ from django.db.models.fields import DateTimeField
+ fields = [DateTimeField()]
+ else:
+ needs_string_cast = self.connection.features.needs_datetime_string_cast
+
+ offset = len(self.query.extra_select)
+ for rows in self.execute_sql(MULTI):
+ for row in rows:
+ date = row[offset]
+ if self.connection.ops.oracle:
+ date = self.resolve_columns(row, fields)[offset]
+ elif needs_string_cast:
+ date = typecast_date(str(date))
+ if isinstance(date, datetime.datetime):
+ date = date.date()
+ yield date
+
+class SQLDateTimeCompiler(compiler.SQLDateTimeCompiler, GeoSQLCompiler):
+ """
+ This is overridden for GeoDjango to properly cast date columns, since
+ `GeoQuery.resolve_columns` is used for spatial values.
+ See #14648, #16757.
+ """
+ def results_iter(self):
+ if self.connection.ops.oracle:
+ from django.db.models.fields import DateTimeField
+ fields = [DateTimeField()]
+ else:
+ needs_string_cast = self.connection.features.needs_datetime_string_cast
+
+ offset = len(self.query.extra_select)
+ for rows in self.execute_sql(MULTI):
+ for row in rows:
+ datetime = row[offset]
+ if self.connection.ops.oracle:
+ datetime = self.resolve_columns(row, fields)[offset]
+ elif needs_string_cast:
+ datetime = typecast_timestamp(str(datetime))
+ # Datetimes are artifically returned in UTC on databases that
+ # don't support time zone. Restore the zone used in the query.
+ if settings.USE_TZ:
+ datetime = datetime.replace(tzinfo=None)
+ datetime = timezone.make_aware(datetime, self.query.tzinfo)
+ yield datetime
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/conversion.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/conversion.py
new file mode 100644
index 0000000..160b623
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/conversion.py
@@ -0,0 +1,27 @@
+"""
+This module holds simple classes used by GeoQuery.convert_values
+to convert geospatial values from the database.
+"""
+
+class BaseField(object):
+ empty_strings_allowed = True
+ def get_internal_type(self):
+ "Overloaded method so OracleQuery.convert_values doesn't balk."
+ return None
+
+class AreaField(BaseField):
+ "Wrapper for Area values."
+ def __init__(self, area_att):
+ self.area_att = area_att
+
+class DistanceField(BaseField):
+ "Wrapper for Distance values."
+ def __init__(self, distance_att):
+ self.distance_att = distance_att
+
+class GeomField(BaseField):
+ """
+ Wrapper for Geometry values. It is a lightweight alternative to
+ using GeometryField (which requires an SQL query upon instantiation).
+ """
+ pass
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/query.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/query.py
new file mode 100644
index 0000000..5877f29
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/query.py
@@ -0,0 +1,121 @@
+from django.db import connections
+from django.db.models.query import sql
+
+from django.contrib.gis.db.models.fields import GeometryField
+from django.contrib.gis.db.models.sql import aggregates as gis_aggregates
+from django.contrib.gis.db.models.sql.conversion import AreaField, DistanceField, GeomField
+from django.contrib.gis.db.models.sql.where import GeoWhereNode
+from django.contrib.gis.geometry.backend import Geometry
+from django.contrib.gis.measure import Area, Distance
+
+
+ALL_TERMS = set([
+ 'bbcontains', 'bboverlaps', 'contained', 'contains',
+ 'contains_properly', 'coveredby', 'covers', 'crosses', 'disjoint',
+ 'distance_gt', 'distance_gte', 'distance_lt', 'distance_lte',
+ 'dwithin', 'equals', 'exact',
+ 'intersects', 'overlaps', 'relate', 'same_as', 'touches', 'within',
+ 'left', 'right', 'overlaps_left', 'overlaps_right',
+ 'overlaps_above', 'overlaps_below',
+ 'strictly_above', 'strictly_below'
+ ])
+ALL_TERMS.update(sql.constants.QUERY_TERMS)
+
+class GeoQuery(sql.Query):
+ """
+ A single spatial SQL query.
+ """
+ # Overridding the valid query terms.
+ query_terms = ALL_TERMS
+ aggregates_module = gis_aggregates
+
+ compiler = 'GeoSQLCompiler'
+
+ #### Methods overridden from the base Query class ####
+ def __init__(self, model, where=GeoWhereNode):
+ super(GeoQuery, self).__init__(model, where)
+ # The following attributes are customized for the GeoQuerySet.
+ # The GeoWhereNode and SpatialBackend classes contain backend-specific
+ # routines and functions.
+ self.custom_select = {}
+ self.transformed_srid = None
+ self.extra_select_fields = {}
+
+ def clone(self, *args, **kwargs):
+ obj = super(GeoQuery, self).clone(*args, **kwargs)
+ # Customized selection dictionary and transformed srid flag have
+ # to also be added to obj.
+ obj.custom_select = self.custom_select.copy()
+ obj.transformed_srid = self.transformed_srid
+ obj.extra_select_fields = self.extra_select_fields.copy()
+ return obj
+
+ def convert_values(self, value, field, connection):
+ """
+ Using the same routines that Oracle does we can convert our
+ extra selection objects into Geometry and Distance objects.
+ TODO: Make converted objects 'lazy' for less overhead.
+ """
+ if connection.ops.oracle:
+ # Running through Oracle's first.
+ value = super(GeoQuery, self).convert_values(value, field or GeomField(), connection)
+
+ if value is None:
+ # Output from spatial function is NULL (e.g., called
+ # function on a geometry field with NULL value).
+ pass
+ elif isinstance(field, DistanceField):
+ # Using the field's distance attribute, can instantiate
+ # `Distance` with the right context.
+ value = Distance(**{field.distance_att : value})
+ elif isinstance(field, AreaField):
+ value = Area(**{field.area_att : value})
+ elif isinstance(field, (GeomField, GeometryField)) and value:
+ value = Geometry(value)
+ elif field is not None:
+ return super(GeoQuery, self).convert_values(value, field, connection)
+ return value
+
+ def get_aggregation(self, using):
+ # Remove any aggregates marked for reduction from the subquery
+ # and move them to the outer AggregateQuery.
+ connection = connections[using]
+ for alias, aggregate in self.aggregate_select.items():
+ if isinstance(aggregate, gis_aggregates.GeoAggregate):
+ if not getattr(aggregate, 'is_extent', False) or connection.ops.oracle:
+ self.extra_select_fields[alias] = GeomField()
+ return super(GeoQuery, self).get_aggregation(using)
+
+ def resolve_aggregate(self, value, aggregate, connection):
+ """
+ Overridden from GeoQuery's normalize to handle the conversion of
+ GeoAggregate objects.
+ """
+ if isinstance(aggregate, self.aggregates_module.GeoAggregate):
+ if aggregate.is_extent:
+ if aggregate.is_extent == '3D':
+ return connection.ops.convert_extent3d(value)
+ else:
+ return connection.ops.convert_extent(value)
+ else:
+ return connection.ops.convert_geom(value, aggregate.source)
+ else:
+ return super(GeoQuery, self).resolve_aggregate(value, aggregate, connection)
+
+ # Private API utilities, subject to change.
+ def _geo_field(self, field_name=None):
+ """
+ Returns the first Geometry field encountered; or specified via the
+ `field_name` keyword. The `field_name` may be a string specifying
+ the geometry field on this GeoQuery's model, or a lookup string
+ to a geometry field via a ForeignKey relation.
+ """
+ if field_name is None:
+ # Incrementing until the first geographic field is found.
+ for fld in self.model._meta.fields:
+ if isinstance(fld, GeometryField): return fld
+ return False
+ else:
+ # Otherwise, check by the given field name -- which may be
+ # a lookup to a _related_ geographic field.
+ return GeoWhereNode._check_geo_field(self.model._meta, field_name)
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/where.py b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/where.py
new file mode 100644
index 0000000..c29533b
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/where.py
@@ -0,0 +1,91 @@
+from django.db.models.constants import LOOKUP_SEP
+from django.db.models.fields import FieldDoesNotExist
+from django.db.models.sql.expressions import SQLEvaluator
+from django.db.models.sql.where import Constraint, WhereNode
+from django.contrib.gis.db.models.fields import GeometryField
+
+class GeoConstraint(Constraint):
+ """
+ This subclass overrides `process` to better handle geographic SQL
+ construction.
+ """
+ def __init__(self, init_constraint):
+ self.alias = init_constraint.alias
+ self.col = init_constraint.col
+ self.field = init_constraint.field
+
+ def process(self, lookup_type, value, connection):
+ if isinstance(value, SQLEvaluator):
+ # Make sure the F Expression destination field exists, and
+ # set an `srid` attribute with the same as that of the
+ # destination.
+ geo_fld = GeoWhereNode._check_geo_field(value.opts, value.expression.name)
+ if not geo_fld:
+ raise ValueError('No geographic field found in expression.')
+ value.srid = geo_fld.srid
+ db_type = self.field.db_type(connection=connection)
+ params = self.field.get_db_prep_lookup(lookup_type, value, connection=connection)
+ return (self.alias, self.col, db_type), params
+
+class GeoWhereNode(WhereNode):
+ """
+ Used to represent the SQL where-clause for spatial databases --
+ these are tied to the GeoQuery class that created it.
+ """
+
+ def _prepare_data(self, data):
+ if isinstance(data, (list, tuple)):
+ obj, lookup_type, value = data
+ if ( isinstance(obj, Constraint) and
+ isinstance(obj.field, GeometryField) ):
+ data = (GeoConstraint(obj), lookup_type, value)
+ return super(GeoWhereNode, self)._prepare_data(data)
+
+ def make_atom(self, child, qn, connection):
+ lvalue, lookup_type, value_annot, params_or_value = child
+ if isinstance(lvalue, GeoConstraint):
+ data, params = lvalue.process(lookup_type, params_or_value, connection)
+ spatial_sql, spatial_params = connection.ops.spatial_lookup_sql(
+ data, lookup_type, params_or_value, lvalue.field, qn)
+ return spatial_sql, spatial_params + params
+ else:
+ return super(GeoWhereNode, self).make_atom(child, qn, connection)
+
+ @classmethod
+ def _check_geo_field(cls, opts, lookup):
+ """
+ Utility for checking the given lookup with the given model options.
+ The lookup is a string either specifying the geographic field, e.g.
+ 'point, 'the_geom', or a related lookup on a geographic field like
+ 'address__point'.
+
+ If a GeometryField exists according to the given lookup on the model
+ options, it will be returned. Otherwise returns None.
+ """
+ # This takes into account the situation where the lookup is a
+ # lookup to a related geographic field, e.g., 'address__point'.
+ field_list = lookup.split(LOOKUP_SEP)
+
+ # Reversing so list operates like a queue of related lookups,
+ # and popping the top lookup.
+ field_list.reverse()
+ fld_name = field_list.pop()
+
+ try:
+ geo_fld = opts.get_field(fld_name)
+ # If the field list is still around, then it means that the
+ # lookup was for a geometry field across a relationship --
+ # thus we keep on getting the related model options and the
+ # model field associated with the next field in the list
+ # until there's no more left.
+ while len(field_list):
+ opts = geo_fld.rel.to._meta
+ geo_fld = opts.get_field(field_list.pop())
+ except (FieldDoesNotExist, AttributeError):
+ return False
+
+ # Finally, make sure we got a Geographic field and return.
+ if isinstance(geo_fld, GeometryField):
+ return geo_fld
+ else:
+ return False