File: query.py

package info (click to toggle)
python-django 1.2.3-3%2Bsqueeze15
  • links: PTS, VCS
  • area: main
  • in suites: squeeze-lts
  • size: 29,720 kB
  • ctags: 21,538
  • sloc: python: 101,631; xml: 574; makefile: 149; sh: 121; sql: 7
file content (119 lines) | stat: -rw-r--r-- 5,314 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
from django.db import connections
from django.db.models.query import sql

from django.contrib.gis.db.models.fields import GeometryField
from django.contrib.gis.db.models.sql import aggregates as gis_aggregates
from django.contrib.gis.db.models.sql.conversion import AreaField, DistanceField, GeomField
from django.contrib.gis.db.models.sql.where import GeoWhereNode
from django.contrib.gis.geometry.backend import Geometry
from django.contrib.gis.measure import Area, Distance


ALL_TERMS = dict([(x, None) for x in (
            'bbcontains', 'bboverlaps', 'contained', 'contains',
            'contains_properly', 'coveredby', 'covers', 'crosses', 'disjoint',
            'distance_gt', 'distance_gte', 'distance_lt', 'distance_lte',
            'dwithin', 'equals', 'exact',
            'intersects', 'overlaps', 'relate', 'same_as', 'touches', 'within',
            'left', 'right', 'overlaps_left', 'overlaps_right',
            'overlaps_above', 'overlaps_below',
            'strictly_above', 'strictly_below'
            )])
ALL_TERMS.update(sql.constants.QUERY_TERMS)

class GeoQuery(sql.Query):
    """
    A single spatial SQL query.
    """
    # Overridding the valid query terms.
    query_terms = ALL_TERMS
    aggregates_module = gis_aggregates

    compiler = 'GeoSQLCompiler'

    #### Methods overridden from the base Query class ####
    def __init__(self, model, where=GeoWhereNode):
        super(GeoQuery, self).__init__(model, where)
        # The following attributes are customized for the GeoQuerySet.
        # The GeoWhereNode and SpatialBackend classes contain backend-specific
        # routines and functions.
        self.custom_select = {}
        self.transformed_srid = None
        self.extra_select_fields = {}

    def clone(self, *args, **kwargs):
        obj = super(GeoQuery, self).clone(*args, **kwargs)
        # Customized selection dictionary and transformed srid flag have
        # to also be added to obj.
        obj.custom_select = self.custom_select.copy()
        obj.transformed_srid = self.transformed_srid
        obj.extra_select_fields = self.extra_select_fields.copy()
        return obj

    def convert_values(self, value, field, connection):
        """
        Using the same routines that Oracle does we can convert our
        extra selection objects into Geometry and Distance objects.
        TODO: Make converted objects 'lazy' for less overhead.
        """
        if connection.ops.oracle:
            # Running through Oracle's first.
            value = super(GeoQuery, self).convert_values(value, field or GeomField(), connection)

        if value is None:
            # Output from spatial function is NULL (e.g., called
            # function on a geometry field with NULL value).
            pass
        elif isinstance(field, DistanceField):
            # Using the field's distance attribute, can instantiate
            # `Distance` with the right context.
            value = Distance(**{field.distance_att : value})
        elif isinstance(field, AreaField):
            value = Area(**{field.area_att : value})
        elif isinstance(field, (GeomField, GeometryField)) and value:
            value = Geometry(value)
        return value

    def get_aggregation(self, using):
        # Remove any aggregates marked for reduction from the subquery
        # and move them to the outer AggregateQuery.
        connection = connections[using]
        for alias, aggregate in self.aggregate_select.items():
            if isinstance(aggregate, gis_aggregates.GeoAggregate):
                if not getattr(aggregate, 'is_extent', False) or connection.ops.oracle:
                    self.extra_select_fields[alias] = GeomField()
        return super(GeoQuery, self).get_aggregation(using)

    def resolve_aggregate(self, value, aggregate, connection):
        """
        Overridden from GeoQuery's normalize to handle the conversion of
        GeoAggregate objects.
        """
        if isinstance(aggregate, self.aggregates_module.GeoAggregate):
            if aggregate.is_extent:
                if aggregate.is_extent == '3D':
                    return connection.ops.convert_extent3d(value)
                else:
                    return connection.ops.convert_extent(value)
            else:
                return connection.ops.convert_geom(value, aggregate.source)
        else:
            return super(GeoQuery, self).resolve_aggregate(value, aggregate, connection)

    # Private API utilities, subject to change.
    def _geo_field(self, field_name=None):
        """
        Returns the first Geometry field encountered; or specified via the
        `field_name` keyword.  The `field_name` may be a string specifying
        the geometry field on this GeoQuery's model, or a lookup string
        to a geometry field via a ForeignKey relation.
        """
        if field_name is None:
            # Incrementing until the first geographic field is found.
            for fld in self.model._meta.fields:
                if isinstance(fld, GeometryField): return fld
            return False
        else:
            # Otherwise, check by the given field name -- which may be
            # a lookup to a _related_ geographic field.
            return GeoWhereNode._check_geo_field(self.model._meta, field_name)