/django/contrib/gis/db/models/sql/query.py

https://code.google.com/p/mango-py/ · Python · 119 lines · 75 code · 11 blank · 33 comment · 12 complexity · 62ed891af61c0877616ee118411f3598 MD5 · raw file

  1. from django.db import connections
  2. from django.db.models.query import sql
  3. from django.contrib.gis.db.models.fields import GeometryField
  4. from django.contrib.gis.db.models.sql import aggregates as gis_aggregates
  5. from django.contrib.gis.db.models.sql.conversion import AreaField, DistanceField, GeomField
  6. from django.contrib.gis.db.models.sql.where import GeoWhereNode
  7. from django.contrib.gis.geometry.backend import Geometry
  8. from django.contrib.gis.measure import Area, Distance
  9. ALL_TERMS = dict([(x, None) for x in (
  10. 'bbcontains', 'bboverlaps', 'contained', 'contains',
  11. 'contains_properly', 'coveredby', 'covers', 'crosses', 'disjoint',
  12. 'distance_gt', 'distance_gte', 'distance_lt', 'distance_lte',
  13. 'dwithin', 'equals', 'exact',
  14. 'intersects', 'overlaps', 'relate', 'same_as', 'touches', 'within',
  15. 'left', 'right', 'overlaps_left', 'overlaps_right',
  16. 'overlaps_above', 'overlaps_below',
  17. 'strictly_above', 'strictly_below'
  18. )])
  19. ALL_TERMS.update(sql.constants.QUERY_TERMS)
  20. class GeoQuery(sql.Query):
  21. """
  22. A single spatial SQL query.
  23. """
  24. # Overridding the valid query terms.
  25. query_terms = ALL_TERMS
  26. aggregates_module = gis_aggregates
  27. compiler = 'GeoSQLCompiler'
  28. #### Methods overridden from the base Query class ####
  29. def __init__(self, model, where=GeoWhereNode):
  30. super(GeoQuery, self).__init__(model, where)
  31. # The following attributes are customized for the GeoQuerySet.
  32. # The GeoWhereNode and SpatialBackend classes contain backend-specific
  33. # routines and functions.
  34. self.custom_select = {}
  35. self.transformed_srid = None
  36. self.extra_select_fields = {}
  37. def clone(self, *args, **kwargs):
  38. obj = super(GeoQuery, self).clone(*args, **kwargs)
  39. # Customized selection dictionary and transformed srid flag have
  40. # to also be added to obj.
  41. obj.custom_select = self.custom_select.copy()
  42. obj.transformed_srid = self.transformed_srid
  43. obj.extra_select_fields = self.extra_select_fields.copy()
  44. return obj
  45. def convert_values(self, value, field, connection):
  46. """
  47. Using the same routines that Oracle does we can convert our
  48. extra selection objects into Geometry and Distance objects.
  49. TODO: Make converted objects 'lazy' for less overhead.
  50. """
  51. if connection.ops.oracle:
  52. # Running through Oracle's first.
  53. value = super(GeoQuery, self).convert_values(value, field or GeomField(), connection)
  54. if value is None:
  55. # Output from spatial function is NULL (e.g., called
  56. # function on a geometry field with NULL value).
  57. pass
  58. elif isinstance(field, DistanceField):
  59. # Using the field's distance attribute, can instantiate
  60. # `Distance` with the right context.
  61. value = Distance(**{field.distance_att : value})
  62. elif isinstance(field, AreaField):
  63. value = Area(**{field.area_att : value})
  64. elif isinstance(field, (GeomField, GeometryField)) and value:
  65. value = Geometry(value)
  66. return value
  67. def get_aggregation(self, using):
  68. # Remove any aggregates marked for reduction from the subquery
  69. # and move them to the outer AggregateQuery.
  70. connection = connections[using]
  71. for alias, aggregate in self.aggregate_select.items():
  72. if isinstance(aggregate, gis_aggregates.GeoAggregate):
  73. if not getattr(aggregate, 'is_extent', False) or connection.ops.oracle:
  74. self.extra_select_fields[alias] = GeomField()
  75. return super(GeoQuery, self).get_aggregation(using)
  76. def resolve_aggregate(self, value, aggregate, connection):
  77. """
  78. Overridden from GeoQuery's normalize to handle the conversion of
  79. GeoAggregate objects.
  80. """
  81. if isinstance(aggregate, self.aggregates_module.GeoAggregate):
  82. if aggregate.is_extent:
  83. if aggregate.is_extent == '3D':
  84. return connection.ops.convert_extent3d(value)
  85. else:
  86. return connection.ops.convert_extent(value)
  87. else:
  88. return connection.ops.convert_geom(value, aggregate.source)
  89. else:
  90. return super(GeoQuery, self).resolve_aggregate(value, aggregate, connection)
  91. # Private API utilities, subject to change.
  92. def _geo_field(self, field_name=None):
  93. """
  94. Returns the first Geometry field encountered; or specified via the
  95. `field_name` keyword. The `field_name` may be a string specifying
  96. the geometry field on this GeoQuery's model, or a lookup string
  97. to a geometry field via a ForeignKey relation.
  98. """
  99. if field_name is None:
  100. # Incrementing until the first geographic field is found.
  101. for fld in self.model._meta.fields:
  102. if isinstance(fld, GeometryField): return fld
  103. return False
  104. else:
  105. # Otherwise, check by the given field name -- which may be
  106. # a lookup to a _related_ geographic field.
  107. return GeoWhereNode._check_geo_field(self.model._meta, field_name)