diff --git a/django/contrib/gis/db/models/query.py b/django/contrib/gis/db/models/query.py index 3d31f73c29..d055260a58 100644 --- a/django/contrib/gis/db/models/query.py +++ b/django/contrib/gis/db/models/query.py @@ -62,6 +62,14 @@ class GeoQuerySet(QuerySet): """ return self._geom_attribute('centroid', **kwargs) + def collect(self, **kwargs): + """ + Performs an aggregate collect operation on the given geometry field. + This is analagous to a union operation, but much faster because + boundaries are not dissolved. + """ + return self._spatial_aggregate(aggregates.Collect, **kwargs) + def difference(self, geom, **kwargs): """ Returns the spatial difference of the geographic field in a `difference` diff --git a/django/contrib/gis/tests/relatedapp/tests.py b/django/contrib/gis/tests/relatedapp/tests.py index 502a3c0be9..dcc5af1ac7 100644 --- a/django/contrib/gis/tests/relatedapp/tests.py +++ b/django/contrib/gis/tests/relatedapp/tests.py @@ -1,7 +1,7 @@ import os, unittest from django.contrib.gis.geos import * from django.contrib.gis.db.backend import SpatialBackend -from django.contrib.gis.db.models import Count, Extent, F, Union +from django.contrib.gis.db.models import Collect, Count, Extent, F, Union from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_spatialite from django.conf import settings from models import City, Location, DirectoryEntry, Parcel, Book, Author @@ -264,6 +264,26 @@ class RelatedGeoModelTest(unittest.TestCase): # Should be `None`, and not a 'dummy' model. self.assertEqual(None, b.author) + @no_mysql + @no_oracle + @no_spatialite + def test14_collect(self): + "Testing the `collect` GeoQuerySet method and `Collect` aggregate." + # Reference query: + # SELECT AsText(ST_Collect("relatedapp_location"."point")) FROM "relatedapp_city" LEFT OUTER JOIN + # "relatedapp_location" ON ("relatedapp_city"."location_id" = "relatedapp_location"."id") + # WHERE "relatedapp_city"."state" = 'TX'; + ref_geom = fromstr('MULTIPOINT(-97.516111 33.058333,-96.801611 32.782057,-95.363151 29.763374,-96.801611 32.782057)') + + c1 = City.objects.filter(state='TX').collect(field_name='location__point') + c2 = City.objects.filter(state='TX').aggregate(Collect('location__point'))['location__point__collect'] + + for coll in (c1, c2): + # Even though Dallas and Ft. Worth share same point, Collect doesn't + # consolidate -- that's why 4 points in MultiPoint. + self.assertEqual(4, len(coll)) + self.assertEqual(ref_geom, coll) + # TODO: Related tests for KML, GML, and distance lookups. def suite():