1
0
mirror of https://github.com/django/django.git synced 2025-10-31 09:41:08 +00:00

Refs #33476 -- Reformatted code with Black.

This commit is contained in:
django-bot
2022-02-03 20:24:19 +01:00
committed by Mariusz Felisiak
parent f68fa8b45d
commit 9c19aff7c7
1992 changed files with 139577 additions and 96284 deletions

View File

@@ -4,14 +4,14 @@ from .models import City
class TestGeoRSS1(feeds.Feed):
link = '/city/'
title = 'Test GeoDjango Cities'
link = "/city/"
title = "Test GeoDjango Cities"
def items(self):
return City.objects.all()
def item_link(self, item):
return '/city/%s/' % item.pk
return "/city/%s/" % item.pk
def item_geometry(self, item):
return item.point
@@ -56,16 +56,17 @@ class TestW3CGeo3(TestGeoRSS1):
def item_geometry(self, item):
from django.contrib.gis.geos import Polygon
return Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
# The feed dictionary to use for URLs.
feed_dict = {
'rss1': TestGeoRSS1,
'rss2': TestGeoRSS2,
'atom1': TestGeoAtom1,
'atom2': TestGeoAtom2,
'w3cgeo1': TestW3CGeo1,
'w3cgeo2': TestW3CGeo2,
'w3cgeo3': TestW3CGeo3,
"rss1": TestGeoRSS1,
"rss2": TestGeoRSS2,
"atom1": TestGeoAtom1,
"atom2": TestGeoAtom2,
"w3cgeo1": TestW3CGeo1,
"w3cgeo2": TestW3CGeo2,
"w3cgeo3": TestW3CGeo3,
}

View File

@@ -25,7 +25,7 @@ class City(NamedModel):
point = models.PointField()
class Meta:
app_label = 'geoapp'
app_label = "geoapp"
# This is an inherited model from City
@@ -34,14 +34,16 @@ class PennsylvaniaCity(City):
founded = models.DateTimeField(null=True)
class Meta:
app_label = 'geoapp'
app_label = "geoapp"
class State(NamedModel):
poly = models.PolygonField(null=gisfield_may_be_null) # Allowing NULL geometries here.
poly = models.PolygonField(
null=gisfield_may_be_null
) # Allowing NULL geometries here.
class Meta:
app_label = 'geoapp'
app_label = "geoapp"
class Track(NamedModel):
@@ -59,8 +61,8 @@ class UniqueTogetherModel(models.Model):
point = models.PointField()
class Meta:
unique_together = ('city', 'point')
required_db_features = ['supports_geometry_field_unique_index']
unique_together = ("city", "point")
required_db_features = ["supports_geometry_field_unique_index"]
class Truth(models.Model):
@@ -76,7 +78,6 @@ class MinusOneSRID(models.Model):
class NonConcreteField(models.IntegerField):
def db_type(self, connection):
return None

View File

@@ -3,6 +3,6 @@ from django.contrib.gis.sitemaps import KMLSitemap, KMZSitemap
from .models import City, Country
sitemaps = {
'kml': KMLSitemap([City, Country]),
'kmz': KMZSitemap([City, Country]),
"kml": KMLSitemap([City, Country]),
"kmz": KMZSitemap([City, Country]),
}

View File

@@ -8,24 +8,30 @@ from .models import City, ManyPointModel, MultiFields
class GeoExpressionsTests(TestCase):
fixtures = ['initial']
fixtures = ["initial"]
def test_geometry_value_annotation(self):
p = Point(1, 1, srid=4326)
point = City.objects.annotate(p=Value(p, GeometryField(srid=4326))).first().p
self.assertEqual(point, p)
@skipUnlessDBFeature('supports_transform')
@skipUnlessDBFeature("supports_transform")
def test_geometry_value_annotation_different_srid(self):
p = Point(1, 1, srid=32140)
point = City.objects.annotate(p=Value(p, GeometryField(srid=4326))).first().p
self.assertTrue(point.equals_exact(p.transform(4326, clone=True), 10 ** -5))
self.assertTrue(point.equals_exact(p.transform(4326, clone=True), 10**-5))
self.assertEqual(point.srid, 4326)
@skipUnlessDBFeature('supports_geography')
@skipUnlessDBFeature("supports_geography")
def test_geography_value(self):
p = Polygon(((1, 1), (1, 2), (2, 2), (2, 1), (1, 1)))
area = City.objects.annotate(a=functions.Area(Value(p, GeometryField(srid=4326, geography=True)))).first().a
area = (
City.objects.annotate(
a=functions.Area(Value(p, GeometryField(srid=4326, geography=True)))
)
.first()
.a
)
self.assertAlmostEqual(area.sq_km, 12305.1, 0)
def test_update_from_other_field(self):
@@ -37,29 +43,37 @@ class GeoExpressionsTests(TestCase):
point3=p2.transform(3857, clone=True),
)
# Updating a point to a point of the same SRID.
ManyPointModel.objects.filter(pk=obj.pk).update(point2=F('point1'))
ManyPointModel.objects.filter(pk=obj.pk).update(point2=F("point1"))
obj.refresh_from_db()
self.assertEqual(obj.point2, p1)
# Updating a point to a point with a different SRID.
if connection.features.supports_transform:
ManyPointModel.objects.filter(pk=obj.pk).update(point3=F('point1'))
ManyPointModel.objects.filter(pk=obj.pk).update(point3=F("point1"))
obj.refresh_from_db()
self.assertTrue(obj.point3.equals_exact(p1.transform(3857, clone=True), 0.1))
self.assertTrue(
obj.point3.equals_exact(p1.transform(3857, clone=True), 0.1)
)
def test_multiple_annotation(self):
multi_field = MultiFields.objects.create(
point=Point(1, 1),
city=City.objects.get(name='Houston'),
city=City.objects.get(name="Houston"),
poly=Polygon(((1, 1), (1, 2), (2, 2), (2, 1), (1, 1))),
)
qs = City.objects.values('name').annotate(
distance=Min(functions.Distance('multifields__point', multi_field.city.point)),
).annotate(count=Count('multifields'))
qs = (
City.objects.values("name")
.annotate(
distance=Min(
functions.Distance("multifields__point", multi_field.city.point)
),
)
.annotate(count=Count("multifields"))
)
self.assertTrue(qs.first())
@skipUnlessDBFeature('has_Translate_function')
@skipUnlessDBFeature("has_Translate_function")
def test_update_with_expression(self):
city = City.objects.create(point=Point(1, 1, srid=4326))
City.objects.filter(pk=city.pk).update(point=functions.Translate('point', 1, 1))
City.objects.filter(pk=city.pk).update(point=functions.Translate("point", 1, 1))
city.refresh_from_db()
self.assertEqual(city.point, Point(2, 2, srid=4326))

View File

@@ -7,10 +7,10 @@ from django.test import TestCase, modify_settings, override_settings
from .models import City
@modify_settings(INSTALLED_APPS={'append': 'django.contrib.sites'})
@override_settings(ROOT_URLCONF='gis_tests.geoapp.urls')
@modify_settings(INSTALLED_APPS={"append": "django.contrib.sites"})
@override_settings(ROOT_URLCONF="gis_tests.geoapp.urls")
class GeoFeedTest(TestCase):
fixtures = ['initial']
fixtures = ["initial"]
@classmethod
def setUpTestData(cls):
@@ -25,64 +25,87 @@ class GeoFeedTest(TestCase):
def test_geofeed_rss(self):
"Tests geographic feeds using GeoRSS over RSSv2."
# Uses `GEOSGeometry` in `item_geometry`
doc1 = minidom.parseString(self.client.get('/feeds/rss1/').content)
doc1 = minidom.parseString(self.client.get("/feeds/rss1/").content)
# Uses a 2-tuple in `item_geometry`
doc2 = minidom.parseString(self.client.get('/feeds/rss2/').content)
doc2 = minidom.parseString(self.client.get("/feeds/rss2/").content)
feed1, feed2 = doc1.firstChild, doc2.firstChild
# Making sure the box got added to the second GeoRSS feed.
self.assertChildNodes(feed2.getElementsByTagName('channel')[0],
['title', 'link', 'description', 'language',
'lastBuildDate', 'item', 'georss:box', 'atom:link']
)
self.assertChildNodes(
feed2.getElementsByTagName("channel")[0],
[
"title",
"link",
"description",
"language",
"lastBuildDate",
"item",
"georss:box",
"atom:link",
],
)
# Incrementing through the feeds.
for feed in [feed1, feed2]:
# Ensuring the georss namespace was added to the <rss> element.
self.assertEqual(feed.getAttribute('xmlns:georss'), 'http://www.georss.org/georss')
chan = feed.getElementsByTagName('channel')[0]
items = chan.getElementsByTagName('item')
self.assertEqual(
feed.getAttribute("xmlns:georss"), "http://www.georss.org/georss"
)
chan = feed.getElementsByTagName("channel")[0]
items = chan.getElementsByTagName("item")
self.assertEqual(len(items), City.objects.count())
# Ensuring the georss element was added to each item in the feed.
for item in items:
self.assertChildNodes(item, ['title', 'link', 'description', 'guid', 'georss:point'])
self.assertChildNodes(
item, ["title", "link", "description", "guid", "georss:point"]
)
def test_geofeed_atom(self):
"Testing geographic feeds using GeoRSS over Atom."
doc1 = minidom.parseString(self.client.get('/feeds/atom1/').content)
doc2 = minidom.parseString(self.client.get('/feeds/atom2/').content)
doc1 = minidom.parseString(self.client.get("/feeds/atom1/").content)
doc2 = minidom.parseString(self.client.get("/feeds/atom2/").content)
feed1, feed2 = doc1.firstChild, doc2.firstChild
# Making sure the box got added to the second GeoRSS feed.
self.assertChildNodes(feed2, ['title', 'link', 'id', 'updated', 'entry', 'georss:box'])
self.assertChildNodes(
feed2, ["title", "link", "id", "updated", "entry", "georss:box"]
)
for feed in [feed1, feed2]:
# Ensuring the georsss namespace was added to the <feed> element.
self.assertEqual(feed.getAttribute('xmlns:georss'), 'http://www.georss.org/georss')
entries = feed.getElementsByTagName('entry')
self.assertEqual(
feed.getAttribute("xmlns:georss"), "http://www.georss.org/georss"
)
entries = feed.getElementsByTagName("entry")
self.assertEqual(len(entries), City.objects.count())
# Ensuring the georss element was added to each entry in the feed.
for entry in entries:
self.assertChildNodes(entry, ['title', 'link', 'id', 'summary', 'georss:point'])
self.assertChildNodes(
entry, ["title", "link", "id", "summary", "georss:point"]
)
def test_geofeed_w3c(self):
"Testing geographic feeds using W3C Geo."
doc = minidom.parseString(self.client.get('/feeds/w3cgeo1/').content)
doc = minidom.parseString(self.client.get("/feeds/w3cgeo1/").content)
feed = doc.firstChild
# Ensuring the geo namespace was added to the <feed> element.
self.assertEqual(feed.getAttribute('xmlns:geo'), 'http://www.w3.org/2003/01/geo/wgs84_pos#')
chan = feed.getElementsByTagName('channel')[0]
items = chan.getElementsByTagName('item')
self.assertEqual(
feed.getAttribute("xmlns:geo"), "http://www.w3.org/2003/01/geo/wgs84_pos#"
)
chan = feed.getElementsByTagName("channel")[0]
items = chan.getElementsByTagName("item")
self.assertEqual(len(items), City.objects.count())
# Ensuring the geo:lat and geo:lon element was added to each item in the feed.
for item in items:
self.assertChildNodes(item, ['title', 'link', 'description', 'guid', 'geo:lat', 'geo:lon'])
self.assertChildNodes(
item, ["title", "link", "description", "guid", "geo:lat", "geo:lon"]
)
# Boxes and Polygons aren't allowed in W3C Geo feeds.
with self.assertRaises(ValueError): # Box in <channel>
self.client.get('/feeds/w3cgeo2/')
self.client.get("/feeds/w3cgeo2/")
with self.assertRaises(ValueError): # Polygons in <entry>
self.client.get('/feeds/w3cgeo3/')
self.client.get("/feeds/w3cgeo3/")

View File

