diff --git a/django/contrib/gis/db/models/sql/aggregates.py b/django/contrib/gis/db/models/sql/aggregates.py index 370182c721..b534288891 100644 --- a/django/contrib/gis/db/models/sql/aggregates.py +++ b/django/contrib/gis/db/models/sql/aggregates.py @@ -16,7 +16,7 @@ def convert_geom(wkt, geo_field): if SpatialBackend.postgis: def convert_extent(box): - # Box text will be something like "BOX(-90.0 30.0, -85.0 40.0)"; + # Box text will be something like "BOX(-90.0 30.0, -85.0 40.0)"; # parsing out and returning as a 4-tuple. ll, ur = box[4:-1].split(',') xmin, ymin = map(float, ll.split()) @@ -32,19 +32,28 @@ elif SpatialBackend.oracle: def convert_extent(clob): if clob: - # Oracle returns a polygon for the extent, we construct - # the 4-tuple from the coordinates in the polygon. - poly = SpatialBackend.Geometry(clob.read()) - shell = poly.shell - ll, ur = shell[0], shell[2] + # Generally, Oracle returns a polygon for the extent -- however, + # it can return a single point if there's only one Point in the + # table. + ext_geom = SpatialBackend.Geometry(clob.read()) + gtype = str(ext_geom.geom_type) + if gtype == 'Polygon': + # Construct the 4-tuple from the coordinates in the polygon. + shell = ext_geom.shell + ll, ur = shell[0][:2], shell[2][:2] + elif gtype == 'Point': + ll = ext_geom.coords[:2] + ur = ll + else: + raise Exception('Unexpected geometry type returned for extent: %s' % gtype) xmin, ymin = ll xmax, ymax = ur return (xmin, ymin, xmax, ymax) else: return None - + def convert_geom(clob, geo_field): - if clob: + if clob: return SpatialBackend.Geometry(clob.read(), geo_field.srid) else: return None @@ -73,7 +82,7 @@ class GeoAggregate(Aggregate): self.extra.setdefault('tolerance', 0.05) # Can't use geographic aggregates on non-geometry fields. - if not isinstance(self.source, GeometryField): + if not isinstance(self.source, GeometryField): raise ValueError('Geospatial aggregates only allowed on geometry fields.') # Making sure the SQL function is available for this spatial backend. @@ -87,7 +96,7 @@ class Collect(GeoAggregate): class Extent(GeoAggregate): is_extent = True sql_function = SpatialBackend.extent - + if SpatialBackend.oracle: # Have to change Extent's attributes here for Oracle. Extent.conversion_class = GeomField diff --git a/django/contrib/gis/tests/geoapp/test_regress.py b/django/contrib/gis/tests/geoapp/test_regress.py index 6eecfc8de4..043dace769 100644 --- a/django/contrib/gis/tests/geoapp/test_regress.py +++ b/django/contrib/gis/tests/geoapp/test_regress.py @@ -1,6 +1,6 @@ import os, unittest from django.contrib.gis.db.backend import SpatialBackend -from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_postgis +from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_postgis, no_spatialite from django.contrib.gis.shortcuts import render_to_kmz from models import City @@ -27,3 +27,9 @@ class GeoRegressionTests(unittest.TestCase): }] kmz = render_to_kmz('gis/kml/placemarks.kml', {'places' : places}) + @no_spatialite + def test03_extent(self): + "Testing `extent` on a table with a single point, see #11827." + pnt = City.objects.get(name='Pueblo').point + ref_ext = (pnt.x, pnt.y, pnt.x, pnt.y) + self.assertEqual(ref_ext, City.objects.filter(name='Pueblo').extent())