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:
committed by
Mariusz Felisiak
parent
f68fa8b45d
commit
9c19aff7c7
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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]),
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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/")
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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", "{}")
|
||||
|
||||
@@ -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")),
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user