web/lib/django/contrib/gis/db/models/query.py
author ymh <ymh.work@gmail.com>
Wed, 02 Jun 2010 18:57:35 +0200
changeset 38 77b6da96e6f1
permissions -rw-r--r--
update django
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
38
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
from django.db import connections
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
from django.db.models.query import QuerySet, Q, ValuesQuerySet, ValuesListQuerySet
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
from django.contrib.gis.db.models import aggregates
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
from django.contrib.gis.db.models.fields import get_srid_info, GeometryField, PointField, LineStringField
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
from django.contrib.gis.db.models.sql import AreaField, DistanceField, GeomField, GeoQuery, GeoWhereNode
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
from django.contrib.gis.geometry.backend import Geometry
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
from django.contrib.gis.measure import Area, Distance
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
class GeoQuerySet(QuerySet):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
    "The Geographic QuerySet."
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
    ### Methods overloaded from QuerySet ###
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
    def __init__(self, model=None, query=None, using=None):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
        super(GeoQuerySet, self).__init__(model=model, query=query, using=using)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
        self.query = query or GeoQuery(self.model)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
    def values(self, *fields):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
        return self._clone(klass=GeoValuesQuerySet, setup=True, _fields=fields)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
    def values_list(self, *fields, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
        flat = kwargs.pop('flat', False)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
        if kwargs:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
            raise TypeError('Unexpected keyword arguments to values_list: %s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
                    % (kwargs.keys(),))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
        if flat and len(fields) > 1:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
            raise TypeError("'flat' is not valid when values_list is called with more than one field.")
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
        return self._clone(klass=GeoValuesListQuerySet, setup=True, flat=flat,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
                           _fields=fields)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
    ### GeoQuerySet Methods ###
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
    def area(self, tolerance=0.05, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
        Returns the area of the geographic field in an `area` attribute on
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
        each element of this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
        # Peforming setup here rather than in `_spatial_attribute` so that
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
        # we can get the units for `AreaField`.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
        procedure_args, geo_field = self._spatial_setup('area', field_name=kwargs.get('field_name', None))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
        s = {'procedure_args' : procedure_args,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
             'geo_field' : geo_field,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
             'setup' : False,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
             }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
        connection = connections[self.db]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
        backend = connection.ops
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
        if backend.oracle:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
            s['procedure_fmt'] = '%(geo_col)s,%(tolerance)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
            s['procedure_args']['tolerance'] = tolerance
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
            s['select_field'] = AreaField('sq_m') # Oracle returns area in units of meters.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
        elif backend.postgis or backend.spatialite:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
            if not geo_field.geodetic(connection):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
                # Getting the area units of the geographic field.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
                s['select_field'] = AreaField(Area.unit_attname(geo_field.units_name(connection)))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
            else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
                # TODO: Do we want to support raw number areas for geodetic fields?
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
                raise Exception('Area on geodetic coordinate systems not supported.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
        return self._spatial_attribute('area', s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
    def centroid(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
        Returns the centroid of the geographic field in a `centroid`
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
        attribute on each element of this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
        return self._geom_attribute('centroid', **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
    def collect(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
        Performs an aggregate collect operation on the given geometry field.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
        This is analagous to a union operation, but much faster because
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
        boundaries are not dissolved.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
        return self._spatial_aggregate(aggregates.Collect, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
    def difference(self, geom, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
        Returns the spatial difference of the geographic field in a `difference`
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
        attribute on each element of this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
        return self._geomset_attribute('difference', geom, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
    def distance(self, geom, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
        Returns the distance from the given geographic field name to the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
        given geometry in a `distance` attribute on each element of the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
        GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
        Keyword Arguments:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
         `spheroid`  => If the geometry field is geodetic and PostGIS is
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
                        the spatial database, then the more accurate
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
                        spheroid calculation will be used instead of the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
                        quicker sphere calculation.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
         `tolerance` => Used only for Oracle. The tolerance is
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
                        in meters -- a default of 5 centimeters (0.05)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
                        is used.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
        return self._distance_attribute('distance', geom, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
    def envelope(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
        Returns a Geometry representing the bounding box of the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
        Geometry field in an `envelope` attribute on each element of
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
        the GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
        return self._geom_attribute('envelope', **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
    def extent(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
        Returns the extent (aggregate) of the features in the GeoQuerySet.  The
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
        extent will be returned as a 4-tuple, consisting of (xmin, ymin, xmax, ymax).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
        return self._spatial_aggregate(aggregates.Extent, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
    def extent3d(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
        Returns the aggregate extent, in 3D, of the features in the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
        GeoQuerySet. It is returned as a 6-tuple, comprising:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
          (xmin, ymin, zmin, xmax, ymax, zmax).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
        return self._spatial_aggregate(aggregates.Extent3D, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
    def force_rhr(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
        Returns a modified version of the Polygon/MultiPolygon in which
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
        all of the vertices follow the Right-Hand-Rule.  By default,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
        this is attached as the `force_rhr` attribute on each element
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
        of the GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
        return self._geom_attribute('force_rhr', **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
    def geojson(self, precision=8, crs=False, bbox=False, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
        Returns a GeoJSON representation of the geomtry field in a `geojson`
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
        attribute on each element of the GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
        The `crs` and `bbox` keywords may be set to True if the users wants
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
        the coordinate reference system and the bounding box to be included
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
        in the GeoJSON representation of the geometry.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
        backend = connections[self.db].ops
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
        if not backend.geojson:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
            raise NotImplementedError('Only PostGIS 1.3.4+ supports GeoJSON serialization.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
        if not isinstance(precision, (int, long)):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
            raise TypeError('Precision keyword must be set with an integer.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
        # Setting the options flag -- which depends on which version of
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
        # PostGIS we're using.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
        if backend.spatial_version >= (1, 4, 0):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
            options = 0
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
            if crs and bbox: options = 3
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
            elif bbox: options = 1
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
            elif crs: options = 2
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
            options = 0
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
            if crs and bbox: options = 3
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
            elif crs: options = 1
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   158
            elif bbox: options = 2
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   159
        s = {'desc' : 'GeoJSON',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   160
             'procedure_args' : {'precision' : precision, 'options' : options},
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   161
             'procedure_fmt' : '%(geo_col)s,%(precision)s,%(options)s',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   162
             }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   163
        return self._spatial_attribute('geojson', s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   164
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   165
    def geohash(self, precision=20, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   166
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   167
        Returns a GeoHash representation of the given field in a `geohash`
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   168
        attribute on each element of the GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   169
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   170
        The `precision` keyword may be used to custom the number of
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   171
        _characters_ used in the output GeoHash, the default is 20.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   172
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   173
        s = {'desc' : 'GeoHash', 
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   174
             'procedure_args': {'precision': precision},
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   175
             'procedure_fmt': '%(geo_col)s,%(precision)s',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   176
             }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   177
        return self._spatial_attribute('geohash', s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   178
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   179
    def gml(self, precision=8, version=2, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   180
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   181
        Returns GML representation of the given field in a `gml` attribute
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   182
        on each element of the GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   183
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   184
        backend = connections[self.db].ops
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   185
        s = {'desc' : 'GML', 'procedure_args' : {'precision' : precision}}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   186
        if backend.postgis:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   187
            # PostGIS AsGML() aggregate function parameter order depends on the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   188
            # version -- uggh.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   189
            if backend.spatial_version > (1, 3, 1):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   190
                procedure_fmt = '%(version)s,%(geo_col)s,%(precision)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   191
            else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   192
                procedure_fmt = '%(geo_col)s,%(precision)s,%(version)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   193
            s['procedure_args'] = {'precision' : precision, 'version' : version}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   194
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   195
        return self._spatial_attribute('gml', s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   196
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   197
    def intersection(self, geom, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   198
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   199
        Returns the spatial intersection of the Geometry field in
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   200
        an `intersection` attribute on each element of this
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   201
        GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   202
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   203
        return self._geomset_attribute('intersection', geom, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   204
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   205
    def kml(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   206
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   207
        Returns KML representation of the geometry field in a `kml`
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   208
        attribute on each element of this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   209
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   210
        s = {'desc' : 'KML',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   211
             'procedure_fmt' : '%(geo_col)s,%(precision)s',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   212
             'procedure_args' : {'precision' : kwargs.pop('precision', 8)},
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   213
             }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   214
        return self._spatial_attribute('kml', s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   215
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   216
    def length(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   217
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   218
        Returns the length of the geometry field as a `Distance` object
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   219
        stored in a `length` attribute on each element of this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   220
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   221
        return self._distance_attribute('length', None, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   222
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   223
    def make_line(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   224
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   225
        Creates a linestring from all of the PointField geometries in the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   226
        this GeoQuerySet and returns it.  This is a spatial aggregate
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   227
        method, and thus returns a geometry rather than a GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   228
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   229
        return self._spatial_aggregate(aggregates.MakeLine, geo_field_type=PointField, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   230
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   231
    def mem_size(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   232
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   233
        Returns the memory size (number of bytes) that the geometry field takes
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   234
        in a `mem_size` attribute  on each element of this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   235
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   236
        return self._spatial_attribute('mem_size', {}, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   237
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   238
    def num_geom(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   239
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   240
        Returns the number of geometries if the field is a
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   241
        GeometryCollection or Multi* Field in a `num_geom`
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   242
        attribute on each element of this GeoQuerySet; otherwise
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   243
        the sets with None.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   244
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   245
        return self._spatial_attribute('num_geom', {}, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   246
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   247
    def num_points(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   248
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   249
        Returns the number of points in the first linestring in the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   250
        Geometry field in a `num_points` attribute on each element of
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   251
        this GeoQuerySet; otherwise sets with None.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   252
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   253
        return self._spatial_attribute('num_points', {}, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   254
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   255
    def perimeter(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   256
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   257
        Returns the perimeter of the geometry field as a `Distance` object
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   258
        stored in a `perimeter` attribute on each element of this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   259
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   260
        return self._distance_attribute('perimeter', None, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   261
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   262
    def point_on_surface(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   263
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   264
        Returns a Point geometry guaranteed to lie on the surface of the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   265
        Geometry field in a `point_on_surface` attribute on each element
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   266
        of this GeoQuerySet; otherwise sets with None.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   267
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   268
        return self._geom_attribute('point_on_surface', **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   269
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   270
    def reverse_geom(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   271
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   272
        Reverses the coordinate order of the geometry, and attaches as a
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   273
        `reverse` attribute on each element of this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   274
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   275
        s = {'select_field' : GeomField(),}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   276
        kwargs.setdefault('model_att', 'reverse_geom')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   277
        if connections[self.db].ops.oracle:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   278
            s['geo_field_type'] = LineStringField
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   279
        return self._spatial_attribute('reverse', s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   280
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   281
    def scale(self, x, y, z=0.0, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   282
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   283
        Scales the geometry to a new size by multiplying the ordinates
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   284
        with the given x,y,z scale factors.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   285
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   286
        if connections[self.db].ops.spatialite:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   287
            if z != 0.0:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   288
                raise NotImplementedError('SpatiaLite does not support 3D scaling.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   289
            s = {'procedure_fmt' : '%(geo_col)s,%(x)s,%(y)s',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   290
                 'procedure_args' : {'x' : x, 'y' : y},
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   291
                 'select_field' : GeomField(),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   292
                 }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   293
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   294
            s = {'procedure_fmt' : '%(geo_col)s,%(x)s,%(y)s,%(z)s',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   295
                 'procedure_args' : {'x' : x, 'y' : y, 'z' : z},
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   296
                 'select_field' : GeomField(),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   297
                 }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   298
        return self._spatial_attribute('scale', s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   299
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   300
    def snap_to_grid(self, *args, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   301
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   302
        Snap all points of the input geometry to the grid.  How the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   303
        geometry is snapped to the grid depends on how many arguments
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   304
        were given:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   305
          - 1 argument : A single size to snap both the X and Y grids to.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   306
          - 2 arguments: X and Y sizes to snap the grid to.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   307
          - 4 arguments: X, Y sizes and the X, Y origins.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   308
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   309
        if False in [isinstance(arg, (float, int, long)) for arg in args]:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   310
            raise TypeError('Size argument(s) for the grid must be a float or integer values.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   311
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   312
        nargs = len(args)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   313
        if nargs == 1:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   314
            size = args[0]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   315
            procedure_fmt = '%(geo_col)s,%(size)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   316
            procedure_args = {'size' : size}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   317
        elif nargs == 2:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   318
            xsize, ysize = args
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   319
            procedure_fmt = '%(geo_col)s,%(xsize)s,%(ysize)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   320
            procedure_args = {'xsize' : xsize, 'ysize' : ysize}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   321
        elif nargs == 4:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   322
            xsize, ysize, xorigin, yorigin = args
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   323
            procedure_fmt = '%(geo_col)s,%(xorigin)s,%(yorigin)s,%(xsize)s,%(ysize)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   324
            procedure_args = {'xsize' : xsize, 'ysize' : ysize,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   325
                              'xorigin' : xorigin, 'yorigin' : yorigin}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   326
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   327
            raise ValueError('Must provide 1, 2, or 4 arguments to `snap_to_grid`.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   328
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   329
        s = {'procedure_fmt' : procedure_fmt,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   330
             'procedure_args' : procedure_args,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   331
             'select_field' : GeomField(),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   332
             }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   333
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   334
        return self._spatial_attribute('snap_to_grid', s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   335
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   336
    def svg(self, relative=False, precision=8, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   337
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   338
        Returns SVG representation of the geographic field in a `svg`
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   339
        attribute on each element of this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   340
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   341
        Keyword Arguments:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   342
         `relative`  => If set to True, this will evaluate the path in
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   343
                        terms of relative moves (rather than absolute).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   344
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   345
         `precision` => May be used to set the maximum number of decimal
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   346
                        digits used in output (defaults to 8).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   347
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   348
        relative = int(bool(relative))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   349
        if not isinstance(precision, (int, long)):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   350
            raise TypeError('SVG precision keyword argument must be an integer.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   351
        s = {'desc' : 'SVG',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   352
             'procedure_fmt' : '%(geo_col)s,%(rel)s,%(precision)s',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   353
             'procedure_args' : {'rel' : relative,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   354
                                 'precision' : precision,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   355
                                 }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   356
             }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   357
        return self._spatial_attribute('svg', s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   358
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   359
    def sym_difference(self, geom, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   360
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   361
        Returns the symmetric difference of the geographic field in a
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   362
        `sym_difference` attribute on each element of this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   363
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   364
        return self._geomset_attribute('sym_difference', geom, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   365
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   366
    def translate(self, x, y, z=0.0, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   367
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   368
        Translates the geometry to a new location using the given numeric
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   369
        parameters as offsets.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   370
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   371
        if connections[self.db].ops.spatialite:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   372
            if z != 0.0:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   373
                raise NotImplementedError('SpatiaLite does not support 3D translation.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   374
            s = {'procedure_fmt' : '%(geo_col)s,%(x)s,%(y)s',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   375
                 'procedure_args' : {'x' : x, 'y' : y},
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   376
                 'select_field' : GeomField(),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   377
                 }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   378
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   379
            s = {'procedure_fmt' : '%(geo_col)s,%(x)s,%(y)s,%(z)s',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   380
                 'procedure_args' : {'x' : x, 'y' : y, 'z' : z},
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   381
                 'select_field' : GeomField(),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   382
                 }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   383
        return self._spatial_attribute('translate', s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   384
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   385
    def transform(self, srid=4326, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   386
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   387
        Transforms the given geometry field to the given SRID.  If no SRID is
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   388
        provided, the transformation will default to using 4326 (WGS84).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   389
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   390
        if not isinstance(srid, (int, long)):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   391
            raise TypeError('An integer SRID must be provided.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   392
        field_name = kwargs.get('field_name', None)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   393
        tmp, geo_field = self._spatial_setup('transform', field_name=field_name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   394
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   395
        # Getting the selection SQL for the given geographic field.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   396
        field_col = self._geocol_select(geo_field, field_name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   397
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   398
        # Why cascading substitutions? Because spatial backends like
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   399
        # Oracle and MySQL already require a function call to convert to text, thus
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   400
        # when there's also a transformation we need to cascade the substitutions.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   401
        # For example, 'SDO_UTIL.TO_WKTGEOMETRY(SDO_CS.TRANSFORM( ... )'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   402
        geo_col = self.query.custom_select.get(geo_field, field_col)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   403
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   404
        # Setting the key for the field's column with the custom SELECT SQL to
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   405
        # override the geometry column returned from the database.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   406
        custom_sel = '%s(%s, %s)' % (connections[self.db].ops.transform, geo_col, srid)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   407
        # TODO: Should we have this as an alias?
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   408
        # custom_sel = '(%s(%s, %s)) AS %s' % (SpatialBackend.transform, geo_col, srid, qn(geo_field.name))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   409
        self.query.transformed_srid = srid # So other GeoQuerySet methods
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   410
        self.query.custom_select[geo_field] = custom_sel
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   411
        return self._clone()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   412
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   413
    def union(self, geom, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   414
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   415
        Returns the union of the geographic field with the given
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   416
        Geometry in a `union` attribute on each element of this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   417
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   418
        return self._geomset_attribute('union', geom, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   419
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   420
    def unionagg(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   421
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   422
        Performs an aggregate union on the given geometry field.  Returns
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   423
        None if the GeoQuerySet is empty.  The `tolerance` keyword is for
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   424
        Oracle backends only.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   425
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   426
        return self._spatial_aggregate(aggregates.Union, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   427
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   428
    ### Private API -- Abstracted DRY routines. ###
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   429
    def _spatial_setup(self, att, desc=None, field_name=None, geo_field_type=None):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   430
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   431
        Performs set up for executing the spatial function.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   432
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   433
        # Does the spatial backend support this?
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   434
        connection = connections[self.db]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   435
        func = getattr(connection.ops, att, False)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   436
        if desc is None: desc = att
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   437
        if not func:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   438
            raise NotImplementedError('%s stored procedure not available on '
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   439
                                      'the %s backend.' %
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   440
                                      (desc, connection.ops.name))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   441
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   442
        # Initializing the procedure arguments.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   443
        procedure_args = {'function' : func}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   444
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   445
        # Is there a geographic field in the model to perform this
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   446
        # operation on?
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   447
        geo_field = self.query._geo_field(field_name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   448
        if not geo_field:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   449
            raise TypeError('%s output only available on GeometryFields.' % func)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   450
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   451
        # If the `geo_field_type` keyword was used, then enforce that
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   452
        # type limitation.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   453
        if not geo_field_type is None and not isinstance(geo_field, geo_field_type):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   454
            raise TypeError('"%s" stored procedures may only be called on %ss.' % (func, geo_field_type.__name__))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   455
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   456
        # Setting the procedure args.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   457
        procedure_args['geo_col'] = self._geocol_select(geo_field, field_name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   458
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   459
        return procedure_args, geo_field
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   460
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   461
    def _spatial_aggregate(self, aggregate, field_name=None,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   462
                           geo_field_type=None, tolerance=0.05):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   463
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   464
        DRY routine for calling aggregate spatial stored procedures and
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   465
        returning their result to the caller of the function.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   466
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   467
        # Getting the field the geographic aggregate will be called on.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   468
        geo_field = self.query._geo_field(field_name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   469
        if not geo_field:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   470
            raise TypeError('%s aggregate only available on GeometryFields.' % aggregate.name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   471
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   472
        # Checking if there are any geo field type limitations on this
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   473
        # aggregate (e.g. ST_Makeline only operates on PointFields).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   474
        if not geo_field_type is None and not isinstance(geo_field, geo_field_type):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   475
            raise TypeError('%s aggregate may only be called on %ss.' % (aggregate.name, geo_field_type.__name__))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   476
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   477
        # Getting the string expression of the field name, as this is the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   478
        # argument taken by `Aggregate` objects.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   479
        agg_col = field_name or geo_field.name
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   480
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   481
        # Adding any keyword parameters for the Aggregate object. Oracle backends
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   482
        # in particular need an additional `tolerance` parameter.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   483
        agg_kwargs = {}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   484
        if connections[self.db].ops.oracle: agg_kwargs['tolerance'] = tolerance
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   485
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   486
        # Calling the QuerySet.aggregate, and returning only the value of the aggregate.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   487
        return self.aggregate(geoagg=aggregate(agg_col, **agg_kwargs))['geoagg']
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   488
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   489
    def _spatial_attribute(self, att, settings, field_name=None, model_att=None):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   490
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   491
        DRY routine for calling a spatial stored procedure on a geometry column
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   492
        and attaching its output as an attribute of the model.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   493
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   494
        Arguments:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   495
         att:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   496
          The name of the spatial attribute that holds the spatial
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   497
          SQL function to call.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   498
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   499
         settings:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   500
          Dictonary of internal settings to customize for the spatial procedure.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   501
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   502
        Public Keyword Arguments:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   503
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   504
         field_name:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   505
          The name of the geographic field to call the spatial
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   506
          function on.  May also be a lookup to a geometry field
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   507
          as part of a foreign key relation.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   508
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   509
         model_att:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   510
          The name of the model attribute to attach the output of
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   511
          the spatial function to.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   512
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   513
        # Default settings.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   514
        settings.setdefault('desc', None)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   515
        settings.setdefault('geom_args', ())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   516
        settings.setdefault('geom_field', None)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   517
        settings.setdefault('procedure_args', {})
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   518
        settings.setdefault('procedure_fmt', '%(geo_col)s')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   519
        settings.setdefault('select_params', [])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   520
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   521
        connection = connections[self.db]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   522
        backend = connection.ops
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   523
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   524
        # Performing setup for the spatial column, unless told not to.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   525
        if settings.get('setup', True):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   526
            default_args, geo_field = self._spatial_setup(att, desc=settings['desc'], field_name=field_name,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   527
                                                          geo_field_type=settings.get('geo_field_type', None))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   528
            for k, v in default_args.iteritems(): settings['procedure_args'].setdefault(k, v)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   529
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   530
            geo_field = settings['geo_field']
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   531
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   532
        # The attribute to attach to the model.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   533
        if not isinstance(model_att, basestring): model_att = att
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   534
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   535
        # Special handling for any argument that is a geometry.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   536
        for name in settings['geom_args']:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   537
            # Using the field's get_placeholder() routine to get any needed
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   538
            # transformation SQL.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   539
            geom = geo_field.get_prep_value(settings['procedure_args'][name])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   540
            params = geo_field.get_db_prep_lookup('contains', geom, connection=connection)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   541
            geom_placeholder = geo_field.get_placeholder(geom, connection)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   542
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   543
            # Replacing the procedure format with that of any needed
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   544
            # transformation SQL.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   545
            old_fmt = '%%(%s)s' % name
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   546
            new_fmt = geom_placeholder % '%%s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   547
            settings['procedure_fmt'] = settings['procedure_fmt'].replace(old_fmt, new_fmt)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   548
            settings['select_params'].extend(params)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   549
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   550
        # Getting the format for the stored procedure.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   551
        fmt = '%%(function)s(%s)' % settings['procedure_fmt']
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   552
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   553
        # If the result of this function needs to be converted.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   554
        if settings.get('select_field', False):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   555
            sel_fld = settings['select_field']
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   556
            if isinstance(sel_fld, GeomField) and backend.select:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   557
                self.query.custom_select[model_att] = backend.select
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   558
            if connection.ops.oracle:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   559
                sel_fld.empty_strings_allowed = False
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   560
            self.query.extra_select_fields[model_att] = sel_fld
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   561
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   562
        # Finally, setting the extra selection attribute with
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   563
        # the format string expanded with the stored procedure
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   564
        # arguments.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   565
        return self.extra(select={model_att : fmt % settings['procedure_args']},
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   566
                          select_params=settings['select_params'])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   567
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   568
    def _distance_attribute(self, func, geom=None, tolerance=0.05, spheroid=False, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   569
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   570
        DRY routine for GeoQuerySet distance attribute routines.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   571
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   572
        # Setting up the distance procedure arguments.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   573
        procedure_args, geo_field = self._spatial_setup(func, field_name=kwargs.get('field_name', None))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   574
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   575
        # If geodetic defaulting distance attribute to meters (Oracle and
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   576
        # PostGIS spherical distances return meters).  Otherwise, use the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   577
        # units of the geometry field.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   578
        connection = connections[self.db]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   579
        geodetic = geo_field.geodetic(connection)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   580
        geography = geo_field.geography
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   581
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   582
        if geodetic:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   583
            dist_att = 'm'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   584
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   585
            dist_att = Distance.unit_attname(geo_field.units_name(connection))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   586
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   587
        # Shortcut booleans for what distance function we're using and
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   588
        # whether the geometry field is 3D.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   589
        distance = func == 'distance'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   590
        length = func == 'length'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   591
        perimeter = func == 'perimeter'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   592
        if not (distance or length or perimeter):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   593
            raise ValueError('Unknown distance function: %s' % func)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   594
        geom_3d = geo_field.dim == 3
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   595
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   596
        # The field's get_db_prep_lookup() is used to get any
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   597
        # extra distance parameters.  Here we set up the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   598
        # parameters that will be passed in to field's function.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   599
        lookup_params = [geom or 'POINT (0 0)', 0]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   600
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   601
        # Getting the spatial backend operations.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   602
        backend = connection.ops
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   603
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   604
        # If the spheroid calculation is desired, either by the `spheroid`
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   605
        # keyword or when calculating the length of geodetic field, make
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   606
        # sure the 'spheroid' distance setting string is passed in so we
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   607
        # get the correct spatial stored procedure.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   608
        if spheroid or (backend.postgis and geodetic and
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   609
                        (not geography) and length):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   610
            lookup_params.append('spheroid')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   611
        lookup_params = geo_field.get_prep_value(lookup_params)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   612
        params = geo_field.get_db_prep_lookup('distance_lte', lookup_params, connection=connection)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   613
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   614
        # The `geom_args` flag is set to true if a geometry parameter was
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   615
        # passed in.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   616
        geom_args = bool(geom)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   617
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   618
        if backend.oracle:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   619
            if distance:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   620
                procedure_fmt = '%(geo_col)s,%(geom)s,%(tolerance)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   621
            elif length or perimeter:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   622
                procedure_fmt = '%(geo_col)s,%(tolerance)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   623
            procedure_args['tolerance'] = tolerance
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   624
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   625
            # Getting whether this field is in units of degrees since the field may have
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   626
            # been transformed via the `transform` GeoQuerySet method.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   627
            if self.query.transformed_srid:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   628
                u, unit_name, s = get_srid_info(self.query.transformed_srid, connection)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   629
                geodetic = unit_name in geo_field.geodetic_units
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   630
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   631
            if backend.spatialite and geodetic:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   632
                raise ValueError('SQLite does not support linear distance calculations on geodetic coordinate systems.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   633
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   634
            if distance:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   635
                if self.query.transformed_srid:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   636
                    # Setting the `geom_args` flag to false because we want to handle
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   637
                    # transformation SQL here, rather than the way done by default
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   638
                    # (which will transform to the original SRID of the field rather
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   639
                    #  than to what was transformed to).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   640
                    geom_args = False
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   641
                    procedure_fmt = '%s(%%(geo_col)s, %s)' % (backend.transform, self.query.transformed_srid)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   642
                    if geom.srid is None or geom.srid == self.query.transformed_srid:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   643
                        # If the geom parameter srid is None, it is assumed the coordinates
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   644
                        # are in the transformed units.  A placeholder is used for the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   645
                        # geometry parameter.  `GeomFromText` constructor is also needed
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   646
                        # to wrap geom placeholder for SpatiaLite.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   647
                        if backend.spatialite:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   648
                            procedure_fmt += ', %s(%%%%s, %s)' % (backend.from_text, self.query.transformed_srid)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   649
                        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   650
                            procedure_fmt += ', %%s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   651
                    else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   652
                        # We need to transform the geom to the srid specified in `transform()`,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   653
                        # so wrapping the geometry placeholder in transformation SQL.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   654
                        # SpatiaLite also needs geometry placeholder wrapped in `GeomFromText`
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   655
                        # constructor.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   656
                        if backend.spatialite:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   657
                            procedure_fmt += ', %s(%s(%%%%s, %s), %s)' % (backend.transform, backend.from_text,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   658
                                                                          geom.srid, self.query.transformed_srid)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   659
                        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   660
                            procedure_fmt += ', %s(%%%%s, %s)' % (backend.transform, self.query.transformed_srid)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   661
                else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   662
                    # `transform()` was not used on this GeoQuerySet.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   663
                    procedure_fmt  = '%(geo_col)s,%(geom)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   664
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   665
                if not geography and geodetic:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   666
                    # Spherical distance calculation is needed (because the geographic
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   667
                    # field is geodetic). However, the PostGIS ST_distance_sphere/spheroid()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   668
                    # procedures may only do queries from point columns to point geometries
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   669
                    # some error checking is required.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   670
                    if not backend.geography:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   671
                        if not isinstance(geo_field, PointField):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   672
                            raise ValueError('Spherical distance calculation only supported on PointFields.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   673
                        if not str(Geometry(buffer(params[0].ewkb)).geom_type) == 'Point':
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   674
                            raise ValueError('Spherical distance calculation only supported with Point Geometry parameters')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   675
                    # The `function` procedure argument needs to be set differently for
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   676
                    # geodetic distance calculations.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   677
                    if spheroid:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   678
                        # Call to distance_spheroid() requires spheroid param as well.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   679
                        procedure_fmt += ",'%(spheroid)s'"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   680
                        procedure_args.update({'function' : backend.distance_spheroid, 'spheroid' : params[1]})
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   681
                    else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   682
                        procedure_args.update({'function' : backend.distance_sphere})
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   683
            elif length or perimeter:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   684
                procedure_fmt = '%(geo_col)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   685
                if not geography and geodetic and length:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   686
                    # There's no `length_sphere`, and `length_spheroid` also
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   687
                    # works on 3D geometries.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   688
                    procedure_fmt += ",'%(spheroid)s'"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   689
                    procedure_args.update({'function' : backend.length_spheroid, 'spheroid' : params[1]})
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   690
                elif geom_3d and backend.postgis:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   691
                    # Use 3D variants of perimeter and length routines on PostGIS.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   692
                    if perimeter:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   693
                        procedure_args.update({'function' : backend.perimeter3d})
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   694
                    elif length:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   695
                        procedure_args.update({'function' : backend.length3d})
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   696
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   697
        # Setting up the settings for `_spatial_attribute`.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   698
        s = {'select_field' : DistanceField(dist_att),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   699
             'setup' : False,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   700
             'geo_field' : geo_field,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   701
             'procedure_args' : procedure_args,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   702
             'procedure_fmt' : procedure_fmt,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   703
             }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   704
        if geom_args:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   705
            s['geom_args'] = ('geom',)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   706
            s['procedure_args']['geom'] = geom
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   707
        elif geom:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   708
            # The geometry is passed in as a parameter because we handled
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   709
            # transformation conditions in this routine.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   710
            s['select_params'] = [backend.Adapter(geom)]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   711
        return self._spatial_attribute(func, s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   712
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   713
    def _geom_attribute(self, func, tolerance=0.05, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   714
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   715
        DRY routine for setting up a GeoQuerySet method that attaches a
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   716
        Geometry attribute (e.g., `centroid`, `point_on_surface`).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   717
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   718
        s = {'select_field' : GeomField(),}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   719
        if connections[self.db].ops.oracle:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   720
            s['procedure_fmt'] = '%(geo_col)s,%(tolerance)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   721
            s['procedure_args'] = {'tolerance' : tolerance}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   722
        return self._spatial_attribute(func, s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   723
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   724
    def _geomset_attribute(self, func, geom, tolerance=0.05, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   725
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   726
        DRY routine for setting up a GeoQuerySet method that attaches a
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   727
        Geometry attribute and takes a Geoemtry parameter.  This is used
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   728
        for geometry set-like operations (e.g., intersection, difference,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   729
        union, sym_difference).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   730
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   731
        s = {'geom_args' : ('geom',),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   732
             'select_field' : GeomField(),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   733
             'procedure_fmt' : '%(geo_col)s,%(geom)s',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   734
             'procedure_args' : {'geom' : geom},
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   735
            }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   736
        if connections[self.db].ops.oracle:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   737
            s['procedure_fmt'] += ',%(tolerance)s'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   738
            s['procedure_args']['tolerance'] = tolerance
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   739
        return self._spatial_attribute(func, s, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   740
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   741
    def _geocol_select(self, geo_field, field_name):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   742
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   743
        Helper routine for constructing the SQL to select the geographic
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   744
        column.  Takes into account if the geographic field is in a
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   745
        ForeignKey relation to the current model.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   746
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   747
        opts = self.model._meta
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   748
        if not geo_field in opts.fields:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   749
            # Is this operation going to be on a related geographic field?
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   750
            # If so, it'll have to be added to the select related information
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   751
            # (e.g., if 'location__point' was given as the field name).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   752
            self.query.add_select_related([field_name])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   753
            compiler = self.query.get_compiler(self.db)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   754
            compiler.pre_sql_setup()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   755
            rel_table, rel_col = self.query.related_select_cols[self.query.related_select_fields.index(geo_field)]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   756
            return compiler._field_column(geo_field, rel_table)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   757
        elif not geo_field in opts.local_fields:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   758
            # This geographic field is inherited from another model, so we have to
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   759
            # use the db table for the _parent_ model instead.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   760
            tmp_fld, parent_model, direct, m2m = opts.get_field_by_name(geo_field.name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   761
            return self.query.get_compiler(self.db)._field_column(geo_field, parent_model._meta.db_table)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   762
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   763
            return self.query.get_compiler(self.db)._field_column(geo_field)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   764
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   765
class GeoValuesQuerySet(ValuesQuerySet):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   766
    def __init__(self, *args, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   767
        super(GeoValuesQuerySet, self).__init__(*args, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   768
        # This flag tells `resolve_columns` to run the values through
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   769
        # `convert_values`.  This ensures that Geometry objects instead
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   770
        # of string values are returned with `values()` or `values_list()`.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   771
        self.query.geo_values = True
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   772
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   773
class GeoValuesListQuerySet(GeoValuesQuerySet, ValuesListQuerySet):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   774
    pass