@@ -4,9 +4,7 @@ import re
from decimal import Decimal
from django.contrib.gis.db.models import GeometryField, PolygonField, functions
from django.contrib.gis.geos import (
GEOSGeometry, LineString, Point, Polygon, fromstr,
)
from django.contrib.gis.geos import GEOSGeometry, LineString, Point, Polygon, fromstr
from django.contrib.gis.measure import Area
from django.db import NotSupportedError, connection
from django.db.models import IntegerField, Sum, Value
@@ -23,12 +21,13 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
Please keep the tests in function's alphabetic order.
"""
fixtures = ['initial']
fixtures = ["initial"]
def test_asgeojson(self):
if not connection.features.has_AsGeoJSON_function:
with self.assertRaises(NotSupportedError):
list(Country.objects.annotate(json=functions.AsGeoJSON('mpoly')))
list(Country.objects.annotate(json=functions.AsGeoJSON("mpoly")))
return
pueblo_json = '{"type":"Point","coordinates":[-104.609252,38.255001]}'
@@ -44,32 +43,36 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
'{"type":"Point","crs":{"type":"name","properties":{"name":"EPSG:4326"}},'
'"bbox":[-87.65018,41.85039,-87.65018,41.85039],"coordinates":[-87.65018,41.85039]}'
)
if 'crs' in connection.features.unsupported_geojson_options:
del houston_json['crs']
del chicago_json['crs']
if 'bbox' in connection.features.unsupported_geojson_options:
del chicago_json['bbox']
del victoria_json['bbox']
if 'precision' in connection.features.unsupported_geojson_options:
chicago_json['coordinates'] = [-87.650175, 41.850385]
if "crs" in connection.features.unsupported_geojson_options:
del houston_json["crs"]
del chicago_json["crs"]
if "bbox" in connection.features.unsupported_geojson_options:
del chicago_json["bbox"]
del victoria_json["bbox"]
if "precision" in connection.features.unsupported_geojson_options:
chicago_json["coordinates"] = [-87.650175, 41.850385]
# Precision argument should only be an integer
with self.assertRaises(TypeError):
City.objects.annotate(geojson=functions.AsGeoJSON('point', precision='foo'))
City.objects.annotate(geojson=functions.AsGeoJSON("point", precision="foo"))
# Reference queries and values.
# SELECT ST_AsGeoJson("geoapp_city"."point", 8, 0)
# FROM "geoapp_city" WHERE "geoapp_city"."name" = 'Pueblo';
self.assertJSONEqual(
pueblo_json,
City.objects.annotate(geojson=functions.AsGeoJSON('point')).get(name='Pueblo').geojson
City.objects.annotate(geojson=functions.AsGeoJSON("point"))
.get(name="Pueblo")
.geojson,
)
# SELECT ST_AsGeoJson("geoapp_city"."point", 8, 2) FROM "geoapp_city"
# WHERE "geoapp_city"."name" = 'Houston';
# This time we want to include the CRS by using the `crs` keyword.
self.assertJSONEqual(
City.objects.annotate(json=functions.AsGeoJSON('point', crs=True)).get(name='Houston').json,
City.objects.annotate(json=functions.AsGeoJSON("point", crs=True))
.get(name="Houston")
.json,
houston_json,
)
@@ -77,9 +80,9 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
# WHERE "geoapp_city"."name" = 'Houston';
# This time we include the bounding box by using the `bbox` keyword.
self.assertJSONEqual(
City.objects.annotate(
geojson=functions.AsGeoJSON('point', bbox=True)
).get(name='Victoria').geojson,
City.objects.annotate(geojson=functions.AsGeoJSON("point", bbox=True))
.get(name="Victoria")
.geojson,
victoria_json,
)
@@ -88,21 +91,29 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
# Finally, we set every available keyword.
# MariaDB doesn't limit the number of decimals in bbox.
if connection.ops.mariadb:
chicago_json['bbox'] = [-87.650175, 41.850385, -87.650175, 41.850385]
chicago_json["bbox"] = [-87.650175, 41.850385, -87.650175, 41.850385]
try:
self.assertJSONEqual(
City.objects.annotate(
geojson=functions.AsGeoJSON('point', bbox=True, crs=True, precision=5)
).get(name='Chicago').geojson,
geojson=functions.AsGeoJSON(
"point", bbox=True, crs=True, precision=5
)
)
.get(name="Chicago")
.geojson,
chicago_json,
)
except AssertionError:
# Give a second chance with different coords rounding.
chicago_json['coordinates'][1] = 41.85038
chicago_json["coordinates"][1] = 41.85038
self.assertJSONEqual(
City.objects.annotate(
geojson=functions.AsGeoJSON('point', bbox=True, crs=True, precision=5)
).get(name='Chicago').geojson,
geojson=functions.AsGeoJSON(
"point", bbox=True, crs=True, precision=5
)
)
.get(name="Chicago")
.geojson,
chicago_json,
)
@@ -112,25 +123,29 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
# non-geometry field.
qs = City.objects.all()
with self.assertRaises(TypeError):
qs.annotate(gml=functions.AsGML('name'))
ptown = City.objects.annotate(gml=functions.AsGML('point', precision=9)).get(name='Pueblo')
qs.annotate(gml=functions.AsGML("name"))
ptown = City.objects.annotate(gml=functions.AsGML("point", precision=9)).get(
name="Pueblo"
)
if connection.ops.oracle:
# No precision parameter for Oracle :-/
gml_regex = re.compile(
r'^<gml:Point srsName="EPSG:4326" xmlns:gml="http://www.opengis.net/gml">'
r'<gml:coordinates decimal="\." cs="," ts=" ">-104.60925\d+,38.25500\d+ '
r'</gml:coordinates></gml:Point>'
r"</gml:coordinates></gml:Point>"
)
else:
gml_regex = re.compile(
r'^<gml:Point srsName="EPSG:4326"><gml:coordinates>'
r'-104\.60925\d+,38\.255001</gml:coordinates></gml:Point>'
r"-104\.60925\d+,38\.255001</gml:coordinates></gml:Point>"
)
self.assertTrue(gml_regex.match(ptown.gml))
self.assertIn(
'<gml:pos srsDimension="2">',
City.objects.annotate(gml=functions.AsGML('point', version=3)).get(name='Pueblo').gml
City.objects.annotate(gml=functions.AsGML("point", version=3))
.get(name="Pueblo")
.gml,
)
@skipUnlessDBFeature("has_AsKML_function")
@@ -138,46 +153,68 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
# Should throw a TypeError when trying to obtain KML from a
# non-geometry field.
with self.assertRaises(TypeError):
City.objects.annotate(kml=functions.AsKML('name'))
City.objects.annotate(kml=functions.AsKML("name"))
# Ensuring the KML is as expected.
ptown = City.objects.annotate(kml=functions.AsKML('point', precision=9)).get(name='Pueblo')
self.assertEqual('<Point><coordinates>-104.609252,38.255001</coordinates></Point>', ptown.kml)
ptown = City.objects.annotate(kml=functions.AsKML("point", precision=9)).get(
name="Pueblo"
)
self.assertEqual(
"<Point><coordinates>-104.609252,38.255001</coordinates></Point>", ptown.kml
)
@skipUnlessDBFeature("has_AsSVG_function")
def test_assvg(self):
with self.assertRaises(TypeError):
City.objects.annotate(svg=functions.AsSVG('point', precision='foo'))
City.objects.annotate(svg=functions.AsSVG("point", precision="foo"))
# SELECT AsSVG(geoapp_city.point, 0, 8) FROM geoapp_city WHERE name = 'Pueblo';
svg1 = 'cx="-104.609252" cy="-38.255001"'
# Even though relative, only one point so it's practically the same except for
# the 'c' letter prefix on the x,y values.
svg2 = svg1.replace('c', '')
self.assertEqual(svg1, City.objects.annotate(svg=functions.AsSVG('point')).get(name='Pueblo').svg)
self.assertEqual(svg2, City.objects.annotate(svg=functions.AsSVG('point', relative=5)).get(name='Pueblo').svg)
svg2 = svg1.replace("c", "")
self.assertEqual(
svg1,
City.objects.annotate(svg=functions.AsSVG("point")).get(name="Pueblo").svg,
)
self.assertEqual(
svg2,
City.objects.annotate(svg=functions.AsSVG("point", relative=5))
.get(name="Pueblo")
.svg,
)
@skipUnlessDBFeature('has_AsWKB_function')
@skipUnlessDBFeature("has_AsWKB_function")
def test_aswkb(self):
wkb = City.objects.annotate(
wkb=functions.AsWKB(Point(1, 2, srid=4326)),
).first().wkb
wkb = (
City.objects.annotate(
wkb=functions.AsWKB(Point(1, 2, srid=4326)),
)
.first()
.wkb
)
# WKB is either XDR or NDR encoded.
self.assertIn(
bytes(wkb),
(
b'\x00\x00\x00\x00\x01?\xf0\x00\x00\x00\x00\x00\x00@\x00\x00'
b'\x00\x00\x00\x00\x00',
b'\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00'
b'\x00\x00\x00\x00\x00@',
b"\x00\x00\x00\x00\x01?\xf0\x00\x00\x00\x00\x00\x00@\x00\x00"
b"\x00\x00\x00\x00\x00",
b"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00"
b"\x00\x00\x00\x00\x00@",
),
)
@skipUnlessDBFeature('has_AsWKT_function')
@skipUnlessDBFeature("has_AsWKT_function")
def test_aswkt(self):
wkt = City.objects.annotate(
wkt=functions.AsWKT(Point(1, 2, srid=4326)),
).first().wkt
self.assertEqual(wkt, 'POINT (1.0 2.0)' if connection.ops.oracle else 'POINT(1 2)')
wkt = (
City.objects.annotate(
wkt=functions.AsWKT(Point(1, 2, srid=4326)),
)
.first()
.wkt
)
self.assertEqual(
wkt, "POINT (1.0 2.0)" if connection.ops.oracle else "POINT(1 2)"
)
@skipUnlessDBFeature("has_Azimuth_function")
def test_azimuth(self):
@@ -199,7 +236,9 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
return (4 * num_seg) + 1
expected_areas = (169, 136) if connection.ops.postgis else (171, 126)
qs = Country.objects.annotate(circle=functions.BoundingCircle('mpoly')).order_by('name')
qs = Country.objects.annotate(
circle=functions.BoundingCircle("mpoly")
).order_by("name")
self.assertAlmostEqual(qs[0].circle.area, expected_areas[0], 0)
self.assertAlmostEqual(qs[1].circle.area, expected_areas[1], 0)
if connection.ops.postgis:
@@ -211,8 +250,8 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
for num_seq in tests:
with self.subTest(num_seq=num_seq):
qs = Country.objects.annotate(
circle=functions.BoundingCircle('mpoly', num_seg=num_seq),
).order_by('name')
circle=functions.BoundingCircle("mpoly", num_seg=num_seq),
).order_by("name")
if connection.ops.postgis:
self.assertGreater(qs[0].circle.area, 168.4, 0)
self.assertLess(qs[0].circle.area, 169.5, 0)
@@ -225,21 +264,27 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
@skipUnlessDBFeature("has_Centroid_function")
def test_centroid(self):
qs = State.objects.exclude(poly__isnull=True).annotate(centroid=functions.Centroid('poly'))
tol = 1.8 if connection.ops.mysql else (0.1 if connection.ops.oracle else 0.00001)
qs = State.objects.exclude(poly__isnull=True).annotate(
centroid=functions.Centroid("poly")
)
tol = (
1.8 if connection.ops.mysql else (0.1 if connection.ops.oracle else 0.00001)
)
for state in qs:
self.assertTrue(state.poly.centroid.equals_exact(state.centroid, tol))
with self.assertRaisesMessage(TypeError, "'Centroid' takes exactly 1 argument (2 given)"):
State.objects.annotate(centroid=functions.Centroid('poly', 'poly'))
with self.assertRaisesMessage(
TypeError, "'Centroid' takes exactly 1 argument (2 given)"
):
State.objects.annotate(centroid=functions.Centroid("poly", "poly"))
@skipUnlessDBFeature("has_Difference_function")
def test_difference(self):
geom = Point(5, 23, srid=4326)
qs = Country.objects.annotate(diff=functions.Difference('mpoly', geom))
qs = Country.objects.annotate(diff=functions.Difference("mpoly", geom))
# Oracle does something screwy with the Texas geometry.
if connection.ops.oracle:
qs = qs.exclude(name='Texas')
qs = qs.exclude(name="Texas")
for c in qs:
self.assertTrue(c.mpoly.difference(geom).equals(c.diff))
@@ -248,16 +293,16 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
def test_difference_mixed_srid(self):
"""Testing with mixed SRID (Country has default 4326)."""
geom = Point(556597.4, 2632018.6, srid=3857) # Spherical Mercator
qs = Country.objects.annotate(difference=functions.Difference('mpoly', geom))
qs = Country.objects.annotate(difference=functions.Difference("mpoly", geom))
# Oracle does something screwy with the Texas geometry.
if connection.ops.oracle:
qs = qs.exclude(name='Texas')
qs = qs.exclude(name="Texas")
for c in qs:
self.assertTrue(c.mpoly.difference(geom).equals(c.difference))
@skipUnlessDBFeature("has_Envelope_function")
def test_envelope(self):
countries = Country.objects.annotate(envelope=functions.Envelope('mpoly'))
countries = Country.objects.annotate(envelope=functions.Envelope("mpoly"))
for country in countries:
self.assertTrue(country.envelope.equals(country.mpoly.envelope))
@@ -271,8 +316,10 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
((0, 0), (0, 5), (5, 0), (0, 0)),
((1, 1), (3, 1), (1, 3), (1, 1)),
)
State.objects.create(name='Foo', poly=Polygon(*rings))
st = State.objects.annotate(force_polygon_cw=functions.ForcePolygonCW('poly')).get(name='Foo')
State.objects.create(name="Foo", poly=Polygon(*rings))
st = State.objects.annotate(
force_polygon_cw=functions.ForcePolygonCW("poly")
).get(name="Foo")
self.assertEqual(rhr_rings, st.force_polygon_cw.coords)
@skipUnlessDBFeature("has_GeoHash_function")
@@ -280,16 +327,22 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
# Reference query:
# SELECT ST_GeoHash(point) FROM geoapp_city WHERE name='Houston';
# SELECT ST_GeoHash(point, 5) FROM geoapp_city WHERE name='Houston';
ref_hash = '9vk1mfq8jx0c8e0386z6'
h1 = City.objects.annotate(geohash=functions.GeoHash('point')).get(name='Houston')
h2 = City.objects.annotate(geohash=functions.GeoHash('point', precision=5)).get(name='Houston')
self.assertEqual(ref_hash, h1.geohash[:len(ref_hash)])
ref_hash = "9vk1mfq8jx0c8e0386z6"
h1 = City.objects.annotate(geohash=functions.GeoHash("point")).get(
name="Houston"
)
h2 = City.objects.annotate(geohash=functions.GeoHash("point", precision=5)).get(
name="Houston"
)
self.assertEqual(ref_hash, h1.geohash[: len(ref_hash)])
self.assertEqual(ref_hash[:5], h2.geohash)
@skipUnlessDBFeature('has_GeometryDistance_function')
@skipUnlessDBFeature("has_GeometryDistance_function")
def test_geometry_distance(self):
point = Point(-90, 40, srid=4326)
qs = City.objects.annotate(distance=functions.GeometryDistance('point', point)).order_by('distance')
qs = City.objects.annotate(
distance=functions.GeometryDistance("point", point)
).order_by("distance")
distances = (
2.99091995527296,
5.33507274054713,
@@ -307,7 +360,7 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
@skipUnlessDBFeature("has_Intersection_function")
def test_intersection(self):
geom = Point(5, 23, srid=4326)
qs = Country.objects.annotate(inter=functions.Intersection('mpoly', geom))
qs = Country.objects.annotate(inter=functions.Intersection("mpoly", geom))
for c in qs:
if connection.features.empty_intersection_returns_none:
self.assertIsNone(c.inter)
@@ -316,12 +369,20 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
@skipUnlessDBFeature("has_IsValid_function")
def test_isvalid(self):
valid_geom = fromstr('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))')
invalid_geom = fromstr('POLYGON((0 0, 0 1, 1 1, 1 0, 1 1, 1 0, 0 0))')
State.objects.create(name='valid', poly=valid_geom)
State.objects.create(name='invalid', poly=invalid_geom)
valid = State.objects.filter(name='valid').annotate(isvalid=functions.IsValid('poly')).first()
invalid = State.objects.filter(name='invalid').annotate(isvalid=functions.IsValid('poly')).first()
valid_geom = fromstr("POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))")
invalid_geom = fromstr("POLYGON((0 0, 0 1, 1 1, 1 0, 1 1, 1 0, 0 0))")
State.objects.create(name="valid", poly=valid_geom)
State.objects.create(name="invalid", poly=invalid_geom)
valid = (
State.objects.filter(name="valid")
.annotate(isvalid=functions.IsValid("poly"))
.first()
)
invalid = (
State.objects.filter(name="invalid")
.annotate(isvalid=functions.IsValid("poly"))
.first()
)
self.assertIs(valid.isvalid, True)
self.assertIs(invalid.isvalid, False)
@@ -329,12 +390,14 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
def test_area_with_regular_aggregate(self):
# Create projected country objects, for this test to work on all backends.
for c in Country.objects.all():
CountryWebMercator.objects.create(name=c.name, mpoly=c.mpoly.transform(3857, clone=True))
CountryWebMercator.objects.create(
name=c.name, mpoly=c.mpoly.transform(3857, clone=True)
)
# Test in projected coordinate system
qs = CountryWebMercator.objects.annotate(area_sum=Sum(functions.Area('mpoly')))
qs = CountryWebMercator.objects.annotate(area_sum=Sum(functions.Area("mpoly")))
# Some backends (e.g. Oracle) cannot group by multipolygon values, so
# defer such fields in the aggregation query.
for c in qs.defer('mpoly'):
for c in qs.defer("mpoly"):
result = c.area_sum
# If the result is a measure object, get value.
if isinstance(result, Area):
@@ -348,43 +411,68 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
CountryWebMercator(name=c.name, mpoly=c.mpoly.transform(3857, clone=True))
for c in Country.objects.all()
)
qs = CountryWebMercator.objects.annotate(area=functions.Area('mpoly'))
self.assertEqual(qs.get(area__lt=Area(sq_km=500000)), CountryWebMercator.objects.get(name='New Zealand'))
qs = CountryWebMercator.objects.annotate(area=functions.Area("mpoly"))
self.assertEqual(
qs.get(area__lt=Area(sq_km=500000)),
CountryWebMercator.objects.get(name="New Zealand"),
)
with self.assertRaisesMessage(ValueError, 'AreaField only accepts Area measurement objects.'):
with self.assertRaisesMessage(
ValueError, "AreaField only accepts Area measurement objects."
):
qs.get(area__lt=500000)
@skipUnlessDBFeature("has_LineLocatePoint_function")
def test_line_locate_point(self):
pos_expr = functions.LineLocatePoint(LineString((0, 0), (0, 3), srid=4326), Point(0, 1, srid=4326))
self.assertAlmostEqual(State.objects.annotate(pos=pos_expr).first().pos, 0.3333333)
pos_expr = functions.LineLocatePoint(
LineString((0, 0), (0, 3), srid=4326), Point(0, 1, srid=4326)
)
self.assertAlmostEqual(
State.objects.annotate(pos=pos_expr).first().pos, 0.3333333
)
@skipUnlessDBFeature("has_MakeValid_function")
def test_make_valid(self):
invalid_geom = fromstr('POLYGON((0 0, 0 1, 1 1, 1 0, 1 1, 1 0, 0 0))')
State.objects.create(name='invalid', poly=invalid_geom)
invalid = State.objects.filter(name='invalid').annotate(repaired=functions.MakeValid('poly')).first()
invalid_geom = fromstr("POLYGON((0 0, 0 1, 1 1, 1 0, 1 1, 1 0, 0 0))")
State.objects.create(name="invalid", poly=invalid_geom)
invalid = (
State.objects.filter(name="invalid")
.annotate(repaired=functions.MakeValid("poly"))
.first()
)
self.assertIs(invalid.repaired.valid, True)
self.assertTrue(invalid.repaired.equals(fromstr('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))', srid=invalid.poly.srid)))
self.assertTrue(
invalid.repaired.equals(
fromstr("POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))", srid=invalid.poly.srid)
)
)
@skipUnlessDBFeature('has_MakeValid_function')
@skipUnlessDBFeature("has_MakeValid_function")
def test_make_valid_multipolygon(self):
invalid_geom = fromstr(
'POLYGON((0 0, 0 1 , 1 1 , 1 0, 0 0), (10 0, 10 1, 11 1, 11 0, 10 0))'
"POLYGON((0 0, 0 1 , 1 1 , 1 0, 0 0), (10 0, 10 1, 11 1, 11 0, 10 0))"
)
State.objects.create(name="invalid", poly=invalid_geom)
invalid = (
State.objects.filter(name="invalid")
.annotate(
repaired=functions.MakeValid("poly"),
)
.get()
)
State.objects.create(name='invalid', poly=invalid_geom)
invalid = State.objects.filter(name='invalid').annotate(
repaired=functions.MakeValid('poly'),
).get()
self.assertIs(invalid.repaired.valid, True)
self.assertTrue(invalid.repaired.equals(fromstr(
'MULTIPOLYGON (((0 0, 0 1, 1 1, 1 0, 0 0)), '
'((10 0, 10 1, 11 1, 11 0, 10 0)))',
srid=invalid.poly.srid,
)))
self.assertTrue(
invalid.repaired.equals(
fromstr(
"MULTIPOLYGON (((0 0, 0 1, 1 1, 1 0, 0 0)), "
"((10 0, 10 1, 11 1, 11 0, 10 0)))",
srid=invalid.poly.srid,
)
)
)
self.assertEqual(len(invalid.repaired), 2)
@skipUnlessDBFeature('has_MakeValid_function')
@skipUnlessDBFeature("has_MakeValid_function")
def test_make_valid_output_field(self):
# output_field is GeometryField instance because different geometry
# types can be returned.
@@ -396,17 +484,21 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
@skipUnlessDBFeature("has_MemSize_function")
def test_memsize(self):
ptown = City.objects.annotate(size=functions.MemSize('point')).get(name='Pueblo')
ptown = City.objects.annotate(size=functions.MemSize("point")).get(
name="Pueblo"
)
# Exact value depends on database and version.
self.assertTrue(20 <= ptown.size <= 105)
@skipUnlessDBFeature("has_NumGeom_function")
def test_num_geom(self):
# Both 'countries' only have two geometries.
for c in Country.objects.annotate(num_geom=functions.NumGeometries('mpoly')):
for c in Country.objects.annotate(num_geom=functions.NumGeometries("mpoly")):
self.assertEqual(2, c.num_geom)
qs = City.objects.filter(point__isnull=False).annotate(num_geom=functions.NumGeometries('point'))
qs = City.objects.filter(point__isnull=False).annotate(
num_geom=functions.NumGeometries("point")
)
for city in qs:
# The results for the number of geometries on non-collections
# depends on the database.
@@ -418,10 +510,10 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
@skipUnlessDBFeature("has_NumPoint_function")
def test_num_points(self):
coords = [(-95.363151, 29.763374), (-95.448601, 29.713803)]
Track.objects.create(name='Foo', line=LineString(coords))
qs = Track.objects.annotate(num_points=functions.NumPoints('line'))
Track.objects.create(name="Foo", line=LineString(coords))
qs = Track.objects.annotate(num_points=functions.NumPoints("line"))
self.assertEqual(qs.first().num_points, 2)
mpoly_qs = Country.objects.annotate(num_points=functions.NumPoints('mpoly'))
mpoly_qs = Country.objects.annotate(num_points=functions.NumPoints("mpoly"))
if not connection.features.supports_num_points_poly:
for c in mpoly_qs:
self.assertIsNone(c.num_points)
@@ -430,20 +522,24 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
for c in mpoly_qs:
self.assertEqual(c.mpoly.num_points, c.num_points)
for c in City.objects.annotate(num_points=functions.NumPoints('point')):
for c in City.objects.annotate(num_points=functions.NumPoints("point")):
self.assertEqual(c.num_points, 1)
@skipUnlessDBFeature("has_PointOnSurface_function")
def test_point_on_surface(self):
qs = Country.objects.annotate(point_on_surface=functions.PointOnSurface('mpoly'))
qs = Country.objects.annotate(
point_on_surface=functions.PointOnSurface("mpoly")
)
for country in qs:
self.assertTrue(country.mpoly.intersection(country.point_on_surface))
@skipUnlessDBFeature("has_Reverse_function")
def test_reverse_geom(self):
coords = [(-95.363151, 29.763374), (-95.448601, 29.713803)]
Track.objects.create(name='Foo', line=LineString(coords))
track = Track.objects.annotate(reverse_geom=functions.Reverse('line')).get(name='Foo')
Track.objects.create(name="Foo", line=LineString(coords))
track = Track.objects.annotate(reverse_geom=functions.Reverse("line")).get(
name="Foo"
)
coords.reverse()
self.assertEqual(tuple(coords), track.reverse_geom.coords)
@@ -451,7 +547,7 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
def test_scale(self):
xfac, yfac = 2, 3
tol = 5 # The low precision tolerance is for SpatiaLite
qs = Country.objects.annotate(scaled=functions.Scale('mpoly', xfac, yfac))
qs = Country.objects.annotate(scaled=functions.Scale("mpoly", xfac, yfac))
for country in qs:
for p1, p2 in zip(country.mpoly, country.scaled):
for r1, r2 in zip(p1, p2):
@@ -459,7 +555,9 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
self.assertAlmostEqual(c1[0] * xfac, c2[0], tol)
self.assertAlmostEqual(c1[1] * yfac, c2[1], tol)
# Test float/Decimal values
qs = Country.objects.annotate(scaled=functions.Scale('mpoly', 1.5, Decimal('2.5')))
qs = Country.objects.annotate(
scaled=functions.Scale("mpoly", 1.5, Decimal("2.5"))
)
self.assertGreater(qs[0].scaled.area, qs[0].mpoly.area)
@skipUnlessDBFeature("has_SnapToGrid_function")
@@ -467,22 +565,24 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
# Let's try and break snap_to_grid() with bad combinations of arguments.
for bad_args in ((), range(3), range(5)):
with self.assertRaises(ValueError):
Country.objects.annotate(snap=functions.SnapToGrid('mpoly', *bad_args))
for bad_args in (('1.0',), (1.0, None), tuple(map(str, range(4)))):
Country.objects.annotate(snap=functions.SnapToGrid("mpoly", *bad_args))
for bad_args in (("1.0",), (1.0, None), tuple(map(str, range(4)))):
with self.assertRaises(TypeError):
Country.objects.annotate(snap=functions.SnapToGrid('mpoly', *bad_args))
Country.objects.annotate(snap=functions.SnapToGrid("mpoly", *bad_args))
# Boundary for San Marino, courtesy of Bjorn Sandvik of thematicmapping.org
# from the world borders dataset he provides.
wkt = ('MULTIPOLYGON(((12.41580 43.95795,12.45055 43.97972,12.45389 43.98167,'
'12.46250 43.98472,12.47167 43.98694,12.49278 43.98917,'
'12.50555 43.98861,12.51000 43.98694,12.51028 43.98277,'
'12.51167 43.94333,12.51056 43.93916,12.49639 43.92333,'
'12.49500 43.91472,12.48778 43.90583,12.47444 43.89722,'
'12.46472 43.89555,12.45917 43.89611,12.41639 43.90472,'
'12.41222 43.90610,12.40782 43.91366,12.40389 43.92667,'
'12.40500 43.94833,12.40889 43.95499,12.41580 43.95795)))')
Country.objects.create(name='San Marino', mpoly=fromstr(wkt))
wkt = (
"MULTIPOLYGON(((12.41580 43.95795,12.45055 43.97972,12.45389 43.98167,"
"12.46250 43.98472,12.47167 43.98694,12.49278 43.98917,"
"12.50555 43.98861,12.51000 43.98694,12.51028 43.98277,"
"12.51167 43.94333,12.51056 43.93916,12.49639 43.92333,"
"12.49500 43.91472,12.48778 43.90583,12.47444 43.89722,"
"12.46472 43.89555,12.45917 43.89611,12.41639 43.90472,"
"12.41222 43.90610,12.40782 43.91366,12.40389 43.92667,"
"12.40500 43.94833,12.40889 43.95499,12.41580 43.95795)))"
)
Country.objects.create(name="San Marino", mpoly=fromstr(wkt))
# Because floating-point arithmetic isn't exact, we set a tolerance
# to pass into GEOS `equals_exact`.
@@ -490,60 +590,70 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
# SELECT AsText(ST_SnapToGrid("geoapp_country"."mpoly", 0.1)) FROM "geoapp_country"
# WHERE "geoapp_country"."name" = 'San Marino';
ref = fromstr('MULTIPOLYGON(((12.4 44,12.5 44,12.5 43.9,12.4 43.9,12.4 44)))')
ref = fromstr("MULTIPOLYGON(((12.4 44,12.5 44,12.5 43.9,12.4 43.9,12.4 44)))")
self.assertTrue(
ref.equals_exact(
Country.objects.annotate(
snap=functions.SnapToGrid('mpoly', 0.1)
).get(name='San Marino').snap,
tol
Country.objects.annotate(snap=functions.SnapToGrid("mpoly", 0.1))
.get(name="San Marino")
.snap,
tol,
)
)
# SELECT AsText(ST_SnapToGrid("geoapp_country"."mpoly", 0.05, 0.23)) FROM "geoapp_country"
# WHERE "geoapp_country"."name" = 'San Marino';
ref = fromstr('MULTIPOLYGON(((12.4 43.93,12.45 43.93,12.5 43.93,12.45 43.93,12.4 43.93)))')
ref = fromstr(
"MULTIPOLYGON(((12.4 43.93,12.45 43.93,12.5 43.93,12.45 43.93,12.4 43.93)))"
)
self.assertTrue(
ref.equals_exact(
Country.objects.annotate(
snap=functions.SnapToGrid('mpoly', 0.05, 0.23)
).get(name='San Marino').snap,
tol
Country.objects.annotate(snap=functions.SnapToGrid("mpoly", 0.05, 0.23))
.get(name="San Marino")
.snap,
tol,
)
)
# SELECT AsText(ST_SnapToGrid("geoapp_country"."mpoly", 0.5, 0.17, 0.05, 0.23)) FROM "geoapp_country"
# WHERE "geoapp_country"."name" = 'San Marino';
ref = fromstr(
'MULTIPOLYGON(((12.4 43.87,12.45 43.87,12.45 44.1,12.5 44.1,12.5 43.87,12.45 43.87,12.4 43.87)))'
"MULTIPOLYGON(((12.4 43.87,12.45 43.87,12.45 44.1,12.5 44.1,12.5 43.87,12.45 43.87,12.4 43.87)))"
)
self.assertTrue(
ref.equals_exact(
Country.objects.annotate(
snap=functions.SnapToGrid('mpoly', 0.05, 0.23, 0.5, 0.17)
).get(name='San Marino').snap,
tol
snap=functions.SnapToGrid("mpoly", 0.05, 0.23, 0.5, 0.17)
)
.get(name="San Marino")
.snap,
tol,
)
)
@skipUnlessDBFeature("has_SymDifference_function")
def test_sym_difference(self):
geom = Point(5, 23, srid=4326)
qs = Country.objects.annotate(sym_difference=functions.SymDifference('mpoly', geom))
qs = Country.objects.annotate(
sym_difference=functions.SymDifference("mpoly", geom)
)
# Oracle does something screwy with the Texas geometry.
if connection.ops.oracle:
qs = qs.exclude(name='Texas')
qs = qs.exclude(name="Texas")
for country in qs:
self.assertTrue(country.mpoly.sym_difference(geom).equals(country.sym_difference))
self.assertTrue(
country.mpoly.sym_difference(geom).equals(country.sym_difference)
)
@skipUnlessDBFeature("has_Transform_function")
def test_transform(self):
# Pre-transformed points for Houston and Pueblo.
ptown = fromstr('POINT(992363.390841912 481455.395105533)', srid=2774)
ptown = fromstr("POINT(992363.390841912 481455.395105533)", srid=2774)
# Asserting the result of the transform operation with the values in
# the pre-transformed points.
h = City.objects.annotate(pt=functions.Transform('point', ptown.srid)).get(name='Pueblo')
h = City.objects.annotate(pt=functions.Transform("point", ptown.srid)).get(
name="Pueblo"
)
self.assertEqual(2774, h.pt.srid)
# Precision is low due to version variations in PROJ and GDAL.
self.assertLess(ptown.x - h.pt.x, 1)
@@ -552,7 +662,9 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
@skipUnlessDBFeature("has_Translate_function")
def test_translate(self):
xfac, yfac = 5, -23
qs = Country.objects.annotate(translated=functions.Translate('mpoly', xfac, yfac))
qs = Country.objects.annotate(
translated=functions.Translate("mpoly", xfac, yfac)
)
for c in qs:
for p1, p2 in zip(c.mpoly, c.translated):
for r1, r2 in zip(p1, p2):
@@ -563,15 +675,18 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
# Some combined function tests
@skipUnlessDBFeature(
"has_Difference_function", "has_Intersection_function",
"has_SymDifference_function", "has_Union_function")
"has_Difference_function",
"has_Intersection_function",
"has_SymDifference_function",
"has_Union_function",
)
def test_diff_intersection_union(self):
geom = Point(5, 23, srid=4326)
qs = Country.objects.all().annotate(
difference=functions.Difference('mpoly', geom),
sym_difference=functions.SymDifference('mpoly', geom),
union=functions.Union('mpoly', geom),
intersection=functions.Intersection('mpoly', geom),
difference=functions.Difference("mpoly", geom),
sym_difference=functions.SymDifference("mpoly", geom),
union=functions.Union("mpoly", geom),
intersection=functions.Intersection("mpoly", geom),
)
if connection.ops.oracle:
@@ -593,18 +708,36 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
"""Union with all combinations of geometries/geometry fields."""
geom = Point(-95.363151, 29.763374, srid=4326)
union = City.objects.annotate(union=functions.Union('point', geom)).get(name='Dallas').union
expected = fromstr('MULTIPOINT(-96.801611 32.782057,-95.363151 29.763374)', srid=4326)
union = (
City.objects.annotate(union=functions.Union("point", geom))
.get(name="Dallas")
.union
)
expected = fromstr(
"MULTIPOINT(-96.801611 32.782057,-95.363151 29.763374)", srid=4326
)
self.assertTrue(expected.equals(union))
union = City.objects.annotate(union=functions.Union(geom, 'point')).get(name='Dallas').union
union = (
City.objects.annotate(union=functions.Union(geom, "point"))
.get(name="Dallas")
.union
)
self.assertTrue(expected.equals(union))
union = City.objects.annotate(union=functions.Union('point', 'point')).get(name='Dallas').union
expected = GEOSGeometry('POINT(-96.801611 32.782057)', srid=4326)
union = (
City.objects.annotate(union=functions.Union("point", "point"))
.get(name="Dallas")
.union
)
expected = GEOSGeometry("POINT(-96.801611 32.782057)", srid=4326)
self.assertTrue(expected.equals(union))
union = City.objects.annotate(union=functions.Union(geom, geom)).get(name='Dallas').union
union = (
City.objects.annotate(union=functions.Union(geom, geom))
.get(name="Dallas")
.union
)
self.assertTrue(geom.equals(union))
@skipUnlessDBFeature("has_Union_function", "has_Transform_function")
@@ -614,24 +747,28 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
geom_3857 = geom.transform(3857, clone=True)
tol = 0.001
for city in City.objects.annotate(union=functions.Union('point', geom_3857)):
for city in City.objects.annotate(union=functions.Union("point", geom_3857)):
expected = city.point | geom
self.assertTrue(city.union.equals_exact(expected, tol))
self.assertEqual(city.union.srid, 4326)
for city in City.objects.annotate(union=functions.Union(geom_3857, 'point')):
for city in City.objects.annotate(union=functions.Union(geom_3857, "point")):
expected = geom_3857 | city.point.transform(3857, clone=True)
self.assertTrue(expected.equals_exact(city.union, tol))
self.assertEqual(city.union.srid, 3857)
def test_argument_validation(self):
with self.assertRaisesMessage(ValueError, 'SRID is required for all geometries.'):
with self.assertRaisesMessage(
ValueError, "SRID is required for all geometries."
):
City.objects.annotate(geo=functions.GeoFunc(Point(1, 1)))
msg = 'GeoFunc function requires a GeometryField in position 1, got CharField.'
msg = "GeoFunc function requires a GeometryField in position 1, got CharField."
with self.assertRaisesMessage(TypeError, msg):
City.objects.annotate(geo=functions.GeoFunc('name'))
City.objects.annotate(geo=functions.GeoFunc("name"))
msg = 'GeoFunc function requires a geometric argument in position 1.'
msg = "GeoFunc function requires a geometric argument in position 1."
with self.assertRaisesMessage(TypeError, msg):
City.objects.annotate(union=functions.GeoFunc(1, 'point')).get(name='Dallas')
City.objects.annotate(union=functions.GeoFunc(1, "point")).get(
name="Dallas"
)

View File

@@ -15,9 +15,9 @@ class SchemaIndexesTests(TransactionTestCase):
with connection.cursor() as cursor:
constraints = connection.introspection.get_constraints(cursor, table)
return {
name: constraint['columns']
name: constraint["columns"]
for name, constraint in constraints.items()
if constraint['index']
if constraint["index"]
}
def has_spatial_indexes(self, table):
@@ -32,31 +32,31 @@ class SchemaIndexesTests(TransactionTestCase):
def test_using_sql(self):
if not connection.ops.postgis:
self.skipTest('This is a PostGIS-specific test.')
index = Index(fields=['point'])
self.skipTest("This is a PostGIS-specific test.")
index = Index(fields=["point"])
editor = connection.schema_editor()
self.assertIn(
'%s USING ' % editor.quote_name(City._meta.db_table),
"%s USING " % editor.quote_name(City._meta.db_table),
str(index.create_sql(City, editor)),
)
@isolate_apps('gis_tests.geoapp')
@isolate_apps("gis_tests.geoapp")
def test_namespaced_db_table(self):
if not connection.ops.postgis:
self.skipTest('PostGIS-specific test.')
self.skipTest("PostGIS-specific test.")
class SchemaCity(models.Model):
point = models.PointField()
class Meta:
app_label = 'geoapp'
app_label = "geoapp"
db_table = 'django_schema"."geoapp_schema_city'
index = Index(fields=['point'])
index = Index(fields=["point"])
editor = connection.schema_editor()
create_index_sql = str(index.create_sql(SchemaCity, editor))
self.assertIn(
'%s USING ' % editor.quote_name(SchemaCity._meta.db_table),
"%s USING " % editor.quote_name(SchemaCity._meta.db_table),
create_index_sql,
)
self.assertIn(
@@ -66,12 +66,12 @@ class SchemaIndexesTests(TransactionTestCase):
def test_index_name(self):
if not self.has_spatial_indexes(City._meta.db_table):
self.skipTest('Spatial indexes in Meta.indexes are not supported.')
index_name = 'custom_point_index_name'
index = Index(fields=['point'], name=index_name)
self.skipTest("Spatial indexes in Meta.indexes are not supported.")
index_name = "custom_point_index_name"
index = Index(fields=["point"], name=index_name)
with connection.schema_editor() as editor:
editor.add_index(City, index)
indexes = self.get_indexes(City._meta.db_table)
self.assertIn(index_name, indexes)
self.assertEqual(indexes[index_name], ['point'])
self.assertEqual(indexes[index_name], ["point"])
editor.remove_index(City, index)

View File

@@ -9,20 +9,20 @@ from .models import City, PennsylvaniaCity, State, Truth
class GeoRegressionTests(TestCase):
fixtures = ['initial']
fixtures = ["initial"]
def test_update(self):
"Testing QuerySet.update() (#10411)."
pueblo = City.objects.get(name='Pueblo')
pueblo = City.objects.get(name="Pueblo")
bak = pueblo.point.clone()
pueblo.point.y += 0.005
pueblo.point.x += 0.005
City.objects.filter(name='Pueblo').update(point=pueblo.point)
City.objects.filter(name="Pueblo").update(point=pueblo.point)
pueblo.refresh_from_db()
self.assertAlmostEqual(bak.y + 0.005, pueblo.point.y, 6)
self.assertAlmostEqual(bak.x + 0.005, pueblo.point.x, 6)
City.objects.filter(name='Pueblo').update(point=bak)
City.objects.filter(name="Pueblo").update(point=bak)
pueblo.refresh_from_db()
self.assertAlmostEqual(bak.y, pueblo.point.y, 6)
self.assertAlmostEqual(bak.x, pueblo.point.x, 6)
@@ -30,45 +30,61 @@ class GeoRegressionTests(TestCase):
def test_kmz(self):
"Testing `render_to_kmz` with non-ASCII data. See #11624."
name = "Åland Islands"
places = [{
'name': name,
'description': name,
'kml': '<Point><coordinates>5.0,23.0</coordinates></Point>'
}]
render_to_kmz('gis/kml/placemarks.kml', {'places': places})
places = [
{
"name": name,
"description": name,
"kml": "<Point><coordinates>5.0,23.0</coordinates></Point>",
}
]
render_to_kmz("gis/kml/placemarks.kml", {"places": places})
@skipUnlessDBFeature("supports_extent_aggr")
def test_extent(self):
"Testing `extent` on a table with a single point. See #11827."
pnt = City.objects.get(name='Pueblo').point
pnt = City.objects.get(name="Pueblo").point
ref_ext = (pnt.x, pnt.y, pnt.x, pnt.y)
extent = City.objects.filter(name='Pueblo').aggregate(Extent('point'))['point__extent']
extent = City.objects.filter(name="Pueblo").aggregate(Extent("point"))[
"point__extent"
]
for ref_val, val in zip(ref_ext, extent):
self.assertAlmostEqual(ref_val, val, 4)
def test_unicode_date(self):
"Testing dates are converted properly, even on SpatiaLite. See #16408."
founded = datetime(1857, 5, 23)
PennsylvaniaCity.objects.create(name='Mansfield', county='Tioga', point='POINT(-77.071445 41.823881)',
founded=founded)
self.assertEqual(founded, PennsylvaniaCity.objects.datetimes('founded', 'day')[0])
self.assertEqual(founded, PennsylvaniaCity.objects.aggregate(Min('founded'))['founded__min'])
PennsylvaniaCity.objects.create(
name="Mansfield",
county="Tioga",
point="POINT(-77.071445 41.823881)",
founded=founded,
)
self.assertEqual(
founded, PennsylvaniaCity.objects.datetimes("founded", "day")[0]
)
self.assertEqual(
founded, PennsylvaniaCity.objects.aggregate(Min("founded"))["founded__min"]
)
def test_empty_count(self):
"Testing that PostGISAdapter.__eq__ does check empty strings. See #13670."
# contrived example, but need a geo lookup paired with an id__in lookup
pueblo = City.objects.get(name='Pueblo')
pueblo = City.objects.get(name="Pueblo")
state = State.objects.filter(poly__contains=pueblo.point)
cities_within_state = City.objects.filter(id__in=state)
# .count() should not throw TypeError in __eq__
self.assertEqual(cities_within_state.count(), 1)
@skipUnlessDBFeature('allows_group_by_lob')
@skipUnlessDBFeature("allows_group_by_lob")
def test_defer_or_only_with_annotate(self):
"Regression for #16409. Make sure defer() and only() work with annotate()"
self.assertIsInstance(list(City.objects.annotate(Count('point')).defer('name')), list)
self.assertIsInstance(list(City.objects.annotate(Count('point')).only('name')), list)
self.assertIsInstance(
list(City.objects.annotate(Count("point")).defer("name")), list
)
self.assertIsInstance(
list(City.objects.annotate(Count("point")).only("name")), list
)
def test_boolean_conversion(self):
"Testing Boolean value conversion with the spatial backend, see #15169."

View File

@@ -8,7 +8,7 @@ from .models import City, MultiFields, PennsylvaniaCity
class GeoJSONSerializerTests(TestCase):
fixtures = ['initial']
fixtures = ["initial"]
def test_builtin_serializers(self):
"""
@@ -17,17 +17,17 @@ class GeoJSONSerializerTests(TestCase):
all_formats = set(serializers.get_serializer_formats())
public_formats = set(serializers.get_public_serializer_formats())
self.assertIn('geojson', all_formats),
self.assertIn('geojson', public_formats)
self.assertIn("geojson", all_formats),
self.assertIn("geojson", public_formats)
def test_serialization_base(self):
geojson = serializers.serialize('geojson', City.objects.all().order_by('name'))
geojson = serializers.serialize("geojson", City.objects.all().order_by("name"))
geodata = json.loads(geojson)
self.assertEqual(len(geodata['features']), len(City.objects.all()))
self.assertEqual(geodata['features'][0]['geometry']['type'], 'Point')
self.assertEqual(geodata['features'][0]['properties']['name'], 'Chicago')
first_city = City.objects.all().order_by('name').first()
self.assertEqual(geodata['features'][0]['properties']['pk'], str(first_city.pk))
self.assertEqual(len(geodata["features"]), len(City.objects.all()))
self.assertEqual(geodata["features"][0]["geometry"]["type"], "Point")
self.assertEqual(geodata["features"][0]["properties"]["name"], "Chicago")
first_city = City.objects.all().order_by("name").first()
self.assertEqual(geodata["features"][0]["properties"]["pk"], str(first_city.pk))
def test_geometry_field_option(self):
"""
@@ -35,49 +35,56 @@ class GeoJSONSerializerTests(TestCase):
can be used to specify the field to use as the 'geometry' key.
"""
MultiFields.objects.create(
city=City.objects.first(), name='Name', point=Point(5, 23),
poly=Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))))
city=City.objects.first(),
name="Name",
point=Point(5, 23),
poly=Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))),
)
geojson = serializers.serialize('geojson', MultiFields.objects.all())
geojson = serializers.serialize("geojson", MultiFields.objects.all())
geodata = json.loads(geojson)
self.assertEqual(geodata['features'][0]['geometry']['type'], 'Point')
self.assertEqual(geodata["features"][0]["geometry"]["type"], "Point")
geojson = serializers.serialize(
'geojson',
MultiFields.objects.all(),
geometry_field='poly'
"geojson", MultiFields.objects.all(), geometry_field="poly"
)
geodata = json.loads(geojson)
self.assertEqual(geodata['features'][0]['geometry']['type'], 'Polygon')
self.assertEqual(geodata["features"][0]["geometry"]["type"], "Polygon")
# geometry_field is considered even if not in fields (#26138).
geojson = serializers.serialize(
'geojson',
"geojson",
MultiFields.objects.all(),
geometry_field='poly',
fields=('city',)
geometry_field="poly",
fields=("city",),
)
geodata = json.loads(geojson)
self.assertEqual(geodata['features'][0]['geometry']['type'], 'Polygon')
self.assertEqual(geodata["features"][0]["geometry"]["type"], "Polygon")
def test_fields_option(self):
"""
The fields option allows to define a subset of fields to be present in
the 'properties' of the generated output.
"""
PennsylvaniaCity.objects.create(name='Mansfield', county='Tioga', point='POINT(-77.071445 41.823881)')
PennsylvaniaCity.objects.create(
name="Mansfield", county="Tioga", point="POINT(-77.071445 41.823881)"
)
geojson = serializers.serialize(
'geojson', PennsylvaniaCity.objects.all(), fields=('county', 'point'),
"geojson",
PennsylvaniaCity.objects.all(),
fields=("county", "point"),
)
geodata = json.loads(geojson)
self.assertIn('county', geodata['features'][0]['properties'])
self.assertNotIn('founded', geodata['features'][0]['properties'])
self.assertNotIn('pk', geodata['features'][0]['properties'])
self.assertIn("county", geodata["features"][0]["properties"])
self.assertNotIn("founded", geodata["features"][0]["properties"])
self.assertNotIn("pk", geodata["features"][0]["properties"])
def test_srid_option(self):
geojson = serializers.serialize('geojson', City.objects.all().order_by('name'), srid=2847)
geojson = serializers.serialize(
"geojson", City.objects.all().order_by("name"), srid=2847
)
geodata = json.loads(geojson)
coordinates = geodata['features'][0]['geometry']['coordinates']
coordinates = geodata["features"][0]["geometry"]["coordinates"]
# Different PROJ versions use different transformations, all are
# correct as having a 1 meter accuracy.
self.assertAlmostEqual(coordinates[0], 1564802, -1)
@@ -88,4 +95,4 @@ class GeoJSONSerializerTests(TestCase):
GeoJSON cannot be deserialized.
"""
with self.assertRaises(serializers.base.SerializerDoesNotExist):
serializers.deserialize('geojson', '{}')
serializers.deserialize("geojson", "{}")

View File

@@ -9,10 +9,11 @@ from django.test import TestCase, modify_settings, override_settings
from .models import City, Country
@modify_settings(INSTALLED_APPS={'append': ['django.contrib.sites', 'django.contrib.sitemaps']})
@override_settings(ROOT_URLCONF='gis_tests.geoapp.urls')
@modify_settings(
INSTALLED_APPS={"append": ["django.contrib.sites", "django.contrib.sitemaps"]}
)
@override_settings(ROOT_URLCONF="gis_tests.geoapp.urls")
class GeoSitemapTest(TestCase):
@classmethod
def setUpTestData(cls):
Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
@@ -25,34 +26,46 @@ class GeoSitemapTest(TestCase):
def test_geositemap_kml(self):
"Tests KML/KMZ geographic sitemaps."
for kml_type in ('kml', 'kmz'):
doc = minidom.parseString(self.client.get('/sitemaps/%s.xml' % kml_type).content)
for kml_type in ("kml", "kmz"):
doc = minidom.parseString(
self.client.get("/sitemaps/%s.xml" % kml_type).content
)
# Ensuring the right sitemaps namespace is present.
urlset = doc.firstChild
self.assertEqual(urlset.getAttribute('xmlns'), 'http://www.sitemaps.org/schemas/sitemap/0.9')
self.assertEqual(
urlset.getAttribute("xmlns"),
"http://www.sitemaps.org/schemas/sitemap/0.9",
)
urls = urlset.getElementsByTagName('url')
urls = urlset.getElementsByTagName("url")
self.assertEqual(2, len(urls)) # Should only be 2 sitemaps.
for url in urls:
self.assertChildNodes(url, ['loc'])
self.assertChildNodes(url, ["loc"])
# Getting the relative URL since we don't have a real site.
kml_url = url.getElementsByTagName('loc')[0].childNodes[0].data.split('http://example.com')[1]
kml_url = (
url.getElementsByTagName("loc")[0]
.childNodes[0]
.data.split("http://example.com")[1]
)
if kml_type == 'kml':
if kml_type == "kml":
kml_doc = minidom.parseString(self.client.get(kml_url).content)
elif kml_type == 'kmz':
elif kml_type == "kmz":
# Have to decompress KMZ before parsing.
buf = BytesIO(self.client.get(kml_url).content)
with zipfile.ZipFile(buf) as zf:
self.assertEqual(1, len(zf.filelist))
self.assertEqual('doc.kml', zf.filelist[0].filename)
kml_doc = minidom.parseString(zf.read('doc.kml'))
self.assertEqual("doc.kml", zf.filelist[0].filename)
kml_doc = minidom.parseString(zf.read("doc.kml"))
# Ensuring the correct number of placemarks are in the KML doc.
if 'city' in kml_url:
if "city" in kml_url:
model = City
elif 'country' in kml_url:
elif "country" in kml_url:
model = Country
self.assertEqual(model.objects.count(), len(kml_doc.getElementsByTagName('Placemark')))
self.assertEqual(
model.objects.count(),
len(kml_doc.getElementsByTagName("Placemark")),
)

View File

@@ -4,8 +4,16 @@ from io import StringIO
from django.contrib.gis import gdal
from django.contrib.gis.db.models import Extent, MakeLine, Union, functions
from django.contrib.gis.geos import (
GeometryCollection, GEOSGeometry, LinearRing, LineString, MultiLineString,
MultiPoint, MultiPolygon, Point, Polygon, fromstr,
GeometryCollection,
GEOSGeometry,
LinearRing,
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
fromstr,
)
from django.core.management import call_command
from django.db import DatabaseError, NotSupportedError, connection
@@ -15,13 +23,20 @@ from django.test.utils import CaptureQueriesContext
from ..utils import skipUnlessGISLookup
from .models import (
City, Country, Feature, MinusOneSRID, MultiFields, NonConcreteModel,
PennsylvaniaCity, State, Track,
City,
Country,
Feature,
MinusOneSRID,
MultiFields,
NonConcreteModel,
PennsylvaniaCity,
State,
Track,
)
class GeoModelTest(TestCase):
fixtures = ['initial']
fixtures = ["initial"]
def test_fixtures(self):
"Testing geographic model initialization from fixtures."
@@ -34,13 +49,13 @@ class GeoModelTest(TestCase):
"Testing Lazy-Geometry support (using the GeometryProxy)."
# Testing on a Point
pnt = Point(0, 0)
nullcity = City(name='NullCity', point=pnt)
nullcity = City(name="NullCity", point=pnt)
nullcity.save()
# Making sure TypeError is thrown when trying to set with an
# incompatible type.
for bad in [5, 2.0, LineString((0, 0), (1, 1))]:
with self.assertRaisesMessage(TypeError, 'Cannot set'):
with self.assertRaisesMessage(TypeError, "Cannot set"):
nullcity.point = bad
# Now setting with a compatible GEOS Geometry, saving, and ensuring
@@ -54,15 +69,19 @@ class GeoModelTest(TestCase):
nullcity.save()
# Ensuring the point was saved correctly after saving
self.assertEqual(new, City.objects.get(name='NullCity').point)
self.assertEqual(new, City.objects.get(name="NullCity").point)
# Setting the X and Y of the Point
nullcity.point.x = 23
nullcity.point.y = 5
# Checking assignments pre & post-save.
self.assertNotEqual(Point(23, 5, srid=4326), City.objects.get(name='NullCity').point)
self.assertNotEqual(
Point(23, 5, srid=4326), City.objects.get(name="NullCity").point
)
nullcity.save()
self.assertEqual(Point(23, 5, srid=4326), City.objects.get(name='NullCity').point)
self.assertEqual(
Point(23, 5, srid=4326), City.objects.get(name="NullCity").point
)
nullcity.delete()
# Testing on a Polygon
@@ -71,18 +90,18 @@ class GeoModelTest(TestCase):
# Creating a State object using a built Polygon
ply = Polygon(shell, inner)
nullstate = State(name='NullState', poly=ply)
nullstate = State(name="NullState", poly=ply)
self.assertEqual(4326, nullstate.poly.srid) # SRID auto-set from None
nullstate.save()
ns = State.objects.get(name='NullState')
ns = State.objects.get(name="NullState")
self.assertEqual(connection.ops.Adapter._fix_polygon(ply), ns.poly)
# Testing the `ogr` and `srs` lazy-geometry properties.
self.assertIsInstance(ns.poly.ogr, gdal.OGRGeometry)
self.assertEqual(ns.poly.wkb, ns.poly.ogr.wkb)
self.assertIsInstance(ns.poly.srs, gdal.SpatialReference)
self.assertEqual('WGS 84', ns.poly.srs.name)
self.assertEqual("WGS 84", ns.poly.srs.name)
# Changing the interior ring on the poly attribute.
new_inner = LinearRing((30, 30), (30, 70), (70, 70), (70, 30), (30, 30))
@@ -92,7 +111,7 @@ class GeoModelTest(TestCase):
ns.save()
self.assertEqual(
connection.ops.Adapter._fix_polygon(ply),
State.objects.get(name='NullState').poly
State.objects.get(name="NullState").poly,
)
ns.delete()
@@ -100,7 +119,7 @@ class GeoModelTest(TestCase):
def test_lookup_insert_transform(self):
"Testing automatic transform for lookups and inserts."
# San Antonio in 'WGS84' (SRID 4326)
sa_4326 = 'POINT (-98.493183 29.424170)'
sa_4326 = "POINT (-98.493183 29.424170)"
wgs_pnt = fromstr(sa_4326, srid=4326) # Our reference point in WGS84
# San Antonio in 'WGS 84 / Pseudo-Mercator' (SRID 3857)
other_srid_pnt = wgs_pnt.transform(3857, clone=True)
@@ -111,13 +130,13 @@ class GeoModelTest(TestCase):
tx = Country.objects.get(mpoly__contains=other_srid_pnt)
else:
tx = Country.objects.get(mpoly__intersects=other_srid_pnt)
self.assertEqual('Texas', tx.name)
self.assertEqual("Texas", tx.name)
# Creating San Antonio. Remember the Alamo.
sa = City.objects.create(name='San Antonio', point=other_srid_pnt)
sa = City.objects.create(name="San Antonio", point=other_srid_pnt)
# Now verifying that San Antonio was transformed correctly
sa = City.objects.get(name='San Antonio')
sa = City.objects.get(name="San Antonio")
self.assertAlmostEqual(wgs_pnt.x, sa.point.x, 6)
self.assertAlmostEqual(wgs_pnt.y, sa.point.y, 6)
@@ -134,23 +153,31 @@ class GeoModelTest(TestCase):
def test_geometryfield(self):
"Testing the general GeometryField."
Feature(name='Point', geom=Point(1, 1)).save()
Feature(name='LineString', geom=LineString((0, 0), (1, 1), (5, 5))).save()
Feature(name='Polygon', geom=Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)))).save()
Feature(name='GeometryCollection',
geom=GeometryCollection(Point(2, 2), LineString((0, 0), (2, 2)),
Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))))).save()
Feature(name="Point", geom=Point(1, 1)).save()
Feature(name="LineString", geom=LineString((0, 0), (1, 1), (5, 5))).save()
Feature(
name="Polygon",
geom=Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))),
).save()
Feature(
name="GeometryCollection",
geom=GeometryCollection(
Point(2, 2),
LineString((0, 0), (2, 2)),
Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))),
),
).save()
f_1 = Feature.objects.get(name='Point')
f_1 = Feature.objects.get(name="Point")
self.assertIsInstance(f_1.geom, Point)
self.assertEqual((1.0, 1.0), f_1.geom.tuple)
f_2 = Feature.objects.get(name='LineString')
f_2 = Feature.objects.get(name="LineString")
self.assertIsInstance(f_2.geom, LineString)
self.assertEqual(((0.0, 0.0), (1.0, 1.0), (5.0, 5.0)), f_2.geom.tuple)
f_3 = Feature.objects.get(name='Polygon')
f_3 = Feature.objects.get(name="Polygon")
self.assertIsInstance(f_3.geom, Polygon)
f_4 = Feature.objects.get(name='GeometryCollection')
f_4 = Feature.objects.get(name="GeometryCollection")
self.assertIsInstance(f_4.geom, GeometryCollection)
self.assertEqual(f_3.geom, f_4.geom[2])
@@ -158,11 +185,15 @@ class GeoModelTest(TestCase):
def test_inherited_geofields(self):
"Database functions on inherited Geometry fields."
# Creating a Pennsylvanian city.
PennsylvaniaCity.objects.create(name='Mansfield', county='Tioga', point='POINT(-77.071445 41.823881)')
PennsylvaniaCity.objects.create(
name="Mansfield", county="Tioga", point="POINT(-77.071445 41.823881)"
)
# All transformation SQL will need to be performed on the
# _parent_ table.
qs = PennsylvaniaCity.objects.annotate(new_point=functions.Transform('point', srid=32128))
qs = PennsylvaniaCity.objects.annotate(
new_point=functions.Transform("point", srid=32128)
)
self.assertEqual(1, qs.count())
for pc in qs:
@@ -171,10 +202,12 @@ class GeoModelTest(TestCase):
def test_raw_sql_query(self):
"Testing raw SQL query."
cities1 = City.objects.all()
point_select = connection.ops.select % 'point'
cities2 = list(City.objects.raw(
'select id, name, %s as point from geoapp_city' % point_select
))
point_select = connection.ops.select % "point"
cities2 = list(
City.objects.raw(
"select id, name, %s as point from geoapp_city" % point_select
)
)
self.assertEqual(len(cities1), len(cities2))
with self.assertNumQueries(0): # Ensure point isn't deferred.
self.assertIsInstance(cities2[0].point, Point)
@@ -184,18 +217,18 @@ class GeoModelTest(TestCase):
Test a dumpdata/loaddata cycle with geographic data.
"""
out = StringIO()
original_data = list(City.objects.all().order_by('name'))
call_command('dumpdata', 'geoapp.City', stdout=out)
original_data = list(City.objects.all().order_by("name"))
call_command("dumpdata", "geoapp.City", stdout=out)
result = out.getvalue()
houston = City.objects.get(name='Houston')
houston = City.objects.get(name="Houston")
self.assertIn('"point": "%s"' % houston.point.ewkt, result)
# Reload now dumped data
with tempfile.NamedTemporaryFile(mode='w', suffix='.json') as tmp:
with tempfile.NamedTemporaryFile(mode="w", suffix=".json") as tmp:
tmp.write(result)
tmp.seek(0)
call_command('loaddata', tmp.name, verbosity=0)
self.assertEqual(original_data, list(City.objects.all().order_by('name')))
call_command("loaddata", tmp.name, verbosity=0)
self.assertEqual(original_data, list(City.objects.all().order_by("name")))
@skipUnlessDBFeature("supports_empty_geometries")
def test_empty_geometries(self):
@@ -211,7 +244,7 @@ class GeoModelTest(TestCase):
]
for klass in geometry_classes:
g = klass(srid=4326)
feature = Feature.objects.create(name='Empty %s' % klass.__name__, geom=g)
feature = Feature.objects.create(name="Empty %s" % klass.__name__, geom=g)
feature.refresh_from_db()
if klass is LinearRing:
# LinearRing isn't representable in WKB, so GEOSGeomtry.wkb
@@ -222,21 +255,21 @@ class GeoModelTest(TestCase):
class GeoLookupTest(TestCase):
fixtures = ['initial']
fixtures = ["initial"]
def test_disjoint_lookup(self):
"Testing the `disjoint` lookup type."
ptown = City.objects.get(name='Pueblo')
ptown = City.objects.get(name="Pueblo")
qs1 = City.objects.filter(point__disjoint=ptown.point)
self.assertEqual(7, qs1.count())
qs2 = State.objects.filter(poly__disjoint=ptown.point)
self.assertEqual(1, qs2.count())
self.assertEqual('Kansas', qs2[0].name)
self.assertEqual("Kansas", qs2[0].name)
def test_contains_contained_lookups(self):
"Testing the 'contained', 'contains', and 'bbcontains' lookup types."
# Getting Texas, yes we were a country -- once ;)
texas = Country.objects.get(name='Texas')
texas = Country.objects.get(name="Texas")
# Seeing what cities are in Texas, should get Houston and Dallas,
# and Oklahoma City because 'contained' only checks on the
@@ -244,69 +277,80 @@ class GeoLookupTest(TestCase):
if connection.features.supports_contained_lookup:
qs = City.objects.filter(point__contained=texas.mpoly)
self.assertEqual(3, qs.count())
cities = ['Houston', 'Dallas', 'Oklahoma City']
cities = ["Houston", "Dallas", "Oklahoma City"]
for c in qs:
self.assertIn(c.name, cities)
# Pulling out some cities.
houston = City.objects.get(name='Houston')
wellington = City.objects.get(name='Wellington')
pueblo = City.objects.get(name='Pueblo')
okcity = City.objects.get(name='Oklahoma City')
lawrence = City.objects.get(name='Lawrence')
houston = City.objects.get(name="Houston")
wellington = City.objects.get(name="Wellington")
pueblo = City.objects.get(name="Pueblo")
okcity = City.objects.get(name="Oklahoma City")
lawrence = City.objects.get(name="Lawrence")
# Now testing contains on the countries using the points for
# Houston and Wellington.
tx = Country.objects.get(mpoly__contains=houston.point) # Query w/GEOSGeometry
nz = Country.objects.get(mpoly__contains=wellington.point.hex) # Query w/EWKBHEX
self.assertEqual('Texas', tx.name)
self.assertEqual('New Zealand', nz.name)
nz = Country.objects.get(
mpoly__contains=wellington.point.hex
) # Query w/EWKBHEX
self.assertEqual("Texas", tx.name)
self.assertEqual("New Zealand", nz.name)
# Testing `contains` on the states using the point for Lawrence.
ks = State.objects.get(poly__contains=lawrence.point)
self.assertEqual('Kansas', ks.name)
self.assertEqual("Kansas", ks.name)
# Pueblo and Oklahoma City (even though OK City is within the bounding box of Texas)
# are not contained in Texas or New Zealand.
self.assertEqual(len(Country.objects.filter(mpoly__contains=pueblo.point)), 0) # Query w/GEOSGeometry object
self.assertEqual(len(Country.objects.filter(mpoly__contains=okcity.point.wkt)), 0) # Query w/WKT
self.assertEqual(
len(Country.objects.filter(mpoly__contains=pueblo.point)), 0
) # Query w/GEOSGeometry object
self.assertEqual(
len(Country.objects.filter(mpoly__contains=okcity.point.wkt)), 0
) # Query w/WKT
# OK City is contained w/in bounding box of Texas.
if connection.features.supports_bbcontains_lookup:
qs = Country.objects.filter(mpoly__bbcontains=okcity.point)
self.assertEqual(1, len(qs))
self.assertEqual('Texas', qs[0].name)
self.assertEqual("Texas", qs[0].name)
@skipUnlessDBFeature("supports_crosses_lookup")
def test_crosses_lookup(self):
Track.objects.create(
name='Line1',
line=LineString([(-95, 29), (-60, 0)])
Track.objects.create(name="Line1", line=LineString([(-95, 29), (-60, 0)]))
self.assertEqual(
Track.objects.filter(
line__crosses=LineString([(-95, 0), (-60, 29)])
).count(),
1,
)
self.assertEqual(
Track.objects.filter(line__crosses=LineString([(-95, 0), (-60, 29)])).count(),
1
)
self.assertEqual(
Track.objects.filter(line__crosses=LineString([(-95, 30), (0, 30)])).count(),
0
Track.objects.filter(
line__crosses=LineString([(-95, 30), (0, 30)])
).count(),
0,
)
@skipUnlessDBFeature("supports_isvalid_lookup")
def test_isvalid_lookup(self):
invalid_geom = fromstr('POLYGON((0 0, 0 1, 1 1, 1 0, 1 1, 1 0, 0 0))')
State.objects.create(name='invalid', poly=invalid_geom)
invalid_geom = fromstr("POLYGON((0 0, 0 1, 1 1, 1 0, 1 1, 1 0, 0 0))")
State.objects.create(name="invalid", poly=invalid_geom)
qs = State.objects.all()
if connection.ops.oracle or (connection.ops.mysql and connection.mysql_version < (8, 0, 0)):
if connection.ops.oracle or (
connection.ops.mysql and connection.mysql_version < (8, 0, 0)
):
# Kansas has adjacent vertices with distance 6.99244813842e-12
# which is smaller than the default Oracle tolerance.
# It's invalid on MySQL < 8 also.
qs = qs.exclude(name='Kansas')
self.assertEqual(State.objects.filter(name='Kansas', poly__isvalid=False).count(), 1)
qs = qs.exclude(name="Kansas")
self.assertEqual(
State.objects.filter(name="Kansas", poly__isvalid=False).count(), 1
)
self.assertEqual(qs.filter(poly__isvalid=False).count(), 1)
self.assertEqual(qs.filter(poly__isvalid=True).count(), qs.count() - 1)
@skipUnlessGISLookup('left', 'right')
@skipUnlessGISLookup("left", "right")
def test_left_right_lookups(self):
"Testing the 'left' and 'right' lookup types."
# Left: A << B => true if xmax(A) < xmin(B)
@@ -314,22 +358,28 @@ class GeoLookupTest(TestCase):
# See: BOX2D_left() and BOX2D_right() in lwgeom_box2dfloat4.c in PostGIS source.
# Getting the borders for Colorado & Kansas
co_border = State.objects.get(name='Colorado').poly
ks_border = State.objects.get(name='Kansas').poly
co_border = State.objects.get(name="Colorado").poly
ks_border = State.objects.get(name="Kansas").poly
# Note: Wellington has an 'X' value of 174, so it will not be considered
# to the left of CO.
# These cities should be strictly to the right of the CO border.
cities = ['Houston', 'Dallas', 'Oklahoma City',
'Lawrence', 'Chicago', 'Wellington']
cities = [
"Houston",
"Dallas",
"Oklahoma City",
"Lawrence",
"Chicago",
"Wellington",
]
qs = City.objects.filter(point__right=co_border)
self.assertEqual(6, len(qs))
for c in qs:
self.assertIn(c.name, cities)
# These cities should be strictly to the right of the KS border.
cities = ['Chicago', 'Wellington']
cities = ["Chicago", "Wellington"]
qs = City.objects.filter(point__right=ks_border)
self.assertEqual(2, len(qs))
for c in qs:
@@ -338,9 +388,9 @@ class GeoLookupTest(TestCase):
# Note: Wellington has an 'X' value of 174, so it will not be considered
# to the left of CO.
vic = City.objects.get(point__left=co_border)
self.assertEqual('Victoria', vic.name)
self.assertEqual("Victoria", vic.name)
cities = ['Pueblo', 'Victoria']
cities = ["Pueblo", "Victoria"]
qs = City.objects.filter(point__left=ks_border)
self.assertEqual(2, len(qs))
for c in qs:
@@ -348,32 +398,32 @@ class GeoLookupTest(TestCase):
@skipUnlessGISLookup("strictly_above", "strictly_below")
def test_strictly_above_below_lookups(self):
dallas = City.objects.get(name='Dallas')
dallas = City.objects.get(name="Dallas")
self.assertQuerysetEqual(
City.objects.filter(point__strictly_above=dallas.point).order_by('name'),
['Chicago', 'Lawrence', 'Oklahoma City', 'Pueblo', 'Victoria'],
lambda b: b.name
City.objects.filter(point__strictly_above=dallas.point).order_by("name"),
["Chicago", "Lawrence", "Oklahoma City", "Pueblo", "Victoria"],
lambda b: b.name,
)
self.assertQuerysetEqual(
City.objects.filter(point__strictly_below=dallas.point).order_by('name'),
['Houston', 'Wellington'],
lambda b: b.name
City.objects.filter(point__strictly_below=dallas.point).order_by("name"),
["Houston", "Wellington"],
lambda b: b.name,
)
def test_equals_lookups(self):
"Testing the 'same_as' and 'equals' lookup types."
pnt = fromstr('POINT (-95.363151 29.763374)', srid=4326)
pnt = fromstr("POINT (-95.363151 29.763374)", srid=4326)
c1 = City.objects.get(point=pnt)
c2 = City.objects.get(point__same_as=pnt)
c3 = City.objects.get(point__equals=pnt)
for c in [c1, c2, c3]:
self.assertEqual('Houston', c.name)
self.assertEqual("Houston", c.name)
@skipUnlessDBFeature("supports_null_geometries")
def test_null_geometries(self):
"Testing NULL geometry support, and the `isnull` lookup type."
# Creating a state with a NULL boundary.
State.objects.create(name='Puerto Rico')
State.objects.create(name="Puerto Rico")
# Querying for both NULL and Non-NULL values.
nullqs = State.objects.filter(poly__isnull=True)
@@ -381,7 +431,7 @@ class GeoLookupTest(TestCase):
# Puerto Rico should be NULL (it's a commonwealth unincorporated territory)
self.assertEqual(1, len(nullqs))
self.assertEqual('Puerto Rico', nullqs[0].name)
self.assertEqual("Puerto Rico", nullqs[0].name)
# GeometryField=None is an alias for __isnull=True.
self.assertCountEqual(State.objects.filter(poly=None), nullqs)
self.assertCountEqual(State.objects.exclude(poly=None), validqs)
@@ -389,110 +439,135 @@ class GeoLookupTest(TestCase):
# The valid states should be Colorado & Kansas
self.assertEqual(2, len(validqs))
state_names = [s.name for s in validqs]
self.assertIn('Colorado', state_names)
self.assertIn('Kansas', state_names)
self.assertIn("Colorado", state_names)
self.assertIn("Kansas", state_names)
# Saving another commonwealth w/a NULL geometry.
nmi = State.objects.create(name='Northern Mariana Islands', poly=None)
nmi = State.objects.create(name="Northern Mariana Islands", poly=None)
self.assertIsNone(nmi.poly)
# Assigning a geometry and saving -- then UPDATE back to NULL.
nmi.poly = 'POLYGON((0 0,1 0,1 1,1 0,0 0))'
nmi.poly = "POLYGON((0 0,1 0,1 1,1 0,0 0))"
nmi.save()
State.objects.filter(name='Northern Mariana Islands').update(poly=None)
self.assertIsNone(State.objects.get(name='Northern Mariana Islands').poly)
State.objects.filter(name="Northern Mariana Islands").update(poly=None)
self.assertIsNone(State.objects.get(name="Northern Mariana Islands").poly)
@skipUnlessDBFeature('supports_null_geometries', 'supports_crosses_lookup', 'supports_relate_lookup')
@skipUnlessDBFeature(
"supports_null_geometries", "supports_crosses_lookup", "supports_relate_lookup"
)
def test_null_geometries_excluded_in_lookups(self):
"""NULL features are excluded in spatial lookup functions."""
null = State.objects.create(name='NULL', poly=None)
null = State.objects.create(name="NULL", poly=None)
queries = [
('equals', Point(1, 1)),
('disjoint', Point(1, 1)),
('touches', Point(1, 1)),
('crosses', LineString((0, 0), (1, 1), (5, 5))),
('within', Point(1, 1)),
('overlaps', LineString((0, 0), (1, 1), (5, 5))),
('contains', LineString((0, 0), (1, 1), (5, 5))),
('intersects', LineString((0, 0), (1, 1), (5, 5))),
('relate', (Point(1, 1), 'T*T***FF*')),
('same_as', Point(1, 1)),
('exact', Point(1, 1)),
('coveredby', Point(1, 1)),
('covers', Point(1, 1)),
("equals", Point(1, 1)),
("disjoint", Point(1, 1)),
("touches", Point(1, 1)),
("crosses", LineString((0, 0), (1, 1), (5, 5))),
("within", Point(1, 1)),
("overlaps", LineString((0, 0), (1, 1), (5, 5))),
("contains", LineString((0, 0), (1, 1), (5, 5))),
("intersects", LineString((0, 0), (1, 1), (5, 5))),
("relate", (Point(1, 1), "T*T***FF*")),
("same_as", Point(1, 1)),
("exact", Point(1, 1)),
("coveredby", Point(1, 1)),
("covers", Point(1, 1)),
]
for lookup, geom in queries:
with self.subTest(lookup=lookup):
self.assertNotIn(null, State.objects.filter(**{'poly__%s' % lookup: geom}))
self.assertNotIn(
null, State.objects.filter(**{"poly__%s" % lookup: geom})
)
def test_wkt_string_in_lookup(self):
# Valid WKT strings don't emit error logs.
with self.assertNoLogs('django.contrib.gis', 'ERROR'):
State.objects.filter(poly__intersects='LINESTRING(0 0, 1 1, 5 5)')
with self.assertNoLogs("django.contrib.gis", "ERROR"):
State.objects.filter(poly__intersects="LINESTRING(0 0, 1 1, 5 5)")
@skipUnlessDBFeature("supports_relate_lookup")
def test_relate_lookup(self):
"Testing the 'relate' lookup type."
# To make things more interesting, we will have our Texas reference point in
# different SRIDs.
pnt1 = fromstr('POINT (649287.0363174 4177429.4494686)', srid=2847)
pnt2 = fromstr('POINT(-98.4919715741052 29.4333344025053)', srid=4326)
pnt1 = fromstr("POINT (649287.0363174 4177429.4494686)", srid=2847)
pnt2 = fromstr("POINT(-98.4919715741052 29.4333344025053)", srid=4326)
# Not passing in a geometry as first param raises a TypeError when
# initializing the QuerySet.
with self.assertRaises(ValueError):
Country.objects.filter(mpoly__relate=(23, 'foo'))
Country.objects.filter(mpoly__relate=(23, "foo"))
# Making sure the right exception is raised for the given
# bad arguments.
for bad_args, e in [((pnt1, 0), ValueError), ((pnt2, 'T*T***FF*', 0), ValueError)]:
for bad_args, e in [
((pnt1, 0), ValueError),
((pnt2, "T*T***FF*", 0), ValueError),
]:
qs = Country.objects.filter(mpoly__relate=bad_args)
with self.assertRaises(e):
qs.count()
contains_mask = 'T*T***FF*'
within_mask = 'T*F**F***'
intersects_mask = 'T********'
contains_mask = "T*T***FF*"
within_mask = "T*F**F***"
intersects_mask = "T********"
# Relate works differently on Oracle.
if connection.ops.oracle:
contains_mask = 'contains'
within_mask = 'inside'
contains_mask = "contains"
within_mask = "inside"
# TODO: This is not quite the same as the PostGIS mask above
intersects_mask = 'overlapbdyintersect'
intersects_mask = "overlapbdyintersect"
# Testing contains relation mask.
if connection.features.supports_transform:
self.assertEqual(
Country.objects.get(mpoly__relate=(pnt1, contains_mask)).name,
'Texas',
"Texas",
)
self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt2, contains_mask)).name)
self.assertEqual(
"Texas", Country.objects.get(mpoly__relate=(pnt2, contains_mask)).name
)
# Testing within relation mask.
ks = State.objects.get(name='Kansas')
self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, within_mask)).name)
ks = State.objects.get(name="Kansas")
self.assertEqual(
"Lawrence", City.objects.get(point__relate=(ks.poly, within_mask)).name
)
# Testing intersection relation mask.
if not connection.ops.oracle:
if connection.features.supports_transform:
self.assertEqual(
Country.objects.get(mpoly__relate=(pnt1, intersects_mask)).name,
'Texas',
"Texas",
)
self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt2, intersects_mask)).name)
self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, intersects_mask)).name)
self.assertEqual(
"Texas", Country.objects.get(mpoly__relate=(pnt2, intersects_mask)).name
)
self.assertEqual(
"Lawrence",
City.objects.get(point__relate=(ks.poly, intersects_mask)).name,
)
# With a complex geometry expression
mask = 'anyinteract' if connection.ops.oracle else within_mask
self.assertFalse(City.objects.exclude(point__relate=(functions.Union('point', 'point'), mask)))
mask = "anyinteract" if connection.ops.oracle else within_mask
self.assertFalse(
City.objects.exclude(
point__relate=(functions.Union("point", "point"), mask)
)
)
def test_gis_lookups_with_complex_expressions(self):
multiple_arg_lookups = {'dwithin', 'relate'} # These lookups are tested elsewhere.
multiple_arg_lookups = {
"dwithin",
"relate",
} # These lookups are tested elsewhere.
lookups = connection.ops.gis_operators.keys() - multiple_arg_lookups
self.assertTrue(lookups, 'No lookups found')
self.assertTrue(lookups, "No lookups found")
for lookup in lookups:
with self.subTest(lookup):
City.objects.filter(**{'point__' + lookup: functions.Union('point', 'point')}).exists()
City.objects.filter(
**{"point__" + lookup: functions.Union("point", "point")}
).exists()
def test_subquery_annotation(self):
multifields = MultiFields.objects.create(
@@ -501,18 +576,20 @@ class GeoLookupTest(TestCase):
poly=Polygon.from_bbox((0, 0, 2, 2)),
)
qs = MultiFields.objects.annotate(
city_point=Subquery(City.objects.filter(
id=OuterRef('city'),
).values('point')),
city_point=Subquery(
City.objects.filter(
id=OuterRef("city"),
).values("point")
),
).filter(
city_point__within=F('poly'),
city_point__within=F("poly"),
)
self.assertEqual(qs.get(), multifields)
class GeoQuerySetTest(TestCase):
# TODO: GeoQuerySet is removed, organize these test better.
fixtures = ['initial']
fixtures = ["initial"]
@skipUnlessDBFeature("supports_extent_aggr")
def test_extent(self):
@@ -522,21 +599,30 @@ class GeoQuerySetTest(TestCase):
# Reference query:
# `SELECT ST_extent(point) FROM geoapp_city WHERE (name='Houston' or name='Dallas');`
# => BOX(-96.8016128540039 29.7633724212646,-95.3631439208984 32.7820587158203)
expected = (-96.8016128540039, 29.7633724212646, -95.3631439208984, 32.782058715820)
expected = (
-96.8016128540039,
29.7633724212646,
-95.3631439208984,
32.782058715820,
)
qs = City.objects.filter(name__in=('Houston', 'Dallas'))
extent = qs.aggregate(Extent('point'))['point__extent']
qs = City.objects.filter(name__in=("Houston", "Dallas"))
extent = qs.aggregate(Extent("point"))["point__extent"]
for val, exp in zip(extent, expected):
self.assertAlmostEqual(exp, val, 4)
self.assertIsNone(City.objects.filter(name=('Smalltown')).aggregate(Extent('point'))['point__extent'])
self.assertIsNone(
City.objects.filter(name=("Smalltown")).aggregate(Extent("point"))[
"point__extent"
]
)
@skipUnlessDBFeature("supports_extent_aggr")
def test_extent_with_limit(self):
"""
Testing if extent supports limit.
"""
extent1 = City.objects.all().aggregate(Extent('point'))['point__extent']
extent2 = City.objects.all()[:3].aggregate(Extent('point'))['point__extent']
extent1 = City.objects.all().aggregate(Extent("point"))["point__extent"]
extent2 = City.objects.all()[:3].aggregate(Extent("point"))["point__extent"]
self.assertNotEqual(extent1, extent2)
def test_make_line(self):
@@ -545,90 +631,94 @@ class GeoQuerySetTest(TestCase):
"""
if not connection.features.supports_make_line_aggr:
with self.assertRaises(NotSupportedError):
City.objects.all().aggregate(MakeLine('point'))
City.objects.all().aggregate(MakeLine("point"))
return
# MakeLine on an inappropriate field returns simply None
self.assertIsNone(State.objects.aggregate(MakeLine('poly'))['poly__makeline'])
self.assertIsNone(State.objects.aggregate(MakeLine("poly"))["poly__makeline"])
# Reference query:
# SELECT AsText(ST_MakeLine(geoapp_city.point)) FROM geoapp_city;
ref_line = GEOSGeometry(
'LINESTRING(-95.363151 29.763374,-96.801611 32.782057,'
'-97.521157 34.464642,174.783117 -41.315268,-104.609252 38.255001,'
'-95.23506 38.971823,-87.650175 41.850385,-123.305196 48.462611)',
srid=4326
"LINESTRING(-95.363151 29.763374,-96.801611 32.782057,"
"-97.521157 34.464642,174.783117 -41.315268,-104.609252 38.255001,"
"-95.23506 38.971823,-87.650175 41.850385,-123.305196 48.462611)",
srid=4326,
)
# We check for equality with a tolerance of 10e-5 which is a lower bound
# of the precisions of ref_line coordinates
line = City.objects.aggregate(MakeLine('point'))['point__makeline']
line = City.objects.aggregate(MakeLine("point"))["point__makeline"]
self.assertTrue(
ref_line.equals_exact(line, tolerance=10e-5),
"%s != %s" % (ref_line, line)
ref_line.equals_exact(line, tolerance=10e-5), "%s != %s" % (ref_line, line)
)
@skipUnlessDBFeature('supports_union_aggr')
@skipUnlessDBFeature("supports_union_aggr")
def test_unionagg(self):
"""
Testing the `Union` aggregate.
"""
tx = Country.objects.get(name='Texas').mpoly
tx = Country.objects.get(name="Texas").mpoly
# Houston, Dallas -- Ordering may differ depending on backend or GEOS version.
union = GEOSGeometry('MULTIPOINT(-96.801611 32.782057,-95.363151 29.763374)')
union = GEOSGeometry("MULTIPOINT(-96.801611 32.782057,-95.363151 29.763374)")
qs = City.objects.filter(point__within=tx)
with self.assertRaises(ValueError):
qs.aggregate(Union('name'))
qs.aggregate(Union("name"))
# Using `field_name` keyword argument in one query and specifying an
# order in the other (which should not be used because this is
# an aggregate method on a spatial column)
u1 = qs.aggregate(Union('point'))['point__union']
u2 = qs.order_by('name').aggregate(Union('point'))['point__union']
u1 = qs.aggregate(Union("point"))["point__union"]
u2 = qs.order_by("name").aggregate(Union("point"))["point__union"]
self.assertTrue(union.equals(u1))
self.assertTrue(union.equals(u2))
qs = City.objects.filter(name='NotACity')
self.assertIsNone(qs.aggregate(Union('point'))['point__union'])
qs = City.objects.filter(name="NotACity")
self.assertIsNone(qs.aggregate(Union("point"))["point__union"])
@skipUnlessDBFeature('supports_union_aggr')
@skipUnlessDBFeature("supports_union_aggr")
def test_geoagg_subquery(self):
tx = Country.objects.get(name='Texas')
union = GEOSGeometry('MULTIPOINT(-96.801611 32.782057,-95.363151 29.763374)')
tx = Country.objects.get(name="Texas")
union = GEOSGeometry("MULTIPOINT(-96.801611 32.782057,-95.363151 29.763374)")
# Use distinct() to force the usage of a subquery for aggregation.
with CaptureQueriesContext(connection) as ctx:
self.assertIs(union.equals(
City.objects.filter(point__within=tx.mpoly).distinct().aggregate(
Union('point'),
)['point__union'],
), True)
self.assertIn('subquery', ctx.captured_queries[0]['sql'])
self.assertIs(
union.equals(
City.objects.filter(point__within=tx.mpoly)
.distinct()
.aggregate(
Union("point"),
)["point__union"],
),
True,
)
self.assertIn("subquery", ctx.captured_queries[0]["sql"])
@skipUnlessDBFeature('supports_tolerance_parameter')
@skipUnlessDBFeature("supports_tolerance_parameter")
def test_unionagg_tolerance(self):
City.objects.create(
point=fromstr('POINT(-96.467222 32.751389)', srid=4326),
name='Forney',
point=fromstr("POINT(-96.467222 32.751389)", srid=4326),
name="Forney",
)
tx = Country.objects.get(name='Texas').mpoly
tx = Country.objects.get(name="Texas").mpoly
# Tolerance is greater than distance between Forney and Dallas, that's
# why Dallas is ignored.
forney_houston = GEOSGeometry(
'MULTIPOINT(-95.363151 29.763374, -96.467222 32.751389)',
"MULTIPOINT(-95.363151 29.763374, -96.467222 32.751389)",
srid=4326,
)
self.assertIs(
forney_houston.equals_exact(
City.objects.filter(point__within=tx).aggregate(
Union('point', tolerance=32000),
)['point__union'],
Union("point", tolerance=32000),
)["point__union"],
tolerance=10e-6,
),
True,
)
@skipUnlessDBFeature('supports_tolerance_parameter')
@skipUnlessDBFeature("supports_tolerance_parameter")
def test_unionagg_tolerance_escaping(self):
tx = Country.objects.get(name='Texas').mpoly
tx = Country.objects.get(name="Texas").mpoly
with self.assertRaises(DatabaseError):
City.objects.filter(point__within=tx).aggregate(
Union('point', tolerance='0.05))), (((1'),
Union("point", tolerance="0.05))), (((1"),
)
def test_within_subquery(self):
@@ -637,13 +727,16 @@ class GeoQuerySetTest(TestCase):
(#14483).
"""
tex_cities = City.objects.filter(
point__within=Country.objects.filter(name='Texas').values('mpoly')).order_by('name')
self.assertEqual(list(tex_cities.values_list('name', flat=True)), ['Dallas', 'Houston'])
point__within=Country.objects.filter(name="Texas").values("mpoly")
).order_by("name")
self.assertEqual(
list(tex_cities.values_list("name", flat=True)), ["Dallas", "Houston"]
)
def test_non_concrete_field(self):
NonConcreteModel.objects.create(point=Point(0, 0), name='name')
NonConcreteModel.objects.create(point=Point(0, 0), name="name")
list(NonConcreteModel.objects.all())
def test_values_srid(self):
for c, v in zip(City.objects.all(), City.objects.values()):
self.assertEqual(c.point.srid, v['point'].srid)
self.assertEqual(c.point.srid, v["point"].srid)

View File

@@ -7,20 +7,22 @@ from .feeds import feed_dict
from .sitemaps import sitemaps
urlpatterns = [
path('feeds/<path:url>/', gis_views.feed, {'feed_dict': feed_dict}),
path("feeds/<path:url>/", gis_views.feed, {"feed_dict": feed_dict}),
]
urlpatterns += [
path('sitemaps/<section>.xml', sitemap_views.sitemap, {'sitemaps': sitemaps}),
path("sitemaps/<section>.xml", sitemap_views.sitemap, {"sitemaps": sitemaps}),
]
urlpatterns += [
path(
'sitemaps/kml/<label>/<model>/<field_name>.kml',
"sitemaps/kml/<label>/<model>/<field_name>.kml",
gis_sitemap_views.kml,
name='django.contrib.gis.sitemaps.views.kml'),
name="django.contrib.gis.sitemaps.views.kml",
),
path(
'sitemaps/kml/<label>/<model>/<field_name>.kmz',
"sitemaps/kml/<label>/<model>/<field_name>.kmz",
gis_sitemap_views.kmz,
name='django.contrib.gis.sitemaps.views.kmz'),
name="django.contrib.gis.sitemaps.views.kmz",
),
]