diff --git a/docs/ref/contrib/gis/model-api.txt b/docs/ref/contrib/gis/model-api.txt index d5e1d79afa..f795ea7c20 100644 --- a/docs/ref/contrib/gis/model-api.txt +++ b/docs/ref/contrib/gis/model-api.txt @@ -235,6 +235,18 @@ available for geography columns: * :lookup:`covers` * :lookup:`intersects` +If you need to use a spatial lookup or aggregate that doesn't support the +geography type as input, you can use the +:class:`~django.db.models.functions.Cast` database function to convert the +geography column to a geometry type in the query:: + + from django.contrib.gis.db.models import PointField + from django.db.models.functions import Cast + + Zipcode.objects.annotate( + geom=Cast('geography_field', PointField()) + ).filter(geom__within=poly) + For more information, the PostGIS documentation contains a helpful section on determining `when to use geography data type over geometry data type `_. diff --git a/tests/gis_tests/geogapp/tests.py b/tests/gis_tests/geogapp/tests.py index c314c93c5d..0711e26bc1 100644 --- a/tests/gis_tests/geogapp/tests.py +++ b/tests/gis_tests/geogapp/tests.py @@ -6,9 +6,12 @@ from __future__ import unicode_literals import os from unittest import skipUnless +from django.contrib.gis.db import models from django.contrib.gis.db.models.functions import Area, Distance from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.measure import D +from django.db import connection +from django.db.models.functions import Cast from django.test import TestCase, ignore_warnings, skipUnlessDBFeature from django.utils._os import upath from django.utils.deprecation import RemovedInDjango20Warning @@ -113,6 +116,21 @@ class GeographyTest(TestCase): class GeographyFunctionTests(TestCase): fixtures = ['initial'] + @skipUnlessDBFeature("supports_extent_aggr") + def test_cast_aggregate(self): + """ + Cast a geography to a geometry field for an aggregate function that + expects a geometry input. + """ + if not connection.ops.geography: + self.skipTest("This test needs geography support") + expected = (-96.8016128540039, 29.7633724212646, -95.3631439208984, 32.782058715820) + res = City.objects.filter( + name__in=('Houston', 'Dallas') + ).aggregate(extent=models.Extent(Cast('point', models.PointField()))) + for val, exp in zip(res['extent'], expected): + self.assertAlmostEqual(exp, val, 4) + @skipUnlessDBFeature("has_Distance_function", "supports_distance_geodetic") def test_distance_function(self): """