diff options
author | django-bot <ops@djangoproject.com> | 2022-02-03 20:24:19 +0100 |
---|---|---|
committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2022-02-07 20:37:05 +0100 |
commit | 9c19aff7c7561e3a82978a272ecdaad40dda5c00 (patch) | |
tree | f0506b668a013d0063e5fba3dbf4863b466713ba /tests/gis_tests | |
parent | f68fa8b45dfac545cfc4111d4e52804c86db68d3 (diff) | |
download | django-9c19aff7c7561e3a82978a272ecdaad40dda5c00.tar.gz |
Refs #33476 -- Reformatted code with Black.
Diffstat (limited to 'tests/gis_tests')
59 files changed, 3959 insertions, 2588 deletions
diff --git a/tests/gis_tests/distapp/tests.py b/tests/gis_tests/distapp/tests.py index 244c99a1d7..a28a698a81 100644 --- a/tests/gis_tests/distapp/tests.py +++ b/tests/gis_tests/distapp/tests.py @@ -1,32 +1,52 @@ from django.contrib.gis.db.models.functions import ( - Area, Distance, Length, Perimeter, Transform, Union, + Area, + Distance, + Length, + Perimeter, + Transform, + Union, ) from django.contrib.gis.geos import GEOSGeometry, LineString, Point from django.contrib.gis.measure import D # alias for Distance from django.db import NotSupportedError, connection from django.db.models import ( - Case, Count, Exists, F, IntegerField, OuterRef, Q, Value, When, + Case, + Count, + Exists, + F, + IntegerField, + OuterRef, + Q, + Value, + When, ) from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature from ..utils import FuncTestMixin from .models import ( - AustraliaCity, CensusZipcode, Interstate, SouthTexasCity, SouthTexasCityFt, - SouthTexasInterstate, SouthTexasZipcode, + AustraliaCity, + CensusZipcode, + Interstate, + SouthTexasCity, + SouthTexasCityFt, + SouthTexasInterstate, + SouthTexasZipcode, ) class DistanceTest(TestCase): - fixtures = ['initial'] + fixtures = ["initial"] def setUp(self): # A point we are testing distances with -- using a WGS84 # coordinate that'll be implicitly transformed to that to # the coordinate system of the field, EPSG:32140 (Texas South Central # w/units in meters) - self.stx_pnt = GEOSGeometry('POINT (-95.370401017314293 29.704867409475465)', 4326) + self.stx_pnt = GEOSGeometry( + "POINT (-95.370401017314293 29.704867409475465)", 4326 + ) # Another one for Australia - self.au_pnt = GEOSGeometry('POINT (150.791 -34.4919)', 4326) + self.au_pnt = GEOSGeometry("POINT (150.791 -34.4919)", 4326) def get_names(self, qs): cities = [c.name for c in qs] @@ -57,8 +77,8 @@ class DistanceTest(TestCase): au_dists = [(0.5, 32000), D(km=32), D(mi=19.884)] # Expected cities for Australia and Texas. - tx_cities = ['Downtown Houston', 'Southside Place'] - au_cities = ['Mittagong', 'Shellharbour', 'Thirroul', 'Wollongong'] + tx_cities = ["Downtown Houston", "Southside Place"] + au_cities = ["Mittagong", "Shellharbour", "Thirroul", "Wollongong"] # Performing distance queries on two projected coordinate systems one # with units in meters and the other in units of U.S. survey feet. @@ -74,7 +94,9 @@ class DistanceTest(TestCase): self.assertEqual(tx_cities, self.get_names(qs)) # With a complex geometry expression - self.assertFalse(SouthTexasCity.objects.exclude(point__dwithin=(Union('point', 'point'), 0))) + self.assertFalse( + SouthTexasCity.objects.exclude(point__dwithin=(Union("point", "point"), 0)) + ) # Now performing the `dwithin` queries on a geodetic coordinate system. for dist in au_dists: @@ -89,15 +111,20 @@ class DistanceTest(TestCase): dist = dist[0] # Creating the query set. - qs = AustraliaCity.objects.order_by('name') + qs = AustraliaCity.objects.order_by("name") if type_error: # A ValueError should be raised on PostGIS when trying to # pass Distance objects into a DWithin query using a # geodetic field. with self.assertRaises(ValueError): - AustraliaCity.objects.filter(point__dwithin=(self.au_pnt, dist)).count() + AustraliaCity.objects.filter( + point__dwithin=(self.au_pnt, dist) + ).count() else: - self.assertEqual(au_cities, self.get_names(qs.filter(point__dwithin=(self.au_pnt, dist)))) + self.assertEqual( + au_cities, + self.get_names(qs.filter(point__dwithin=(self.au_pnt, dist))), + ) @skipUnlessDBFeature("supports_distances_lookups") def test_distance_lookups(self): @@ -108,20 +135,26 @@ class DistanceTest(TestCase): # (thus, Houston and Southside place will be excluded as tested in # the `test02_dwithin` above). for model in [SouthTexasCity, SouthTexasCityFt]: - stx_pnt = self.stx_pnt.transform(model._meta.get_field('point').srid, clone=True) + stx_pnt = self.stx_pnt.transform( + model._meta.get_field("point").srid, clone=True + ) qs = model.objects.filter(point__distance_gte=(stx_pnt, D(km=7))).filter( point__distance_lte=(stx_pnt, D(km=20)), ) cities = self.get_names(qs) - self.assertEqual(cities, ['Bellaire', 'Pearland', 'West University Place']) + self.assertEqual(cities, ["Bellaire", "Pearland", "West University Place"]) # Doing a distance query using Polygons instead of a Point. - z = SouthTexasZipcode.objects.get(name='77005') - qs = SouthTexasZipcode.objects.exclude(name='77005').filter(poly__distance_lte=(z.poly, D(m=275))) - self.assertEqual(['77025', '77401'], self.get_names(qs)) + z = SouthTexasZipcode.objects.get(name="77005") + qs = SouthTexasZipcode.objects.exclude(name="77005").filter( + poly__distance_lte=(z.poly, D(m=275)) + ) + self.assertEqual(["77025", "77401"], self.get_names(qs)) # If we add a little more distance 77002 should be included. - qs = SouthTexasZipcode.objects.exclude(name='77005').filter(poly__distance_lte=(z.poly, D(m=300))) - self.assertEqual(['77002', '77025', '77401'], self.get_names(qs)) + qs = SouthTexasZipcode.objects.exclude(name="77005").filter( + poly__distance_lte=(z.poly, D(m=300)) + ) + self.assertEqual(["77002", "77025", "77401"], self.get_names(qs)) @skipUnlessDBFeature("supports_distances_lookups", "supports_distance_geodetic") def test_geodetic_distance_lookups(self): @@ -130,12 +163,18 @@ class DistanceTest(TestCase): """ # Line is from Canberra to Sydney. Query is for all other cities within # a 100km of that line (which should exclude only Hobart & Adelaide). - line = GEOSGeometry('LINESTRING(144.9630 -37.8143,151.2607 -33.8870)', 4326) + line = GEOSGeometry("LINESTRING(144.9630 -37.8143,151.2607 -33.8870)", 4326) dist_qs = AustraliaCity.objects.filter(point__distance_lte=(line, D(km=100))) expected_cities = [ - 'Batemans Bay', 'Canberra', 'Hillsdale', - 'Melbourne', 'Mittagong', 'Shellharbour', - 'Sydney', 'Thirroul', 'Wollongong', + "Batemans Bay", + "Canberra", + "Hillsdale", + "Melbourne", + "Mittagong", + "Shellharbour", + "Sydney", + "Thirroul", + "Wollongong", ] if connection.ops.spatialite: # SpatiaLite is less accurate and returns 102.8km for Batemans Bay. @@ -144,141 +183,185 @@ class DistanceTest(TestCase): msg = "2, 3, or 4-element tuple required for 'distance_lte' lookup." with self.assertRaisesMessage(ValueError, msg): # Too many params. - len(AustraliaCity.objects.filter(point__distance_lte=('POINT(5 23)', D(km=100), 'spheroid', '4', None))) + len( + AustraliaCity.objects.filter( + point__distance_lte=( + "POINT(5 23)", + D(km=100), + "spheroid", + "4", + None, + ) + ) + ) with self.assertRaisesMessage(ValueError, msg): # Too few params. - len(AustraliaCity.objects.filter(point__distance_lte=('POINT(5 23)',))) + len(AustraliaCity.objects.filter(point__distance_lte=("POINT(5 23)",))) msg = "For 4-element tuples the last argument must be the 'spheroid' directive." with self.assertRaisesMessage(ValueError, msg): - len(AustraliaCity.objects.filter(point__distance_lte=('POINT(5 23)', D(km=100), 'spheroid', '4'))) + len( + AustraliaCity.objects.filter( + point__distance_lte=("POINT(5 23)", D(km=100), "spheroid", "4") + ) + ) # Getting all cities w/in 550 miles of Hobart. - hobart = AustraliaCity.objects.get(name='Hobart') - qs = AustraliaCity.objects.exclude(name='Hobart').filter(point__distance_lte=(hobart.point, D(mi=550))) + hobart = AustraliaCity.objects.get(name="Hobart") + qs = AustraliaCity.objects.exclude(name="Hobart").filter( + point__distance_lte=(hobart.point, D(mi=550)) + ) cities = self.get_names(qs) - self.assertEqual(cities, ['Batemans Bay', 'Canberra', 'Melbourne']) + self.assertEqual(cities, ["Batemans Bay", "Canberra", "Melbourne"]) # Cities that are either really close or really far from Wollongong -- # and using different units of distance. - wollongong = AustraliaCity.objects.get(name='Wollongong') + wollongong = AustraliaCity.objects.get(name="Wollongong") d1, d2 = D(yd=19500), D(nm=400) # Yards (~17km) & Nautical miles. # Normal geodetic distance lookup (uses `distance_sphere` on PostGIS. gq1 = Q(point__distance_lte=(wollongong.point, d1)) gq2 = Q(point__distance_gte=(wollongong.point, d2)) - qs1 = AustraliaCity.objects.exclude(name='Wollongong').filter(gq1 | gq2) + qs1 = AustraliaCity.objects.exclude(name="Wollongong").filter(gq1 | gq2) # Geodetic distance lookup but telling GeoDjango to use `distance_spheroid` # instead (we should get the same results b/c accuracy variance won't matter # in this test case). querysets = [qs1] if connection.features.has_DistanceSpheroid_function: - gq3 = Q(point__distance_lte=(wollongong.point, d1, 'spheroid')) - gq4 = Q(point__distance_gte=(wollongong.point, d2, 'spheroid')) - qs2 = AustraliaCity.objects.exclude(name='Wollongong').filter(gq3 | gq4) + gq3 = Q(point__distance_lte=(wollongong.point, d1, "spheroid")) + gq4 = Q(point__distance_gte=(wollongong.point, d2, "spheroid")) + qs2 = AustraliaCity.objects.exclude(name="Wollongong").filter(gq3 | gq4) querysets.append(qs2) for qs in querysets: cities = self.get_names(qs) - self.assertEqual(cities, ['Adelaide', 'Hobart', 'Shellharbour', 'Thirroul']) + self.assertEqual(cities, ["Adelaide", "Hobart", "Shellharbour", "Thirroul"]) @skipUnlessDBFeature("supports_distances_lookups") def test_distance_lookups_with_expression_rhs(self): - stx_pnt = self.stx_pnt.transform(SouthTexasCity._meta.get_field('point').srid, clone=True) + stx_pnt = self.stx_pnt.transform( + SouthTexasCity._meta.get_field("point").srid, clone=True + ) qs = SouthTexasCity.objects.filter( - point__distance_lte=(stx_pnt, F('radius')), - ).order_by('name') + point__distance_lte=(stx_pnt, F("radius")), + ).order_by("name") self.assertEqual( self.get_names(qs), - ['Bellaire', 'Downtown Houston', 'Southside Place', 'West University Place'] + [ + "Bellaire", + "Downtown Houston", + "Southside Place", + "West University Place", + ], ) # With a combined expression qs = SouthTexasCity.objects.filter( - point__distance_lte=(stx_pnt, F('radius') * 2), - ).order_by('name') + point__distance_lte=(stx_pnt, F("radius") * 2), + ).order_by("name") self.assertEqual(len(qs), 5) - self.assertIn('Pearland', self.get_names(qs)) + self.assertIn("Pearland", self.get_names(qs)) # With spheroid param if connection.features.supports_distance_geodetic: - hobart = AustraliaCity.objects.get(name='Hobart') + hobart = AustraliaCity.objects.get(name="Hobart") AustraliaCity.objects.update(ref_point=hobart.point) - for ref_point in [hobart.point, F('ref_point')]: + for ref_point in [hobart.point, F("ref_point")]: qs = AustraliaCity.objects.filter( - point__distance_lte=(ref_point, F('radius') * 70, 'spheroid'), - ).order_by('name') - self.assertEqual(self.get_names(qs), ['Canberra', 'Hobart', 'Melbourne']) + point__distance_lte=(ref_point, F("radius") * 70, "spheroid"), + ).order_by("name") + self.assertEqual( + self.get_names(qs), ["Canberra", "Hobart", "Melbourne"] + ) # With a complex geometry expression - self.assertFalse(SouthTexasCity.objects.filter(point__distance_gt=(Union('point', 'point'), 0))) + self.assertFalse( + SouthTexasCity.objects.filter( + point__distance_gt=(Union("point", "point"), 0) + ) + ) self.assertEqual( - SouthTexasCity.objects.filter(point__distance_lte=(Union('point', 'point'), 0)).count(), + SouthTexasCity.objects.filter( + point__distance_lte=(Union("point", "point"), 0) + ).count(), SouthTexasCity.objects.count(), ) - @skipUnlessDBFeature('supports_distances_lookups') + @skipUnlessDBFeature("supports_distances_lookups") def test_distance_annotation_group_by(self): stx_pnt = self.stx_pnt.transform( - SouthTexasCity._meta.get_field('point').srid, + SouthTexasCity._meta.get_field("point").srid, clone=True, ) - qs = SouthTexasCity.objects.annotate( - relative_distance=Case( - When(point__distance_lte=(stx_pnt, D(km=20)), then=Value(20)), - default=Value(100), - output_field=IntegerField(), - ), - ).values('relative_distance').annotate(count=Count('pk')) - self.assertCountEqual(qs, [ - {'relative_distance': 20, 'count': 5}, - {'relative_distance': 100, 'count': 4}, - ]) + qs = ( + SouthTexasCity.objects.annotate( + relative_distance=Case( + When(point__distance_lte=(stx_pnt, D(km=20)), then=Value(20)), + default=Value(100), + output_field=IntegerField(), + ), + ) + .values("relative_distance") + .annotate(count=Count("pk")) + ) + self.assertCountEqual( + qs, + [ + {"relative_distance": 20, "count": 5}, + {"relative_distance": 100, "count": 4}, + ], + ) def test_mysql_geodetic_distance_error(self): if not connection.ops.mysql: - self.skipTest('This is a MySQL-specific test.') - msg = 'Only numeric values of degree units are allowed on geodetic distance queries.' + self.skipTest("This is a MySQL-specific test.") + msg = "Only numeric values of degree units are allowed on geodetic distance queries." with self.assertRaisesMessage(ValueError, msg): - AustraliaCity.objects.filter(point__distance_lte=(Point(0, 0), D(m=100))).exists() + AustraliaCity.objects.filter( + point__distance_lte=(Point(0, 0), D(m=100)) + ).exists() - @skipUnlessDBFeature('supports_dwithin_lookup') + @skipUnlessDBFeature("supports_dwithin_lookup") def test_dwithin_subquery(self): """dwithin lookup in a subquery using OuterRef as a parameter.""" qs = CensusZipcode.objects.annotate( - annotated_value=Exists(SouthTexasCity.objects.filter( - point__dwithin=(OuterRef('poly'), D(m=10)), - )) + annotated_value=Exists( + SouthTexasCity.objects.filter( + point__dwithin=(OuterRef("poly"), D(m=10)), + ) + ) ).filter(annotated_value=True) - self.assertEqual(self.get_names(qs), ['77002', '77025', '77401']) + self.assertEqual(self.get_names(qs), ["77002", "77025", "77401"]) - @skipUnlessDBFeature('supports_dwithin_lookup', 'supports_dwithin_distance_expr') + @skipUnlessDBFeature("supports_dwithin_lookup", "supports_dwithin_distance_expr") def test_dwithin_with_expression_rhs(self): # LineString of Wollongong and Adelaide coords. ls = LineString(((150.902, -34.4245), (138.6, -34.9258)), srid=4326) qs = AustraliaCity.objects.filter( - point__dwithin=(ls, F('allowed_distance')), - ).order_by('name') + point__dwithin=(ls, F("allowed_distance")), + ).order_by("name") self.assertEqual( self.get_names(qs), - ['Adelaide', 'Mittagong', 'Shellharbour', 'Thirroul', 'Wollongong'], + ["Adelaide", "Mittagong", "Shellharbour", "Thirroul", "Wollongong"], ) - @skipIfDBFeature('supports_dwithin_distance_expr') + @skipIfDBFeature("supports_dwithin_distance_expr") def test_dwithin_with_expression_rhs_not_supported(self): ls = LineString(((150.902, -34.4245), (138.6, -34.9258)), srid=4326) msg = ( - 'This backend does not support expressions for specifying ' - 'distance in the dwithin lookup.' + "This backend does not support expressions for specifying " + "distance in the dwithin lookup." ) with self.assertRaisesMessage(NotSupportedError, msg): - list(AustraliaCity.objects.filter( - point__dwithin=(ls, F('allowed_distance')), - )) + list( + AustraliaCity.objects.filter( + point__dwithin=(ls, F("allowed_distance")), + ) + ) -''' +""" ============================= Distance functions on PostGIS ============================= @@ -310,20 +393,27 @@ ST_Distance(geom1, geom2, use_ellipsoid=False) | N/A | OK ( Perimeter(geom1) | OK | :-( (degrees) -''' # NOQA +""" # NOQA class DistanceFunctionsTests(FuncTestMixin, TestCase): - fixtures = ['initial'] + fixtures = ["initial"] @skipUnlessDBFeature("has_Area_function") def test_area(self): # Reference queries: # SELECT ST_Area(poly) FROM distapp_southtexaszipcode; - area_sq_m = [5437908.90234375, 10183031.4389648, 11254471.0073242, 9881708.91772461] + area_sq_m = [ + 5437908.90234375, + 10183031.4389648, + 11254471.0073242, + 9881708.91772461, + ] # Tolerance has to be lower for Oracle tol = 2 - for i, z in enumerate(SouthTexasZipcode.objects.annotate(area=Area('poly')).order_by('name')): + for i, z in enumerate( + SouthTexasZipcode.objects.annotate(area=Area("poly")).order_by("name") + ): self.assertAlmostEqual(area_sq_m[i], z.area.sq_m, tol) @skipUnlessDBFeature("has_Distance_function") @@ -332,14 +422,14 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): Test a simple distance query, with projected coordinates and without transformation. """ - lagrange = GEOSGeometry('POINT(805066.295722839 4231496.29461335)', 32140) - houston = SouthTexasCity.objects.annotate(dist=Distance('point', lagrange)).order_by('id').first() - tol = 2 if connection.ops.oracle else 5 - self.assertAlmostEqual( - houston.dist.m, - 147075.069813, - tol + lagrange = GEOSGeometry("POINT(805066.295722839 4231496.29461335)", 32140) + houston = ( + SouthTexasCity.objects.annotate(dist=Distance("point", lagrange)) + .order_by("id") + .first() ) + tol = 2 if connection.ops.oracle else 5 + self.assertAlmostEqual(houston.dist.m, 147075.069813, tol) @skipUnlessDBFeature("has_Distance_function", "has_Transform_function") def test_distance_projected(self): @@ -347,24 +437,44 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): Test the `Distance` function on projected coordinate systems. """ # The point for La Grange, TX - lagrange = GEOSGeometry('POINT(-96.876369 29.905320)', 4326) + lagrange = GEOSGeometry("POINT(-96.876369 29.905320)", 4326) # Reference distances in feet and in meters. Got these values from # using the provided raw SQL statements. # SELECT ST_Distance(point, ST_Transform(ST_GeomFromText('POINT(-96.876369 29.905320)', 4326), 32140)) # FROM distapp_southtexascity; - m_distances = [147075.069813, 139630.198056, 140888.552826, - 138809.684197, 158309.246259, 212183.594374, - 70870.188967, 165337.758878, 139196.085105] + m_distances = [ + 147075.069813, + 139630.198056, + 140888.552826, + 138809.684197, + 158309.246259, + 212183.594374, + 70870.188967, + 165337.758878, + 139196.085105, + ] # SELECT ST_Distance(point, ST_Transform(ST_GeomFromText('POINT(-96.876369 29.905320)', 4326), 2278)) # FROM distapp_southtexascityft; - ft_distances = [482528.79154625, 458103.408123001, 462231.860397575, - 455411.438904354, 519386.252102563, 696139.009211594, - 232513.278304279, 542445.630586414, 456679.155883207] + ft_distances = [ + 482528.79154625, + 458103.408123001, + 462231.860397575, + 455411.438904354, + 519386.252102563, + 696139.009211594, + 232513.278304279, + 542445.630586414, + 456679.155883207, + ] # Testing using different variations of parameters and using models # with different projected coordinate systems. - dist1 = SouthTexasCity.objects.annotate(distance=Distance('point', lagrange)).order_by('id') - dist2 = SouthTexasCityFt.objects.annotate(distance=Distance('point', lagrange)).order_by('id') + dist1 = SouthTexasCity.objects.annotate( + distance=Distance("point", lagrange) + ).order_by("id") + dist2 = SouthTexasCityFt.objects.annotate( + distance=Distance("point", lagrange) + ).order_by("id") dist_qs = [dist1, dist2] # Ensuring expected distances are returned for each distance queryset. @@ -386,10 +496,22 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): # Reference query: # SELECT ST_distance_sphere(point, ST_GeomFromText('LINESTRING(150.9020 -34.4245,150.8700 -34.5789)', 4326)) # FROM distapp_australiacity ORDER BY name; - distances = [1120954.92533513, 140575.720018241, 640396.662906304, - 60580.9693849269, 972807.955955075, 568451.8357838, - 40435.4335201384, 0, 68272.3896586844, 12375.0643697706, 0] - qs = AustraliaCity.objects.annotate(distance=Distance('point', ls)).order_by('name') + distances = [ + 1120954.92533513, + 140575.720018241, + 640396.662906304, + 60580.9693849269, + 972807.955955075, + 568451.8357838, + 40435.4335201384, + 0, + 68272.3896586844, + 12375.0643697706, + 0, + ] + qs = AustraliaCity.objects.annotate(distance=Distance("point", ls)).order_by( + "name" + ) for city, distance in zip(qs, distances): with self.subTest(city=city, distance=distance): # Testing equivalence to within a meter (kilometer on SpatiaLite). @@ -406,30 +528,46 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): # SELECT ST_distance_sphere(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326)) # FROM distapp_australiacity WHERE (NOT (id = 11)); st_distance_sphere spheroid_distances = [ - 60504.0628957201, 77023.9489850262, 49154.8867574404, - 90847.4358768573, 217402.811919332, 709599.234564757, - 640011.483550888, 7772.00667991925, 1047861.78619339, + 60504.0628957201, + 77023.9489850262, + 49154.8867574404, + 90847.4358768573, + 217402.811919332, + 709599.234564757, + 640011.483550888, + 7772.00667991925, + 1047861.78619339, 1165126.55236034, ] sphere_distances = [ - 60580.9693849267, 77144.0435286473, 49199.4415344719, - 90804.7533823494, 217713.384600405, 709134.127242793, - 639828.157159169, 7786.82949717788, 1049204.06569028, + 60580.9693849267, + 77144.0435286473, + 49199.4415344719, + 90804.7533823494, + 217713.384600405, + 709134.127242793, + 639828.157159169, + 7786.82949717788, + 1049204.06569028, 1162623.7238134, ] # Testing with spheroid distances first. - hillsdale = AustraliaCity.objects.get(name='Hillsdale') - qs = AustraliaCity.objects.exclude(id=hillsdale.id).annotate( - distance=Distance('point', hillsdale.point, spheroid=True) - ).order_by('id') + hillsdale = AustraliaCity.objects.get(name="Hillsdale") + qs = ( + AustraliaCity.objects.exclude(id=hillsdale.id) + .annotate(distance=Distance("point", hillsdale.point, spheroid=True)) + .order_by("id") + ) for i, c in enumerate(qs): with self.subTest(c=c): self.assertAlmostEqual(spheroid_distances[i], c.distance.m, tol) if connection.ops.postgis or connection.ops.spatialite: # PostGIS uses sphere-only distances by default, testing these as well. - qs = AustraliaCity.objects.exclude(id=hillsdale.id).annotate( - distance=Distance('point', hillsdale.point) - ).order_by('id') + qs = ( + AustraliaCity.objects.exclude(id=hillsdale.id) + .annotate(distance=Distance("point", hillsdale.point)) + .order_by("id") + ) for i, c in enumerate(qs): with self.subTest(c=c): self.assertAlmostEqual(sphere_distances[i], c.distance.m, tol) @@ -437,9 +575,13 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): @skipIfDBFeature("supports_distance_geodetic") @skipUnlessDBFeature("has_Distance_function") def test_distance_function_raw_result(self): - distance = Interstate.objects.annotate( - d=Distance(Point(0, 0, srid=4326), Point(0, 1, srid=4326)), - ).first().d + distance = ( + Interstate.objects.annotate( + d=Distance(Point(0, 0, srid=4326), Point(0, 1, srid=4326)), + ) + .first() + .d + ) self.assertEqual(distance, 1) @skipUnlessDBFeature("has_Distance_function") @@ -449,29 +591,37 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): ).filter(d=D(m=1)) self.assertTrue(qs.exists()) - @skipUnlessDBFeature('supports_tolerance_parameter') + @skipUnlessDBFeature("supports_tolerance_parameter") def test_distance_function_tolerance_escaping(self): - qs = Interstate.objects.annotate( - d=Distance( - Point(500, 500, srid=3857), - Point(0, 0, srid=3857), - tolerance='0.05) = 1 OR 1=1 OR (1+1', - ), - ).filter(d=D(m=1)).values('pk') - msg = 'The tolerance parameter has the wrong type' + qs = ( + Interstate.objects.annotate( + d=Distance( + Point(500, 500, srid=3857), + Point(0, 0, srid=3857), + tolerance="0.05) = 1 OR 1=1 OR (1+1", + ), + ) + .filter(d=D(m=1)) + .values("pk") + ) + msg = "The tolerance parameter has the wrong type" with self.assertRaisesMessage(TypeError, msg): qs.exists() - @skipUnlessDBFeature('supports_tolerance_parameter') + @skipUnlessDBFeature("supports_tolerance_parameter") def test_distance_function_tolerance(self): # Tolerance is greater than distance. - qs = Interstate.objects.annotate( - d=Distance( - Point(0, 0, srid=3857), - Point(1, 1, srid=3857), - tolerance=1.5, - ), - ).filter(d=0).values('pk') + qs = ( + Interstate.objects.annotate( + d=Distance( + Point(0, 0, srid=3857), + Point(1, 1, srid=3857), + tolerance=1.5, + ), + ) + .filter(d=0) + .values("pk") + ) self.assertIs(qs.exists(), True) @skipIfDBFeature("supports_distance_geodetic") @@ -480,11 +630,11 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): qs = Interstate.objects.annotate( d=Distance(Point(0, 0, srid=4326), Point(0, 1, srid=4326)), ).filter(d=D(m=1)) - msg = 'Distance measure is supplied, but units are unknown for result.' + msg = "Distance measure is supplied, but units are unknown for result." with self.assertRaisesMessage(ValueError, msg): list(qs) - @skipUnlessDBFeature("has_Distance_function", 'has_Transform_function') + @skipUnlessDBFeature("has_Distance_function", "has_Transform_function") def test_distance_transform(self): """ Test the `Distance` function used with `Transform` on a geographic field. @@ -493,7 +643,7 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): # of 77005 to 100m) -- which aren't allowed in geographic distance # queries normally, however our field has been transformed to # a non-geographic system. - z = SouthTexasZipcode.objects.get(name='77005') + z = SouthTexasZipcode.objects.get(name="77005") # Reference query: # SELECT ST_Distance(ST_Transform("distapp_censuszipcode"."poly", 32140), @@ -508,22 +658,29 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): # however. buf1 = z.poly.centroid.buffer(100) buf2 = buf1.transform(4269, clone=True) - ref_zips = ['77002', '77025', '77401'] + ref_zips = ["77002", "77025", "77401"] for buf in [buf1, buf2]: - qs = CensusZipcode.objects.exclude(name='77005').annotate( - distance=Distance(Transform('poly', 32140), buf) - ).order_by('name') + qs = ( + CensusZipcode.objects.exclude(name="77005") + .annotate(distance=Distance(Transform("poly", 32140), buf)) + .order_by("name") + ) self.assertEqual(ref_zips, sorted(c.name for c in qs)) for i, z in enumerate(qs): self.assertAlmostEqual(z.distance.m, dists_m[i], 5) @skipUnlessDBFeature("has_Distance_function") def test_distance_order_by(self): - qs = SouthTexasCity.objects.annotate(distance=Distance('point', Point(3, 3, srid=32140))).order_by( - 'distance' - ).values_list('name', flat=True).filter(name__in=('San Antonio', 'Pearland')) - self.assertSequenceEqual(qs, ['San Antonio', 'Pearland']) + qs = ( + SouthTexasCity.objects.annotate( + distance=Distance("point", Point(3, 3, srid=32140)) + ) + .order_by("distance") + .values_list("name", flat=True) + .filter(name__in=("San Antonio", "Pearland")) + ) + self.assertSequenceEqual(qs, ["San Antonio", "Pearland"]) @skipUnlessDBFeature("has_Length_function") def test_length(self): @@ -537,20 +694,24 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): len_m2 = 4617.668 if connection.features.supports_length_geodetic: - qs = Interstate.objects.annotate(length=Length('path')) + qs = Interstate.objects.annotate(length=Length("path")) tol = 2 if connection.ops.oracle else 3 self.assertAlmostEqual(len_m1, qs[0].length.m, tol) # TODO: test with spheroid argument (True and False) else: # Does not support geodetic coordinate systems. with self.assertRaises(NotSupportedError): - list(Interstate.objects.annotate(length=Length('path'))) + list(Interstate.objects.annotate(length=Length("path"))) # Now doing length on a projected coordinate system. - i10 = SouthTexasInterstate.objects.annotate(length=Length('path')).get(name='I-10') + i10 = SouthTexasInterstate.objects.annotate(length=Length("path")).get( + name="I-10" + ) self.assertAlmostEqual(len_m2, i10.length.m, 2) self.assertTrue( - SouthTexasInterstate.objects.annotate(length=Length('path')).filter(length__gt=4000).exists() + SouthTexasInterstate.objects.annotate(length=Length("path")) + .filter(length__gt=4000) + .exists() ) # Length with an explicit geometry value. qs = Interstate.objects.annotate(length=Length(i10.path)) @@ -563,14 +724,21 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): """ # Reference query: # SELECT ST_Perimeter(distapp_southtexaszipcode.poly) FROM distapp_southtexaszipcode; - perim_m = [18404.3550889361, 15627.2108551001, 20632.5588368978, 17094.5996143697] + perim_m = [ + 18404.3550889361, + 15627.2108551001, + 20632.5588368978, + 17094.5996143697, + ] tol = 2 if connection.ops.oracle else 7 - qs = SouthTexasZipcode.objects.annotate(perimeter=Perimeter('poly')).order_by('name') + qs = SouthTexasZipcode.objects.annotate(perimeter=Perimeter("poly")).order_by( + "name" + ) for i, z in enumerate(qs): self.assertAlmostEqual(perim_m[i], z.perimeter.m, tol) # Running on points; should return 0. - qs = SouthTexasCity.objects.annotate(perim=Perimeter('point')) + qs = SouthTexasCity.objects.annotate(perim=Perimeter("point")) for city in qs: self.assertEqual(0, city.perim.m) @@ -578,28 +746,32 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase): def test_perimeter_geodetic(self): # Currently only Oracle supports calculating the perimeter on geodetic # geometries (without being transformed). - qs1 = CensusZipcode.objects.annotate(perim=Perimeter('poly')) + qs1 = CensusZipcode.objects.annotate(perim=Perimeter("poly")) if connection.features.supports_perimeter_geodetic: self.assertAlmostEqual(qs1[0].perim.m, 18406.3818954314, 3) else: with self.assertRaises(NotSupportedError): list(qs1) # But should work fine when transformed to projected coordinates - qs2 = CensusZipcode.objects.annotate(perim=Perimeter(Transform('poly', 32140))).filter(name='77002') + qs2 = CensusZipcode.objects.annotate( + perim=Perimeter(Transform("poly", 32140)) + ).filter(name="77002") self.assertAlmostEqual(qs2[0].perim.m, 18404.355, 3) - @skipUnlessDBFeature("supports_null_geometries", "has_Area_function", "has_Distance_function") + @skipUnlessDBFeature( + "supports_null_geometries", "has_Area_function", "has_Distance_function" + ) def test_measurement_null_fields(self): """ Test the measurement functions on fields with NULL values. """ # Creating SouthTexasZipcode w/NULL value. - SouthTexasZipcode.objects.create(name='78212') + SouthTexasZipcode.objects.create(name="78212") # Performing distance/area queries against the NULL PolygonField, # and ensuring the result of the operations is None. - htown = SouthTexasCity.objects.get(name='Downtown Houston') + htown = SouthTexasCity.objects.get(name="Downtown Houston") z = SouthTexasZipcode.objects.annotate( - distance=Distance('poly', htown.point), area=Area('poly') - ).get(name='78212') + distance=Distance("poly", htown.point), area=Area("poly") + ).get(name="78212") self.assertIsNone(z.distance) self.assertIsNone(z.area) diff --git a/tests/gis_tests/gdal_tests/test_driver.py b/tests/gis_tests/gdal_tests/test_driver.py index c2dcc80d65..e7c03ae98d 100644 --- a/tests/gis_tests/gdal_tests/test_driver.py +++ b/tests/gis_tests/gdal_tests/test_driver.py @@ -5,28 +5,37 @@ from django.contrib.gis.gdal import Driver, GDALException valid_drivers = ( # vector - 'ESRI Shapefile', 'MapInfo File', 'TIGER', 'S57', 'DGN', 'Memory', 'CSV', - 'GML', 'KML', + "ESRI Shapefile", + "MapInfo File", + "TIGER", + "S57", + "DGN", + "Memory", + "CSV", + "GML", + "KML", # raster - 'GTiff', 'JPEG', 'MEM', 'PNG', + "GTiff", + "JPEG", + "MEM", + "PNG", ) -invalid_drivers = ('Foo baz', 'clucka', 'ESRI Shp', 'ESRI rast') +invalid_drivers = ("Foo baz", "clucka", "ESRI Shp", "ESRI rast") aliases = { - 'eSrI': 'ESRI Shapefile', - 'TigER/linE': 'TIGER', - 'SHAPE': 'ESRI Shapefile', - 'sHp': 'ESRI Shapefile', - 'tiFf': 'GTiff', - 'tIf': 'GTiff', - 'jPEg': 'JPEG', - 'jpG': 'JPEG', + "eSrI": "ESRI Shapefile", + "TigER/linE": "TIGER", + "SHAPE": "ESRI Shapefile", + "sHp": "ESRI Shapefile", + "tiFf": "GTiff", + "tIf": "GTiff", + "jPEg": "JPEG", + "jpG": "JPEG", } class DriverTest(unittest.TestCase): - def test01_valid_driver(self): "Testing valid GDAL/OGR Data Source Drivers." for d in valid_drivers: @@ -45,15 +54,16 @@ class DriverTest(unittest.TestCase): dr = Driver(alias) self.assertEqual(full_name, str(dr)) - @mock.patch('django.contrib.gis.gdal.driver.vcapi.get_driver_count') - @mock.patch('django.contrib.gis.gdal.driver.rcapi.get_driver_count') - @mock.patch('django.contrib.gis.gdal.driver.vcapi.register_all') - @mock.patch('django.contrib.gis.gdal.driver.rcapi.register_all') + @mock.patch("django.contrib.gis.gdal.driver.vcapi.get_driver_count") + @mock.patch("django.contrib.gis.gdal.driver.rcapi.get_driver_count") + @mock.patch("django.contrib.gis.gdal.driver.vcapi.register_all") + @mock.patch("django.contrib.gis.gdal.driver.rcapi.register_all") def test_registered(self, rreg, vreg, rcount, vcount): """ Prototypes are registered only if their respective driver counts are zero. """ + def check(rcount_val, vcount_val): vreg.reset_mock() rreg.reset_mock() diff --git a/tests/gis_tests/gdal_tests/test_ds.py b/tests/gis_tests/gdal_tests/test_ds.py index 2632e817b4..fae11cfea9 100644 --- a/tests/gis_tests/gdal_tests/test_ds.py +++ b/tests/gis_tests/gdal_tests/test_ds.py @@ -3,12 +3,8 @@ import re from datetime import datetime from pathlib import Path -from django.contrib.gis.gdal import ( - DataSource, Envelope, GDALException, OGRGeometry, -) -from django.contrib.gis.gdal.field import ( - OFTDateTime, OFTInteger, OFTReal, OFTString, -) +from django.contrib.gis.gdal import DataSource, Envelope, GDALException, OGRGeometry +from django.contrib.gis.gdal.field import OFTDateTime, OFTInteger, OFTReal, OFTString from django.contrib.gis.geos import GEOSGeometry from django.test import SimpleTestCase @@ -17,87 +13,105 @@ from ..test_data import TEST_DATA, TestDS, get_ds_file wgs_84_wkt = ( 'GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",' '6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",' - '0.017453292519943295]]' + "0.017453292519943295]]" ) # Using a regex because of small differences depending on GDAL versions. wgs_84_wkt_regex = r'^GEOGCS\["(GCS_)?WGS[ _](19)?84".*$' -datetime_format = '%Y-%m-%dT%H:%M:%S' +datetime_format = "%Y-%m-%dT%H:%M:%S" # List of acceptable data sources. ds_list = ( TestDS( - 'test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='ESRI Shapefile', - fields={'dbl': OFTReal, 'int': OFTInteger, 'str': OFTString}, + "test_point", + nfeat=5, + nfld=3, + geom="POINT", + gtype=1, + driver="ESRI Shapefile", + fields={"dbl": OFTReal, "int": OFTInteger, "str": OFTString}, extent=(-1.35011, 0.166623, -0.524093, 0.824508), # Got extent from QGIS srs_wkt=wgs_84_wkt, field_values={ - 'dbl': [float(i) for i in range(1, 6)], - 'int': list(range(1, 6)), - 'str': [str(i) for i in range(1, 6)], + "dbl": [float(i) for i in range(1, 6)], + "int": list(range(1, 6)), + "str": [str(i) for i in range(1, 6)], }, - fids=range(5) + fids=range(5), ), TestDS( - 'test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype='Point25D', - driver='OGR_VRT', + "test_vrt", + ext="vrt", + nfeat=3, + nfld=3, + geom="POINT", + gtype="Point25D", + driver="OGR_VRT", fields={ - 'POINT_X': OFTString, - 'POINT_Y': OFTString, - 'NUM': OFTString, + "POINT_X": OFTString, + "POINT_Y": OFTString, + "NUM": OFTString, }, # VRT uses CSV, which all types are OFTString. extent=(1.0, 2.0, 100.0, 523.5), # Min/Max from CSV field_values={ - 'POINT_X': ['1.0', '5.0', '100.0'], - 'POINT_Y': ['2.0', '23.0', '523.5'], - 'NUM': ['5', '17', '23'], + "POINT_X": ["1.0", "5.0", "100.0"], + "POINT_Y": ["2.0", "23.0", "523.5"], + "NUM": ["5", "17", "23"], }, - fids=range(1, 4) + fids=range(1, 4), ), TestDS( - 'test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3, - driver='ESRI Shapefile', - fields={'float': OFTReal, 'int': OFTInteger, 'str': OFTString}, + "test_poly", + nfeat=3, + nfld=3, + geom="POLYGON", + gtype=3, + driver="ESRI Shapefile", + fields={"float": OFTReal, "int": OFTInteger, "str": OFTString}, extent=(-1.01513, -0.558245, 0.161876, 0.839637), # Got extent from QGIS srs_wkt=wgs_84_wkt, ), TestDS( - 'has_nulls', nfeat=3, nfld=6, geom='POLYGON', gtype=3, - driver='GeoJSON', ext='geojson', + "has_nulls", + nfeat=3, + nfld=6, + geom="POLYGON", + gtype=3, + driver="GeoJSON", + ext="geojson", fields={ - 'uuid': OFTString, - 'name': OFTString, - 'num': OFTReal, - 'integer': OFTInteger, - 'datetime': OFTDateTime, - 'boolean': OFTInteger, + "uuid": OFTString, + "name": OFTString, + "num": OFTReal, + "integer": OFTInteger, + "datetime": OFTDateTime, + "boolean": OFTInteger, }, extent=(-75.274200, 39.846504, -74.959717, 40.119040), # Got extent from QGIS field_values={ - 'uuid': [ - '1378c26f-cbe6-44b0-929f-eb330d4991f5', - 'fa2ba67c-a135-4338-b924-a9622b5d869f', - '4494c1f3-55ab-4256-b365-12115cb388d5', + "uuid": [ + "1378c26f-cbe6-44b0-929f-eb330d4991f5", + "fa2ba67c-a135-4338-b924-a9622b5d869f", + "4494c1f3-55ab-4256-b365-12115cb388d5", ], - 'name': ['Philadelphia', None, 'north'], - 'num': [1.001, None, 0.0], - 'integer': [5, None, 8], - 'boolean': [True, None, False], - 'datetime': [ - datetime.strptime('1994-08-14T11:32:14', datetime_format), + "name": ["Philadelphia", None, "north"], + "num": [1.001, None, 0.0], + "integer": [5, None, 8], + "boolean": [True, None, False], + "datetime": [ + datetime.strptime("1994-08-14T11:32:14", datetime_format), None, - datetime.strptime('2018-11-29T03:02:52', datetime_format), - ] + datetime.strptime("2018-11-29T03:02:52", datetime_format), + ], }, fids=range(3), ), ) -bad_ds = (TestDS('foo'),) +bad_ds = (TestDS("foo"),) class DataSourceTest(SimpleTestCase): - def test01_valid_shp(self): "Testing valid SHP Data Source files." @@ -115,15 +129,17 @@ class DataSourceTest(SimpleTestCase): self.assertEqual(source.driver, str(ds.driver)) # Making sure indexing works - msg = 'Index out of range when accessing layers in a datasource: %s.' + msg = "Index out of range when accessing layers in a datasource: %s." with self.assertRaisesMessage(IndexError, msg % len(ds)): ds.__getitem__(len(ds)) - with self.assertRaisesMessage(IndexError, 'Invalid OGR layer name given: invalid.'): - ds.__getitem__('invalid') + with self.assertRaisesMessage( + IndexError, "Invalid OGR layer name given: invalid." + ): + ds.__getitem__("invalid") def test_ds_input_pathlib(self): - test_shp = Path(get_ds_file('test_point', 'shp')) + test_shp = Path(get_ds_file("test_point", "shp")) ds = DataSource(test_shp) self.assertEqual(len(ds), 1) @@ -162,12 +178,14 @@ class DataSourceTest(SimpleTestCase): self.assertIn(f, source.fields) # Negative FIDs are not allowed. - with self.assertRaisesMessage(IndexError, 'Negative indices are not allowed on OGR Layers.'): + with self.assertRaisesMessage( + IndexError, "Negative indices are not allowed on OGR Layers." + ): layer.__getitem__(-1) - with self.assertRaisesMessage(IndexError, 'Invalid feature id: 50000.'): + with self.assertRaisesMessage(IndexError, "Invalid feature id: 50000."): layer.__getitem__(50000) - if hasattr(source, 'field_values'): + if hasattr(source, "field_values"): # Testing `Layer.get_fields` (which uses Layer.__iter__) for fld_name, fld_value in source.field_values.items(): self.assertEqual(fld_value, layer.get_fields(fld_name)) @@ -181,12 +199,16 @@ class DataSourceTest(SimpleTestCase): for fld_name, fld_value in source.field_values.items(): self.assertEqual(fld_value[i], feat.get(fld_name)) - msg = 'Index out of range when accessing field in a feature: %s.' + msg = ( + "Index out of range when accessing field in a feature: %s." + ) with self.assertRaisesMessage(IndexError, msg % len(feat)): feat.__getitem__(len(feat)) - with self.assertRaisesMessage(IndexError, 'Invalid OFT field name given: invalid.'): - feat.__getitem__('invalid') + with self.assertRaisesMessage( + IndexError, "Invalid OFT field name given: invalid." + ): + feat.__getitem__("invalid") def test03b_layer_slice(self): "Test indexing and slicing on Layers." @@ -223,7 +245,7 @@ class DataSourceTest(SimpleTestCase): self.assertEqual(source.gtype, lyr.geom_type.num) # Same issue for Feature/Field objects, see #18640 - self.assertEqual(str(lyr[0]['str']), "1") + self.assertEqual(str(lyr[0]["str"]), "1") def test04_features(self): "Testing Data Source Features." @@ -271,12 +293,12 @@ class DataSourceTest(SimpleTestCase): self.assertEqual(source.gtype, g.geom_type) # Making sure the SpatialReference is as expected. - if hasattr(source, 'srs_wkt'): + if hasattr(source, "srs_wkt"): self.assertIsNotNone(re.match(wgs_84_wkt_regex, g.srs.wkt)) def test06_spatial_filter(self): "Testing the Layer.spatial_filter property." - ds = DataSource(get_ds_file('cities', 'shp')) + ds = DataSource(get_ds_file("cities", "shp")) lyr = ds[0] # When not set, it should be None. @@ -284,7 +306,7 @@ class DataSourceTest(SimpleTestCase): # Must be set a/an OGRGeometry or 4-tuple. with self.assertRaises(TypeError): - lyr._set_spatial_filter('foo') + lyr._set_spatial_filter("foo") # Setting the spatial filter with a tuple/list with the extent of # a buffer centering around Pueblo. @@ -295,19 +317,19 @@ class DataSourceTest(SimpleTestCase): self.assertEqual(OGRGeometry.from_bbox(filter_extent), lyr.spatial_filter) feats = [feat for feat in lyr] self.assertEqual(1, len(feats)) - self.assertEqual('Pueblo', feats[0].get('Name')) + self.assertEqual("Pueblo", feats[0].get("Name")) # Setting the spatial filter with an OGRGeometry for buffer centering # around Houston. filter_geom = OGRGeometry( - 'POLYGON((-96.363151 28.763374,-94.363151 28.763374,' - '-94.363151 30.763374,-96.363151 30.763374,-96.363151 28.763374))' + "POLYGON((-96.363151 28.763374,-94.363151 28.763374," + "-94.363151 30.763374,-96.363151 30.763374,-96.363151 28.763374))" ) lyr.spatial_filter = filter_geom self.assertEqual(filter_geom, lyr.spatial_filter) feats = [feat for feat in lyr] self.assertEqual(1, len(feats)) - self.assertEqual('Houston', feats[0].get('Name')) + self.assertEqual("Houston", feats[0].get("Name")) # Clearing the spatial filter by setting it to None. Now # should indicate that there are 3 features in the Layer. @@ -319,14 +341,14 @@ class DataSourceTest(SimpleTestCase): # Using *.dbf from Census 2010 TIGER Shapefile for Texas, # which has land area ('ALAND10') stored in a Real field # with no precision. - ds = DataSource(os.path.join(TEST_DATA, 'texas.dbf')) + ds = DataSource(os.path.join(TEST_DATA, "texas.dbf")) feat = ds[0][0] # Reference value obtained using `ogrinfo`. - self.assertEqual(676586997978, feat.get('ALAND10')) + self.assertEqual(676586997978, feat.get("ALAND10")) def test_nonexistent_field(self): source = ds_list[0] ds = DataSource(source.ds) - msg = 'invalid field name: nonexistent' + msg = "invalid field name: nonexistent" with self.assertRaisesMessage(GDALException, msg): - ds[0].get_fields('nonexistent') + ds[0].get_fields("nonexistent") diff --git a/tests/gis_tests/gdal_tests/test_envelope.py b/tests/gis_tests/gdal_tests/test_envelope.py index c414bea533..d55aae168b 100644 --- a/tests/gis_tests/gdal_tests/test_envelope.py +++ b/tests/gis_tests/gdal_tests/test_envelope.py @@ -10,7 +10,6 @@ class TestPoint: class EnvelopeTest(unittest.TestCase): - def setUp(self): self.e = Envelope(0, 0, 5, 5) @@ -18,7 +17,7 @@ class EnvelopeTest(unittest.TestCase): "Testing Envelope initialization." e1 = Envelope((0, 0, 5, 5)) Envelope(0, 0, 5, 5) - Envelope(0, '0', '5', 5) # Thanks to ww for this + Envelope(0, "0", "5", 5) # Thanks to ww for this Envelope(e1._envelope) with self.assertRaises(GDALException): Envelope((5, 5, 0, 0)) @@ -29,9 +28,9 @@ class EnvelopeTest(unittest.TestCase): with self.assertRaises(GDALException): Envelope(()) with self.assertRaises(ValueError): - Envelope(0, 'a', 5, 5) + Envelope(0, "a", 5, 5) with self.assertRaises(TypeError): - Envelope('foo') + Envelope("foo") with self.assertRaises(GDALException): Envelope((1, 1, 0, 0)) # Shouldn't raise an exception for min_x == max_x or min_y == max_y @@ -47,8 +46,8 @@ class EnvelopeTest(unittest.TestCase): self.assertEqual((0, 0), e.ll) self.assertEqual((2, 3), e.ur) self.assertEqual((0, 0, 2, 3), e.tuple) - self.assertEqual('POLYGON((0.0 0.0,0.0 3.0,2.0 3.0,2.0 0.0,0.0 0.0))', e.wkt) - self.assertEqual('(0.0, 0.0, 2.0, 3.0)', str(e)) + self.assertEqual("POLYGON((0.0 0.0,0.0 3.0,2.0 3.0,2.0 0.0,0.0 0.0))", e.wkt) + self.assertEqual("(0.0, 0.0, 2.0, 3.0)", str(e)) def test03_equivalence(self): "Testing Envelope equivalence." diff --git a/tests/gis_tests/gdal_tests/test_geom.py b/tests/gis_tests/gdal_tests/test_geom.py index 68f43e2404..cb4588450c 100644 --- a/tests/gis_tests/gdal_tests/test_geom.py +++ b/tests/gis_tests/gdal_tests/test_geom.py @@ -2,7 +2,11 @@ import json import pickle from django.contrib.gis.gdal import ( - CoordTransform, GDALException, OGRGeometry, OGRGeomType, SpatialReference, + CoordTransform, + GDALException, + OGRGeometry, + OGRGeomType, + SpatialReference, ) from django.template import Context from django.template.engine import Engine @@ -20,46 +24,48 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): # OGRGeomType should initialize on all these inputs. OGRGeomType(1) OGRGeomType(7) - OGRGeomType('point') - OGRGeomType('GeometrycollectioN') - OGRGeomType('LINearrING') - OGRGeomType('Unknown') + OGRGeomType("point") + OGRGeomType("GeometrycollectioN") + OGRGeomType("LINearrING") + OGRGeomType("Unknown") # Should throw TypeError on this input with self.assertRaises(GDALException): OGRGeomType(23) with self.assertRaises(GDALException): - OGRGeomType('fooD') + OGRGeomType("fooD") with self.assertRaises(GDALException): OGRGeomType(9) # Equivalence can take strings, ints, and other OGRGeomTypes self.assertEqual(OGRGeomType(1), OGRGeomType(1)) - self.assertEqual(OGRGeomType(7), 'GeometryCollection') - self.assertEqual(OGRGeomType('point'), 'POINT') - self.assertNotEqual(OGRGeomType('point'), 2) - self.assertEqual(OGRGeomType('unknown'), 0) - self.assertEqual(OGRGeomType(6), 'MULtiPolyGON') - self.assertEqual(OGRGeomType(1), OGRGeomType('point')) - self.assertNotEqual(OGRGeomType('POINT'), OGRGeomType(6)) + self.assertEqual(OGRGeomType(7), "GeometryCollection") + self.assertEqual(OGRGeomType("point"), "POINT") + self.assertNotEqual(OGRGeomType("point"), 2) + self.assertEqual(OGRGeomType("unknown"), 0) + self.assertEqual(OGRGeomType(6), "MULtiPolyGON") + self.assertEqual(OGRGeomType(1), OGRGeomType("point")) + self.assertNotEqual(OGRGeomType("POINT"), OGRGeomType(6)) # Testing the Django field name equivalent property. - self.assertEqual('PointField', OGRGeomType('Point').django) - self.assertEqual('GeometryField', OGRGeomType('Geometry').django) - self.assertEqual('GeometryField', OGRGeomType('Unknown').django) - self.assertIsNone(OGRGeomType('none').django) + self.assertEqual("PointField", OGRGeomType("Point").django) + self.assertEqual("GeometryField", OGRGeomType("Geometry").django) + self.assertEqual("GeometryField", OGRGeomType("Unknown").django) + self.assertIsNone(OGRGeomType("none").django) # 'Geometry' initialization implies an unknown geometry type. - gt = OGRGeomType('Geometry') + gt = OGRGeomType("Geometry") self.assertEqual(0, gt.num) - self.assertEqual('Unknown', gt.name) + self.assertEqual("Unknown", gt.name) def test_geomtype_25d(self): "Testing OGRGeomType object with 25D types." wkb25bit = OGRGeomType.wkb25bit - self.assertEqual(OGRGeomType(wkb25bit + 1), 'Point25D') - self.assertEqual(OGRGeomType('MultiLineString25D'), (5 + wkb25bit)) - self.assertEqual('GeometryCollectionField', OGRGeomType('GeometryCollection25D').django) + self.assertEqual(OGRGeomType(wkb25bit + 1), "Point25D") + self.assertEqual(OGRGeomType("MultiLineString25D"), (5 + wkb25bit)) + self.assertEqual( + "GeometryCollectionField", OGRGeomType("GeometryCollection25D").django + ) def test_wkt(self): "Testing WKT output." @@ -69,11 +75,11 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): def test_ewkt(self): "Testing EWKT input/output." - for ewkt_val in ('POINT (1 2 3)', 'LINEARRING (0 0,1 1,2 1,0 0)'): + for ewkt_val in ("POINT (1 2 3)", "LINEARRING (0 0,1 1,2 1,0 0)"): # First with ewkt output when no SRID in EWKT self.assertEqual(ewkt_val, OGRGeometry(ewkt_val).ewkt) # No test consumption with an SRID specified. - ewkt_val = 'SRID=4326;%s' % ewkt_val + ewkt_val = "SRID=4326;%s" % ewkt_val geom = OGRGeometry(ewkt_val) self.assertEqual(ewkt_val, geom.ewkt) self.assertEqual(4326, geom.srs.srid) @@ -108,24 +114,26 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): "Testing GeoJSON input/output." for g in self.geometries.json_geoms: geom = OGRGeometry(g.wkt) - if not hasattr(g, 'not_equal'): + if not hasattr(g, "not_equal"): # Loading jsons to prevent decimal differences self.assertEqual(json.loads(g.json), json.loads(geom.json)) self.assertEqual(json.loads(g.json), json.loads(geom.geojson)) self.assertEqual(OGRGeometry(g.wkt), OGRGeometry(geom.json)) # Test input with some garbage content (but valid json) (#15529) - geom = OGRGeometry('{"type": "Point", "coordinates": [ 100.0, 0.0 ], "other": "<test>"}') + geom = OGRGeometry( + '{"type": "Point", "coordinates": [ 100.0, 0.0 ], "other": "<test>"}' + ) self.assertIsInstance(geom, OGRGeometry) def test_points(self): "Testing Point objects." - OGRGeometry('POINT(0 0)') + OGRGeometry("POINT(0 0)") for p in self.geometries.points: - if not hasattr(p, 'z'): # No 3D + if not hasattr(p, "z"): # No 3D pnt = OGRGeometry(p.wkt) self.assertEqual(1, pnt.geom_type) - self.assertEqual('POINT', pnt.geom_name) + self.assertEqual("POINT", pnt.geom_name) self.assertEqual(p.x, pnt.x) self.assertEqual(p.y, pnt.y) self.assertEqual((p.x, p.y), pnt.tuple) @@ -135,9 +143,9 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): for mp in self.geometries.multipoints: mgeom1 = OGRGeometry(mp.wkt) # First one from WKT self.assertEqual(4, mgeom1.geom_type) - self.assertEqual('MULTIPOINT', mgeom1.geom_name) - mgeom2 = OGRGeometry('MULTIPOINT') # Creating empty multipoint - mgeom3 = OGRGeometry('MULTIPOINT') + self.assertEqual("MULTIPOINT", mgeom1.geom_name) + mgeom2 = OGRGeometry("MULTIPOINT") # Creating empty multipoint + mgeom3 = OGRGeometry("MULTIPOINT") for g in mgeom1: mgeom2.add(g) # adding each point from the multipoints mgeom3.add(g.wkt) # should take WKT as well @@ -148,16 +156,16 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): def test_linestring(self): "Testing LineString objects." - prev = OGRGeometry('POINT(0 0)') + prev = OGRGeometry("POINT(0 0)") for ls in self.geometries.linestrings: linestr = OGRGeometry(ls.wkt) self.assertEqual(2, linestr.geom_type) - self.assertEqual('LINESTRING', linestr.geom_name) + self.assertEqual("LINESTRING", linestr.geom_name) self.assertEqual(ls.n_p, linestr.point_count) self.assertEqual(ls.coords, linestr.tuple) self.assertEqual(linestr, OGRGeometry(ls.wkt)) self.assertNotEqual(linestr, prev) - msg = 'Index out of range when accessing points of a line string: %s.' + msg = "Index out of range when accessing points of a line string: %s." with self.assertRaisesMessage(IndexError, msg % len(linestr)): linestr.__getitem__(len(linestr)) prev = linestr @@ -170,11 +178,11 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): def test_multilinestring(self): "Testing MultiLineString objects." - prev = OGRGeometry('POINT(0 0)') + prev = OGRGeometry("POINT(0 0)") for mls in self.geometries.multilinestrings: mlinestr = OGRGeometry(mls.wkt) self.assertEqual(5, mlinestr.geom_type) - self.assertEqual('MULTILINESTRING', mlinestr.geom_name) + self.assertEqual("MULTILINESTRING", mlinestr.geom_name) self.assertEqual(mls.n_p, mlinestr.point_count) self.assertEqual(mls.coords, mlinestr.tuple) self.assertEqual(mlinestr, OGRGeometry(mls.wkt)) @@ -182,18 +190,18 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): prev = mlinestr for ls in mlinestr: self.assertEqual(2, ls.geom_type) - self.assertEqual('LINESTRING', ls.geom_name) - msg = 'Index out of range when accessing geometry in a collection: %s.' + self.assertEqual("LINESTRING", ls.geom_name) + msg = "Index out of range when accessing geometry in a collection: %s." with self.assertRaisesMessage(IndexError, msg % len(mlinestr)): mlinestr.__getitem__(len(mlinestr)) def test_linearring(self): "Testing LinearRing objects." - prev = OGRGeometry('POINT(0 0)') + prev = OGRGeometry("POINT(0 0)") for rr in self.geometries.linearrings: lr = OGRGeometry(rr.wkt) # self.assertEqual(101, lr.geom_type.num) - self.assertEqual('LINEARRING', lr.geom_name) + self.assertEqual("LINEARRING", lr.geom_name) self.assertEqual(rr.n_p, len(lr)) self.assertEqual(lr, OGRGeometry(rr.wkt)) self.assertNotEqual(lr, prev) @@ -207,14 +215,14 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): p = OGRGeometry.from_bbox(bbox) self.assertEqual(bbox, p.extent) - prev = OGRGeometry('POINT(0 0)') + prev = OGRGeometry("POINT(0 0)") for p in self.geometries.polygons: poly = OGRGeometry(p.wkt) self.assertEqual(3, poly.geom_type) - self.assertEqual('POLYGON', poly.geom_name) + self.assertEqual("POLYGON", poly.geom_name) self.assertEqual(p.n_p, poly.point_count) self.assertEqual(p.n_i + 1, len(poly)) - msg = 'Index out of range when accessing rings of a polygon: %s.' + msg = "Index out of range when accessing rings of a polygon: %s." with self.assertRaisesMessage(IndexError, msg % len(poly)): poly.__getitem__(len(poly)) @@ -235,43 +243,45 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): self.assertEqual(len(p.ext_ring_cs), ring.point_count) for r in poly: - self.assertEqual('LINEARRING', r.geom_name) + self.assertEqual("LINEARRING", r.geom_name) def test_polygons_templates(self): # Accessing Polygon attributes in templates should work. engine = Engine() - template = engine.from_string('{{ polygons.0.wkt }}') + template = engine.from_string("{{ polygons.0.wkt }}") polygons = [OGRGeometry(p.wkt) for p in self.geometries.multipolygons[:2]] - content = template.render(Context({'polygons': polygons})) - self.assertIn('MULTIPOLYGON (((100', content) + content = template.render(Context({"polygons": polygons})) + self.assertIn("MULTIPOLYGON (((100", content) def test_closepolygons(self): "Testing closing Polygon objects." # Both rings in this geometry are not closed. - poly = OGRGeometry('POLYGON((0 0, 5 0, 5 5, 0 5), (1 1, 2 1, 2 2, 2 1))') + poly = OGRGeometry("POLYGON((0 0, 5 0, 5 5, 0 5), (1 1, 2 1, 2 2, 2 1))") self.assertEqual(8, poly.point_count) with self.assertRaises(GDALException): poly.centroid poly.close_rings() - self.assertEqual(10, poly.point_count) # Two closing points should've been added - self.assertEqual(OGRGeometry('POINT(2.5 2.5)'), poly.centroid) + self.assertEqual( + 10, poly.point_count + ) # Two closing points should've been added + self.assertEqual(OGRGeometry("POINT(2.5 2.5)"), poly.centroid) def test_multipolygons(self): "Testing MultiPolygon objects." - OGRGeometry('POINT(0 0)') + OGRGeometry("POINT(0 0)") for mp in self.geometries.multipolygons: mpoly = OGRGeometry(mp.wkt) self.assertEqual(6, mpoly.geom_type) - self.assertEqual('MULTIPOLYGON', mpoly.geom_name) + self.assertEqual("MULTIPOLYGON", mpoly.geom_name) if mp.valid: self.assertEqual(mp.n_p, mpoly.point_count) self.assertEqual(mp.num_geom, len(mpoly)) - msg = 'Index out of range when accessing geometry in a collection: %s.' + msg = "Index out of range when accessing geometry in a collection: %s." with self.assertRaisesMessage(IndexError, msg % len(mpoly)): mpoly.__getitem__(len(mpoly)) for p in mpoly: - self.assertEqual('POLYGON', p.geom_name) + self.assertEqual("POLYGON", p.geom_name) self.assertEqual(3, p.geom_type) self.assertEqual(mpoly.wkt, OGRGeometry(mp.wkt).wkt) @@ -279,7 +289,7 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): "Testing OGR Geometries with Spatial Reference objects." for mp in self.geometries.multipolygons: # Creating a geometry w/spatial reference - sr = SpatialReference('WGS84') + sr = SpatialReference("WGS84") mpoly = OGRGeometry(mp.wkt, sr) self.assertEqual(sr.wkt, mpoly.srs.wkt) @@ -307,7 +317,7 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): self.assertEqual(4326, mpoly.srid) mpoly.srs = SpatialReference(4269) self.assertEqual(4269, mpoly.srid) - self.assertEqual('NAD83', mpoly.srs.name) + self.assertEqual("NAD83", mpoly.srs.name) # Incrementing through the multipolygon after the spatial reference # has been re-assigned. @@ -317,13 +327,13 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): for ring in poly: # Changing each ring in the polygon self.assertEqual(32140, ring.srs.srid) - self.assertEqual('NAD83 / Texas South Central', ring.srs.name) + self.assertEqual("NAD83 / Texas South Central", ring.srs.name) ring.srs = str(SpatialReference(4326)) # back to WGS84 self.assertEqual(4326, ring.srs.srid) # Using the `srid` property. ring.srid = 4322 - self.assertEqual('WGS 72', ring.srs.name) + self.assertEqual("WGS 72", ring.srs.name) self.assertEqual(4322, ring.srid) # srs/srid may be assigned their own values, even when srs is None. @@ -333,15 +343,15 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): def test_srs_transform(self): "Testing transform()." - orig = OGRGeometry('POINT (-104.609 38.255)', 4326) - trans = OGRGeometry('POINT (992385.4472045 481455.4944650)', 2774) + orig = OGRGeometry("POINT (-104.609 38.255)", 4326) + trans = OGRGeometry("POINT (992385.4472045 481455.4944650)", 2774) # Using an srid, a SpatialReference object, and a CoordTransform object # or transformations. t1, t2, t3 = orig.clone(), orig.clone(), orig.clone() t1.transform(trans.srid) - t2.transform(SpatialReference('EPSG:2774')) - ct = CoordTransform(SpatialReference('WGS84'), SpatialReference(2774)) + t2.transform(SpatialReference("EPSG:2774")) + ct = CoordTransform(SpatialReference("WGS84"), SpatialReference(2774)) t3.transform(ct) # Testing use of the `clone` keyword. @@ -359,8 +369,8 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): def test_transform_dim(self): "Testing coordinate dimension is the same on transformed geometries." - ls_orig = OGRGeometry('LINESTRING(-104.609 38.255)', 4326) - ls_trans = OGRGeometry('LINESTRING(992385.4472045 481455.4944650)', 2774) + ls_orig = OGRGeometry("LINESTRING(-104.609 38.255)", 4326) + ls_trans = OGRGeometry("LINESTRING(992385.4472045 481455.4944650)", 2774) # Different PROJ versions use different transformations, all are # correct as having a 1 meter accuracy. @@ -379,7 +389,9 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): d1 = OGRGeometry(self.geometries.diff_geoms[i].wkt) d2 = a.difference(b) self.assertTrue(d1.geos.equals(d2.geos)) - self.assertTrue(d1.geos.equals((a - b).geos)) # __sub__ is difference operator + self.assertTrue( + d1.geos.equals((a - b).geos) + ) # __sub__ is difference operator a -= b # testing __isub__ self.assertTrue(d1.geos.equals(a.geos)) @@ -392,7 +404,9 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): self.assertTrue(a.intersects(b)) i2 = a.intersection(b) self.assertTrue(i1.geos.equals(i2.geos)) - self.assertTrue(i1.geos.equals((a & b).geos)) # __and__ is intersection operator + self.assertTrue( + i1.geos.equals((a & b).geos) + ) # __and__ is intersection operator a &= b # testing __iand__ self.assertTrue(i1.geos.equals(a.geos)) @@ -404,7 +418,9 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): d1 = OGRGeometry(self.geometries.sdiff_geoms[i].wkt) d2 = a.sym_difference(b) self.assertTrue(d1.geos.equals(d2.geos)) - self.assertTrue(d1.geos.equals((a ^ b).geos)) # __xor__ is symmetric difference operator + self.assertTrue( + d1.geos.equals((a ^ b).geos) + ) # __xor__ is symmetric difference operator a ^= b # testing __ixor__ self.assertTrue(d1.geos.equals(a.geos)) @@ -423,8 +439,8 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): def test_add(self): "Testing GeometryCollection.add()." # Can't insert a Point into a MultiPolygon. - mp = OGRGeometry('MultiPolygon') - pnt = OGRGeometry('POINT(5 23)') + mp = OGRGeometry("MultiPolygon") + pnt = OGRGeometry("POINT(5 23)") with self.assertRaises(GDALException): mp.add(pnt) @@ -432,9 +448,9 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): # of the same type all child geoms will be added individually) or WKT. for mp in self.geometries.multipolygons: mpoly = OGRGeometry(mp.wkt) - mp1 = OGRGeometry('MultiPolygon') - mp2 = OGRGeometry('MultiPolygon') - mp3 = OGRGeometry('MultiPolygon') + mp1 = OGRGeometry("MultiPolygon") + mp2 = OGRGeometry("MultiPolygon") + mp3 = OGRGeometry("MultiPolygon") for poly in mpoly: mp1.add(poly) # Adding a geometry at a time @@ -446,7 +462,7 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): def test_extent(self): "Testing `extent` property." # The xmin, ymin, xmax, ymax of the MultiPoint should be returned. - mp = OGRGeometry('MULTIPOINT(5 23, 0 0, 10 50)') + mp = OGRGeometry("MULTIPOINT(5 23, 0 0, 10 50)") self.assertEqual((0.0, 0.0, 10.0, 50.0), mp.extent) # Testing on the 'real world' Polygon. poly = OGRGeometry(self.geometries.polygons[3].wkt) @@ -458,18 +474,18 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): def test_25D(self): "Testing 2.5D geometries." - pnt_25d = OGRGeometry('POINT(1 2 3)') - self.assertEqual('Point25D', pnt_25d.geom_type.name) + pnt_25d = OGRGeometry("POINT(1 2 3)") + self.assertEqual("Point25D", pnt_25d.geom_type.name) self.assertEqual(3.0, pnt_25d.z) self.assertEqual(3, pnt_25d.coord_dim) - ls_25d = OGRGeometry('LINESTRING(1 1 1,2 2 2,3 3 3)') - self.assertEqual('LineString25D', ls_25d.geom_type.name) + ls_25d = OGRGeometry("LINESTRING(1 1 1,2 2 2,3 3 3)") + self.assertEqual("LineString25D", ls_25d.geom_type.name) self.assertEqual([1.0, 2.0, 3.0], ls_25d.z) self.assertEqual(3, ls_25d.coord_dim) def test_pickle(self): "Testing pickle support." - g1 = OGRGeometry('LINESTRING(1 1 1,2 2 2,3 3 3)', 'WGS84') + g1 = OGRGeometry("LINESTRING(1 1 1,2 2 2,3 3 3)", "WGS84") g2 = pickle.loads(pickle.dumps(g1)) self.assertEqual(g1, g2) self.assertEqual(4326, g2.srs.srid) @@ -504,63 +520,114 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): def test_equivalence_regression(self): "Testing equivalence methods with non-OGRGeometry instances." - self.assertIsNotNone(OGRGeometry('POINT(0 0)')) - self.assertNotEqual(OGRGeometry('LINESTRING(0 0, 1 1)'), 3) + self.assertIsNotNone(OGRGeometry("POINT(0 0)")) + self.assertNotEqual(OGRGeometry("LINESTRING(0 0, 1 1)"), 3) def test_contains(self): - self.assertIs(OGRGeometry('POINT(0 0)').contains(OGRGeometry('POINT(0 0)')), True) - self.assertIs(OGRGeometry('POINT(0 0)').contains(OGRGeometry('POINT(0 1)')), False) + self.assertIs( + OGRGeometry("POINT(0 0)").contains(OGRGeometry("POINT(0 0)")), True + ) + self.assertIs( + OGRGeometry("POINT(0 0)").contains(OGRGeometry("POINT(0 1)")), False + ) def test_crosses(self): - self.assertIs(OGRGeometry('LINESTRING(0 0, 1 1)').crosses(OGRGeometry('LINESTRING(0 1, 1 0)')), True) - self.assertIs(OGRGeometry('LINESTRING(0 0, 0 1)').crosses(OGRGeometry('LINESTRING(1 0, 1 1)')), False) + self.assertIs( + OGRGeometry("LINESTRING(0 0, 1 1)").crosses( + OGRGeometry("LINESTRING(0 1, 1 0)") + ), + True, + ) + self.assertIs( + OGRGeometry("LINESTRING(0 0, 0 1)").crosses( + OGRGeometry("LINESTRING(1 0, 1 1)") + ), + False, + ) def test_disjoint(self): - self.assertIs(OGRGeometry('LINESTRING(0 0, 1 1)').disjoint(OGRGeometry('LINESTRING(0 1, 1 0)')), False) - self.assertIs(OGRGeometry('LINESTRING(0 0, 0 1)').disjoint(OGRGeometry('LINESTRING(1 0, 1 1)')), True) + self.assertIs( + OGRGeometry("LINESTRING(0 0, 1 1)").disjoint( + OGRGeometry("LINESTRING(0 1, 1 0)") + ), + False, + ) + self.assertIs( + OGRGeometry("LINESTRING(0 0, 0 1)").disjoint( + OGRGeometry("LINESTRING(1 0, 1 1)") + ), + True, + ) def test_equals(self): - self.assertIs(OGRGeometry('POINT(0 0)').contains(OGRGeometry('POINT(0 0)')), True) - self.assertIs(OGRGeometry('POINT(0 0)').contains(OGRGeometry('POINT(0 1)')), False) + self.assertIs( + OGRGeometry("POINT(0 0)").contains(OGRGeometry("POINT(0 0)")), True + ) + self.assertIs( + OGRGeometry("POINT(0 0)").contains(OGRGeometry("POINT(0 1)")), False + ) def test_intersects(self): - self.assertIs(OGRGeometry('LINESTRING(0 0, 1 1)').intersects(OGRGeometry('LINESTRING(0 1, 1 0)')), True) - self.assertIs(OGRGeometry('LINESTRING(0 0, 0 1)').intersects(OGRGeometry('LINESTRING(1 0, 1 1)')), False) + self.assertIs( + OGRGeometry("LINESTRING(0 0, 1 1)").intersects( + OGRGeometry("LINESTRING(0 1, 1 0)") + ), + True, + ) + self.assertIs( + OGRGeometry("LINESTRING(0 0, 0 1)").intersects( + OGRGeometry("LINESTRING(1 0, 1 1)") + ), + False, + ) def test_overlaps(self): self.assertIs( - OGRGeometry('POLYGON ((0 0, 0 2, 2 2, 2 0, 0 0))').overlaps( - OGRGeometry('POLYGON ((1 1, 1 5, 5 5, 5 1, 1 1))') - ), True + OGRGeometry("POLYGON ((0 0, 0 2, 2 2, 2 0, 0 0))").overlaps( + OGRGeometry("POLYGON ((1 1, 1 5, 5 5, 5 1, 1 1))") + ), + True, + ) + self.assertIs( + OGRGeometry("POINT(0 0)").overlaps(OGRGeometry("POINT(0 1)")), False ) - self.assertIs(OGRGeometry('POINT(0 0)').overlaps(OGRGeometry('POINT(0 1)')), False) def test_touches(self): self.assertIs( - OGRGeometry('POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))').touches(OGRGeometry('LINESTRING(0 2, 2 0)')), True + OGRGeometry("POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))").touches( + OGRGeometry("LINESTRING(0 2, 2 0)") + ), + True, + ) + self.assertIs( + OGRGeometry("POINT(0 0)").touches(OGRGeometry("POINT(0 1)")), False ) - self.assertIs(OGRGeometry('POINT(0 0)').touches(OGRGeometry('POINT(0 1)')), False) def test_within(self): self.assertIs( - OGRGeometry('POINT(0.5 0.5)').within(OGRGeometry('POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))')), True + OGRGeometry("POINT(0.5 0.5)").within( + OGRGeometry("POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))") + ), + True, + ) + self.assertIs( + OGRGeometry("POINT(0 0)").within(OGRGeometry("POINT(0 1)")), False ) - self.assertIs(OGRGeometry('POINT(0 0)').within(OGRGeometry('POINT(0 1)')), False) def test_from_gml(self): self.assertEqual( - OGRGeometry('POINT(0 0)'), + OGRGeometry("POINT(0 0)"), OGRGeometry.from_gml( '<gml:Point gml:id="p21" srsName="http://www.opengis.net/def/crs/EPSG/0/4326">' ' <gml:pos srsDimension="2">0 0</gml:pos>' - '</gml:Point>' + "</gml:Point>" ), ) def test_empty(self): - self.assertIs(OGRGeometry('POINT (0 0)').empty, False) - self.assertIs(OGRGeometry('POINT EMPTY').empty, True) + self.assertIs(OGRGeometry("POINT (0 0)").empty, False) + self.assertIs(OGRGeometry("POINT EMPTY").empty, True) def test_empty_point_to_geos(self): - p = OGRGeometry('POINT EMPTY', srs=4326) + p = OGRGeometry("POINT EMPTY", srs=4326) self.assertEqual(p.geos.ewkt, p.ewkt) diff --git a/tests/gis_tests/gdal_tests/test_raster.py b/tests/gis_tests/gdal_tests/test_raster.py index 229f09eaf4..795fdd079f 100644 --- a/tests/gis_tests/gdal_tests/test_raster.py +++ b/tests/gis_tests/gdal_tests/test_raster.py @@ -18,8 +18,11 @@ class GDALRasterTests(SimpleTestCase): """ Test a GDALRaster instance created from a file (GeoTiff). """ + def setUp(self): - self.rs_path = os.path.join(os.path.dirname(__file__), '../data/rasters/raster.tif') + self.rs_path = os.path.join( + os.path.dirname(__file__), "../data/rasters/raster.tif" + ) self.rs = GDALRaster(self.rs_path) def test_rs_name_repr(self): @@ -27,7 +30,7 @@ class GDALRasterTests(SimpleTestCase): self.assertRegex(repr(self.rs), r"<Raster object at 0x\w+>") def test_rs_driver(self): - self.assertEqual(self.rs.driver.name, 'GTiff') + self.assertEqual(self.rs.driver.name, "GTiff") def test_rs_size(self): self.assertEqual(self.rs.width, 163) @@ -35,14 +38,16 @@ class GDALRasterTests(SimpleTestCase): def test_rs_srs(self): self.assertEqual(self.rs.srs.srid, 3086) - self.assertEqual(self.rs.srs.units, (1.0, 'metre')) + self.assertEqual(self.rs.srs.units, (1.0, "metre")) def test_rs_srid(self): - rast = GDALRaster({ - 'width': 16, - 'height': 16, - 'srid': 4326, - }) + rast = GDALRaster( + { + "width": 16, + "height": 16, + "srid": 4326, + } + ) self.assertEqual(rast.srid, 4326) rast.srid = 3086 self.assertEqual(rast.srid, 3086) @@ -51,7 +56,7 @@ class GDALRasterTests(SimpleTestCase): # Assert correct values for file based raster self.assertEqual( self.rs.geotransform, - [511700.4680706557, 100.0, 0.0, 435103.3771231986, 0.0, -100.0] + [511700.4680706557, 100.0, 0.0, 435103.3771231986, 0.0, -100.0], ) self.assertEqual(self.rs.origin, [511700.4680706557, 435103.3771231986]) self.assertEqual(self.rs.origin.x, 511700.4680706557) @@ -85,18 +90,25 @@ class GDALRasterTests(SimpleTestCase): rsmem = GDALRaster(JSON_RASTER) error_geotransforms = [ [1, 2], - [1, 2, 3, 4, 5, 'foo'], - [1, 2, 3, 4, 5, 6, 'foo'], + [1, 2, 3, 4, 5, "foo"], + [1, 2, 3, 4, 5, 6, "foo"], ] - msg = 'Geotransform must consist of 6 numeric values.' + msg = "Geotransform must consist of 6 numeric values." for geotransform in error_geotransforms: - with self.subTest(i=geotransform), self.assertRaisesMessage(ValueError, msg): + with self.subTest(i=geotransform), self.assertRaisesMessage( + ValueError, msg + ): rsmem.geotransform = geotransform def test_rs_extent(self): self.assertEqual( self.rs.extent, - (511700.4680706557, 417703.3771231986, 528000.4680706557, 435103.3771231986) + ( + 511700.4680706557, + 417703.3771231986, + 528000.4680706557, + 435103.3771231986, + ), ) def test_rs_bands(self): @@ -105,16 +117,20 @@ class GDALRasterTests(SimpleTestCase): def test_memory_based_raster_creation(self): # Create uint8 raster with full pixel data range (0-255) - rast = GDALRaster({ - 'datatype': 1, - 'width': 16, - 'height': 16, - 'srid': 4326, - 'bands': [{ - 'data': range(256), - 'nodata_value': 255, - }], - }) + rast = GDALRaster( + { + "datatype": 1, + "width": 16, + "height": 16, + "srid": 4326, + "bands": [ + { + "data": range(256), + "nodata_value": 255, + } + ], + } + ) # Get array from raster result = rast.bands[0].data() @@ -126,38 +142,41 @@ class GDALRasterTests(SimpleTestCase): def test_file_based_raster_creation(self): # Prepare tempfile - rstfile = tempfile.NamedTemporaryFile(suffix='.tif') + rstfile = tempfile.NamedTemporaryFile(suffix=".tif") # Create file-based raster from scratch - GDALRaster({ - 'datatype': self.rs.bands[0].datatype(), - 'driver': 'tif', - 'name': rstfile.name, - 'width': 163, - 'height': 174, - 'nr_of_bands': 1, - 'srid': self.rs.srs.wkt, - 'origin': (self.rs.origin.x, self.rs.origin.y), - 'scale': (self.rs.scale.x, self.rs.scale.y), - 'skew': (self.rs.skew.x, self.rs.skew.y), - 'bands': [{ - 'data': self.rs.bands[0].data(), - 'nodata_value': self.rs.bands[0].nodata_value, - }], - }) + GDALRaster( + { + "datatype": self.rs.bands[0].datatype(), + "driver": "tif", + "name": rstfile.name, + "width": 163, + "height": 174, + "nr_of_bands": 1, + "srid": self.rs.srs.wkt, + "origin": (self.rs.origin.x, self.rs.origin.y), + "scale": (self.rs.scale.x, self.rs.scale.y), + "skew": (self.rs.skew.x, self.rs.skew.y), + "bands": [ + { + "data": self.rs.bands[0].data(), + "nodata_value": self.rs.bands[0].nodata_value, + } + ], + } + ) # Reload newly created raster from file restored_raster = GDALRaster(rstfile.name) # Presence of TOWGS84 depend on GDAL/Proj versions. self.assertEqual( - restored_raster.srs.wkt.replace('TOWGS84[0,0,0,0,0,0,0],', ''), - self.rs.srs.wkt.replace('TOWGS84[0,0,0,0,0,0,0],', '') + restored_raster.srs.wkt.replace("TOWGS84[0,0,0,0,0,0,0],", ""), + self.rs.srs.wkt.replace("TOWGS84[0,0,0,0,0,0,0],", ""), ) self.assertEqual(restored_raster.geotransform, self.rs.geotransform) if numpy: numpy.testing.assert_equal( - restored_raster.bands[0].data(), - self.rs.bands[0].data() + restored_raster.bands[0].data(), self.rs.bands[0].data() ) else: self.assertEqual(restored_raster.bands[0].data(), self.rs.bands[0].data()) @@ -165,11 +184,11 @@ class GDALRasterTests(SimpleTestCase): def test_nonexistent_file(self): msg = 'Unable to read raster source input "nonexistent.tif".' with self.assertRaisesMessage(GDALException, msg): - GDALRaster('nonexistent.tif') + GDALRaster("nonexistent.tif") def test_vsi_raster_creation(self): # Open a raster as a file object. - with open(self.rs_path, 'rb') as dat: + with open(self.rs_path, "rb") as dat: # Instantiate a raster from the file binary buffer. vsimem = GDALRaster(dat.read()) # The data of the in-memory file is equal to the source file. @@ -181,18 +200,22 @@ class GDALRasterTests(SimpleTestCase): self.assertEqual(result, target) def test_vsi_raster_deletion(self): - path = '/vsimem/raster.tif' + path = "/vsimem/raster.tif" # Create a vsi-based raster from scratch. - vsimem = GDALRaster({ - 'name': path, - 'driver': 'tif', - 'width': 4, - 'height': 4, - 'srid': 4326, - 'bands': [{ - 'data': range(16), - }], - }) + vsimem = GDALRaster( + { + "name": path, + "driver": "tif", + "width": 4, + "height": 4, + "srid": 4326, + "bands": [ + { + "data": range(16), + } + ], + } + ) # The virtual file exists. rst = GDALRaster(path) self.assertEqual(rst.width, 4) @@ -205,22 +228,26 @@ class GDALRasterTests(SimpleTestCase): GDALRaster(path) def test_vsi_invalid_buffer_error(self): - msg = 'Failed creating VSI raster from the input buffer.' + msg = "Failed creating VSI raster from the input buffer." with self.assertRaisesMessage(GDALException, msg): - GDALRaster(b'not-a-raster-buffer') + GDALRaster(b"not-a-raster-buffer") def test_vsi_buffer_property(self): # Create a vsi-based raster from scratch. - rast = GDALRaster({ - 'name': '/vsimem/raster.tif', - 'driver': 'tif', - 'width': 4, - 'height': 4, - 'srid': 4326, - 'bands': [{ - 'data': range(16), - }], - }) + rast = GDALRaster( + { + "name": "/vsimem/raster.tif", + "driver": "tif", + "width": 4, + "height": 4, + "srid": 4326, + "bands": [ + { + "data": range(16), + } + ], + } + ) # Do a round trip from raster to buffer to raster. result = GDALRaster(rast.vsi_buffer).bands[0].data() if numpy: @@ -231,10 +258,10 @@ class GDALRasterTests(SimpleTestCase): self.assertIsNone(self.rs.vsi_buffer) def test_vsi_vsizip_filesystem(self): - rst_zipfile = tempfile.NamedTemporaryFile(suffix='.zip') - with zipfile.ZipFile(rst_zipfile, mode='w') as zf: - zf.write(self.rs_path, 'raster.tif') - rst_path = '/vsizip/' + os.path.join(rst_zipfile.name, 'raster.tif') + rst_zipfile = tempfile.NamedTemporaryFile(suffix=".zip") + with zipfile.ZipFile(rst_zipfile, mode="w") as zf: + zf.write(self.rs_path, "raster.tif") + rst_path = "/vsizip/" + os.path.join(rst_zipfile.name, "raster.tif") rst = GDALRaster(rst_path) self.assertEqual(rst.driver.name, self.rs.driver.name) self.assertEqual(rst.name, rst_path) @@ -242,38 +269,41 @@ class GDALRasterTests(SimpleTestCase): self.assertIsNone(rst.vsi_buffer) def test_offset_size_and_shape_on_raster_creation(self): - rast = GDALRaster({ - 'datatype': 1, - 'width': 4, - 'height': 4, - 'srid': 4326, - 'bands': [{ - 'data': (1,), - 'offset': (1, 1), - 'size': (2, 2), - 'shape': (1, 1), - 'nodata_value': 2, - }], - }) + rast = GDALRaster( + { + "datatype": 1, + "width": 4, + "height": 4, + "srid": 4326, + "bands": [ + { + "data": (1,), + "offset": (1, 1), + "size": (2, 2), + "shape": (1, 1), + "nodata_value": 2, + } + ], + } + ) # Get array from raster. result = rast.bands[0].data() if numpy: result = result.flatten().tolist() # Band data is equal to nodata value except on input block of ones. - self.assertEqual( - result, - [2, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 2] - ) + self.assertEqual(result, [2, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 2]) def test_set_nodata_value_on_raster_creation(self): # Create raster filled with nodata values. - rast = GDALRaster({ - 'datatype': 1, - 'width': 2, - 'height': 2, - 'srid': 4326, - 'bands': [{'nodata_value': 23}], - }) + rast = GDALRaster( + { + "datatype": 1, + "width": 2, + "height": 2, + "srid": 4326, + "bands": [{"nodata_value": 23}], + } + ) # Get array from raster. result = rast.bands[0].data() if numpy: @@ -283,13 +313,15 @@ class GDALRasterTests(SimpleTestCase): def test_set_nodata_none_on_raster_creation(self): # Create raster without data and without nodata value. - rast = GDALRaster({ - 'datatype': 1, - 'width': 2, - 'height': 2, - 'srid': 4326, - 'bands': [{'nodata_value': None}], - }) + rast = GDALRaster( + { + "datatype": 1, + "width": 2, + "height": 2, + "srid": 4326, + "bands": [{"nodata_value": None}], + } + ) # Get array from raster. result = rast.bands[0].data() if numpy: @@ -299,54 +331,56 @@ class GDALRasterTests(SimpleTestCase): def test_raster_metadata_property(self): data = self.rs.metadata - self.assertEqual(data['DEFAULT'], {'AREA_OR_POINT': 'Area'}) - self.assertEqual(data['IMAGE_STRUCTURE'], {'INTERLEAVE': 'BAND'}) + self.assertEqual(data["DEFAULT"], {"AREA_OR_POINT": "Area"}) + self.assertEqual(data["IMAGE_STRUCTURE"], {"INTERLEAVE": "BAND"}) # Create file-based raster from scratch - source = GDALRaster({ - 'datatype': 1, - 'width': 2, - 'height': 2, - 'srid': 4326, - 'bands': [{'data': range(4), 'nodata_value': 99}], - }) + source = GDALRaster( + { + "datatype": 1, + "width": 2, + "height": 2, + "srid": 4326, + "bands": [{"data": range(4), "nodata_value": 99}], + } + ) # Set metadata on raster and on a band. metadata = { - 'DEFAULT': {'OWNER': 'Django', 'VERSION': '1.0', 'AREA_OR_POINT': 'Point'}, + "DEFAULT": {"OWNER": "Django", "VERSION": "1.0", "AREA_OR_POINT": "Point"}, } source.metadata = metadata source.bands[0].metadata = metadata - self.assertEqual(source.metadata['DEFAULT'], metadata['DEFAULT']) - self.assertEqual(source.bands[0].metadata['DEFAULT'], metadata['DEFAULT']) + self.assertEqual(source.metadata["DEFAULT"], metadata["DEFAULT"]) + self.assertEqual(source.bands[0].metadata["DEFAULT"], metadata["DEFAULT"]) # Update metadata on raster. metadata = { - 'DEFAULT': {'VERSION': '2.0'}, + "DEFAULT": {"VERSION": "2.0"}, } source.metadata = metadata - self.assertEqual(source.metadata['DEFAULT']['VERSION'], '2.0') + self.assertEqual(source.metadata["DEFAULT"]["VERSION"], "2.0") # Remove metadata on raster. metadata = { - 'DEFAULT': {'OWNER': None}, + "DEFAULT": {"OWNER": None}, } source.metadata = metadata - self.assertNotIn('OWNER', source.metadata['DEFAULT']) + self.assertNotIn("OWNER", source.metadata["DEFAULT"]) def test_raster_info_accessor(self): infos = self.rs.info # Data - info_lines = [line.strip() for line in infos.split('\n') if line.strip() != ''] + info_lines = [line.strip() for line in infos.split("\n") if line.strip() != ""] for line in [ - 'Driver: GTiff/GeoTIFF', - 'Files: {}'.format(self.rs_path), - 'Size is 163, 174', - 'Origin = (511700.468070655711927,435103.377123198588379)', - 'Pixel Size = (100.000000000000000,-100.000000000000000)', - 'Metadata:', - 'AREA_OR_POINT=Area', - 'Image Structure Metadata:', - 'INTERLEAVE=BAND', - 'Band 1 Block=163x50 Type=Byte, ColorInterp=Gray', - 'NoData Value=15' + "Driver: GTiff/GeoTIFF", + "Files: {}".format(self.rs_path), + "Size is 163, 174", + "Origin = (511700.468070655711927,435103.377123198588379)", + "Pixel Size = (100.000000000000000,-100.000000000000000)", + "Metadata:", + "AREA_OR_POINT=Area", + "Image Structure Metadata:", + "INTERLEAVE=BAND", + "Band 1 Block=163x50 Type=Byte, ColorInterp=Gray", + "NoData Value=15", ]: self.assertIn(line, info_lines) for line in [ @@ -361,72 +395,87 @@ class GDALRasterTests(SimpleTestCase): self.assertIn("NAD83 / Florida GDL Albers", infos) def test_compressed_file_based_raster_creation(self): - rstfile = tempfile.NamedTemporaryFile(suffix='.tif') + rstfile = tempfile.NamedTemporaryFile(suffix=".tif") # Make a compressed copy of an existing raster. - compressed = self.rs.warp({'papsz_options': {'compress': 'packbits'}, 'name': rstfile.name}) + compressed = self.rs.warp( + {"papsz_options": {"compress": "packbits"}, "name": rstfile.name} + ) # Check physically if compression worked. self.assertLess(os.path.getsize(compressed.name), os.path.getsize(self.rs.name)) # Create file-based raster with options from scratch. - compressed = GDALRaster({ - 'datatype': 1, - 'driver': 'tif', - 'name': rstfile.name, - 'width': 40, - 'height': 40, - 'srid': 3086, - 'origin': (500000, 400000), - 'scale': (100, -100), - 'skew': (0, 0), - 'bands': [{ - 'data': range(40 ^ 2), - 'nodata_value': 255, - }], - 'papsz_options': { - 'compress': 'packbits', - 'pixeltype': 'signedbyte', - 'blockxsize': 23, - 'blockysize': 23, + compressed = GDALRaster( + { + "datatype": 1, + "driver": "tif", + "name": rstfile.name, + "width": 40, + "height": 40, + "srid": 3086, + "origin": (500000, 400000), + "scale": (100, -100), + "skew": (0, 0), + "bands": [ + { + "data": range(40 ^ 2), + "nodata_value": 255, + } + ], + "papsz_options": { + "compress": "packbits", + "pixeltype": "signedbyte", + "blockxsize": 23, + "blockysize": 23, + }, } - }) + ) # Check if options used on creation are stored in metadata. # Reopening the raster ensures that all metadata has been written # to the file. compressed = GDALRaster(compressed.name) - self.assertEqual(compressed.metadata['IMAGE_STRUCTURE']['COMPRESSION'], 'PACKBITS',) - self.assertEqual(compressed.bands[0].metadata['IMAGE_STRUCTURE']['PIXELTYPE'], 'SIGNEDBYTE') - self.assertIn('Block=40x23', compressed.info) + self.assertEqual( + compressed.metadata["IMAGE_STRUCTURE"]["COMPRESSION"], + "PACKBITS", + ) + self.assertEqual( + compressed.bands[0].metadata["IMAGE_STRUCTURE"]["PIXELTYPE"], "SIGNEDBYTE" + ) + self.assertIn("Block=40x23", compressed.info) def test_raster_warp(self): # Create in memory raster - source = GDALRaster({ - 'datatype': 1, - 'driver': 'MEM', - 'name': 'sourceraster', - 'width': 4, - 'height': 4, - 'nr_of_bands': 1, - 'srid': 3086, - 'origin': (500000, 400000), - 'scale': (100, -100), - 'skew': (0, 0), - 'bands': [{ - 'data': range(16), - 'nodata_value': 255, - }], - }) + source = GDALRaster( + { + "datatype": 1, + "driver": "MEM", + "name": "sourceraster", + "width": 4, + "height": 4, + "nr_of_bands": 1, + "srid": 3086, + "origin": (500000, 400000), + "scale": (100, -100), + "skew": (0, 0), + "bands": [ + { + "data": range(16), + "nodata_value": 255, + } + ], + } + ) # Test altering the scale, width, and height of a raster data = { - 'scale': [200, -200], - 'width': 2, - 'height': 2, + "scale": [200, -200], + "width": 2, + "height": 2, } target = source.warp(data) - self.assertEqual(target.width, data['width']) - self.assertEqual(target.height, data['height']) - self.assertEqual(target.scale, data['scale']) + self.assertEqual(target.width, data["width"]) + self.assertEqual(target.height, data["height"]) + self.assertEqual(target.scale, data["scale"]) self.assertEqual(target.bands[0].datatype(), source.bands[0].datatype()) - self.assertEqual(target.name, 'sourceraster_copy.MEM') + self.assertEqual(target.name, "sourceraster_copy.MEM") result = target.bands[0].data() if numpy: result = result.flatten().tolist() @@ -434,70 +483,92 @@ class GDALRasterTests(SimpleTestCase): # Test altering the name and datatype (to float) data = { - 'name': '/path/to/targetraster.tif', - 'datatype': 6, + "name": "/path/to/targetraster.tif", + "datatype": 6, } target = source.warp(data) self.assertEqual(target.bands[0].datatype(), 6) - self.assertEqual(target.name, '/path/to/targetraster.tif') - self.assertEqual(target.driver.name, 'MEM') + self.assertEqual(target.name, "/path/to/targetraster.tif") + self.assertEqual(target.driver.name, "MEM") result = target.bands[0].data() if numpy: result = result.flatten().tolist() self.assertEqual( result, - [0.0, 1.0, 2.0, 3.0, - 4.0, 5.0, 6.0, 7.0, - 8.0, 9.0, 10.0, 11.0, - 12.0, 13.0, 14.0, 15.0] + [ + 0.0, + 1.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, + 11.0, + 12.0, + 13.0, + 14.0, + 15.0, + ], ) def test_raster_warp_nodata_zone(self): # Create in memory raster. - source = GDALRaster({ - 'datatype': 1, - 'driver': 'MEM', - 'width': 4, - 'height': 4, - 'srid': 3086, - 'origin': (500000, 400000), - 'scale': (100, -100), - 'skew': (0, 0), - 'bands': [{ - 'data': range(16), - 'nodata_value': 23, - }], - }) + source = GDALRaster( + { + "datatype": 1, + "driver": "MEM", + "width": 4, + "height": 4, + "srid": 3086, + "origin": (500000, 400000), + "scale": (100, -100), + "skew": (0, 0), + "bands": [ + { + "data": range(16), + "nodata_value": 23, + } + ], + } + ) # Warp raster onto a location that does not cover any pixels of the original. - result = source.warp({'origin': (200000, 200000)}).bands[0].data() + result = source.warp({"origin": (200000, 200000)}).bands[0].data() if numpy: result = result.flatten().tolist() # The result is an empty raster filled with the correct nodata value. self.assertEqual(result, [23] * 16) def test_raster_clone(self): - rstfile = tempfile.NamedTemporaryFile(suffix='.tif') + rstfile = tempfile.NamedTemporaryFile(suffix=".tif") tests = [ - ('MEM', '', 23), # In memory raster. - ('tif', rstfile.name, 99), # In file based raster. + ("MEM", "", 23), # In memory raster. + ("tif", rstfile.name, 99), # In file based raster. ] for driver, name, nodata_value in tests: with self.subTest(driver=driver): - source = GDALRaster({ - 'datatype': 1, - 'driver': driver, - 'name': name, - 'width': 4, - 'height': 4, - 'srid': 3086, - 'origin': (500000, 400000), - 'scale': (100, -100), - 'skew': (0, 0), - 'bands': [{ - 'data': range(16), - 'nodata_value': nodata_value, - }], - }) + source = GDALRaster( + { + "datatype": 1, + "driver": driver, + "name": name, + "width": 4, + "height": 4, + "srid": 3086, + "origin": (500000, 400000), + "scale": (100, -100), + "skew": (0, 0), + "bands": [ + { + "data": range(16), + "nodata_value": nodata_value, + } + ], + } + ) clone = source.clone() self.assertNotEqual(clone.name, source.name) self.assertEqual(clone._write, source._write) @@ -512,31 +583,35 @@ class GDALRasterTests(SimpleTestCase): def test_raster_transform(self): tests = [ 3086, - '3086', + "3086", SpatialReference(3086), ] for srs in tests: with self.subTest(srs=srs): # Prepare tempfile and nodata value. - rstfile = tempfile.NamedTemporaryFile(suffix='.tif') + rstfile = tempfile.NamedTemporaryFile(suffix=".tif") ndv = 99 # Create in file based raster. - source = GDALRaster({ - 'datatype': 1, - 'driver': 'tif', - 'name': rstfile.name, - 'width': 5, - 'height': 5, - 'nr_of_bands': 1, - 'srid': 4326, - 'origin': (-5, 5), - 'scale': (2, -2), - 'skew': (0, 0), - 'bands': [{ - 'data': range(25), - 'nodata_value': ndv, - }], - }) + source = GDALRaster( + { + "datatype": 1, + "driver": "tif", + "name": rstfile.name, + "width": 5, + "height": 5, + "nr_of_bands": 1, + "srid": 4326, + "origin": (-5, 5), + "scale": (2, -2), + "skew": (0, 0), + "bands": [ + { + "data": range(25), + "nodata_value": ndv, + } + ], + } + ) target = source.transform(srs) @@ -560,36 +635,82 @@ class GDALRasterTests(SimpleTestCase): self.assertEqual( result, [ - ndv, ndv, ndv, ndv, 4, ndv, ndv, - ndv, ndv, 2, 3, 9, ndv, ndv, - ndv, 1, 2, 8, 13, 19, ndv, - 0, 6, 6, 12, 18, 18, 24, - ndv, 10, 11, 16, 22, 23, ndv, - ndv, ndv, 15, 21, 22, ndv, ndv, - ndv, ndv, 20, ndv, ndv, ndv, ndv, + ndv, + ndv, + ndv, + ndv, + 4, + ndv, + ndv, + ndv, + ndv, + 2, + 3, + 9, + ndv, + ndv, + ndv, + 1, + 2, + 8, + 13, + 19, + ndv, + 0, + 6, + 6, + 12, + 18, + 18, + 24, + ndv, + 10, + 11, + 16, + 22, + 23, + ndv, + ndv, + ndv, + 15, + 21, + 22, + ndv, + ndv, + ndv, + ndv, + 20, + ndv, + ndv, + ndv, + ndv, ], ) def test_raster_transform_clone(self): - with mock.patch.object(GDALRaster, 'clone') as mocked_clone: + with mock.patch.object(GDALRaster, "clone") as mocked_clone: # Create in file based raster. - rstfile = tempfile.NamedTemporaryFile(suffix='.tif') - source = GDALRaster({ - 'datatype': 1, - 'driver': 'tif', - 'name': rstfile.name, - 'width': 5, - 'height': 5, - 'nr_of_bands': 1, - 'srid': 4326, - 'origin': (-5, 5), - 'scale': (2, -2), - 'skew': (0, 0), - 'bands': [{ - 'data': range(25), - 'nodata_value': 99, - }], - }) + rstfile = tempfile.NamedTemporaryFile(suffix=".tif") + source = GDALRaster( + { + "datatype": 1, + "driver": "tif", + "name": rstfile.name, + "width": 5, + "height": 5, + "nr_of_bands": 1, + "srid": 4326, + "origin": (-5, 5), + "scale": (2, -2), + "skew": (0, 0), + "bands": [ + { + "data": range(25), + "nodata_value": 99, + } + ], + } + ) # transform() returns a clone because it is the same SRID and # driver. source.transform(4326) @@ -597,57 +718,63 @@ class GDALRasterTests(SimpleTestCase): def test_raster_transform_clone_name(self): # Create in file based raster. - rstfile = tempfile.NamedTemporaryFile(suffix='.tif') - source = GDALRaster({ - 'datatype': 1, - 'driver': 'tif', - 'name': rstfile.name, - 'width': 5, - 'height': 5, - 'nr_of_bands': 1, - 'srid': 4326, - 'origin': (-5, 5), - 'scale': (2, -2), - 'skew': (0, 0), - 'bands': [{ - 'data': range(25), - 'nodata_value': 99, - }], - }) - clone_name = rstfile.name + '_respect_name.GTiff' + rstfile = tempfile.NamedTemporaryFile(suffix=".tif") + source = GDALRaster( + { + "datatype": 1, + "driver": "tif", + "name": rstfile.name, + "width": 5, + "height": 5, + "nr_of_bands": 1, + "srid": 4326, + "origin": (-5, 5), + "scale": (2, -2), + "skew": (0, 0), + "bands": [ + { + "data": range(25), + "nodata_value": 99, + } + ], + } + ) + clone_name = rstfile.name + "_respect_name.GTiff" target = source.transform(4326, name=clone_name) self.assertEqual(target.name, clone_name) class GDALBandTests(SimpleTestCase): - rs_path = os.path.join(os.path.dirname(__file__), '../data/rasters/raster.tif') + rs_path = os.path.join(os.path.dirname(__file__), "../data/rasters/raster.tif") def test_band_data(self): rs = GDALRaster(self.rs_path) band = rs.bands[0] self.assertEqual(band.width, 163) self.assertEqual(band.height, 174) - self.assertEqual(band.description, '') + self.assertEqual(band.description, "") self.assertEqual(band.datatype(), 1) - self.assertEqual(band.datatype(as_string=True), 'GDT_Byte') + self.assertEqual(band.datatype(as_string=True), "GDT_Byte") self.assertEqual(band.color_interp(), 1) - self.assertEqual(band.color_interp(as_string=True), 'GCI_GrayIndex') + self.assertEqual(band.color_interp(as_string=True), "GCI_GrayIndex") self.assertEqual(band.nodata_value, 15) if numpy: data = band.data() assert_array = numpy.loadtxt( - os.path.join(os.path.dirname(__file__), '../data/rasters/raster.numpy.txt') + os.path.join( + os.path.dirname(__file__), "../data/rasters/raster.numpy.txt" + ) ) numpy.testing.assert_equal(data, assert_array) self.assertEqual(data.shape, (band.height, band.width)) def test_band_statistics(self): with tempfile.TemporaryDirectory() as tmp_dir: - rs_path = os.path.join(tmp_dir, 'raster.tif') + rs_path = os.path.join(tmp_dir, "raster.tif") shutil.copyfile(self.rs_path, rs_path) rs = GDALRaster(rs_path) band = rs.bands[0] - pam_file = rs_path + '.aux.xml' + pam_file = rs_path + ".aux.xml" smin, smax, smean, sstd = band.statistics(approximate=True) self.assertEqual(smin, 0) self.assertEqual(smax, 9) @@ -676,19 +803,21 @@ class GDALBandTests(SimpleTestCase): # Setting attributes in write mode raises exception in the _flush method with self.assertRaises(GDALException): - setattr(band, 'nodata_value', 10) + setattr(band, "nodata_value", 10) def test_band_data_setters(self): # Create in-memory raster and get band - rsmem = GDALRaster({ - 'datatype': 1, - 'driver': 'MEM', - 'name': 'mem_rst', - 'width': 10, - 'height': 10, - 'nr_of_bands': 1, - 'srid': 4326, - }) + rsmem = GDALRaster( + { + "datatype": 1, + "driver": "MEM", + "name": "mem_rst", + "width": 10, + "height": 10, + "nr_of_bands": 1, + "srid": 4326, + } + ) bandmem = rsmem.bands[0] # Set nodata value @@ -698,13 +827,15 @@ class GDALBandTests(SimpleTestCase): # Set data for entire dataset bandmem.data(range(100)) if numpy: - numpy.testing.assert_equal(bandmem.data(), numpy.arange(100).reshape(10, 10)) + numpy.testing.assert_equal( + bandmem.data(), numpy.arange(100).reshape(10, 10) + ) else: self.assertEqual(bandmem.data(), list(range(100))) # Prepare data for setting values in subsequent tests block = list(range(100, 104)) - packed_block = struct.pack('<' + 'B B B B', *block) + packed_block = struct.pack("<" + "B B B B", *block) # Set data from list bandmem.data(block, (1, 1), (2, 2)) @@ -748,10 +879,10 @@ class GDALBandTests(SimpleTestCase): # Set data from numpy array if numpy: - bandmem.data(numpy.array(block, dtype='int8').reshape(2, 2), (1, 1), (2, 2)) + bandmem.data(numpy.array(block, dtype="int8").reshape(2, 2), (1, 1), (2, 2)) numpy.testing.assert_equal( bandmem.data(offset=(1, 1), size=(2, 2)), - numpy.array(block).reshape(2, 2) + numpy.array(block).reshape(2, 2), ) # Test json input data @@ -759,19 +890,20 @@ class GDALBandTests(SimpleTestCase): bandmemjson = rsmemjson.bands[0] if numpy: numpy.testing.assert_equal( - bandmemjson.data(), - numpy.array(range(25)).reshape(5, 5) + bandmemjson.data(), numpy.array(range(25)).reshape(5, 5) ) else: self.assertEqual(bandmemjson.data(), list(range(25))) def test_band_statistics_automatic_refresh(self): - rsmem = GDALRaster({ - 'srid': 4326, - 'width': 2, - 'height': 2, - 'bands': [{'data': [0] * 4, 'nodata_value': 99}], - }) + rsmem = GDALRaster( + { + "srid": 4326, + "width": 2, + "height": 2, + "bands": [{"data": [0] * 4, "nodata_value": 99}], + } + ) band = rsmem.bands[0] # Populate statistics cache self.assertEqual(band.statistics(), (0, 0, 0, 0)) @@ -785,31 +917,37 @@ class GDALBandTests(SimpleTestCase): self.assertEqual(band.statistics(), (1.0, 1.0, 1.0, 0.0)) def test_band_statistics_empty_band(self): - rsmem = GDALRaster({ - 'srid': 4326, - 'width': 1, - 'height': 1, - 'bands': [{'data': [0], 'nodata_value': 0}], - }) + rsmem = GDALRaster( + { + "srid": 4326, + "width": 1, + "height": 1, + "bands": [{"data": [0], "nodata_value": 0}], + } + ) self.assertEqual(rsmem.bands[0].statistics(), (None, None, None, None)) def test_band_delete_nodata(self): - rsmem = GDALRaster({ - 'srid': 4326, - 'width': 1, - 'height': 1, - 'bands': [{'data': [0], 'nodata_value': 1}], - }) + rsmem = GDALRaster( + { + "srid": 4326, + "width": 1, + "height": 1, + "bands": [{"data": [0], "nodata_value": 1}], + } + ) rsmem.bands[0].nodata_value = None self.assertIsNone(rsmem.bands[0].nodata_value) def test_band_data_replication(self): - band = GDALRaster({ - 'srid': 4326, - 'width': 3, - 'height': 3, - 'bands': [{'data': range(10, 19), 'nodata_value': 0}], - }).bands[0] + band = GDALRaster( + { + "srid": 4326, + "width": 3, + "height": 3, + "bands": [{"data": range(10, 19), "nodata_value": 0}], + } + ).bands[0] # Variations for input (data, shape, expected result). combos = ( @@ -820,6 +958,8 @@ class GDALBandTests(SimpleTestCase): for combo in combos: band.data(combo[0], shape=combo[1]) if numpy: - numpy.testing.assert_equal(band.data(), numpy.array(combo[2]).reshape(3, 3)) + numpy.testing.assert_equal( + band.data(), numpy.array(combo[2]).reshape(3, 3) + ) else: self.assertEqual(band.data(), list(combo[2])) diff --git a/tests/gis_tests/gdal_tests/test_srs.py b/tests/gis_tests/gdal_tests/test_srs.py index ddc2a65bde..320d215482 100644 --- a/tests/gis_tests/gdal_tests/test_srs.py +++ b/tests/gis_tests/gdal_tests/test_srs.py @@ -1,7 +1,11 @@ from unittest import skipIf from django.contrib.gis.gdal import ( - GDAL_VERSION, AxisOrder, CoordTransform, GDALException, SpatialReference, + GDAL_VERSION, + AxisOrder, + CoordTransform, + GDALException, + SpatialReference, SRSException, ) from django.contrib.gis.geos import GEOSGeometry @@ -15,7 +19,7 @@ class TestSRS: setattr(self, key, value) -WGS84_proj = '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ' +WGS84_proj = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs " # Some Spatial Reference examples srlist = ( @@ -25,10 +29,20 @@ srlist = ( 'PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",' '0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],' 'AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]', - epsg=4326, projected=False, geographic=True, local=False, - lin_name='unknown', ang_name='degree', lin_units=1.0, ang_units=0.0174532925199, - auth={'GEOGCS': ('EPSG', '4326'), 'spheroid': ('EPSG', '7030')}, - attr=(('DATUM', 'WGS_1984'), (('SPHEROID', 1), '6378137'), ('primem|authority', 'EPSG'),), + epsg=4326, + projected=False, + geographic=True, + local=False, + lin_name="unknown", + ang_name="degree", + lin_units=1.0, + ang_units=0.0174532925199, + auth={"GEOGCS": ("EPSG", "4326"), "spheroid": ("EPSG", "7030")}, + attr=( + ("DATUM", "WGS_1984"), + (("SPHEROID", 1), "6378137"), + ("primem|authority", "EPSG"), + ), ), TestSRS( 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",' @@ -42,13 +56,23 @@ srlist = ( 'PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],' 'PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],' 'AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32140"]]', - epsg=32140, projected=True, geographic=False, local=False, - lin_name='metre', ang_name='degree', lin_units=1.0, ang_units=0.0174532925199, - auth={'PROJCS': ('EPSG', '32140'), 'spheroid': ('EPSG', '7019'), 'unit': ('EPSG', '9001')}, + epsg=32140, + projected=True, + geographic=False, + local=False, + lin_name="metre", + ang_name="degree", + lin_units=1.0, + ang_units=0.0174532925199, + auth={ + "PROJCS": ("EPSG", "32140"), + "spheroid": ("EPSG", "7019"), + "unit": ("EPSG", "9001"), + }, attr=( - ('DATUM', 'North_American_Datum_1983'), - (('SPHEROID', 2), '298.257222101'), - ('PROJECTION', 'Lambert_Conformal_Conic_2SP'), + ("DATUM", "North_American_Datum_1983"), + (("SPHEROID", 2), "298.257222101"), + ("PROJECTION", "Lambert_Conformal_Conic_2SP"), ), ), TestSRS( @@ -61,18 +85,35 @@ srlist = ( 'PARAMETER["central_meridian",-99],PARAMETER["standard_parallel_1",28.3833333333333],' 'PARAMETER["standard_parallel_2",30.2833333333333],PARAMETER["latitude_of_origin",27.8333333333333],' 'UNIT["US survey foot",0.304800609601219],AXIS["Easting",EAST],AXIS["Northing",NORTH]]', - epsg=None, projected=True, geographic=False, local=False, - lin_name='US survey foot', ang_name='Degree', lin_units=0.3048006096012192, ang_units=0.0174532925199, - auth={'PROJCS': (None, None)}, - attr=(('PROJCS|GeOgCs|spheroid', 'GRS 1980'), (('projcs', 9), 'UNIT'), (('projcs', 11), 'AXIS'),), + epsg=None, + projected=True, + geographic=False, + local=False, + lin_name="US survey foot", + ang_name="Degree", + lin_units=0.3048006096012192, + ang_units=0.0174532925199, + auth={"PROJCS": (None, None)}, + attr=( + ("PROJCS|GeOgCs|spheroid", "GRS 1980"), + (("projcs", 9), "UNIT"), + (("projcs", 11), "AXIS"), + ), ), # This is really ESRI format, not WKT -- but the import should work the same TestSRS( 'LOCAL_CS["Non-Earth (Meter)",LOCAL_DATUM["Local Datum",32767],' 'UNIT["Meter",1],AXIS["X",EAST],AXIS["Y",NORTH]]', - esri=True, epsg=None, projected=False, geographic=False, local=True, - lin_name='Meter', ang_name='degree', lin_units=1.0, ang_units=0.0174532925199, - attr=(('LOCAL_DATUM', 'Local Datum'),), + esri=True, + epsg=None, + projected=False, + geographic=False, + local=True, + lin_name="Meter", + ang_name="degree", + lin_units=1.0, + ang_units=0.0174532925199, + attr=(("LOCAL_DATUM", "Local Datum"),), ), ) @@ -83,8 +124,9 @@ well_known = ( 'AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],' 'PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,' 'AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]', - wk='WGS84', name='WGS 84', - attrs=(('GEOGCS|AUTHORITY', 1, '4326'), ('SPHEROID', 'WGS 84')), + wk="WGS84", + name="WGS 84", + attrs=(("GEOGCS|AUTHORITY", 1, "4326"), ("SPHEROID", "WGS 84")), ), TestSRS( 'GEOGCS["WGS 72",DATUM["WGS_1972",SPHEROID["WGS 72",6378135,298.26,' @@ -92,8 +134,9 @@ well_known = ( 'PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],' 'UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],' 'AUTHORITY["EPSG","4322"]]', - wk='WGS72', name='WGS 72', - attrs=(('GEOGCS|AUTHORITY', 1, '4322'), ('SPHEROID', 'WGS 72')), + wk="WGS72", + name="WGS 72", + attrs=(("GEOGCS|AUTHORITY", 1, "4322"), ("SPHEROID", "WGS 72")), ), TestSRS( 'GEOGCS["NAD27",DATUM["North_American_Datum_1927",' @@ -102,8 +145,9 @@ well_known = ( 'PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],' 'UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],' 'AUTHORITY["EPSG","4267"]]', - wk='NAD27', name='NAD27', - attrs=(('GEOGCS|AUTHORITY', 1, '4267'), ('SPHEROID', 'Clarke 1866')) + wk="NAD27", + name="NAD27", + attrs=(("GEOGCS|AUTHORITY", 1, "4267"), ("SPHEROID", "Clarke 1866")), ), TestSRS( 'GEOGCS["NAD83",DATUM["North_American_Datum_1983",' @@ -112,15 +156,16 @@ well_known = ( 'PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],' 'UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],' 'AUTHORITY["EPSG","4269"]]', - wk='NAD83', name='NAD83', - attrs=(('GEOGCS|AUTHORITY', 1, '4269'), ('SPHEROID', 'GRS 1980')), + wk="NAD83", + name="NAD83", + attrs=(("GEOGCS|AUTHORITY", 1, "4269"), ("SPHEROID", "GRS 1980")), ), TestSRS( 'PROJCS["NZGD49 / Karamea Circuit",GEOGCS["NZGD49",' 'DATUM["New_Zealand_Geodetic_Datum_1949",' 'SPHEROID["International 1924",6378388,297,' 'AUTHORITY["EPSG","7022"]],' - 'TOWGS84[59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993],' + "TOWGS84[59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993]," 'AUTHORITY["EPSG","6272"]],PRIMEM["Greenwich",0,' 'AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,' 'AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4272"]],' @@ -130,13 +175,17 @@ well_known = ( 'PARAMETER["scale_factor",1],PARAMETER["false_easting",300000],' 'PARAMETER["false_northing",700000],' 'UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","27216"]]', - wk='EPSG:27216', name='NZGD49 / Karamea Circuit', - attrs=(('PROJECTION', 'Transverse_Mercator'), ('SPHEROID', 'International 1924')), + wk="EPSG:27216", + name="NZGD49 / Karamea Circuit", + attrs=( + ("PROJECTION", "Transverse_Mercator"), + ("SPHEROID", "International 1924"), + ), ), ) bad_srlist = ( - 'Foobar', + "Foobar", 'OOJCS["NAD83 / Texas South Central",GEOGCS["NAD83",' 'DATUM["North_American_Datum_1983",' 'SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],' @@ -153,7 +202,6 @@ bad_srlist = ( class SpatialRefTest(SimpleTestCase): - def test01_wkt(self): "Testing initialization on valid OGC WKT." for s in srlist: @@ -176,14 +224,18 @@ class SpatialRefTest(SimpleTestCase): srs = SpatialReference(s.wkt) # GDAL 3 strips UNIT part in the last occurrence. self.assertEqual( - s.wkt.replace(',UNIT["Meter",1]', ''), - srs.wkt.replace(',UNIT["Meter",1]', ''), + s.wkt.replace(',UNIT["Meter",1]', ""), + srs.wkt.replace(',UNIT["Meter",1]', ""), ) def test04_proj(self): """PROJ import and export.""" proj_parts = [ - '+proj=longlat', '+ellps=WGS84', '+towgs84=0,0,0,0,0,0,0', '+datum=WGS84', '+no_defs' + "+proj=longlat", + "+ellps=WGS84", + "+towgs84=0,0,0,0,0,0,0", + "+datum=WGS84", + "+no_defs", ] srs1 = SpatialReference(srlist[0].wkt) srs2 = SpatialReference(WGS84_proj) @@ -197,7 +249,7 @@ class SpatialRefTest(SimpleTestCase): srs1 = SpatialReference(s.wkt) srs2 = SpatialReference(s.epsg) srs3 = SpatialReference(str(s.epsg)) - srs4 = SpatialReference('EPSG:%d' % s.epsg) + srs4 = SpatialReference("EPSG:%d" % s.epsg) for srs in (srs1, srs2, srs3, srs4): for attr, expected in s.attr: self.assertEqual(expected, srs[attr]) @@ -221,7 +273,7 @@ class SpatialRefTest(SimpleTestCase): def test09_authority(self): "Testing the authority name & code routines." for s in srlist: - if hasattr(s, 'auth'): + if hasattr(s, "auth"): srs = SpatialReference(s.wkt) for target, tup in s.auth.items(): self.assertEqual(tup[0], srs.auth_name(target)) @@ -252,21 +304,21 @@ class SpatialRefTest(SimpleTestCase): def test12_coordtransform(self): "Testing initialization of a CoordTransform." - target = SpatialReference('WGS84') + target = SpatialReference("WGS84") CoordTransform(SpatialReference(srlist[0].wkt), target) def test13_attr_value(self): "Testing the attr_value() method." - s1 = SpatialReference('WGS84') + s1 = SpatialReference("WGS84") with self.assertRaises(TypeError): s1.__getitem__(0) with self.assertRaises(TypeError): - s1.__getitem__(('GEOGCS', 'foo')) - self.assertEqual('WGS 84', s1['GEOGCS']) - self.assertEqual('WGS_1984', s1['DATUM']) - self.assertEqual('EPSG', s1['AUTHORITY']) - self.assertEqual(4326, int(s1['AUTHORITY', 1])) - self.assertIsNone(s1['FOOBAR']) + s1.__getitem__(("GEOGCS", "foo")) + self.assertEqual("WGS 84", s1["GEOGCS"]) + self.assertEqual("WGS_1984", s1["DATUM"]) + self.assertEqual("EPSG", s1["AUTHORITY"]) + self.assertEqual(4326, int(s1["AUTHORITY", 1])) + self.assertIsNone(s1["FOOBAR"]) def test_unicode(self): wkt = ( @@ -285,17 +337,17 @@ class SpatialRefTest(SimpleTestCase): srs.import_wkt(wkt) for srs in srs_list: - self.assertEqual(srs.name, 'DHDN / Soldner 39 Langschoß') + self.assertEqual(srs.name, "DHDN / Soldner 39 Langschoß") self.assertEqual(srs.wkt, wkt) - self.assertIn('Langschoß', srs.pretty_wkt) - self.assertIn('Langschoß', srs.xml) + self.assertIn("Langschoß", srs.pretty_wkt) + self.assertIn("Langschoß", srs.xml) - @skipIf(GDAL_VERSION < (3, 0), 'GDAL >= 3.0 is required') + @skipIf(GDAL_VERSION < (3, 0), "GDAL >= 3.0 is required") def test_axis_order(self): wgs84_trad = SpatialReference(4326, axis_order=AxisOrder.TRADITIONAL) wgs84_auth = SpatialReference(4326, axis_order=AxisOrder.AUTHORITY) # Coordinate interpretation may depend on the srs axis predicate. - pt = GEOSGeometry('POINT (992385.4472045 481455.4944650)', 2774) + pt = GEOSGeometry("POINT (992385.4472045 481455.4944650)", 2774) pt_trad = pt.transform(wgs84_trad, clone=True) self.assertAlmostEqual(pt_trad.x, -104.609, 3) self.assertAlmostEqual(pt_trad.y, 38.255, 3) @@ -308,18 +360,18 @@ class SpatialRefTest(SimpleTestCase): self.assertAlmostEqual(pt_auth.y, -104.609, 3) def test_axis_order_invalid(self): - msg = 'SpatialReference.axis_order must be an AxisOrder instance.' + msg = "SpatialReference.axis_order must be an AxisOrder instance." with self.assertRaisesMessage(ValueError, msg): - SpatialReference(4326, axis_order='other') + SpatialReference(4326, axis_order="other") @skipIf(GDAL_VERSION > (3, 0), "GDAL < 3.0 doesn't support authority.") def test_axis_order_non_traditional_invalid(self): - msg = 'AxisOrder.AUTHORITY is not supported in GDAL < 3.0.' + msg = "AxisOrder.AUTHORITY is not supported in GDAL < 3.0." with self.assertRaisesMessage(ValueError, msg): SpatialReference(4326, axis_order=AxisOrder.AUTHORITY) def test_esri(self): - srs = SpatialReference('NAD83') + srs = SpatialReference("NAD83") pre_esri_wkt = srs.wkt srs.to_esri() self.assertNotEqual(srs.wkt, pre_esri_wkt) diff --git a/tests/gis_tests/gdal_tests/tests.py b/tests/gis_tests/gdal_tests/tests.py index 607753271f..d4ef7dc7ef 100644 --- a/tests/gis_tests/gdal_tests/tests.py +++ b/tests/gis_tests/gdal_tests/tests.py @@ -1,18 +1,16 @@ import unittest -from django.contrib.gis.gdal import ( - GDAL_VERSION, gdal_full_version, gdal_version, -) +from django.contrib.gis.gdal import GDAL_VERSION, gdal_full_version, gdal_version class GDALTest(unittest.TestCase): def test_gdal_version(self): if GDAL_VERSION: - self.assertEqual(gdal_version(), ('%s.%s.%s' % GDAL_VERSION).encode()) + self.assertEqual(gdal_version(), ("%s.%s.%s" % GDAL_VERSION).encode()) else: - self.assertIn(b'.', gdal_version()) + self.assertIn(b".", gdal_version()) def test_gdal_full_version(self): full_version = gdal_full_version() self.assertIn(gdal_version(), full_version) - self.assertTrue(full_version.startswith(b'GDAL')) + self.assertTrue(full_version.startswith(b"GDAL")) diff --git a/tests/gis_tests/geo3d/models.py b/tests/gis_tests/geo3d/models.py index cd33a1da39..456be077f0 100644 --- a/tests/gis_tests/geo3d/models.py +++ b/tests/gis_tests/geo3d/models.py @@ -16,7 +16,7 @@ class City3D(NamedModel): pointg = models.PointField(dim=3, geography=True) class Meta: - required_db_features = {'supports_3d_storage'} + required_db_features = {"supports_3d_storage"} class Interstate2D(NamedModel): @@ -27,7 +27,7 @@ class Interstate3D(NamedModel): line = models.LineStringField(dim=3, srid=4269) class Meta: - required_db_features = {'supports_3d_storage'} + required_db_features = {"supports_3d_storage"} class InterstateProj2D(NamedModel): @@ -38,7 +38,7 @@ class InterstateProj3D(NamedModel): line = models.LineStringField(dim=3, srid=32140) class Meta: - required_db_features = {'supports_3d_storage'} + required_db_features = {"supports_3d_storage"} class Polygon2D(NamedModel): @@ -49,11 +49,10 @@ class Polygon3D(NamedModel): poly = models.PolygonField(dim=3, srid=32140) class Meta: - required_db_features = {'supports_3d_storage'} + required_db_features = {"supports_3d_storage"} class SimpleModel(models.Model): - class Meta: abstract = True @@ -66,11 +65,11 @@ class Point3D(SimpleModel): point = models.PointField(dim=3) class Meta: - required_db_features = {'supports_3d_storage'} + required_db_features = {"supports_3d_storage"} class MultiPoint3D(SimpleModel): mpoint = models.MultiPointField(dim=3) class Meta: - required_db_features = {'supports_3d_storage'} + required_db_features = {"supports_3d_storage"} diff --git a/tests/gis_tests/geo3d/tests.py b/tests/gis_tests/geo3d/tests.py index ad34caf7e0..2e99d741ef 100644 --- a/tests/gis_tests/geo3d/tests.py +++ b/tests/gis_tests/geo3d/tests.py @@ -3,32 +3,45 @@ import re from django.contrib.gis.db.models import Extent3D, Union from django.contrib.gis.db.models.functions import ( - AsGeoJSON, AsKML, Length, Perimeter, Scale, Translate, + AsGeoJSON, + AsKML, + Length, + Perimeter, + Scale, + Translate, ) from django.contrib.gis.geos import GEOSGeometry, LineString, Point, Polygon from django.test import TestCase, skipUnlessDBFeature from ..utils import FuncTestMixin from .models import ( - City3D, Interstate2D, Interstate3D, InterstateProj2D, InterstateProj3D, - MultiPoint3D, Point2D, Point3D, Polygon2D, Polygon3D, + City3D, + Interstate2D, + Interstate3D, + InterstateProj2D, + InterstateProj3D, + MultiPoint3D, + Point2D, + Point3D, + Polygon2D, + Polygon3D, ) -data_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data')) -city_file = os.path.join(data_path, 'cities', 'cities.shp') -vrt_file = os.path.join(data_path, 'test_vrt', 'test_vrt.vrt') +data_path = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "data")) +city_file = os.path.join(data_path, "cities", "cities.shp") +vrt_file = os.path.join(data_path, "test_vrt", "test_vrt.vrt") # The coordinates of each city, with Z values corresponding to their # altitude in meters. city_data = ( - ('Houston', (-95.363151, 29.763374, 18)), - ('Dallas', (-96.801611, 32.782057, 147)), - ('Oklahoma City', (-97.521157, 34.464642, 380)), - ('Wellington', (174.783117, -41.315268, 14)), - ('Pueblo', (-104.609252, 38.255001, 1433)), - ('Lawrence', (-95.235060, 38.971823, 251)), - ('Chicago', (-87.650175, 41.850385, 181)), - ('Victoria', (-123.305196, 48.462611, 15)), + ("Houston", (-95.363151, 29.763374, 18)), + ("Dallas", (-96.801611, 32.782057, 147)), + ("Oklahoma City", (-97.521157, 34.464642, 380)), + ("Wellington", (174.783117, -41.315268, 14)), + ("Pueblo", (-104.609252, 38.255001, 1433)), + ("Lawrence", (-95.235060, 38.971823, 251)), + ("Chicago", (-87.650175, 41.850385, 181)), + ("Victoria", (-123.305196, 48.462611, 15)), ) # Reference mapping of city name to its altitude (Z value). @@ -37,32 +50,53 @@ city_dict = {name: coords for name, coords in city_data} # 3D freeway data derived from the National Elevation Dataset: # http://seamless.usgs.gov/products/9arc.php interstate_data = ( - ('I-45', - 'LINESTRING(-95.3708481 29.7765870 11.339,-95.3694580 29.7787980 4.536,' - '-95.3690305 29.7797359 9.762,-95.3691886 29.7812450 12.448,' - '-95.3696447 29.7850144 10.457,-95.3702511 29.7868518 9.418,' - '-95.3706724 29.7881286 14.858,-95.3711632 29.7896157 15.386,' - '-95.3714525 29.7936267 13.168,-95.3717848 29.7955007 15.104,' - '-95.3717719 29.7969804 16.516,-95.3717305 29.7982117 13.923,' - '-95.3717254 29.8000778 14.385,-95.3719875 29.8013539 15.160,' - '-95.3720575 29.8026785 15.544,-95.3721321 29.8040912 14.975,' - '-95.3722074 29.8050998 15.688,-95.3722779 29.8060430 16.099,' - '-95.3733818 29.8076750 15.197,-95.3741563 29.8103686 17.268,' - '-95.3749458 29.8129927 19.857,-95.3763564 29.8144557 15.435)', - (11.339, 4.536, 9.762, 12.448, 10.457, 9.418, 14.858, - 15.386, 13.168, 15.104, 16.516, 13.923, 14.385, 15.16, - 15.544, 14.975, 15.688, 16.099, 15.197, 17.268, 19.857, - 15.435), - ), + ( + "I-45", + "LINESTRING(-95.3708481 29.7765870 11.339,-95.3694580 29.7787980 4.536," + "-95.3690305 29.7797359 9.762,-95.3691886 29.7812450 12.448," + "-95.3696447 29.7850144 10.457,-95.3702511 29.7868518 9.418," + "-95.3706724 29.7881286 14.858,-95.3711632 29.7896157 15.386," + "-95.3714525 29.7936267 13.168,-95.3717848 29.7955007 15.104," + "-95.3717719 29.7969804 16.516,-95.3717305 29.7982117 13.923," + "-95.3717254 29.8000778 14.385,-95.3719875 29.8013539 15.160," + "-95.3720575 29.8026785 15.544,-95.3721321 29.8040912 14.975," + "-95.3722074 29.8050998 15.688,-95.3722779 29.8060430 16.099," + "-95.3733818 29.8076750 15.197,-95.3741563 29.8103686 17.268," + "-95.3749458 29.8129927 19.857,-95.3763564 29.8144557 15.435)", + ( + 11.339, + 4.536, + 9.762, + 12.448, + 10.457, + 9.418, + 14.858, + 15.386, + 13.168, + 15.104, + 16.516, + 13.923, + 14.385, + 15.16, + 15.544, + 14.975, + 15.688, + 16.099, + 15.197, + 17.268, + 19.857, + 15.435, + ), + ), ) # Bounding box polygon for inner-loop of Houston (in projected coordinate # system 32140), with elevation values from the National Elevation Dataset # (see above). bbox_data = ( - 'POLYGON((941527.97 4225693.20,962596.48 4226349.75,963152.57 4209023.95,' - '942051.75 4208366.38,941527.97 4225693.20))', - (21.71, 13.21, 9.12, 16.40, 21.71) + "POLYGON((941527.97 4225693.20,962596.48 4226349.75,963152.57 4209023.95," + "942051.75 4208366.38,941527.97 4225693.20))", + (21.71, 13.21, 9.12, 16.40, 21.71), ) @@ -83,15 +117,19 @@ class Geo3DLoadingHelper: def _load_city_data(self): for name, pnt_data in city_data: City3D.objects.create( - name=name, point=Point(*pnt_data, srid=4326), pointg=Point(*pnt_data, srid=4326), + name=name, + point=Point(*pnt_data, srid=4326), + pointg=Point(*pnt_data, srid=4326), ) def _load_polygon_data(self): bbox_wkt, bbox_z = bbox_data bbox_2d = GEOSGeometry(bbox_wkt, srid=32140) - bbox_3d = Polygon(tuple((x, y, z) for (x, y), z in zip(bbox_2d[0].coords, bbox_z)), srid=32140) - Polygon2D.objects.create(name='2D BBox', poly=bbox_2d) - Polygon3D.objects.create(name='3D BBox', poly=bbox_3d) + bbox_3d = Polygon( + tuple((x, y, z) for (x, y), z in zip(bbox_2d[0].coords, bbox_z)), srid=32140 + ) + Polygon2D.objects.create(name="2D BBox", poly=bbox_2d) + Polygon3D.objects.create(name="3D BBox", poly=bbox_3d) @skipUnlessDBFeature("supports_3d_storage") @@ -132,7 +170,7 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): Test the creation of polygon 3D models. """ self._load_polygon_data() - p3d = Polygon3D.objects.get(name='3D BBox') + p3d = Polygon3D.objects.get(name="3D BBox") self.assertTrue(p3d.poly.hasz) self.assertIsInstance(p3d.poly, Polygon) self.assertEqual(p3d.poly.srid, 32140) @@ -144,8 +182,8 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): # Import here as GDAL is required for those imports from django.contrib.gis.utils import LayerMapError, LayerMapping - point_mapping = {'point': 'POINT'} - mpoint_mapping = {'mpoint': 'MULTIPOINT'} + point_mapping = {"point": "POINT"} + mpoint_mapping = {"mpoint": "MULTIPOINT"} # The VRT is 3D, but should still be able to map sans the Z. lm = LayerMapping(Point2D, vrt_file, point_mapping, transform=False) @@ -177,12 +215,12 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): # `SELECT ST_AsText(ST_Union(point)) FROM geo3d_city3d;` self._load_city_data() ref_ewkt = ( - 'SRID=4326;MULTIPOINT(-123.305196 48.462611 15,-104.609252 38.255001 1433,' - '-97.521157 34.464642 380,-96.801611 32.782057 147,-95.363151 29.763374 18,' - '-95.23506 38.971823 251,-87.650175 41.850385 181,174.783117 -41.315268 14)' + "SRID=4326;MULTIPOINT(-123.305196 48.462611 15,-104.609252 38.255001 1433," + "-97.521157 34.464642 380,-96.801611 32.782057 147,-95.363151 29.763374 18," + "-95.23506 38.971823 251,-87.650175 41.850385 181,174.783117 -41.315268 14)" ) ref_union = GEOSGeometry(ref_ewkt) - union = City3D.objects.aggregate(Union('point'))['point__union'] + union = City3D.objects.aggregate(Union("point"))["point__union"] self.assertTrue(union.hasz) # Ordering of points in the resulting geometry may vary between implementations self.assertEqual({p.ewkt for p in ref_union}, {p.ewkt for p in union}) @@ -195,14 +233,16 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): self._load_city_data() # `SELECT ST_Extent3D(point) FROM geo3d_city3d;` ref_extent3d = (-123.305196, -41.315268, 14, 174.783117, 48.462611, 1433) - extent = City3D.objects.aggregate(Extent3D('point'))['point__extent3d'] + extent = City3D.objects.aggregate(Extent3D("point"))["point__extent3d"] def check_extent3d(extent3d, tol=6): for ref_val, ext_val in zip(ref_extent3d, extent3d): self.assertAlmostEqual(ref_val, ext_val, tol) check_extent3d(extent) - self.assertIsNone(City3D.objects.none().aggregate(Extent3D('point'))['point__extent3d']) + self.assertIsNone( + City3D.objects.none().aggregate(Extent3D("point"))["point__extent3d"] + ) @skipUnlessDBFeature("supports_3d_functions") @@ -212,10 +252,12 @@ class Geo3DFunctionsTests(FuncTestMixin, Geo3DLoadingHelper, TestCase): Test KML() function with Z values. """ self._load_city_data() - h = City3D.objects.annotate(kml=AsKML('point', precision=6)).get(name='Houston') + h = City3D.objects.annotate(kml=AsKML("point", precision=6)).get(name="Houston") # KML should be 3D. # `SELECT ST_AsKML(point, 6) FROM geo3d_city3d WHERE name = 'Houston';` - ref_kml_regex = re.compile(r'^<Point><coordinates>-95.363\d+,29.763\d+,18</coordinates></Point>$') + ref_kml_regex = re.compile( + r"^<Point><coordinates>-95.363\d+,29.763\d+,18</coordinates></Point>$" + ) self.assertTrue(ref_kml_regex.match(h.kml)) def test_geojson(self): @@ -223,10 +265,14 @@ class Geo3DFunctionsTests(FuncTestMixin, Geo3DLoadingHelper, TestCase): Test GeoJSON() function with Z values. """ self._load_city_data() - h = City3D.objects.annotate(geojson=AsGeoJSON('point', precision=6)).get(name='Houston') + h = City3D.objects.annotate(geojson=AsGeoJSON("point", precision=6)).get( + name="Houston" + ) # GeoJSON should be 3D # `SELECT ST_AsGeoJSON(point, 6) FROM geo3d_city3d WHERE name='Houston';` - ref_json_regex = re.compile(r'^{"type":"Point","coordinates":\[-95.363151,29.763374,18(\.0+)?\]}$') + ref_json_regex = re.compile( + r'^{"type":"Point","coordinates":\[-95.363151,29.763374,18(\.0+)?\]}$' + ) self.assertTrue(ref_json_regex.match(h.geojson)) def test_perimeter(self): @@ -239,9 +285,13 @@ class Geo3DFunctionsTests(FuncTestMixin, Geo3DLoadingHelper, TestCase): ref_perim_3d = 76859.2620451 ref_perim_2d = 76859.2577803 tol = 6 - poly2d = Polygon2D.objects.annotate(perimeter=Perimeter('poly')).get(name='2D BBox') + poly2d = Polygon2D.objects.annotate(perimeter=Perimeter("poly")).get( + name="2D BBox" + ) self.assertAlmostEqual(ref_perim_2d, poly2d.perimeter.m, tol) - poly3d = Polygon3D.objects.annotate(perimeter=Perimeter('poly')).get(name='3D BBox') + poly3d = Polygon3D.objects.annotate(perimeter=Perimeter("poly")).get( + name="3D BBox" + ) self.assertAlmostEqual(ref_perim_3d, poly3d.perimeter.m, tol) def test_length(self): @@ -256,9 +306,9 @@ class Geo3DFunctionsTests(FuncTestMixin, Geo3DLoadingHelper, TestCase): tol = 3 ref_length_2d = 4368.1721949481 ref_length_3d = 4368.62547052088 - inter2d = Interstate2D.objects.annotate(length=Length('line')).get(name='I-45') + inter2d = Interstate2D.objects.annotate(length=Length("line")).get(name="I-45") self.assertAlmostEqual(ref_length_2d, inter2d.length.m, tol) - inter3d = Interstate3D.objects.annotate(length=Length('line')).get(name='I-45') + inter3d = Interstate3D.objects.annotate(length=Length("line")).get(name="I-45") self.assertAlmostEqual(ref_length_3d, inter3d.length.m, tol) # Making sure `ST_Length3D` is used on for a projected @@ -267,9 +317,13 @@ class Geo3DFunctionsTests(FuncTestMixin, Geo3DLoadingHelper, TestCase): ref_length_2d = 4367.71564892392 # `SELECT ST_Length3D(line) FROM geo3d_interstateproj3d;` ref_length_3d = 4368.16897234101 - inter2d = InterstateProj2D.objects.annotate(length=Length('line')).get(name='I-45') + inter2d = InterstateProj2D.objects.annotate(length=Length("line")).get( + name="I-45" + ) self.assertAlmostEqual(ref_length_2d, inter2d.length.m, tol) - inter3d = InterstateProj3D.objects.annotate(length=Length('line')).get(name='I-45') + inter3d = InterstateProj3D.objects.annotate(length=Length("line")).get( + name="I-45" + ) self.assertAlmostEqual(ref_length_3d, inter3d.length.m, tol) def test_scale(self): @@ -280,7 +334,7 @@ class Geo3DFunctionsTests(FuncTestMixin, Geo3DLoadingHelper, TestCase): # Mapping of City name to reference Z values. zscales = (-3, 4, 23) for zscale in zscales: - for city in City3D.objects.annotate(scale=Scale('point', 1.0, 1.0, zscale)): + for city in City3D.objects.annotate(scale=Scale("point", 1.0, 1.0, zscale)): self.assertEqual(city_dict[city.name][2] * zscale, city.scale.z) def test_translate(self): @@ -290,5 +344,7 @@ class Geo3DFunctionsTests(FuncTestMixin, Geo3DLoadingHelper, TestCase): self._load_city_data() ztranslations = (5.23, 23, -17) for ztrans in ztranslations: - for city in City3D.objects.annotate(translate=Translate('point', 0, 0, ztrans)): + for city in City3D.objects.annotate( + translate=Translate("point", 0, 0, ztrans) + ): self.assertEqual(city_dict[city.name][2] + ztrans, city.translate.z) diff --git a/tests/gis_tests/geoadmin/models.py b/tests/gis_tests/geoadmin/models.py index 115ed0624e..ea726dd68f 100644 --- a/tests/gis_tests/geoadmin/models.py +++ b/tests/gis_tests/geoadmin/models.py @@ -8,7 +8,7 @@ class City(models.Model): point = models.PointField() class Meta: - app_label = 'geoadmin' + app_label = "geoadmin" def __str__(self): return self.name @@ -16,18 +16,18 @@ class City(models.Model): class CityAdminCustomWidgetKwargs(admin.GISModelAdmin): gis_widget_kwargs = { - 'attrs': { - 'default_lat': 55, - 'default_lon': 37, + "attrs": { + "default_lat": 55, + "default_lon": 37, }, } -site = admin.AdminSite(name='gis_admin_modeladmin') +site = admin.AdminSite(name="gis_admin_modeladmin") site.register(City, admin.ModelAdmin) -site_gis = admin.AdminSite(name='gis_admin_gismodeladmin') +site_gis = admin.AdminSite(name="gis_admin_gismodeladmin") site_gis.register(City, admin.GISModelAdmin) -site_gis_custom = admin.AdminSite(name='gis_admin_gismodeladmin') +site_gis_custom = admin.AdminSite(name="gis_admin_gismodeladmin") site_gis_custom.register(City, CityAdminCustomWidgetKwargs) diff --git a/tests/gis_tests/geoadmin/tests.py b/tests/gis_tests/geoadmin/tests.py index 2e4e4903ec..54b7073d69 100644 --- a/tests/gis_tests/geoadmin/tests.py +++ b/tests/gis_tests/geoadmin/tests.py @@ -4,16 +4,16 @@ from django.test import SimpleTestCase, override_settings from .models import City, site, site_gis, site_gis_custom -@override_settings(ROOT_URLCONF='django.contrib.gis.tests.geoadmin.urls') +@override_settings(ROOT_URLCONF="django.contrib.gis.tests.geoadmin.urls") class GeoAdminTest(SimpleTestCase): admin_site = site # ModelAdmin def test_widget_empty_string(self): geoadmin = self.admin_site._registry[City] - form = geoadmin.get_changelist_form(None)({'point': ''}) - with self.assertRaisesMessage(AssertionError, 'no logs'): - with self.assertLogs('django.contrib.gis', 'ERROR'): - output = str(form['point']) + form = geoadmin.get_changelist_form(None)({"point": ""}) + with self.assertRaisesMessage(AssertionError, "no logs"): + with self.assertLogs("django.contrib.gis", "ERROR"): + output = str(form["point"]) self.assertInHTML( '<textarea id="id_point" class="vSerializedField required" cols="150"' ' rows="10" name="point"></textarea>', @@ -22,9 +22,9 @@ class GeoAdminTest(SimpleTestCase): def test_widget_invalid_string(self): geoadmin = self.admin_site._registry[City] - form = geoadmin.get_changelist_form(None)({'point': 'INVALID()'}) - with self.assertLogs('django.contrib.gis', 'ERROR') as cm: - output = str(form['point']) + form = geoadmin.get_changelist_form(None)({"point": "INVALID()"}) + with self.assertLogs("django.contrib.gis", "ERROR") as cm: + output = str(form["point"]) self.assertInHTML( '<textarea id="id_point" class="vSerializedField required" cols="150"' ' rows="10" name="point"></textarea>', @@ -40,16 +40,16 @@ class GeoAdminTest(SimpleTestCase): def test_widget_has_changed(self): geoadmin = self.admin_site._registry[City] form = geoadmin.get_changelist_form(None)() - has_changed = form.fields['point'].has_changed + has_changed = form.fields["point"].has_changed initial = Point(13.4197458572965953, 52.5194108501149799, srid=4326) - data_same = 'SRID=3857;POINT(1493879.2754093995 6894592.019687599)' - data_almost_same = 'SRID=3857;POINT(1493879.2754093990 6894592.019687590)' - data_changed = 'SRID=3857;POINT(1493884.0527237 6894593.8111804)' + data_same = "SRID=3857;POINT(1493879.2754093995 6894592.019687599)" + data_almost_same = "SRID=3857;POINT(1493879.2754093990 6894592.019687590)" + data_changed = "SRID=3857;POINT(1493884.0527237 6894593.8111804)" self.assertIs(has_changed(None, data_changed), True) - self.assertIs(has_changed(initial, ''), True) - self.assertIs(has_changed(None, ''), False) + self.assertIs(has_changed(initial, ""), True) + self.assertIs(has_changed(None, ""), False) self.assertIs(has_changed(initial, data_same), False) self.assertIs(has_changed(initial, data_almost_same), False) self.assertIs(has_changed(initial, data_changed), True) @@ -61,15 +61,15 @@ class GISAdminTests(GeoAdminTest): def test_default_gis_widget_kwargs(self): geoadmin = self.admin_site._registry[City] form = geoadmin.get_changelist_form(None)() - widget = form['point'].field.widget - self.assertEqual(widget.attrs['default_lat'], 47) - self.assertEqual(widget.attrs['default_lon'], 5) - self.assertEqual(widget.attrs['default_zoom'], 12) + widget = form["point"].field.widget + self.assertEqual(widget.attrs["default_lat"], 47) + self.assertEqual(widget.attrs["default_lon"], 5) + self.assertEqual(widget.attrs["default_zoom"], 12) def test_custom_gis_widget_kwargs(self): geoadmin = site_gis_custom._registry[City] form = geoadmin.get_changelist_form(None)() - widget = form['point'].field.widget - self.assertEqual(widget.attrs['default_lat'], 55) - self.assertEqual(widget.attrs['default_lon'], 37) - self.assertEqual(widget.attrs['default_zoom'], 12) + widget = form["point"].field.widget + self.assertEqual(widget.attrs["default_lat"], 55) + self.assertEqual(widget.attrs["default_lon"], 37) + self.assertEqual(widget.attrs["default_zoom"], 12) diff --git a/tests/gis_tests/geoadmin/urls.py b/tests/gis_tests/geoadmin/urls.py index c27b1d7cda..ce237dbfd1 100644 --- a/tests/gis_tests/geoadmin/urls.py +++ b/tests/gis_tests/geoadmin/urls.py @@ -2,5 +2,5 @@ from django.contrib import admin from django.urls import include, path urlpatterns = [ - path('admin/', include(admin.site.urls)), + path("admin/", include(admin.site.urls)), ] diff --git a/tests/gis_tests/geoadmin_deprecated/models.py b/tests/gis_tests/geoadmin_deprecated/models.py index efd9535977..5e84d0c9fa 100644 --- a/tests/gis_tests/geoadmin_deprecated/models.py +++ b/tests/gis_tests/geoadmin_deprecated/models.py @@ -10,12 +10,12 @@ class City(models.Model): point = models.PointField() class Meta: - app_label = 'geoadmini_deprecated' + app_label = "geoadmini_deprecated" def __str__(self): return self.name -site = admin.AdminSite(name='admin_gis') +site = admin.AdminSite(name="admin_gis") with ignore_warnings(category=RemovedInDjango50Warning): site.register(City, admin.OSMGeoAdmin) diff --git a/tests/gis_tests/geoadmin_deprecated/tests.py b/tests/gis_tests/geoadmin_deprecated/tests.py index 4240de98b1..a2049827a1 100644 --- a/tests/gis_tests/geoadmin_deprecated/tests.py +++ b/tests/gis_tests/geoadmin_deprecated/tests.py @@ -8,9 +8,8 @@ from .models import City, site @ignore_warnings(category=RemovedInDjango50Warning) -@override_settings(ROOT_URLCONF='django.contrib.gis.tests.geoadmin.urls') +@override_settings(ROOT_URLCONF="django.contrib.gis.tests.geoadmin.urls") class GeoAdminTest(SimpleTestCase): - def test_ensure_geographic_media(self): geoadmin = site._registry[City] admin_js = geoadmin.media.render_js() @@ -20,12 +19,14 @@ class GeoAdminTest(SimpleTestCase): delete_all_btn = """<a href="javascript:geodjango_point.clearFeatures()">Delete all Features</a>""" original_geoadmin = site._registry[City] - params = original_geoadmin.get_map_widget(City._meta.get_field('point')).params - result = original_geoadmin.get_map_widget(City._meta.get_field('point'))( - ).render('point', Point(-79.460734, 40.18476), params) + params = original_geoadmin.get_map_widget(City._meta.get_field("point")).params + result = original_geoadmin.get_map_widget( + City._meta.get_field("point") + )().render("point", Point(-79.460734, 40.18476), params) self.assertIn( """geodjango_point.layers.base = new OpenLayers.Layer.OSM("OpenStreetMap (Mapnik)");""", - result) + result, + ) self.assertIn(delete_all_btn, result) @@ -33,9 +34,10 @@ class GeoAdminTest(SimpleTestCase): site.register(City, UnmodifiableAdmin) try: geoadmin = site._registry[City] - params = geoadmin.get_map_widget(City._meta.get_field('point')).params - result = geoadmin.get_map_widget(City._meta.get_field('point'))( - ).render('point', Point(-79.460734, 40.18476), params) + params = geoadmin.get_map_widget(City._meta.get_field("point")).params + result = geoadmin.get_map_widget(City._meta.get_field("point"))().render( + "point", Point(-79.460734, 40.18476), params + ) self.assertNotIn(delete_all_btn, result) finally: @@ -44,12 +46,14 @@ class GeoAdminTest(SimpleTestCase): def test_olmap_WMS_rendering(self): geoadmin = admin.GeoModelAdmin(City, site) - result = geoadmin.get_map_widget(City._meta.get_field('point'))( - ).render('point', Point(-79.460734, 40.18476)) + result = geoadmin.get_map_widget(City._meta.get_field("point"))().render( + "point", Point(-79.460734, 40.18476) + ) self.assertIn( """geodjango_point.layers.base = new OpenLayers.Layer.WMS("OpenLayers WMS", """ """"http://vmap0.tiles.osgeo.org/wms/vmap0", {layers: 'basic', format: 'image/jpeg'});""", - result) + result, + ) def test_olwidget_has_changed(self): """ @@ -57,7 +61,7 @@ class GeoAdminTest(SimpleTestCase): """ geoadmin = site._registry[City] form = geoadmin.get_changelist_form(None)() - has_changed = form.fields['point'].has_changed + has_changed = form.fields["point"].has_changed initial = Point(13.4197458572965953, 52.5194108501149799, srid=4326) data_same = "SRID=3857;POINT(1493879.2754093995 6894592.019687599)" @@ -73,30 +77,30 @@ class GeoAdminTest(SimpleTestCase): def test_olwidget_empty_string(self): geoadmin = site._registry[City] - form = geoadmin.get_changelist_form(None)({'point': ''}) - with self.assertNoLogs('django.contrib.gis', 'ERROR'): - output = str(form['point']) + form = geoadmin.get_changelist_form(None)({"point": ""}) + with self.assertNoLogs("django.contrib.gis", "ERROR"): + output = str(form["point"]) self.assertInHTML( '<textarea id="id_point" class="vWKTField required" cols="150"' ' rows="10" name="point"></textarea>', - output + output, ) def test_olwidget_invalid_string(self): geoadmin = site._registry[City] - form = geoadmin.get_changelist_form(None)({'point': 'INVALID()'}) - with self.assertLogs('django.contrib.gis', 'ERROR') as cm: - output = str(form['point']) + form = geoadmin.get_changelist_form(None)({"point": "INVALID()"}) + with self.assertLogs("django.contrib.gis", "ERROR") as cm: + output = str(form["point"]) self.assertInHTML( '<textarea id="id_point" class="vWKTField required" cols="150"' ' rows="10" name="point"></textarea>', - output + output, ) self.assertEqual(len(cm.records), 1) self.assertEqual( cm.records[0].getMessage(), "Error creating geometry from value 'INVALID()' (String input " - "unrecognized as WKT EWKT, and HEXEWKB.)" + "unrecognized as WKT EWKT, and HEXEWKB.)", ) @@ -109,9 +113,9 @@ class DeprecationTests(SimpleTestCase): pass msg = ( - 'django.contrib.gis.admin.GeoModelAdmin and OSMGeoAdmin are ' - 'deprecated in favor of django.contrib.admin.ModelAdmin and ' - 'django.contrib.gis.admin.GISModelAdmin.' + "django.contrib.gis.admin.GeoModelAdmin and OSMGeoAdmin are " + "deprecated in favor of django.contrib.admin.ModelAdmin and " + "django.contrib.gis.admin.GISModelAdmin." ) with self.assertRaisesMessage(RemovedInDjango50Warning, msg): DeprecatedOSMGeoAdmin(City, site) diff --git a/tests/gis_tests/geoadmin_deprecated/urls.py b/tests/gis_tests/geoadmin_deprecated/urls.py index c27b1d7cda..ce237dbfd1 100644 --- a/tests/gis_tests/geoadmin_deprecated/urls.py +++ b/tests/gis_tests/geoadmin_deprecated/urls.py @@ -2,5 +2,5 @@ from django.contrib import admin from django.urls import include, path urlpatterns = [ - path('admin/', include(admin.site.urls)), + path("admin/", include(admin.site.urls)), ] diff --git a/tests/gis_tests/geoapp/feeds.py b/tests/gis_tests/geoapp/feeds.py index 185f3b56e3..26ee4a8172 100644 --- a/tests/gis_tests/geoapp/feeds.py +++ b/tests/gis_tests/geoapp/feeds.py @@ -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, } diff --git a/tests/gis_tests/geoapp/models.py b/tests/gis_tests/geoapp/models.py index 9845524fb4..42619e8a75 100644 --- a/tests/gis_tests/geoapp/models.py +++ b/tests/gis_tests/geoapp/models.py @@ -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 diff --git a/tests/gis_tests/geoapp/sitemaps.py b/tests/gis_tests/geoapp/sitemaps.py index 91e5d82bb4..e3566b066e 100644 --- a/tests/gis_tests/geoapp/sitemaps.py +++ b/tests/gis_tests/geoapp/sitemaps.py @@ -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]), } diff --git a/tests/gis_tests/geoapp/test_expressions.py b/tests/gis_tests/geoapp/test_expressions.py index 6ca8615d87..d0e92a9599 100644 --- a/tests/gis_tests/geoapp/test_expressions.py +++ b/tests/gis_tests/geoapp/test_expressions.py @@ -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)) diff --git a/tests/gis_tests/geoapp/test_feeds.py b/tests/gis_tests/geoapp/test_feeds.py index 037041e66e..e247d88535 100644 --- a/tests/gis_tests/geoapp/test_feeds.py +++ b/tests/gis_tests/geoapp/test_feeds.py @@ -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/") diff --git a/tests/gis_tests/geoapp/test_functions.py b/tests/gis_tests/geoapp/test_functions.py index 67e4313f3f..076e51b7a2 100644 --- a/tests/gis_tests/geoapp/test_functions.py +++ b/tests/gis_tests/geoapp/test_functions.py @@ -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" + ) diff --git a/tests/gis_tests/geoapp/test_indexes.py b/tests/gis_tests/geoapp/test_indexes.py index ce9376d810..cd58f8e941 100644 --- a/tests/gis_tests/geoapp/test_indexes.py +++ b/tests/gis_tests/geoapp/test_indexes.py @@ -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) diff --git a/tests/gis_tests/geoapp/test_regress.py b/tests/gis_tests/geoapp/test_regress.py index 674f19ba54..9a9226f341 100644 --- a/tests/gis_tests/geoapp/test_regress.py +++ b/tests/gis_tests/geoapp/test_regress.py @@ -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." diff --git a/tests/gis_tests/geoapp/test_serializers.py b/tests/gis_tests/geoapp/test_serializers.py index 6d4b959026..a67a4f16db 100644 --- a/tests/gis_tests/geoapp/test_serializers.py +++ b/tests/gis_tests/geoapp/test_serializers.py @@ -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", "{}") diff --git a/tests/gis_tests/geoapp/test_sitemaps.py b/tests/gis_tests/geoapp/test_sitemaps.py index 1dbd57fd71..7907a71592 100644 --- a/tests/gis_tests/geoapp/test_sitemaps.py +++ b/tests/gis_tests/geoapp/test_sitemaps.py @@ -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")), + ) diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py index afd26eeb65..33d209bcdb 100644 --- a/tests/gis_tests/geoapp/tests.py +++ b/tests/gis_tests/geoapp/tests.py @@ -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() - - f_1 = Feature.objects.get(name='Point') + 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") 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 + 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']) - - @skipUnlessDBFeature('supports_tolerance_parameter') + 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") 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) diff --git a/tests/gis_tests/geoapp/urls.py b/tests/gis_tests/geoapp/urls.py index 8597387f82..a7dd805c8c 100644 --- a/tests/gis_tests/geoapp/urls.py +++ b/tests/gis_tests/geoapp/urls.py @@ -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", + ), ] diff --git a/tests/gis_tests/geogapp/models.py b/tests/gis_tests/geogapp/models.py index 6aa0b1f00e..c5c550feba 100644 --- a/tests/gis_tests/geogapp/models.py +++ b/tests/gis_tests/geogapp/models.py @@ -15,7 +15,7 @@ class City(NamedModel): point = models.PointField(geography=True) class Meta: - app_label = 'geogapp' + app_label = "geogapp" class Zipcode(NamedModel): @@ -28,7 +28,7 @@ class County(NamedModel): mpoly = models.MultiPolygonField(geography=True) class Meta: - app_label = 'geogapp' + app_label = "geogapp" def __str__(self): - return ' County, '.join([self.name, self.state]) + return " County, ".join([self.name, self.state]) diff --git a/tests/gis_tests/geogapp/tests.py b/tests/gis_tests/geogapp/tests.py index 7f465f5753..819b86545f 100644 --- a/tests/gis_tests/geogapp/tests.py +++ b/tests/gis_tests/geogapp/tests.py @@ -15,7 +15,7 @@ from .models import City, County, Zipcode class GeographyTest(TestCase): - fixtures = ['initial'] + fixtures = ["initial"] def test01_fixture_load(self): "Ensure geography features loaded properly." @@ -24,26 +24,28 @@ class GeographyTest(TestCase): @skipUnlessDBFeature("supports_distances_lookups", "supports_distance_geodetic") def test02_distance_lookup(self): "Testing distance lookup support on non-point geography fields." - z = Zipcode.objects.get(code='77002') - cities1 = list(City.objects - .filter(point__distance_lte=(z.poly, D(mi=500))) - .order_by('name') - .values_list('name', flat=True)) - cities2 = list(City.objects - .filter(point__dwithin=(z.poly, D(mi=500))) - .order_by('name') - .values_list('name', flat=True)) + z = Zipcode.objects.get(code="77002") + cities1 = list( + City.objects.filter(point__distance_lte=(z.poly, D(mi=500))) + .order_by("name") + .values_list("name", flat=True) + ) + cities2 = list( + City.objects.filter(point__dwithin=(z.poly, D(mi=500))) + .order_by("name") + .values_list("name", flat=True) + ) for cities in [cities1, cities2]: - self.assertEqual(['Dallas', 'Houston', 'Oklahoma City'], cities) + self.assertEqual(["Dallas", "Houston", "Oklahoma City"], cities) def test04_invalid_operators_functions(self): "Ensuring exceptions are raised for operators & functions invalid on geography fields." if not connection.ops.postgis: - self.skipTest('This is a PostGIS-specific test.') + self.skipTest("This is a PostGIS-specific test.") # Only a subset of the geometry functions & operator are available # to PostGIS geography types. For more information, visit: # http://postgis.refractions.net/documentation/manual-1.5/ch08.html#PostGIS_GeographyFunctions - z = Zipcode.objects.get(code='77002') + z = Zipcode.objects.get(code="77002") # ST_Within not available. with self.assertRaises(ValueError): City.objects.filter(point__within=z.poly).count() @@ -52,7 +54,7 @@ class GeographyTest(TestCase): City.objects.filter(point__contained=z.poly).count() # Regression test for #14060, `~=` was never really implemented for PostGIS. - htown = City.objects.get(name='Houston') + htown = City.objects.get(name="Houston") with self.assertRaises(ValueError): City.objects.get(point__exact=htown.point) @@ -63,22 +65,26 @@ class GeographyTest(TestCase): from django.contrib.gis.utils import LayerMapping # Getting the shapefile and mapping dictionary. - shp_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data')) - co_shp = os.path.join(shp_path, 'counties', 'counties.shp') + shp_path = os.path.realpath( + os.path.join(os.path.dirname(__file__), "..", "data") + ) + co_shp = os.path.join(shp_path, "counties", "counties.shp") co_mapping = { - 'name': 'Name', - 'state': 'State', - 'mpoly': 'MULTIPOLYGON', + "name": "Name", + "state": "State", + "mpoly": "MULTIPOLYGON", } # Reference county names, number of polygons, and state names. - names = ['Bexar', 'Galveston', 'Harris', 'Honolulu', 'Pueblo'] + names = ["Bexar", "Galveston", "Harris", "Honolulu", "Pueblo"] num_polys = [1, 2, 1, 19, 1] # Number of polygons for each. - st_names = ['Texas', 'Texas', 'Texas', 'Hawaii', 'Colorado'] + st_names = ["Texas", "Texas", "Texas", "Hawaii", "Colorado"] - lm = LayerMapping(County, co_shp, co_mapping, source_srs=4269, unique='name') + lm = LayerMapping(County, co_shp, co_mapping, source_srs=4269, unique="name") lm.save(silent=True, strict=True) - for c, name, num_poly, state in zip(County.objects.order_by('name'), names, num_polys, st_names): + for c, name, num_poly, state in zip( + County.objects.order_by("name"), names, num_polys, st_names + ): self.assertEqual(4326, c.mpoly.srid) self.assertEqual(num_poly, len(c.mpoly)) self.assertEqual(name, c.name) @@ -86,7 +92,7 @@ class GeographyTest(TestCase): class GeographyFunctionTests(FuncTestMixin, TestCase): - fixtures = ['initial'] + fixtures = ["initial"] @skipUnlessDBFeature("supports_extent_aggr") def test_cast_aggregate(self): @@ -96,11 +102,16 @@ class GeographyFunctionTests(FuncTestMixin, TestCase): """ if not connection.features.supports_geography: self.skipTest("This test needs geography support") - expected = (-96.8016128540039, 29.7633724212646, -95.3631439208984, 32.782058715820) - res = City.objects.filter( - name__in=('Houston', 'Dallas') - ).aggregate(extent=models.Extent(Cast('point', models.PointField()))) - for val, exp in zip(res['extent'], expected): + expected = ( + -96.8016128540039, + 29.7633724212646, + -95.3631439208984, + 32.782058715820, + ) + res = City.objects.filter(name__in=("Houston", "Dallas")).aggregate( + extent=models.Extent(Cast("point", models.PointField())) + ) + for val, exp in zip(res["extent"], expected): self.assertAlmostEqual(exp, val, 4) @skipUnlessDBFeature("has_Distance_function", "supports_distance_geodetic") @@ -119,10 +130,10 @@ class GeographyFunctionTests(FuncTestMixin, TestCase): ref_dists = [0, 4899.68, 8081.30, 9115.15] else: ref_dists = [0, 4891.20, 8071.64, 9123.95] - htown = City.objects.get(name='Houston') + htown = City.objects.get(name="Houston") qs = Zipcode.objects.annotate( - distance=Distance('poly', htown.point), - distance2=Distance(htown.point, 'poly'), + distance=Distance("poly", htown.point), + distance2=Distance(htown.point, "poly"), ) for z, ref in zip(qs, ref_dists): self.assertAlmostEqual(z.distance.m, ref, 2) @@ -135,7 +146,7 @@ class GeographyFunctionTests(FuncTestMixin, TestCase): if not connection.ops.spatialite: # Distance function combined with a lookup. - hzip = Zipcode.objects.get(code='77002') + hzip = Zipcode.objects.get(code="77002") self.assertEqual(qs.get(distance__lte=0), hzip) @skipUnlessDBFeature("has_Area_function", "supports_area_geodetic") @@ -144,7 +155,7 @@ class GeographyFunctionTests(FuncTestMixin, TestCase): Testing that Area calculations work on geography columns. """ # SELECT ST_Area(poly) FROM geogapp_zipcode WHERE code='77002'; - z = Zipcode.objects.annotate(area=Area('poly')).get(code='77002') + z = Zipcode.objects.annotate(area=Area("poly")).get(code="77002") # Round to the nearest thousand as possible values (depending on # the database and geolib) include 5439084, 5439100, 5439101. rounded_value = z.area.sq_m @@ -154,5 +165,7 @@ class GeographyFunctionTests(FuncTestMixin, TestCase): @skipUnlessDBFeature("has_Area_function") @skipIfDBFeature("supports_area_geodetic") def test_geodetic_area_raises_if_not_supported(self): - with self.assertRaisesMessage(NotSupportedError, 'Area on geodetic coordinate systems not supported.'): - Zipcode.objects.annotate(area=Area('poly')).get(code='77002') + with self.assertRaisesMessage( + NotSupportedError, "Area on geodetic coordinate systems not supported." + ): + Zipcode.objects.annotate(area=Area("poly")).get(code="77002") diff --git a/tests/gis_tests/geos_tests/test_coordseq.py b/tests/gis_tests/geos_tests/test_coordseq.py index f6a0ff89c7..507a22d1a0 100644 --- a/tests/gis_tests/geos_tests/test_coordseq.py +++ b/tests/gis_tests/geos_tests/test_coordseq.py @@ -3,14 +3,13 @@ from django.test import SimpleTestCase class GEOSCoordSeqTest(SimpleTestCase): - def test_getitem(self): coord_seq = LineString([(x, x) for x in range(2)]).coord_seq for i in (0, 1): with self.subTest(i): self.assertEqual(coord_seq[i], (i, i)) for i in (-3, 10): - msg = 'invalid GEOS Geometry index: %s' % i + msg = "invalid GEOS Geometry index: %s" % i with self.subTest(i): with self.assertRaisesMessage(IndexError, msg): coord_seq[i] diff --git a/tests/gis_tests/geos_tests/test_geos.py b/tests/gis_tests/geos_tests/test_geos.py index 08169f38ab..d37d826d39 100644 --- a/tests/gis_tests/geos_tests/test_geos.py +++ b/tests/gis_tests/geos_tests/test_geos.py @@ -9,8 +9,17 @@ from unittest import mock, skipIf from django.contrib.gis import gdal from django.contrib.gis.geos import ( - GeometryCollection, GEOSException, GEOSGeometry, LinearRing, LineString, - MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, fromfile, + GeometryCollection, + GEOSException, + GEOSGeometry, + LinearRing, + LineString, + MultiLineString, + MultiPoint, + MultiPolygon, + Point, + Polygon, + fromfile, fromstr, ) from django.contrib.gis.geos.libgeos import geos_version_tuple @@ -23,7 +32,6 @@ from ..test_data import TestDataMixin class GEOSTest(SimpleTestCase, TestDataMixin): - def test_wkt(self): "Testing WKT output." for g in self.geometries.wkt_out: @@ -32,11 +40,11 @@ class GEOSTest(SimpleTestCase, TestDataMixin): self.assertEqual(g.ewkt, geom.wkt) def test_wkt_invalid(self): - msg = 'String input unrecognized as WKT EWKT, and HEXEWKB.' + msg = "String input unrecognized as WKT EWKT, and HEXEWKB." with self.assertRaisesMessage(ValueError, msg): - fromstr('POINT(٠٠١ ٠)') + fromstr("POINT(٠٠١ ٠)") with self.assertRaisesMessage(ValueError, msg): - fromstr('SRID=٧٥٨٣;POINT(100 0)') + fromstr("SRID=٧٥٨٣;POINT(100 0)") def test_hex(self): "Testing HEX output." @@ -47,12 +55,14 @@ class GEOSTest(SimpleTestCase, TestDataMixin): def test_hexewkb(self): "Testing (HEX)EWKB output." # For testing HEX(EWKB). - ogc_hex = b'01010000000000000000000000000000000000F03F' - ogc_hex_3d = b'01010000800000000000000000000000000000F03F0000000000000040' + ogc_hex = b"01010000000000000000000000000000000000F03F" + ogc_hex_3d = b"01010000800000000000000000000000000000F03F0000000000000040" # `SELECT ST_AsHEXEWKB(ST_GeomFromText('POINT(0 1)', 4326));` - hexewkb_2d = b'0101000020E61000000000000000000000000000000000F03F' + hexewkb_2d = b"0101000020E61000000000000000000000000000000000F03F" # `SELECT ST_AsHEXEWKB(ST_GeomFromEWKT('SRID=4326;POINT(0 1 2)'));` - hexewkb_3d = b'01010000A0E61000000000000000000000000000000000F03F0000000000000040' + hexewkb_3d = ( + b"01010000A0E61000000000000000000000000000000000F03F0000000000000040" + ) pnt_2d = Point(0, 1, srid=4326) pnt_3d = Point(0, 1, 2, srid=4326) @@ -79,7 +89,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): "Testing KML output." for tg in self.geometries.wkt_out: geom = fromstr(tg.wkt) - kml = getattr(tg, 'kml', False) + kml = getattr(tg, "kml", False) if kml: self.assertEqual(kml, geom.kml) @@ -92,7 +102,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): # Bad WKB with self.assertRaises(GEOSException): - GEOSGeometry(memoryview(b'0')) + GEOSGeometry(memoryview(b"0")) class NotAGeometry: pass @@ -133,7 +143,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): srids = (-1, 32140) for srid in srids: for p in self.geometries.polygons: - ewkt = 'SRID=%d;%s' % (srid, p.wkt) + ewkt = "SRID=%d;%s" % (srid, p.wkt) poly = fromstr(ewkt) self.assertEqual(srid, poly.srid) self.assertEqual(srid, poly.shell.srid) @@ -143,7 +153,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): "Testing GeoJSON input/output (via GDAL)." for g in self.geometries.json_geoms: geom = GEOSGeometry(g.wkt) - if not hasattr(g, 'not_equal'): + if not hasattr(g, "not_equal"): # Loading jsons to prevent decimal differences self.assertEqual(json.loads(g.json), json.loads(geom.json)) self.assertEqual(json.loads(g.json), json.loads(geom.geojson)) @@ -155,16 +165,16 @@ class GEOSTest(SimpleTestCase, TestDataMixin): "coordinates": [2, 49], "crs": { "type": "name", - "properties": { - "name": "urn:ogc:def:crs:EPSG::4322" - } - } + "properties": {"name": "urn:ogc:def:crs:EPSG::4322"}, + }, } - self.assertEqual(GEOSGeometry(json.dumps(geojson_data)), Point(2, 49, srid=4322)) + self.assertEqual( + GEOSGeometry(json.dumps(geojson_data)), Point(2, 49, srid=4322) + ) def test_fromfile(self): "Testing the fromfile() factory." - ref_pnt = GEOSGeometry('POINT(5 23)') + ref_pnt = GEOSGeometry("POINT(5 23)") wkt_f = BytesIO() wkt_f.write(ref_pnt.wkt.encode()) @@ -180,18 +190,18 @@ class GEOSTest(SimpleTestCase, TestDataMixin): def test_eq(self): "Testing equivalence." - p = fromstr('POINT(5 23)') + p = fromstr("POINT(5 23)") self.assertEqual(p, p.wkt) - self.assertNotEqual(p, 'foo') - ls = fromstr('LINESTRING(0 0, 1 1, 5 5)') + self.assertNotEqual(p, "foo") + ls = fromstr("LINESTRING(0 0, 1 1, 5 5)") self.assertEqual(ls, ls.wkt) - self.assertNotEqual(p, 'bar') - self.assertEqual(p, 'POINT(5.0 23.0)') + self.assertNotEqual(p, "bar") + self.assertEqual(p, "POINT(5.0 23.0)") # Error shouldn't be raise on equivalence testing with # an invalid type. for g in (p, ls): self.assertIsNotNone(g) - self.assertNotEqual(g, {'foo': 'bar'}) + self.assertNotEqual(g, {"foo": "bar"}) self.assertIsNot(g, False) def test_hash(self): @@ -228,16 +238,16 @@ class GEOSTest(SimpleTestCase, TestDataMixin): # WKT contains no SRID so will not equal self.assertNotEqual(p2, p2.wkt) # SRID of 0 - self.assertEqual(p0, 'SRID=0;POINT (5 23)') - self.assertNotEqual(p1, 'SRID=0;POINT (5 23)') + self.assertEqual(p0, "SRID=0;POINT (5 23)") + self.assertNotEqual(p1, "SRID=0;POINT (5 23)") def test_points(self): "Testing Point objects." - prev = fromstr('POINT(0 0)') + prev = fromstr("POINT(0 0)") for p in self.geometries.points: # Creating the point from the WKT pnt = fromstr(p.wkt) - self.assertEqual(pnt.geom_type, 'Point') + self.assertEqual(pnt.geom_type, "Point") self.assertEqual(pnt.geom_typeid, 0) self.assertEqual(pnt.dims, 0) self.assertEqual(p.x, pnt.x) @@ -250,7 +260,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): self.assertAlmostEqual(p.y, pnt.tuple[1], 9) # Testing the third dimension, and getting the tuple arguments - if hasattr(p, 'z'): + if hasattr(p, "z"): self.assertIs(pnt.hasz, True) self.assertEqual(p.z, pnt.z) self.assertEqual(p.z, pnt.tuple[2], 9) @@ -288,16 +298,16 @@ class GEOSTest(SimpleTestCase, TestDataMixin): prev = pnt # setting the previous geometry def test_point_reverse(self): - point = GEOSGeometry('POINT(144.963 -37.8143)', 4326) + point = GEOSGeometry("POINT(144.963 -37.8143)", 4326) self.assertEqual(point.srid, 4326) point.reverse() - self.assertEqual(point.ewkt, 'SRID=4326;POINT (-37.8143 144.963)') + self.assertEqual(point.ewkt, "SRID=4326;POINT (-37.8143 144.963)") def test_multipoints(self): "Testing MultiPoint objects." for mp in self.geometries.multipoints: mpnt = fromstr(mp.wkt) - self.assertEqual(mpnt.geom_type, 'MultiPoint') + self.assertEqual(mpnt.geom_type, "MultiPoint") self.assertEqual(mpnt.geom_typeid, 4) self.assertEqual(mpnt.dims, 0) @@ -309,24 +319,24 @@ class GEOSTest(SimpleTestCase, TestDataMixin): self.assertEqual(mp.centroid, mpnt.centroid.tuple) self.assertEqual(mp.coords, tuple(m.tuple for m in mpnt)) for p in mpnt: - self.assertEqual(p.geom_type, 'Point') + self.assertEqual(p.geom_type, "Point") self.assertEqual(p.geom_typeid, 0) self.assertIs(p.empty, False) self.assertIs(p.valid, True) def test_linestring(self): "Testing LineString objects." - prev = fromstr('POINT(0 0)') + prev = fromstr("POINT(0 0)") for line in self.geometries.linestrings: ls = fromstr(line.wkt) - self.assertEqual(ls.geom_type, 'LineString') + self.assertEqual(ls.geom_type, "LineString") self.assertEqual(ls.geom_typeid, 1) self.assertEqual(ls.dims, 1) self.assertIs(ls.empty, False) self.assertIs(ls.ring, False) - if hasattr(line, 'centroid'): + if hasattr(line, "centroid"): self.assertEqual(line.centroid, ls.centroid.tuple) - if hasattr(line, 'tup'): + if hasattr(line, "tup"): self.assertEqual(line.tup, ls.tuple) self.assertEqual(ls, fromstr(line.wkt)) @@ -340,65 +350,81 @@ class GEOSTest(SimpleTestCase, TestDataMixin): self.assertEqual(ls, LineString(*ls.tuple)) # as individual arguments self.assertEqual(ls, LineString([list(tup) for tup in ls.tuple])) # as list # Point individual arguments - self.assertEqual(ls.wkt, LineString(*tuple(Point(tup) for tup in ls.tuple)).wkt) + self.assertEqual( + ls.wkt, LineString(*tuple(Point(tup) for tup in ls.tuple)).wkt + ) if numpy: - self.assertEqual(ls, LineString(numpy.array(ls.tuple))) # as numpy array + self.assertEqual( + ls, LineString(numpy.array(ls.tuple)) + ) # as numpy array - with self.assertRaisesMessage(TypeError, 'Each coordinate should be a sequence (list or tuple)'): + with self.assertRaisesMessage( + TypeError, "Each coordinate should be a sequence (list or tuple)" + ): LineString((0, 0)) - with self.assertRaisesMessage(ValueError, 'LineString requires at least 2 points, got 1.'): + with self.assertRaisesMessage( + ValueError, "LineString requires at least 2 points, got 1." + ): LineString([(0, 0)]) if numpy: - with self.assertRaisesMessage(ValueError, 'LineString requires at least 2 points, got 1.'): + with self.assertRaisesMessage( + ValueError, "LineString requires at least 2 points, got 1." + ): LineString(numpy.array([(0, 0)])) - with mock.patch('django.contrib.gis.geos.linestring.numpy', False): - with self.assertRaisesMessage(TypeError, 'Invalid initialization input for LineStrings.'): - LineString('wrong input') + with mock.patch("django.contrib.gis.geos.linestring.numpy", False): + with self.assertRaisesMessage( + TypeError, "Invalid initialization input for LineStrings." + ): + LineString("wrong input") # Test __iter__(). - self.assertEqual(list(LineString((0, 0), (1, 1), (2, 2))), [(0, 0), (1, 1), (2, 2)]) + self.assertEqual( + list(LineString((0, 0), (1, 1), (2, 2))), [(0, 0), (1, 1), (2, 2)] + ) def test_linestring_reverse(self): - line = GEOSGeometry('LINESTRING(144.963 -37.8143,151.2607 -33.887)', 4326) + line = GEOSGeometry("LINESTRING(144.963 -37.8143,151.2607 -33.887)", 4326) self.assertEqual(line.srid, 4326) line.reverse() - self.assertEqual(line.ewkt, 'SRID=4326;LINESTRING (151.2607 -33.887, 144.963 -37.8143)') + self.assertEqual( + line.ewkt, "SRID=4326;LINESTRING (151.2607 -33.887, 144.963 -37.8143)" + ) def _test_is_counterclockwise(self): lr = LinearRing((0, 0), (1, 0), (0, 1), (0, 0)) self.assertIs(lr.is_counterclockwise, True) lr.reverse() self.assertIs(lr.is_counterclockwise, False) - msg = 'Orientation of an empty LinearRing cannot be determined.' + msg = "Orientation of an empty LinearRing cannot be determined." with self.assertRaisesMessage(ValueError, msg): LinearRing().is_counterclockwise - @skipIf(geos_version_tuple() < (3, 7), 'GEOS >= 3.7.0 is required') + @skipIf(geos_version_tuple() < (3, 7), "GEOS >= 3.7.0 is required") def test_is_counterclockwise(self): self._test_is_counterclockwise() - @skipIf(geos_version_tuple() < (3, 7), 'GEOS >= 3.7.0 is required') + @skipIf(geos_version_tuple() < (3, 7), "GEOS >= 3.7.0 is required") def test_is_counterclockwise_geos_error(self): - with mock.patch('django.contrib.gis.geos.prototypes.cs_is_ccw') as mocked: + with mock.patch("django.contrib.gis.geos.prototypes.cs_is_ccw") as mocked: mocked.return_value = 0 - mocked.func_name = 'GEOSCoordSeq_isCCW' + mocked.func_name = "GEOSCoordSeq_isCCW" msg = 'Error encountered in GEOS C function "GEOSCoordSeq_isCCW".' with self.assertRaisesMessage(GEOSException, msg): LinearRing((0, 0), (1, 0), (0, 1), (0, 0)).is_counterclockwise - @mock.patch('django.contrib.gis.geos.libgeos.geos_version', lambda: b'3.6.9') + @mock.patch("django.contrib.gis.geos.libgeos.geos_version", lambda: b"3.6.9") def test_is_counterclockwise_fallback(self): self._test_is_counterclockwise() def test_multilinestring(self): "Testing MultiLineString objects." - prev = fromstr('POINT(0 0)') + prev = fromstr("POINT(0 0)") for line in self.geometries.multilinestrings: ml = fromstr(line.wkt) - self.assertEqual(ml.geom_type, 'MultiLineString') + self.assertEqual(ml.geom_type, "MultiLineString") self.assertEqual(ml.geom_typeid, 5) self.assertEqual(ml.dims, 1) @@ -410,20 +436,22 @@ class GEOSTest(SimpleTestCase, TestDataMixin): prev = ml for ls in ml: - self.assertEqual(ls.geom_type, 'LineString') + self.assertEqual(ls.geom_type, "LineString") self.assertEqual(ls.geom_typeid, 1) self.assertIs(ls.empty, False) with self.assertRaises(IndexError): ml.__getitem__(len(ml)) self.assertEqual(ml.wkt, MultiLineString(*tuple(s.clone() for s in ml)).wkt) - self.assertEqual(ml, MultiLineString(*tuple(LineString(s.tuple) for s in ml))) + self.assertEqual( + ml, MultiLineString(*tuple(LineString(s.tuple) for s in ml)) + ) def test_linearring(self): "Testing LinearRing objects." for rr in self.geometries.linearrings: lr = fromstr(rr.wkt) - self.assertEqual(lr.geom_type, 'LinearRing') + self.assertEqual(lr.geom_type, "LinearRing") self.assertEqual(lr.geom_typeid, 2) self.assertEqual(lr.dims, 1) self.assertEqual(rr.n_p, len(lr)) @@ -437,14 +465,20 @@ class GEOSTest(SimpleTestCase, TestDataMixin): if numpy: self.assertEqual(lr, LinearRing(numpy.array(lr.tuple))) - with self.assertRaisesMessage(ValueError, 'LinearRing requires at least 4 points, got 3.'): + with self.assertRaisesMessage( + ValueError, "LinearRing requires at least 4 points, got 3." + ): LinearRing((0, 0), (1, 1), (0, 0)) - with self.assertRaisesMessage(ValueError, 'LinearRing requires at least 4 points, got 1.'): + with self.assertRaisesMessage( + ValueError, "LinearRing requires at least 4 points, got 1." + ): LinearRing([(0, 0)]) if numpy: - with self.assertRaisesMessage(ValueError, 'LinearRing requires at least 4 points, got 1.'): + with self.assertRaisesMessage( + ValueError, "LinearRing requires at least 4 points, got 1." + ): LinearRing(numpy.array([(0, 0)])) def test_linearring_json(self): @@ -464,16 +498,16 @@ class GEOSTest(SimpleTestCase, TestDataMixin): bbox = (0, 0, 1, x) p = Polygon.from_bbox(bbox) y = p.extent[-1] - self.assertEqual(format(x, '.13f'), format(y, '.13f')) + self.assertEqual(format(x, ".13f"), format(y, ".13f")) def test_polygons(self): "Testing Polygon objects." - prev = fromstr('POINT(0 0)') + prev = fromstr("POINT(0 0)") for p in self.geometries.polygons: # Creating the Polygon, testing its properties. poly = fromstr(p.wkt) - self.assertEqual(poly.geom_type, 'Polygon') + self.assertEqual(poly.geom_type, "Polygon") self.assertEqual(poly.geom_typeid, 3) self.assertEqual(poly.dims, 2) self.assertIs(poly.empty, False) @@ -495,7 +529,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): # Testing the exterior ring ring = poly.exterior_ring - self.assertEqual(ring.geom_type, 'LinearRing') + self.assertEqual(ring.geom_type, "LinearRing") self.assertEqual(ring.geom_typeid, 2) if p.ext_ring_cs: self.assertEqual(p.ext_ring_cs, ring.tuple) @@ -511,14 +545,14 @@ class GEOSTest(SimpleTestCase, TestDataMixin): # Testing __iter__ for r in poly: - self.assertEqual(r.geom_type, 'LinearRing') + self.assertEqual(r.geom_type, "LinearRing") self.assertEqual(r.geom_typeid, 2) # Testing polygon construction. with self.assertRaises(TypeError): Polygon(0, [1, 2, 3]) with self.assertRaises(TypeError): - Polygon('foo') + Polygon("foo") # Polygon(shell, (hole1, ... holeN)) ext_ring, *int_rings = poly @@ -530,15 +564,17 @@ class GEOSTest(SimpleTestCase, TestDataMixin): # Constructing with tuples of LinearRings. self.assertEqual(poly.wkt, Polygon(*tuple(r for r in poly)).wkt) - self.assertEqual(poly.wkt, Polygon(*tuple(LinearRing(r.tuple) for r in poly)).wkt) + self.assertEqual( + poly.wkt, Polygon(*tuple(LinearRing(r.tuple) for r in poly)).wkt + ) def test_polygons_templates(self): # Accessing Polygon attributes in templates should work. engine = Engine() - template = engine.from_string('{{ polygons.0.wkt }}') + template = engine.from_string("{{ polygons.0.wkt }}") polygons = [fromstr(p.wkt) for p in self.geometries.multipolygons[:2]] - content = template.render(Context({'polygons': polygons})) - self.assertIn('MULTIPOLYGON (((100', content) + content = template.render(Context({"polygons": polygons})) + self.assertIn("MULTIPOLYGON (((100", content) def test_polygon_comparison(self): p1 = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0))) @@ -553,10 +589,10 @@ class GEOSTest(SimpleTestCase, TestDataMixin): def test_multipolygons(self): "Testing MultiPolygon objects." - fromstr('POINT (0 0)') + fromstr("POINT (0 0)") for mp in self.geometries.multipolygons: mpoly = fromstr(mp.wkt) - self.assertEqual(mpoly.geom_type, 'MultiPolygon') + self.assertEqual(mpoly.geom_type, "MultiPolygon") self.assertEqual(mpoly.geom_typeid, 6) self.assertEqual(mpoly.dims, 2) self.assertEqual(mp.valid, mpoly.valid) @@ -568,10 +604,12 @@ class GEOSTest(SimpleTestCase, TestDataMixin): with self.assertRaises(IndexError): mpoly.__getitem__(len(mpoly)) for p in mpoly: - self.assertEqual(p.geom_type, 'Polygon') + self.assertEqual(p.geom_type, "Polygon") self.assertEqual(p.geom_typeid, 3) self.assertIs(p.valid, True) - self.assertEqual(mpoly.wkt, MultiPolygon(*tuple(poly.clone() for poly in mpoly)).wkt) + self.assertEqual( + mpoly.wkt, MultiPolygon(*tuple(poly.clone() for poly in mpoly)).wkt + ) def test_memory_hijinks(self): "Testing Geometry __del__() on rings and polygons." @@ -605,8 +643,12 @@ class GEOSTest(SimpleTestCase, TestDataMixin): poly = fromstr(p.wkt) cs = poly.exterior_ring.coord_seq - self.assertEqual(p.ext_ring_cs, cs.tuple) # done in the Polygon test too. - self.assertEqual(len(p.ext_ring_cs), len(cs)) # Making sure __len__ works + self.assertEqual( + p.ext_ring_cs, cs.tuple + ) # done in the Polygon test too. + self.assertEqual( + len(p.ext_ring_cs), len(cs) + ) # Making sure __len__ works # Checks __getitem__ and __setitem__ for i in range(len(p.ext_ring_cs)): @@ -628,9 +670,9 @@ class GEOSTest(SimpleTestCase, TestDataMixin): def test_relate_pattern(self): "Testing relate() and relate_pattern()." - g = fromstr('POINT (0 0)') + g = fromstr("POINT (0 0)") with self.assertRaises(GEOSException): - g.relate_pattern(0, 'invalid pattern, yo') + g.relate_pattern(0, "invalid pattern, yo") for rg in self.geometries.relate_geoms: a = fromstr(rg.wkt_a) b = fromstr(rg.wkt_b) @@ -691,7 +733,9 @@ class GEOSTest(SimpleTestCase, TestDataMixin): d1 = fromstr(self.geometries.sdiff_geoms[i].wkt) d2 = a.sym_difference(b) self.assertTrue(d1.equals(d2)) - self.assertTrue(d1.equals(a ^ b)) # __xor__ is symmetric difference operator + self.assertTrue( + d1.equals(a ^ b) + ) # __xor__ is symmetric difference operator a ^= b # testing __ixor__ self.assertTrue(d1.equals(a)) @@ -703,7 +747,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): with self.assertRaises(ctypes.ArgumentError): g.buffer(bg.width, quadsegs=1.1) - self._test_buffer(self.geometries.buffer_geoms, 'buffer') + self._test_buffer(self.geometries.buffer_geoms, "buffer") def test_buffer_with_style(self): bg = self.geometries.buffer_with_style_geoms[0] @@ -728,8 +772,10 @@ class GEOSTest(SimpleTestCase, TestDataMixin): g.buffer_with_style(bg.width, join_style=66) self._test_buffer( - itertools.chain(self.geometries.buffer_geoms, self.geometries.buffer_with_style_geoms), - 'buffer_with_style', + itertools.chain( + self.geometries.buffer_geoms, self.geometries.buffer_with_style_geoms + ), + "buffer_with_style", ) def _test_buffer(self, geometries, buffer_method_name): @@ -742,7 +788,13 @@ class GEOSTest(SimpleTestCase, TestDataMixin): # Constructing our buffer buf_kwargs = { kwarg_name: getattr(bg, kwarg_name) - for kwarg_name in ('width', 'quadsegs', 'end_cap_style', 'join_style', 'mitre_limit') + for kwarg_name in ( + "width", + "quadsegs", + "end_cap_style", + "join_style", + "mitre_limit", + ) if hasattr(bg, kwarg_name) } buf = getattr(g, buffer_method_name)(**buf_kwargs) @@ -778,7 +830,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): pnt.srid = 3084 self.assertEqual(3084, pnt.srid) with self.assertRaises(ctypes.ArgumentError): - pnt.srid = '4326' + pnt.srid = "4326" # Testing SRID keyword on fromstr(), and on Polygon rings. poly = fromstr(self.geometries.polygons[1].wkt, srid=4269) @@ -789,7 +841,9 @@ class GEOSTest(SimpleTestCase, TestDataMixin): self.assertEqual(4326, poly.shell.srid) # Testing SRID keyword on GeometryCollection - gc = GeometryCollection(Point(5, 23), LineString((0, 0), (1.5, 1.5), (3, 3)), srid=32021) + gc = GeometryCollection( + Point(5, 23), LineString((0, 0), (1.5, 1.5), (3, 3)), srid=32021 + ) self.assertEqual(32021, gc.srid) for i in range(len(gc)): self.assertEqual(32021, gc[i].srid) @@ -797,7 +851,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): # GEOS may get the SRID from HEXEWKB # 'POINT(5 23)' at SRID=4326 in hex form -- obtained from PostGIS # using `SELECT GeomFromText('POINT (5 23)', 4326);`. - hex = '0101000020E610000000000000000014400000000000003740' + hex = "0101000020E610000000000000000014400000000000003740" p1 = fromstr(hex) self.assertEqual(4326, p1.srid) @@ -813,26 +867,35 @@ class GEOSTest(SimpleTestCase, TestDataMixin): # Input geometries that have an SRID. self.assertEqual(GEOSGeometry(pnt.ewkt, srid=pnt.srid).srid, pnt.srid) self.assertEqual(GEOSGeometry(pnt.ewkb, srid=pnt.srid).srid, pnt.srid) - with self.assertRaisesMessage(ValueError, 'Input geometry already has SRID: %d.' % pnt.srid): + with self.assertRaisesMessage( + ValueError, "Input geometry already has SRID: %d." % pnt.srid + ): GEOSGeometry(pnt.ewkt, srid=1) - with self.assertRaisesMessage(ValueError, 'Input geometry already has SRID: %d.' % pnt.srid): + with self.assertRaisesMessage( + ValueError, "Input geometry already has SRID: %d." % pnt.srid + ): GEOSGeometry(pnt.ewkb, srid=1) def test_custom_srid(self): """Test with a null srid and a srid unknown to GDAL.""" for srid in [None, 999999]: pnt = Point(111200, 220900, srid=srid) - self.assertTrue(pnt.ewkt.startswith(("SRID=%s;" % srid if srid else '') + "POINT (111200")) + self.assertTrue( + pnt.ewkt.startswith( + ("SRID=%s;" % srid if srid else "") + "POINT (111200" + ) + ) self.assertIsInstance(pnt.ogr, gdal.OGRGeometry) self.assertIsNone(pnt.srs) # Test conversion from custom to a known srid c2w = gdal.CoordTransform( gdal.SpatialReference( - '+proj=mill +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +R_A +ellps=WGS84 ' - '+datum=WGS84 +units=m +no_defs' + "+proj=mill +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +R_A +ellps=WGS84 " + "+datum=WGS84 +units=m +no_defs" ), - gdal.SpatialReference(4326)) + gdal.SpatialReference(4326), + ) new_pnt = pnt.transform(c2w, clone=True) self.assertEqual(new_pnt.srid, 4326) self.assertAlmostEqual(new_pnt.x, 1, 1) @@ -852,7 +915,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): shell_tup = poly.shell.tuple new_coords = [] for point in shell_tup: - new_coords.append((point[0] + 500., point[1] + 500.)) + new_coords.append((point[0] + 500.0, point[1] + 500.0)) new_shell = LinearRing(*tuple(new_coords)) # Assigning polygon's exterior ring w/the new shell @@ -886,7 +949,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): for j in range(len(poly)): r = poly[j] for k in range(len(r)): - r[k] = (r[k][0] + 500., r[k][1] + 500.) + r[k] = (r[k][0] + 500.0, r[k][1] + 500.0) poly[j] = r self.assertNotEqual(mpoly[i], poly) @@ -950,7 +1013,12 @@ class GEOSTest(SimpleTestCase, TestDataMixin): pol = Polygon() pol[:] = (((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)),) - self.assertEqual(pol, Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)),)) + self.assertEqual( + pol, + Polygon( + ((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)), + ), + ) pol[:] = () self.assertEqual(pol, Polygon()) @@ -969,19 +1037,19 @@ class GEOSTest(SimpleTestCase, TestDataMixin): "Testing three-dimensional geometries." # Testing a 3D Point pnt = Point(2, 3, 8) - self.assertEqual((2., 3., 8.), pnt.coords) + self.assertEqual((2.0, 3.0, 8.0), pnt.coords) with self.assertRaises(TypeError): - pnt.tuple = (1., 2.) - pnt.coords = (1., 2., 3.) - self.assertEqual((1., 2., 3.), pnt.coords) + pnt.tuple = (1.0, 2.0) + pnt.coords = (1.0, 2.0, 3.0) + self.assertEqual((1.0, 2.0, 3.0), pnt.coords) # Testing a 3D LineString - ls = LineString((2., 3., 8.), (50., 250., -117.)) - self.assertEqual(((2., 3., 8.), (50., 250., -117.)), ls.tuple) + ls = LineString((2.0, 3.0, 8.0), (50.0, 250.0, -117.0)) + self.assertEqual(((2.0, 3.0, 8.0), (50.0, 250.0, -117.0)), ls.tuple) with self.assertRaises(TypeError): - ls.__setitem__(0, (1., 2.)) - ls[0] = (1., 2., 3.) - self.assertEqual((1., 2., 3.), ls[0]) + ls.__setitem__(0, (1.0, 2.0)) + ls[0] = (1.0, 2.0, 3.0) + self.assertEqual((1.0, 2.0, 3.0), ls[0]) def test_distance(self): "Testing the distance() function." @@ -1023,17 +1091,17 @@ class GEOSTest(SimpleTestCase, TestDataMixin): "Testing empty geometries and collections." geoms = [ GeometryCollection([]), - fromstr('GEOMETRYCOLLECTION EMPTY'), + fromstr("GEOMETRYCOLLECTION EMPTY"), GeometryCollection(), - fromstr('POINT EMPTY'), + fromstr("POINT EMPTY"), Point(), - fromstr('LINESTRING EMPTY'), + fromstr("LINESTRING EMPTY"), LineString(), - fromstr('POLYGON EMPTY'), + fromstr("POLYGON EMPTY"), Polygon(), - fromstr('MULTILINESTRING EMPTY'), + fromstr("MULTILINESTRING EMPTY"), MultiLineString(), - fromstr('MULTIPOLYGON EMPTY'), + fromstr("MULTIPOLYGON EMPTY"), MultiPolygon(()), MultiPolygon(), ] @@ -1062,7 +1130,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): g.x elif isinstance(g, Polygon): lr = g.shell - self.assertEqual('LINEARRING EMPTY', lr.wkt) + self.assertEqual("LINEARRING EMPTY", lr.wkt) self.assertEqual(0, len(lr)) self.assertIs(lr.empty, True) with self.assertRaises(IndexError): @@ -1081,7 +1149,11 @@ class GEOSTest(SimpleTestCase, TestDataMixin): gc = GeometryCollection(LineString((0, 0), (1, 1)), Point(0, 0)) self.assertEqual(gc.dims, 1) - gc = GeometryCollection(LineString((0, 0), (1, 1)), Polygon(((0, 0), (0, 1), (1, 1), (0, 0))), Point(0, 0)) + gc = GeometryCollection( + LineString((0, 0), (1, 1)), + Polygon(((0, 0), (0, 1), (1, 1), (0, 0))), + Point(0, 0), + ) self.assertEqual(gc.dims, 2) def test_collections_of_collections(self): @@ -1092,7 +1164,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): coll.extend(mls.wkt for mls in self.geometries.multilinestrings) coll.extend(p.wkt for p in self.geometries.polygons) coll.extend(mp.wkt for mp in self.geometries.multipoints) - gc_wkt = 'GEOMETRYCOLLECTION(%s)' % ','.join(coll) + gc_wkt = "GEOMETRYCOLLECTION(%s)" % ",".join(coll) # Should construct ok from WKT gc1 = GEOSGeometry(gc_wkt) @@ -1105,24 +1177,27 @@ class GEOSTest(SimpleTestCase, TestDataMixin): def test_gdal(self): "Testing `ogr` and `srs` properties." - g1 = fromstr('POINT(5 23)') + g1 = fromstr("POINT(5 23)") self.assertIsInstance(g1.ogr, gdal.OGRGeometry) self.assertIsNone(g1.srs) - g1_3d = fromstr('POINT(5 23 8)') + g1_3d = fromstr("POINT(5 23 8)") self.assertIsInstance(g1_3d.ogr, gdal.OGRGeometry) self.assertEqual(g1_3d.ogr.z, 8) - g2 = fromstr('LINESTRING(0 0, 5 5, 23 23)', srid=4326) + g2 = fromstr("LINESTRING(0 0, 5 5, 23 23)", srid=4326) self.assertIsInstance(g2.ogr, gdal.OGRGeometry) self.assertIsInstance(g2.srs, gdal.SpatialReference) self.assertEqual(g2.hex, g2.ogr.hex) - self.assertEqual('WGS 84', g2.srs.name) + self.assertEqual("WGS 84", g2.srs.name) def test_copy(self): "Testing use with the Python `copy` module." import copy - poly = GEOSGeometry('POLYGON((0 0, 0 23, 23 23, 23 0, 0 0), (5 5, 5 10, 10 10, 10 5, 5 5))') + + poly = GEOSGeometry( + "POLYGON((0 0, 0 23, 23 23, 23 0, 0 0), (5 5, 5 10, 10 10, 10 5, 5 5))" + ) cpy1 = copy.copy(poly) cpy2 = copy.deepcopy(poly) self.assertNotEqual(poly._ptr, cpy1._ptr) @@ -1130,15 +1205,17 @@ class GEOSTest(SimpleTestCase, TestDataMixin): def test_transform(self): "Testing `transform` method." - orig = GEOSGeometry('POINT (-104.609 38.255)', 4326) - trans = GEOSGeometry('POINT (992385.4472045 481455.4944650)', 2774) + orig = GEOSGeometry("POINT (-104.609 38.255)", 4326) + trans = GEOSGeometry("POINT (992385.4472045 481455.4944650)", 2774) # Using a srid, a SpatialReference object, and a CoordTransform object # for transformations. t1, t2, t3 = orig.clone(), orig.clone(), orig.clone() t1.transform(trans.srid) - t2.transform(gdal.SpatialReference('EPSG:2774')) - ct = gdal.CoordTransform(gdal.SpatialReference('WGS84'), gdal.SpatialReference(2774)) + t2.transform(gdal.SpatialReference("EPSG:2774")) + ct = gdal.CoordTransform( + gdal.SpatialReference("WGS84"), gdal.SpatialReference(2774) + ) t3.transform(ct) # Testing use of the `clone` keyword. @@ -1155,42 +1232,42 @@ class GEOSTest(SimpleTestCase, TestDataMixin): self.assertAlmostEqual(trans.y, p.y, prec) def test_transform_3d(self): - p3d = GEOSGeometry('POINT (5 23 100)', 4326) + p3d = GEOSGeometry("POINT (5 23 100)", 4326) p3d.transform(2774) self.assertAlmostEqual(p3d.z, 100, 3) def test_transform_noop(self): - """ Testing `transform` method (SRID match) """ + """Testing `transform` method (SRID match)""" # transform() should no-op if source & dest SRIDs match, # regardless of whether GDAL is available. - g = GEOSGeometry('POINT (-104.609 38.255)', 4326) + g = GEOSGeometry("POINT (-104.609 38.255)", 4326) gt = g.tuple g.transform(4326) self.assertEqual(g.tuple, gt) self.assertEqual(g.srid, 4326) - g = GEOSGeometry('POINT (-104.609 38.255)', 4326) + g = GEOSGeometry("POINT (-104.609 38.255)", 4326) g1 = g.transform(4326, clone=True) self.assertEqual(g1.tuple, g.tuple) self.assertEqual(g1.srid, 4326) self.assertIsNot(g1, g, "Clone didn't happen") def test_transform_nosrid(self): - """ Testing `transform` method (no SRID or negative SRID) """ + """Testing `transform` method (no SRID or negative SRID)""" - g = GEOSGeometry('POINT (-104.609 38.255)', srid=None) + g = GEOSGeometry("POINT (-104.609 38.255)", srid=None) with self.assertRaises(GEOSException): g.transform(2774) - g = GEOSGeometry('POINT (-104.609 38.255)', srid=None) + g = GEOSGeometry("POINT (-104.609 38.255)", srid=None) with self.assertRaises(GEOSException): g.transform(2774, clone=True) - g = GEOSGeometry('POINT (-104.609 38.255)', srid=-1) + g = GEOSGeometry("POINT (-104.609 38.255)", srid=-1) with self.assertRaises(GEOSException): g.transform(2774) - g = GEOSGeometry('POINT (-104.609 38.255)', srid=-1) + g = GEOSGeometry("POINT (-104.609 38.255)", srid=-1) with self.assertRaises(GEOSException): g.transform(2774, clone=True) @@ -1216,6 +1293,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): # and setting the SRID on some of them. def get_geoms(lst, srid=None): return [GEOSGeometry(tg.wkt, srid) for tg in lst] + tgeoms = get_geoms(self.geometries.points) tgeoms.extend(get_geoms(self.geometries.multilinestrings, 4326)) tgeoms.extend(get_geoms(self.geometries.polygons, 3084)) @@ -1231,7 +1309,9 @@ class GEOSTest(SimpleTestCase, TestDataMixin): def test_prepared(self): "Testing PreparedGeometry support." # Creating a simple multipolygon and getting a prepared version. - mpoly = GEOSGeometry('MULTIPOLYGON(((0 0,0 5,5 5,5 0,0 0)),((5 5,5 10,10 10,10 5,5 5)))') + mpoly = GEOSGeometry( + "MULTIPOLYGON(((0 0,0 5,5 5,5 0,0 0)),((5 5,5 10,10 10,10 5,5 5)))" + ) prep = mpoly.prepared # A set of test points. @@ -1242,7 +1322,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): self.assertEqual(mpoly.intersects(pnt), prep.intersects(pnt)) self.assertEqual(mpoly.covers(pnt), prep.covers(pnt)) - self.assertTrue(prep.crosses(fromstr('LINESTRING(1 1, 15 15)'))) + self.assertTrue(prep.crosses(fromstr("LINESTRING(1 1, 15 15)"))) self.assertTrue(prep.disjoint(Point(-5, -5))) poly = Polygon(((-1, -1), (1, 1), (1, 0), (-1, -1))) self.assertTrue(prep.overlaps(poly)) @@ -1257,12 +1337,14 @@ class GEOSTest(SimpleTestCase, TestDataMixin): def test_line_merge(self): "Testing line merge support" - ref_geoms = (fromstr('LINESTRING(1 1, 1 1, 3 3)'), - fromstr('MULTILINESTRING((1 1, 3 3), (3 3, 4 2))'), - ) - ref_merged = (fromstr('LINESTRING(1 1, 3 3)'), - fromstr('LINESTRING (1 1, 3 3, 4 2)'), - ) + ref_geoms = ( + fromstr("LINESTRING(1 1, 1 1, 3 3)"), + fromstr("MULTILINESTRING((1 1, 3 3), (3 3, 4 2))"), + ) + ref_merged = ( + fromstr("LINESTRING(1 1, 3 3)"), + fromstr("LINESTRING (1 1, 3 3, 4 2)"), + ) for geom, merged in zip(ref_geoms, ref_merged): self.assertEqual(merged, geom.merged) @@ -1278,13 +1360,15 @@ class GEOSTest(SimpleTestCase, TestDataMixin): self.assertFalse(g.valid) self.assertIsInstance(g.valid_reason, str) - self.assertTrue(g.valid_reason.startswith("Too few points in geometry component")) + self.assertTrue( + g.valid_reason.startswith("Too few points in geometry component") + ) def test_linearref(self): "Testing linear referencing" - ls = fromstr('LINESTRING(0 0, 0 10, 10 10, 10 0)') - mls = fromstr('MULTILINESTRING((0 0, 0 10), (10 0, 10 10))') + ls = fromstr("LINESTRING(0 0, 0 10, 10 10, 10 0)") + mls = fromstr("MULTILINESTRING((0 0, 0 10), (10 0, 10 10))") self.assertEqual(ls.project(Point(0, 20)), 10.0) self.assertEqual(ls.project(Point(7, 6)), 24) @@ -1306,39 +1390,39 @@ class GEOSTest(SimpleTestCase, TestDataMixin): """ point = Point(4.337844, 50.827537, srid=4326) path, args, kwargs = point.deconstruct() - self.assertEqual(path, 'django.contrib.gis.geos.point.Point') + self.assertEqual(path, "django.contrib.gis.geos.point.Point") self.assertEqual(args, (4.337844, 50.827537)) - self.assertEqual(kwargs, {'srid': 4326}) + self.assertEqual(kwargs, {"srid": 4326}) ls = LineString(((0, 0), (1, 1))) path, args, kwargs = ls.deconstruct() - self.assertEqual(path, 'django.contrib.gis.geos.linestring.LineString') + self.assertEqual(path, "django.contrib.gis.geos.linestring.LineString") self.assertEqual(args, (((0, 0), (1, 1)),)) self.assertEqual(kwargs, {}) ls2 = LineString([Point(0, 0), Point(1, 1)], srid=4326) path, args, kwargs = ls2.deconstruct() - self.assertEqual(path, 'django.contrib.gis.geos.linestring.LineString') + self.assertEqual(path, "django.contrib.gis.geos.linestring.LineString") self.assertEqual(args, ([Point(0, 0), Point(1, 1)],)) - self.assertEqual(kwargs, {'srid': 4326}) + self.assertEqual(kwargs, {"srid": 4326}) ext_coords = ((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)) int_coords = ((0.4, 0.4), (0.4, 0.6), (0.6, 0.6), (0.6, 0.4), (0.4, 0.4)) poly = Polygon(ext_coords, int_coords) path, args, kwargs = poly.deconstruct() - self.assertEqual(path, 'django.contrib.gis.geos.polygon.Polygon') + self.assertEqual(path, "django.contrib.gis.geos.polygon.Polygon") self.assertEqual(args, (ext_coords, int_coords)) self.assertEqual(kwargs, {}) lr = LinearRing((0, 0), (0, 1), (1, 1), (0, 0)) path, args, kwargs = lr.deconstruct() - self.assertEqual(path, 'django.contrib.gis.geos.linestring.LinearRing') + self.assertEqual(path, "django.contrib.gis.geos.linestring.LinearRing") self.assertEqual(args, ((0, 0), (0, 1), (1, 1), (0, 0))) self.assertEqual(kwargs, {}) mp = MultiPoint(Point(0, 0), Point(1, 1)) path, args, kwargs = mp.deconstruct() - self.assertEqual(path, 'django.contrib.gis.geos.collections.MultiPoint') + self.assertEqual(path, "django.contrib.gis.geos.collections.MultiPoint") self.assertEqual(args, (Point(0, 0), Point(1, 1))) self.assertEqual(kwargs, {}) @@ -1346,7 +1430,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): ls2 = LineString((2, 2), (3, 3)) mls = MultiLineString(ls1, ls2) path, args, kwargs = mls.deconstruct() - self.assertEqual(path, 'django.contrib.gis.geos.collections.MultiLineString') + self.assertEqual(path, "django.contrib.gis.geos.collections.MultiLineString") self.assertEqual(args, (ls1, ls2)) self.assertEqual(kwargs, {}) @@ -1354,15 +1438,17 @@ class GEOSTest(SimpleTestCase, TestDataMixin): p2 = Polygon(((1, 1), (1, 2), (2, 2), (1, 1))) mp = MultiPolygon(p1, p2) path, args, kwargs = mp.deconstruct() - self.assertEqual(path, 'django.contrib.gis.geos.collections.MultiPolygon') + self.assertEqual(path, "django.contrib.gis.geos.collections.MultiPolygon") self.assertEqual(args, (p1, p2)) self.assertEqual(kwargs, {}) poly = Polygon(((0, 0), (0, 1), (1, 1), (0, 0))) gc = GeometryCollection(Point(0, 0), MultiPoint(Point(0, 0), Point(1, 1)), poly) path, args, kwargs = gc.deconstruct() - self.assertEqual(path, 'django.contrib.gis.geos.collections.GeometryCollection') - self.assertEqual(args, (Point(0, 0), MultiPoint(Point(0, 0), Point(1, 1)), poly)) + self.assertEqual(path, "django.contrib.gis.geos.collections.GeometryCollection") + self.assertEqual( + args, (Point(0, 0), MultiPoint(Point(0, 0), Point(1, 1)), poly) + ) self.assertEqual(kwargs, {}) def test_subclassing(self): @@ -1370,6 +1456,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): GEOSGeometry subclass may itself be subclassed without being forced-cast to the parent class during `__init__`. """ + class ExtendedPolygon(Polygon): def __init__(self, *args, data=0, **kwargs): super().__init__(*args, **kwargs) @@ -1381,7 +1468,9 @@ class GEOSTest(SimpleTestCase, TestDataMixin): ext_poly = ExtendedPolygon(((0, 0), (0, 1), (1, 1), (0, 0)), data=3) self.assertEqual(type(ext_poly), ExtendedPolygon) # ExtendedPolygon.__str__ should be called (instead of Polygon.__str__). - self.assertEqual(str(ext_poly), "EXT_POLYGON - data: 3 - POLYGON ((0 0, 0 1, 1 1, 0 0))") + self.assertEqual( + str(ext_poly), "EXT_POLYGON - data: 3 - POLYGON ((0 0, 0 1, 1 1, 0 0))" + ) self.assertJSONEqual( ext_poly.json, '{"coordinates": [[[0, 0], [0, 1], [1, 1], [0, 0]]], "type": "Polygon"}', @@ -1389,56 +1478,63 @@ class GEOSTest(SimpleTestCase, TestDataMixin): def test_geos_version_tuple(self): versions = ( - (b'3.0.0rc4-CAPI-1.3.3', (3, 0, 0)), - (b'3.0.0-CAPI-1.4.1', (3, 0, 0)), - (b'3.4.0dev-CAPI-1.8.0', (3, 4, 0)), - (b'3.4.0dev-CAPI-1.8.0 r0', (3, 4, 0)), - (b'3.6.2-CAPI-1.10.2 4d2925d6', (3, 6, 2)), + (b"3.0.0rc4-CAPI-1.3.3", (3, 0, 0)), + (b"3.0.0-CAPI-1.4.1", (3, 0, 0)), + (b"3.4.0dev-CAPI-1.8.0", (3, 4, 0)), + (b"3.4.0dev-CAPI-1.8.0 r0", (3, 4, 0)), + (b"3.6.2-CAPI-1.10.2 4d2925d6", (3, 6, 2)), ) for version_string, version_tuple in versions: with self.subTest(version_string=version_string): - with mock.patch('django.contrib.gis.geos.libgeos.geos_version', lambda: version_string): + with mock.patch( + "django.contrib.gis.geos.libgeos.geos_version", + lambda: version_string, + ): self.assertEqual(geos_version_tuple(), version_tuple) def test_from_gml(self): self.assertEqual( - GEOSGeometry('POINT(0 0)'), + GEOSGeometry("POINT(0 0)"), GEOSGeometry.from_gml( '<gml:Point gml:id="p21" srsName="http://www.opengis.net/def/crs/EPSG/0/4326">' ' <gml:pos srsDimension="2">0 0</gml:pos>' - '</gml:Point>' + "</gml:Point>" ), ) def test_from_ewkt(self): - self.assertEqual(GEOSGeometry.from_ewkt('SRID=1;POINT(1 1)'), Point(1, 1, srid=1)) - self.assertEqual(GEOSGeometry.from_ewkt('POINT(1 1)'), Point(1, 1)) + self.assertEqual( + GEOSGeometry.from_ewkt("SRID=1;POINT(1 1)"), Point(1, 1, srid=1) + ) + self.assertEqual(GEOSGeometry.from_ewkt("POINT(1 1)"), Point(1, 1)) def test_from_ewkt_empty_string(self): - msg = 'Expected WKT but got an empty string.' + msg = "Expected WKT but got an empty string." with self.assertRaisesMessage(ValueError, msg): - GEOSGeometry.from_ewkt('') + GEOSGeometry.from_ewkt("") with self.assertRaisesMessage(ValueError, msg): - GEOSGeometry.from_ewkt('SRID=1;') + GEOSGeometry.from_ewkt("SRID=1;") def test_from_ewkt_invalid_srid(self): - msg = 'EWKT has invalid SRID part.' + msg = "EWKT has invalid SRID part." with self.assertRaisesMessage(ValueError, msg): - GEOSGeometry.from_ewkt('SRUD=1;POINT(1 1)') + GEOSGeometry.from_ewkt("SRUD=1;POINT(1 1)") with self.assertRaisesMessage(ValueError, msg): - GEOSGeometry.from_ewkt('SRID=WGS84;POINT(1 1)') + GEOSGeometry.from_ewkt("SRID=WGS84;POINT(1 1)") def test_fromstr_scientific_wkt(self): - self.assertEqual(GEOSGeometry('POINT(1.0e-1 1.0e+1)'), Point(.1, 10)) + self.assertEqual(GEOSGeometry("POINT(1.0e-1 1.0e+1)"), Point(0.1, 10)) def test_normalize(self): g = MultiPoint(Point(0, 0), Point(2, 2), Point(1, 1)) self.assertIsNone(g.normalize()) - self.assertTrue(g.equals_exact(MultiPoint(Point(2, 2), Point(1, 1), Point(0, 0)))) + self.assertTrue( + g.equals_exact(MultiPoint(Point(2, 2), Point(1, 1), Point(0, 0))) + ) - @skipIf(geos_version_tuple() < (3, 8), 'GEOS >= 3.8.0 is required') + @skipIf(geos_version_tuple() < (3, 8), "GEOS >= 3.8.0 is required") def test_make_valid(self): - poly = GEOSGeometry('POLYGON((0 0, 0 23, 23 0, 23 23, 0 0))') + poly = GEOSGeometry("POLYGON((0 0, 0 23, 23 0, 23 23, 0 0))") self.assertIs(poly.valid, False) valid_poly = poly.make_valid() self.assertIs(valid_poly.valid, True) @@ -1448,10 +1544,10 @@ class GEOSTest(SimpleTestCase, TestDataMixin): self.assertIs(valid_poly2.valid, True) self.assertEqual(valid_poly, valid_poly2) - @mock.patch('django.contrib.gis.geos.libgeos.geos_version', lambda: b'3.7.3') + @mock.patch("django.contrib.gis.geos.libgeos.geos_version", lambda: b"3.7.3") def test_make_valid_geos_version(self): - msg = 'GEOSGeometry.make_valid() requires GEOS >= 3.8.0.' - poly = GEOSGeometry('POLYGON((0 0, 0 23, 23 0, 23 23, 0 0))') + msg = "GEOSGeometry.make_valid() requires GEOS >= 3.8.0." + poly = GEOSGeometry("POLYGON((0 0, 0 23, 23 0, 23 23, 0 0))") with self.assertRaisesMessage(GEOSException, msg): poly.make_valid() diff --git a/tests/gis_tests/geos_tests/test_geos_mutation.py b/tests/gis_tests/geos_tests/test_geos_mutation.py index 2eedef31d9..c239ab3efe 100644 --- a/tests/gis_tests/geos_tests/test_geos_mutation.py +++ b/tests/gis_tests/geos_tests/test_geos_mutation.py @@ -5,7 +5,12 @@ import unittest from django.contrib.gis.geos import ( - LinearRing, LineString, MultiPoint, Point, Polygon, fromstr, + LinearRing, + LineString, + MultiPoint, + Point, + Polygon, + fromstr, ) @@ -66,8 +71,9 @@ def api_get_length(x): geos_function_tests = [ - val for name, val in vars().items() - if hasattr(val, '__call__') and name.startswith('api_get_') + val + for name, val in vars().items() + if hasattr(val, "__call__") and name.startswith("api_get_") ] @@ -78,7 +84,7 @@ class GEOSMutationTest(unittest.TestCase): """ def test00_GEOSIndexException(self): - 'Testing Geometry IndexError' + "Testing Geometry IndexError" p = Point(1, 2) for i in range(-2, 2): p._checkindex(i) @@ -88,87 +94,124 @@ class GEOSMutationTest(unittest.TestCase): p._checkindex(-3) def test01_PointMutations(self): - 'Testing Point mutations' - for p in (Point(1, 2, 3), fromstr('POINT (1 2 3)')): - self.assertEqual(p._get_single_external(1), 2.0, 'Point _get_single_external') + "Testing Point mutations" + for p in (Point(1, 2, 3), fromstr("POINT (1 2 3)")): + self.assertEqual( + p._get_single_external(1), 2.0, "Point _get_single_external" + ) # _set_single p._set_single(0, 100) - self.assertEqual(p.coords, (100.0, 2.0, 3.0), 'Point _set_single') + self.assertEqual(p.coords, (100.0, 2.0, 3.0), "Point _set_single") # _set_list p._set_list(2, (50, 3141)) - self.assertEqual(p.coords, (50.0, 3141.0), 'Point _set_list') + self.assertEqual(p.coords, (50.0, 3141.0), "Point _set_list") def test02_PointExceptions(self): - 'Testing Point exceptions' + "Testing Point exceptions" with self.assertRaises(TypeError): Point(range(1)) with self.assertRaises(TypeError): Point(range(4)) def test03_PointApi(self): - 'Testing Point API' + "Testing Point API" q = Point(4, 5, 3) - for p in (Point(1, 2, 3), fromstr('POINT (1 2 3)')): + for p in (Point(1, 2, 3), fromstr("POINT (1 2 3)")): p[0:2] = [4, 5] for f in geos_function_tests: - self.assertEqual(f(q), f(p), 'Point ' + f.__name__) + self.assertEqual(f(q), f(p), "Point " + f.__name__) def test04_LineStringMutations(self): - 'Testing LineString mutations' - for ls in (LineString((1, 0), (4, 1), (6, -1)), - fromstr('LINESTRING (1 0,4 1,6 -1)')): - self.assertEqual(ls._get_single_external(1), (4.0, 1.0), 'LineString _get_single_external') + "Testing LineString mutations" + for ls in ( + LineString((1, 0), (4, 1), (6, -1)), + fromstr("LINESTRING (1 0,4 1,6 -1)"), + ): + self.assertEqual( + ls._get_single_external(1), + (4.0, 1.0), + "LineString _get_single_external", + ) # _set_single ls._set_single(0, (-50, 25)) - self.assertEqual(ls.coords, ((-50.0, 25.0), (4.0, 1.0), (6.0, -1.0)), 'LineString _set_single') + self.assertEqual( + ls.coords, + ((-50.0, 25.0), (4.0, 1.0), (6.0, -1.0)), + "LineString _set_single", + ) # _set_list ls._set_list(2, ((-50.0, 25.0), (6.0, -1.0))) - self.assertEqual(ls.coords, ((-50.0, 25.0), (6.0, -1.0)), 'LineString _set_list') + self.assertEqual( + ls.coords, ((-50.0, 25.0), (6.0, -1.0)), "LineString _set_list" + ) lsa = LineString(ls.coords) for f in geos_function_tests: - self.assertEqual(f(lsa), f(ls), 'LineString ' + f.__name__) + self.assertEqual(f(lsa), f(ls), "LineString " + f.__name__) def test05_Polygon(self): - 'Testing Polygon mutations' - for pg in (Polygon(((1, 0), (4, 1), (6, -1), (8, 10), (1, 0)), - ((5, 4), (6, 4), (6, 3), (5, 4))), - fromstr('POLYGON ((1 0,4 1,6 -1,8 10,1 0),(5 4,6 4,6 3,5 4))')): - self.assertEqual(pg._get_single_external(0), - LinearRing((1, 0), (4, 1), (6, -1), (8, 10), (1, 0)), - 'Polygon _get_single_external(0)') - self.assertEqual(pg._get_single_external(1), - LinearRing((5, 4), (6, 4), (6, 3), (5, 4)), - 'Polygon _get_single_external(1)') + "Testing Polygon mutations" + for pg in ( + Polygon( + ((1, 0), (4, 1), (6, -1), (8, 10), (1, 0)), + ((5, 4), (6, 4), (6, 3), (5, 4)), + ), + fromstr("POLYGON ((1 0,4 1,6 -1,8 10,1 0),(5 4,6 4,6 3,5 4))"), + ): + self.assertEqual( + pg._get_single_external(0), + LinearRing((1, 0), (4, 1), (6, -1), (8, 10), (1, 0)), + "Polygon _get_single_external(0)", + ) + self.assertEqual( + pg._get_single_external(1), + LinearRing((5, 4), (6, 4), (6, 3), (5, 4)), + "Polygon _get_single_external(1)", + ) # _set_list - pg._set_list(2, (((1, 2), (10, 0), (12, 9), (-1, 15), (1, 2)), ((4, 2), (5, 2), (5, 3), (4, 2)))) + pg._set_list( + 2, + ( + ((1, 2), (10, 0), (12, 9), (-1, 15), (1, 2)), + ((4, 2), (5, 2), (5, 3), (4, 2)), + ), + ) self.assertEqual( pg.coords, - (((1.0, 2.0), (10.0, 0.0), (12.0, 9.0), (-1.0, 15.0), (1.0, 2.0)), - ((4.0, 2.0), (5.0, 2.0), (5.0, 3.0), (4.0, 2.0))), - 'Polygon _set_list') + ( + ((1.0, 2.0), (10.0, 0.0), (12.0, 9.0), (-1.0, 15.0), (1.0, 2.0)), + ((4.0, 2.0), (5.0, 2.0), (5.0, 3.0), (4.0, 2.0)), + ), + "Polygon _set_list", + ) lsa = Polygon(*pg.coords) for f in geos_function_tests: - self.assertEqual(f(lsa), f(pg), 'Polygon ' + f.__name__) + self.assertEqual(f(lsa), f(pg), "Polygon " + f.__name__) def test06_Collection(self): - 'Testing Collection mutations' + "Testing Collection mutations" points = ( MultiPoint(*map(Point, ((3, 4), (-1, 2), (5, -4), (2, 8)))), - fromstr('MULTIPOINT (3 4,-1 2,5 -4,2 8)'), + fromstr("MULTIPOINT (3 4,-1 2,5 -4,2 8)"), ) for mp in points: - self.assertEqual(mp._get_single_external(2), Point(5, -4), 'Collection _get_single_external') + self.assertEqual( + mp._get_single_external(2), + Point(5, -4), + "Collection _get_single_external", + ) mp._set_list(3, map(Point, ((5, 5), (3, -2), (8, 1)))) - self.assertEqual(mp.coords, ((5.0, 5.0), (3.0, -2.0), (8.0, 1.0)), 'Collection _set_list') + self.assertEqual( + mp.coords, ((5.0, 5.0), (3.0, -2.0), (8.0, 1.0)), "Collection _set_list" + ) lsa = MultiPoint(*map(Point, ((5, 5), (3, -2), (8, 1)))) for f in geos_function_tests: - self.assertEqual(f(lsa), f(mp), 'MultiPoint ' + f.__name__) + self.assertEqual(f(lsa), f(mp), "MultiPoint " + f.__name__) diff --git a/tests/gis_tests/geos_tests/test_io.py b/tests/gis_tests/geos_tests/test_io.py index a2efc650c8..b9d24cc63e 100644 --- a/tests/gis_tests/geos_tests/test_io.py +++ b/tests/gis_tests/geos_tests/test_io.py @@ -1,18 +1,23 @@ import binascii from django.contrib.gis.geos import ( - GEOSGeometry, Point, Polygon, WKBReader, WKBWriter, WKTReader, WKTWriter, + GEOSGeometry, + Point, + Polygon, + WKBReader, + WKBWriter, + WKTReader, + WKTWriter, ) from django.contrib.gis.geos.libgeos import geos_version_tuple from django.test import SimpleTestCase class GEOSIOTest(SimpleTestCase): - def test01_wktreader(self): # Creating a WKTReader instance wkt_r = WKTReader() - wkt = 'POINT (5 23)' + wkt = "POINT (5 23)" # read() should return a GEOSGeometry ref = GEOSGeometry(wkt) @@ -26,7 +31,7 @@ class GEOSIOTest(SimpleTestCase): with self.assertRaises(TypeError): wkt_r.read(1) with self.assertRaises(TypeError): - wkt_r.read(memoryview(b'foo')) + wkt_r.read(memoryview(b"foo")) def test02_wktwriter(self): # Creating a WKTWriter instance, testing its ptr property. @@ -34,24 +39,24 @@ class GEOSIOTest(SimpleTestCase): with self.assertRaises(TypeError): wkt_w.ptr = WKTReader.ptr_type() - ref = GEOSGeometry('POINT (5 23)') - ref_wkt = 'POINT (5.0000000000000000 23.0000000000000000)' + ref = GEOSGeometry("POINT (5 23)") + ref_wkt = "POINT (5.0000000000000000 23.0000000000000000)" self.assertEqual(ref_wkt, wkt_w.write(ref).decode()) def test_wktwriter_constructor_arguments(self): wkt_w = WKTWriter(dim=3, trim=True, precision=3) - ref = GEOSGeometry('POINT (5.34562 23 1.5)') + ref = GEOSGeometry("POINT (5.34562 23 1.5)") if geos_version_tuple() > (3, 10): - ref_wkt = 'POINT Z (5.346 23 1.5)' + ref_wkt = "POINT Z (5.346 23 1.5)" else: - ref_wkt = 'POINT Z (5.35 23 1.5)' + ref_wkt = "POINT Z (5.35 23 1.5)" self.assertEqual(ref_wkt, wkt_w.write(ref).decode()) def test03_wkbreader(self): # Creating a WKBReader instance wkb_r = WKBReader() - hex = b'000000000140140000000000004037000000000000' + hex = b"000000000140140000000000004037000000000000" wkb = memoryview(binascii.a2b_hex(hex)) ref = GEOSGeometry(hex) @@ -72,17 +77,17 @@ class GEOSIOTest(SimpleTestCase): # Representations of 'POINT (5 23)' in hex -- one normal and # the other with the byte order changed. - g = GEOSGeometry('POINT (5 23)') - hex1 = b'010100000000000000000014400000000000003740' + g = GEOSGeometry("POINT (5 23)") + hex1 = b"010100000000000000000014400000000000003740" wkb1 = memoryview(binascii.a2b_hex(hex1)) - hex2 = b'000000000140140000000000004037000000000000' + hex2 = b"000000000140140000000000004037000000000000" wkb2 = memoryview(binascii.a2b_hex(hex2)) self.assertEqual(hex1, wkb_w.write_hex(g)) self.assertEqual(wkb1, wkb_w.write(g)) # Ensuring bad byteorders are not accepted. - for bad_byteorder in (-1, 2, 523, 'foo', None): + for bad_byteorder in (-1, 2, 523, "foo", None): # Equivalent of `wkb_w.byteorder = bad_byteorder` with self.assertRaises(ValueError): wkb_w._set_byteorder(bad_byteorder) @@ -96,17 +101,21 @@ class GEOSIOTest(SimpleTestCase): wkb_w.byteorder = 1 # Now, trying out the 3D and SRID flags. - g = GEOSGeometry('POINT (5 23 17)') + g = GEOSGeometry("POINT (5 23 17)") g.srid = 4326 - hex3d = b'0101000080000000000000144000000000000037400000000000003140' + hex3d = b"0101000080000000000000144000000000000037400000000000003140" wkb3d = memoryview(binascii.a2b_hex(hex3d)) - hex3d_srid = b'01010000A0E6100000000000000000144000000000000037400000000000003140' + hex3d_srid = ( + b"01010000A0E6100000000000000000144000000000000037400000000000003140" + ) wkb3d_srid = memoryview(binascii.a2b_hex(hex3d_srid)) # Ensuring bad output dimensions are not accepted - for bad_outdim in (-1, 0, 1, 4, 423, 'foo', None): - with self.assertRaisesMessage(ValueError, 'WKB output dimension must be 2 or 3'): + for bad_outdim in (-1, 0, 1, 4, 423, "foo", None): + with self.assertRaisesMessage( + ValueError, "WKB output dimension must be 2 or 3" + ): wkb_w.outdim = bad_outdim # Now setting the output dimensions to be 3 @@ -123,53 +132,73 @@ class GEOSIOTest(SimpleTestCase): def test_wkt_writer_trim(self): wkt_w = WKTWriter() self.assertFalse(wkt_w.trim) - self.assertEqual(wkt_w.write(Point(1, 1)), b'POINT (1.0000000000000000 1.0000000000000000)') + self.assertEqual( + wkt_w.write(Point(1, 1)), b"POINT (1.0000000000000000 1.0000000000000000)" + ) wkt_w.trim = True self.assertTrue(wkt_w.trim) - self.assertEqual(wkt_w.write(Point(1, 1)), b'POINT (1 1)') - self.assertEqual(wkt_w.write(Point(1.1, 1)), b'POINT (1.1 1)') - self.assertEqual(wkt_w.write(Point(1. / 3, 1)), b'POINT (0.3333333333333333 1)') + self.assertEqual(wkt_w.write(Point(1, 1)), b"POINT (1 1)") + self.assertEqual(wkt_w.write(Point(1.1, 1)), b"POINT (1.1 1)") + self.assertEqual( + wkt_w.write(Point(1.0 / 3, 1)), b"POINT (0.3333333333333333 1)" + ) wkt_w.trim = False self.assertFalse(wkt_w.trim) - self.assertEqual(wkt_w.write(Point(1, 1)), b'POINT (1.0000000000000000 1.0000000000000000)') + self.assertEqual( + wkt_w.write(Point(1, 1)), b"POINT (1.0000000000000000 1.0000000000000000)" + ) def test_wkt_writer_precision(self): wkt_w = WKTWriter() self.assertIsNone(wkt_w.precision) - self.assertEqual(wkt_w.write(Point(1. / 3, 2. / 3)), b'POINT (0.3333333333333333 0.6666666666666666)') + self.assertEqual( + wkt_w.write(Point(1.0 / 3, 2.0 / 3)), + b"POINT (0.3333333333333333 0.6666666666666666)", + ) wkt_w.precision = 1 self.assertEqual(wkt_w.precision, 1) - self.assertEqual(wkt_w.write(Point(1. / 3, 2. / 3)), b'POINT (0.3 0.7)') + self.assertEqual(wkt_w.write(Point(1.0 / 3, 2.0 / 3)), b"POINT (0.3 0.7)") wkt_w.precision = 0 self.assertEqual(wkt_w.precision, 0) - self.assertEqual(wkt_w.write(Point(1. / 3, 2. / 3)), b'POINT (0 1)') + self.assertEqual(wkt_w.write(Point(1.0 / 3, 2.0 / 3)), b"POINT (0 1)") wkt_w.precision = None self.assertIsNone(wkt_w.precision) - self.assertEqual(wkt_w.write(Point(1. / 3, 2. / 3)), b'POINT (0.3333333333333333 0.6666666666666666)') + self.assertEqual( + wkt_w.write(Point(1.0 / 3, 2.0 / 3)), + b"POINT (0.3333333333333333 0.6666666666666666)", + ) - with self.assertRaisesMessage(AttributeError, 'WKT output rounding precision must be '): - wkt_w.precision = 'potato' + with self.assertRaisesMessage( + AttributeError, "WKT output rounding precision must be " + ): + wkt_w.precision = "potato" def test_empty_point_wkb(self): p = Point(srid=4326) wkb_w = WKBWriter() wkb_w.srid = False - with self.assertRaisesMessage(ValueError, 'Empty point is not representable in WKB.'): + with self.assertRaisesMessage( + ValueError, "Empty point is not representable in WKB." + ): wkb_w.write(p) - with self.assertRaisesMessage(ValueError, 'Empty point is not representable in WKB.'): + with self.assertRaisesMessage( + ValueError, "Empty point is not representable in WKB." + ): wkb_w.write_hex(p) wkb_w.srid = True - for byteorder, hex in enumerate([ - b'0020000001000010E67FF80000000000007FF8000000000000', - b'0101000020E6100000000000000000F87F000000000000F87F', - ]): + for byteorder, hex in enumerate( + [ + b"0020000001000010E67FF80000000000007FF8000000000000", + b"0101000020E6100000000000000000F87F000000000000F87F", + ] + ): wkb_w.byteorder = byteorder self.assertEqual(wkb_w.write_hex(p), hex) self.assertEqual(GEOSGeometry(wkb_w.write_hex(p)), p) @@ -181,14 +210,18 @@ class GEOSIOTest(SimpleTestCase): p_no_srid = Polygon() wkb_w = WKBWriter() wkb_w.srid = True - for byteorder, hexes in enumerate([ - (b'000000000300000000', b'0020000003000010E600000000'), - (b'010300000000000000', b'0103000020E610000000000000'), - ]): + for byteorder, hexes in enumerate( + [ + (b"000000000300000000", b"0020000003000010E600000000"), + (b"010300000000000000", b"0103000020E610000000000000"), + ] + ): wkb_w.byteorder = byteorder for srid, hex in enumerate(hexes): wkb_w.srid = srid self.assertEqual(wkb_w.write_hex(p), hex) - self.assertEqual(GEOSGeometry(wkb_w.write_hex(p)), p if srid else p_no_srid) + self.assertEqual( + GEOSGeometry(wkb_w.write_hex(p)), p if srid else p_no_srid + ) self.assertEqual(wkb_w.write(p), memoryview(binascii.a2b_hex(hex))) self.assertEqual(GEOSGeometry(wkb_w.write(p)), p if srid else p_no_srid) diff --git a/tests/gis_tests/geos_tests/test_mutable_list.py b/tests/gis_tests/geos_tests/test_mutable_list.py index bb085b2fb2..a092f603cb 100644 --- a/tests/gis_tests/geos_tests/test_mutable_list.py +++ b/tests/gis_tests/geos_tests/test_mutable_list.py @@ -29,7 +29,7 @@ class UserListA(ListMixin): # this would work: # self._list = self._mytype(items) # but then we wouldn't be testing length parameter - itemList = ['x'] * length + itemList = ["x"] * length for i, v in enumerate(items): itemList[i] = v @@ -59,6 +59,7 @@ class ListMixinTest(unittest.TestCase): Tests base class ListMixin by comparing a list clone which is a ListMixin subclass with a real Python list. """ + limit = 3 listType = UserListA @@ -75,57 +76,61 @@ class ListMixinTest(unittest.TestCase): return [*range(-1 - self.limit, 0), *range(1, 1 + self.limit)] def test01_getslice(self): - 'Slice retrieval' + "Slice retrieval" pl, ul = self.lists_of_len() for i in self.limits_plus(1): - self.assertEqual(pl[i:], ul[i:], 'slice [%d:]' % (i)) - self.assertEqual(pl[:i], ul[:i], 'slice [:%d]' % (i)) + self.assertEqual(pl[i:], ul[i:], "slice [%d:]" % (i)) + self.assertEqual(pl[:i], ul[:i], "slice [:%d]" % (i)) for j in self.limits_plus(1): - self.assertEqual(pl[i:j], ul[i:j], 'slice [%d:%d]' % (i, j)) + self.assertEqual(pl[i:j], ul[i:j], "slice [%d:%d]" % (i, j)) for k in self.step_range(): - self.assertEqual(pl[i:j:k], ul[i:j:k], 'slice [%d:%d:%d]' % (i, j, k)) + self.assertEqual( + pl[i:j:k], ul[i:j:k], "slice [%d:%d:%d]" % (i, j, k) + ) for k in self.step_range(): - self.assertEqual(pl[i::k], ul[i::k], 'slice [%d::%d]' % (i, k)) - self.assertEqual(pl[:i:k], ul[:i:k], 'slice [:%d:%d]' % (i, k)) + self.assertEqual(pl[i::k], ul[i::k], "slice [%d::%d]" % (i, k)) + self.assertEqual(pl[:i:k], ul[:i:k], "slice [:%d:%d]" % (i, k)) for k in self.step_range(): - self.assertEqual(pl[::k], ul[::k], 'slice [::%d]' % (k)) + self.assertEqual(pl[::k], ul[::k], "slice [::%d]" % (k)) def test02_setslice(self): - 'Slice assignment' + "Slice assignment" + def setfcn(x, i, j, k, L): x[i:j:k] = range(L) + pl, ul = self.lists_of_len() for slen in range(self.limit + 1): ssl = nextRange(slen) ul[:] = ssl pl[:] = ssl - self.assertEqual(pl, ul[:], 'set slice [:]') + self.assertEqual(pl, ul[:], "set slice [:]") for i in self.limits_plus(1): ssl = nextRange(slen) ul[i:] = ssl pl[i:] = ssl - self.assertEqual(pl, ul[:], 'set slice [%d:]' % (i)) + self.assertEqual(pl, ul[:], "set slice [%d:]" % (i)) ssl = nextRange(slen) ul[:i] = ssl pl[:i] = ssl - self.assertEqual(pl, ul[:], 'set slice [:%d]' % (i)) + self.assertEqual(pl, ul[:], "set slice [:%d]" % (i)) for j in self.limits_plus(1): ssl = nextRange(slen) ul[i:j] = ssl pl[i:j] = ssl - self.assertEqual(pl, ul[:], 'set slice [%d:%d]' % (i, j)) + self.assertEqual(pl, ul[:], "set slice [%d:%d]" % (i, j)) for k in self.step_range(): ssl = nextRange(len(ul[i:j:k])) ul[i:j:k] = ssl pl[i:j:k] = ssl - self.assertEqual(pl, ul[:], 'set slice [%d:%d:%d]' % (i, j, k)) + self.assertEqual(pl, ul[:], "set slice [%d:%d:%d]" % (i, j, k)) sliceLen = len(ul[i:j:k]) with self.assertRaises(ValueError): @@ -138,83 +143,86 @@ class ListMixinTest(unittest.TestCase): ssl = nextRange(len(ul[i::k])) ul[i::k] = ssl pl[i::k] = ssl - self.assertEqual(pl, ul[:], 'set slice [%d::%d]' % (i, k)) + self.assertEqual(pl, ul[:], "set slice [%d::%d]" % (i, k)) ssl = nextRange(len(ul[:i:k])) ul[:i:k] = ssl pl[:i:k] = ssl - self.assertEqual(pl, ul[:], 'set slice [:%d:%d]' % (i, k)) + self.assertEqual(pl, ul[:], "set slice [:%d:%d]" % (i, k)) for k in self.step_range(): ssl = nextRange(len(ul[::k])) ul[::k] = ssl pl[::k] = ssl - self.assertEqual(pl, ul[:], 'set slice [::%d]' % (k)) + self.assertEqual(pl, ul[:], "set slice [::%d]" % (k)) def test03_delslice(self): - 'Delete slice' + "Delete slice" for Len in range(self.limit): pl, ul = self.lists_of_len(Len) del pl[:] del ul[:] - self.assertEqual(pl[:], ul[:], 'del slice [:]') + self.assertEqual(pl[:], ul[:], "del slice [:]") for i in range(-Len - 1, Len + 1): pl, ul = self.lists_of_len(Len) del pl[i:] del ul[i:] - self.assertEqual(pl[:], ul[:], 'del slice [%d:]' % (i)) + self.assertEqual(pl[:], ul[:], "del slice [%d:]" % (i)) pl, ul = self.lists_of_len(Len) del pl[:i] del ul[:i] - self.assertEqual(pl[:], ul[:], 'del slice [:%d]' % (i)) + self.assertEqual(pl[:], ul[:], "del slice [:%d]" % (i)) for j in range(-Len - 1, Len + 1): pl, ul = self.lists_of_len(Len) del pl[i:j] del ul[i:j] - self.assertEqual(pl[:], ul[:], 'del slice [%d:%d]' % (i, j)) + self.assertEqual(pl[:], ul[:], "del slice [%d:%d]" % (i, j)) for k in [*range(-Len - 1, 0), *range(1, Len)]: pl, ul = self.lists_of_len(Len) del pl[i:j:k] del ul[i:j:k] - self.assertEqual(pl[:], ul[:], 'del slice [%d:%d:%d]' % (i, j, k)) + self.assertEqual( + pl[:], ul[:], "del slice [%d:%d:%d]" % (i, j, k) + ) for k in [*range(-Len - 1, 0), *range(1, Len)]: pl, ul = self.lists_of_len(Len) del pl[:i:k] del ul[:i:k] - self.assertEqual(pl[:], ul[:], 'del slice [:%d:%d]' % (i, k)) + self.assertEqual(pl[:], ul[:], "del slice [:%d:%d]" % (i, k)) pl, ul = self.lists_of_len(Len) del pl[i::k] del ul[i::k] - self.assertEqual(pl[:], ul[:], 'del slice [%d::%d]' % (i, k)) + self.assertEqual(pl[:], ul[:], "del slice [%d::%d]" % (i, k)) for k in [*range(-Len - 1, 0), *range(1, Len)]: pl, ul = self.lists_of_len(Len) del pl[::k] del ul[::k] - self.assertEqual(pl[:], ul[:], 'del slice [::%d]' % (k)) + self.assertEqual(pl[:], ul[:], "del slice [::%d]" % (k)) def test04_get_set_del_single(self): - 'Get/set/delete single item' + "Get/set/delete single item" pl, ul = self.lists_of_len() for i in self.limits_plus(0): - self.assertEqual(pl[i], ul[i], 'get single item [%d]' % i) + self.assertEqual(pl[i], ul[i], "get single item [%d]" % i) for i in self.limits_plus(0): pl, ul = self.lists_of_len() pl[i] = 100 ul[i] = 100 - self.assertEqual(pl[:], ul[:], 'set single item [%d]' % i) + self.assertEqual(pl[:], ul[:], "set single item [%d]" % i) for i in self.limits_plus(0): pl, ul = self.lists_of_len() del pl[i] del ul[i] - self.assertEqual(pl[:], ul[:], 'del single item [%d]' % i) + self.assertEqual(pl[:], ul[:], "del single item [%d]" % i) def test05_out_of_range_exceptions(self): - 'Out of range exceptions' + "Out of range exceptions" + def setfcn(x, i): x[i] = 20 @@ -223,6 +231,7 @@ class ListMixinTest(unittest.TestCase): def delfcn(x, i): del x[i] + pl, ul = self.lists_of_len() for i in (-1 - self.limit, self.limit): with self.assertRaises(IndexError): # 'set index %d' % i) @@ -233,39 +242,40 @@ class ListMixinTest(unittest.TestCase): delfcn(ul, i) def test06_list_methods(self): - 'List methods' + "List methods" pl, ul = self.lists_of_len() pl.append(40) ul.append(40) - self.assertEqual(pl[:], ul[:], 'append') + self.assertEqual(pl[:], ul[:], "append") pl.extend(range(50, 55)) ul.extend(range(50, 55)) - self.assertEqual(pl[:], ul[:], 'extend') + self.assertEqual(pl[:], ul[:], "extend") pl.reverse() ul.reverse() - self.assertEqual(pl[:], ul[:], 'reverse') + self.assertEqual(pl[:], ul[:], "reverse") for i in self.limits_plus(1): pl, ul = self.lists_of_len() pl.insert(i, 50) ul.insert(i, 50) - self.assertEqual(pl[:], ul[:], 'insert at %d' % i) + self.assertEqual(pl[:], ul[:], "insert at %d" % i) for i in self.limits_plus(0): pl, ul = self.lists_of_len() - self.assertEqual(pl.pop(i), ul.pop(i), 'popped value at %d' % i) - self.assertEqual(pl[:], ul[:], 'after pop at %d' % i) + self.assertEqual(pl.pop(i), ul.pop(i), "popped value at %d" % i) + self.assertEqual(pl[:], ul[:], "after pop at %d" % i) pl, ul = self.lists_of_len() - self.assertEqual(pl.pop(), ul.pop(i), 'popped value') - self.assertEqual(pl[:], ul[:], 'after pop') + self.assertEqual(pl.pop(), ul.pop(i), "popped value") + self.assertEqual(pl[:], ul[:], "after pop") pl, ul = self.lists_of_len() def popfcn(x, i): x.pop(i) + with self.assertRaises(IndexError): popfcn(ul, self.limit) with self.assertRaises(IndexError): @@ -273,29 +283,30 @@ class ListMixinTest(unittest.TestCase): pl, ul = self.lists_of_len() for val in range(self.limit): - self.assertEqual(pl.index(val), ul.index(val), 'index of %d' % val) + self.assertEqual(pl.index(val), ul.index(val), "index of %d" % val) for val in self.limits_plus(2): - self.assertEqual(pl.count(val), ul.count(val), 'count %d' % val) + self.assertEqual(pl.count(val), ul.count(val), "count %d" % val) for val in range(self.limit): pl, ul = self.lists_of_len() pl.remove(val) ul.remove(val) - self.assertEqual(pl[:], ul[:], 'after remove val %d' % val) + self.assertEqual(pl[:], ul[:], "after remove val %d" % val) def indexfcn(x, v): return x.index(v) def removefcn(x, v): return x.remove(v) + with self.assertRaises(ValueError): indexfcn(ul, 40) with self.assertRaises(ValueError): removefcn(ul, 40) def test07_allowed_types(self): - 'Type-restricted list' + "Type-restricted list" pl, ul = self.lists_of_len() ul._allowed = int ul[1] = 50 @@ -303,13 +314,14 @@ class ListMixinTest(unittest.TestCase): def setfcn(x, i, v): x[i] = v + with self.assertRaises(TypeError): - setfcn(ul, 2, 'hello') + setfcn(ul, 2, "hello") with self.assertRaises(TypeError): - setfcn(ul, slice(0, 3, 2), ('hello', 'goodbye')) + setfcn(ul, slice(0, 3, 2), ("hello", "goodbye")) def test08_min_length(self): - 'Length limits' + "Length limits" pl, ul = self.lists_of_len(5) ul._minlength = 3 @@ -318,12 +330,13 @@ class ListMixinTest(unittest.TestCase): def setfcn(x, i): x[:i] = [] + for i in range(len(ul) - ul._minlength + 1, len(ul)): with self.assertRaises(ValueError): delfcn(ul, i) with self.assertRaises(ValueError): setfcn(ul, i) - del ul[:len(ul) - ul._minlength] + del ul[: len(ul) - ul._minlength] ul._maxlength = 4 for i in range(0, ul._maxlength - len(ul)): @@ -332,99 +345,102 @@ class ListMixinTest(unittest.TestCase): ul.append(10) def test09_iterable_check(self): - 'Error on assigning non-iterable to slice' + "Error on assigning non-iterable to slice" pl, ul = self.lists_of_len(self.limit + 1) def setfcn(x, i, v): x[i] = v + with self.assertRaises(TypeError): setfcn(ul, slice(0, 3, 2), 2) def test10_checkindex(self): - 'Index check' + "Index check" pl, ul = self.lists_of_len() for i in self.limits_plus(0): if i < 0: - self.assertEqual(ul._checkindex(i), i + self.limit, '_checkindex(neg index)') + self.assertEqual( + ul._checkindex(i), i + self.limit, "_checkindex(neg index)" + ) else: - self.assertEqual(ul._checkindex(i), i, '_checkindex(pos index)') + self.assertEqual(ul._checkindex(i), i, "_checkindex(pos index)") for i in (-self.limit - 1, self.limit): with self.assertRaises(IndexError): ul._checkindex(i) def test_11_sorting(self): - 'Sorting' + "Sorting" pl, ul = self.lists_of_len() pl.insert(0, pl.pop()) ul.insert(0, ul.pop()) pl.sort() ul.sort() - self.assertEqual(pl[:], ul[:], 'sort') + self.assertEqual(pl[:], ul[:], "sort") mid = pl[len(pl) // 2] pl.sort(key=lambda x: (mid - x) ** 2) ul.sort(key=lambda x: (mid - x) ** 2) - self.assertEqual(pl[:], ul[:], 'sort w/ key') + self.assertEqual(pl[:], ul[:], "sort w/ key") pl.insert(0, pl.pop()) ul.insert(0, ul.pop()) pl.sort(reverse=True) ul.sort(reverse=True) - self.assertEqual(pl[:], ul[:], 'sort w/ reverse') + self.assertEqual(pl[:], ul[:], "sort w/ reverse") mid = pl[len(pl) // 2] pl.sort(key=lambda x: (mid - x) ** 2) ul.sort(key=lambda x: (mid - x) ** 2) - self.assertEqual(pl[:], ul[:], 'sort w/ key') + self.assertEqual(pl[:], ul[:], "sort w/ key") def test_12_arithmetic(self): - 'Arithmetic' + "Arithmetic" pl, ul = self.lists_of_len() al = list(range(10, 14)) - self.assertEqual(list(pl + al), list(ul + al), 'add') - self.assertEqual(type(ul), type(ul + al), 'type of add result') - self.assertEqual(list(al + pl), list(al + ul), 'radd') - self.assertEqual(type(al), type(al + ul), 'type of radd result') + self.assertEqual(list(pl + al), list(ul + al), "add") + self.assertEqual(type(ul), type(ul + al), "type of add result") + self.assertEqual(list(al + pl), list(al + ul), "radd") + self.assertEqual(type(al), type(al + ul), "type of radd result") objid = id(ul) pl += al ul += al - self.assertEqual(pl[:], ul[:], 'in-place add') - self.assertEqual(objid, id(ul), 'in-place add id') + self.assertEqual(pl[:], ul[:], "in-place add") + self.assertEqual(objid, id(ul), "in-place add id") for n in (-1, 0, 1, 3): pl, ul = self.lists_of_len() - self.assertEqual(list(pl * n), list(ul * n), 'mul by %d' % n) - self.assertEqual(type(ul), type(ul * n), 'type of mul by %d result' % n) - self.assertEqual(list(n * pl), list(n * ul), 'rmul by %d' % n) - self.assertEqual(type(ul), type(n * ul), 'type of rmul by %d result' % n) + self.assertEqual(list(pl * n), list(ul * n), "mul by %d" % n) + self.assertEqual(type(ul), type(ul * n), "type of mul by %d result" % n) + self.assertEqual(list(n * pl), list(n * ul), "rmul by %d" % n) + self.assertEqual(type(ul), type(n * ul), "type of rmul by %d result" % n) objid = id(ul) pl *= n ul *= n - self.assertEqual(pl[:], ul[:], 'in-place mul by %d' % n) - self.assertEqual(objid, id(ul), 'in-place mul by %d id' % n) + self.assertEqual(pl[:], ul[:], "in-place mul by %d" % n) + self.assertEqual(objid, id(ul), "in-place mul by %d id" % n) pl, ul = self.lists_of_len() - self.assertEqual(pl, ul, 'cmp for equal') - self.assertNotEqual(ul, pl + [2], 'cmp for not equal') - self.assertGreaterEqual(pl, ul, 'cmp for gte self') - self.assertLessEqual(pl, ul, 'cmp for lte self') - self.assertGreaterEqual(ul, pl, 'cmp for self gte') - self.assertLessEqual(ul, pl, 'cmp for self lte') - - self.assertGreater(pl + [5], ul, 'cmp') - self.assertGreaterEqual(pl + [5], ul, 'cmp') - self.assertLess(pl, ul + [2], 'cmp') - self.assertLessEqual(pl, ul + [2], 'cmp') - self.assertGreater(ul + [5], pl, 'cmp') - self.assertGreaterEqual(ul + [5], pl, 'cmp') - self.assertLess(ul, pl + [2], 'cmp') - self.assertLessEqual(ul, pl + [2], 'cmp') + self.assertEqual(pl, ul, "cmp for equal") + self.assertNotEqual(ul, pl + [2], "cmp for not equal") + self.assertGreaterEqual(pl, ul, "cmp for gte self") + self.assertLessEqual(pl, ul, "cmp for lte self") + self.assertGreaterEqual(ul, pl, "cmp for self gte") + self.assertLessEqual(ul, pl, "cmp for self lte") + + self.assertGreater(pl + [5], ul, "cmp") + self.assertGreaterEqual(pl + [5], ul, "cmp") + self.assertLess(pl, ul + [2], "cmp") + self.assertLessEqual(pl, ul + [2], "cmp") + self.assertGreater(ul + [5], pl, "cmp") + self.assertGreaterEqual(ul + [5], pl, "cmp") + self.assertLess(ul, pl + [2], "cmp") + self.assertLessEqual(ul, pl + [2], "cmp") pl[1] = 20 - self.assertGreater(pl, ul, 'cmp for gt self') - self.assertLess(ul, pl, 'cmp for self lt') + self.assertGreater(pl, ul, "cmp for gt self") + self.assertLess(ul, pl, "cmp for self lt") pl[1] = -20 - self.assertLess(pl, ul, 'cmp for lt self') - self.assertGreater(ul, pl, 'cmp for gt self') + self.assertLess(pl, ul, "cmp for lt self") + self.assertGreater(ul, pl, "cmp for gt self") class ListMixinTestSingle(ListMixinTest): diff --git a/tests/gis_tests/gis_migrations/migrations/0001_setup_extensions.py b/tests/gis_tests/gis_migrations/migrations/0001_setup_extensions.py index 35451b3a1e..227d6b668d 100644 --- a/tests/gis_tests/gis_migrations/migrations/0001_setup_extensions.py +++ b/tests/gis_tests/gis_migrations/migrations/0001_setup_extensions.py @@ -9,10 +9,12 @@ if connection.features.supports_raster: # PostGIS 3+ requires postgis_raster extension. if pg_version[1:] >= (3,): operations = [ - CreateExtension('postgis_raster'), + CreateExtension("postgis_raster"), ] else: operations = [] + else: + class Migration(migrations.Migration): operations = [] diff --git a/tests/gis_tests/gis_migrations/migrations/0002_create_models.py b/tests/gis_tests/gis_migrations/migrations/0002_create_models.py index ee1191d11e..90bfa87389 100644 --- a/tests/gis_tests/gis_migrations/migrations/0002_create_models.py +++ b/tests/gis_tests/gis_migrations/migrations/0002_create_models.py @@ -3,63 +3,96 @@ from django.db import connection, migrations ops = [ migrations.CreateModel( - name='Neighborhood', + name="Neighborhood", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=100, unique=True)), - ('geom', models.MultiPolygonField(srid=4326)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=100, unique=True)), + ("geom", models.MultiPolygonField(srid=4326)), ], - options={ - }, + options={}, bases=(models.Model,), ), migrations.CreateModel( - name='Household', + name="Household", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('neighborhood', models.ForeignKey( - 'gis_migrations.Neighborhood', - models.SET_NULL, - to_field='id', - null=True, - )), - ('address', models.CharField(max_length=100)), - ('zip_code', models.IntegerField(null=True, blank=True)), - ('geom', models.PointField(srid=4326, geography=True)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "neighborhood", + models.ForeignKey( + "gis_migrations.Neighborhood", + models.SET_NULL, + to_field="id", + null=True, + ), + ), + ("address", models.CharField(max_length=100)), + ("zip_code", models.IntegerField(null=True, blank=True)), + ("geom", models.PointField(srid=4326, geography=True)), ], - options={ - }, + options={}, bases=(models.Model,), ), migrations.CreateModel( - name='Family', + name="Family", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=100, unique=True)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=100, unique=True)), ], - options={ - }, + options={}, bases=(models.Model,), ), migrations.AddField( - model_name='household', - name='family', - field=models.ForeignKey('gis_migrations.Family', models.SET_NULL, blank=True, null=True), + model_name="household", + name="family", + field=models.ForeignKey( + "gis_migrations.Family", models.SET_NULL, blank=True, null=True + ), preserve_default=True, - ) + ), ] if connection.features.supports_raster: ops += [ migrations.CreateModel( - name='Heatmap', + name="Heatmap", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=100, unique=True)), - ('rast', models.fields.RasterField(srid=4326)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=100, unique=True)), + ("rast", models.fields.RasterField(srid=4326)), ], - options={ - }, + options={}, bases=(models.Model,), ), ] @@ -69,7 +102,8 @@ class Migration(migrations.Migration): """ Used for gis-specific migration tests. """ + dependencies = [ - ('gis_migrations', '0001_setup_extensions'), + ("gis_migrations", "0001_setup_extensions"), ] operations = ops diff --git a/tests/gis_tests/gis_migrations/test_commands.py b/tests/gis_tests/gis_migrations/test_commands.py index 31572b1e00..318cd12278 100644 --- a/tests/gis_tests/gis_migrations/test_commands.py +++ b/tests/gis_tests/gis_migrations/test_commands.py @@ -7,6 +7,7 @@ class MigrateTests(TransactionTestCase): """ Tests running the migrate command in Geodjango. """ + available_apps = ["gis_tests.gis_migrations"] def get_table_description(self, table): @@ -52,7 +53,10 @@ class MigrateTests(TransactionTestCase): pass else: qs = GeoColumn.objects.filter( - **{'%s__in' % GeoColumn.table_name_col(): ["gis_neighborhood", "gis_household"]} + **{ + "%s__in" + % GeoColumn.table_name_col(): ["gis_neighborhood", "gis_household"] + } ) self.assertEqual(qs.count(), 0) # Revert the "unmigration" diff --git a/tests/gis_tests/gis_migrations/test_operations.py b/tests/gis_tests/gis_migrations/test_operations.py index b7823dd983..d2ad67945b 100644 --- a/tests/gis_tests/gis_migrations/test_operations.py +++ b/tests/gis_tests/gis_migrations/test_operations.py @@ -6,9 +6,7 @@ from django.core.exceptions import ImproperlyConfigured from django.db import connection, migrations, models from django.db.migrations.migration import Migration from django.db.migrations.state import ProjectState -from django.test import ( - TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature, -) +from django.test import TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature try: GeometryColumns = connection.ops.geometry_columns() @@ -18,25 +16,29 @@ except NotImplementedError: class OperationTestCase(TransactionTestCase): - available_apps = ['gis_tests.gis_migrations'] - get_opclass_query = ''' + available_apps = ["gis_tests.gis_migrations"] + get_opclass_query = """ SELECT opcname, c.relname FROM pg_opclass AS oc JOIN pg_index as i on oc.oid = ANY(i.indclass) JOIN pg_class as c on c.oid = i.indexrelid WHERE c.relname = %s - ''' + """ def tearDown(self): # Delete table after testing - if hasattr(self, 'current_state'): - self.apply_operations('gis', self.current_state, [migrations.DeleteModel('Neighborhood')]) + if hasattr(self, "current_state"): + self.apply_operations( + "gis", self.current_state, [migrations.DeleteModel("Neighborhood")] + ) super().tearDown() @property def has_spatial_indexes(self): if connection.ops.mysql: with connection.cursor() as cursor: - return connection.introspection.supports_spatial_index(cursor, 'gis_neighborhood') + return connection.introspection.supports_spatial_index( + cursor, "gis_neighborhood" + ) return True def get_table_description(self, table): @@ -50,57 +52,67 @@ class OperationTestCase(TransactionTestCase): self.assertNotIn(column, [c.name for c in self.get_table_description(table)]) def apply_operations(self, app_label, project_state, operations): - migration = Migration('name', app_label) + migration = Migration("name", app_label) migration.operations = operations with connection.schema_editor() as editor: return migration.apply(project_state, editor) def set_up_test_model(self, force_raster_creation=False): test_fields = [ - ('id', models.AutoField(primary_key=True)), - ('name', models.CharField(max_length=100, unique=True)), - ('geom', fields.MultiPolygonField(srid=4326)) + ("id", models.AutoField(primary_key=True)), + ("name", models.CharField(max_length=100, unique=True)), + ("geom", fields.MultiPolygonField(srid=4326)), ] if connection.features.supports_raster or force_raster_creation: - test_fields += [('rast', fields.RasterField(srid=4326, null=True))] - operations = [migrations.CreateModel('Neighborhood', test_fields)] - self.current_state = self.apply_operations('gis', ProjectState(), operations) + test_fields += [("rast", fields.RasterField(srid=4326, null=True))] + operations = [migrations.CreateModel("Neighborhood", test_fields)] + self.current_state = self.apply_operations("gis", ProjectState(), operations) def assertGeometryColumnsCount(self, expected_count): self.assertEqual( - GeometryColumns.objects.filter(**{ - '%s__iexact' % GeometryColumns.table_name_col(): 'gis_neighborhood', - }).count(), - expected_count + GeometryColumns.objects.filter( + **{ + "%s__iexact" % GeometryColumns.table_name_col(): "gis_neighborhood", + } + ).count(), + expected_count, ) def assertSpatialIndexExists(self, table, column, raster=False): with connection.cursor() as cursor: constraints = connection.introspection.get_constraints(cursor, table) if raster: - self.assertTrue(any( - 'st_convexhull(%s)' % column in c['definition'] - for c in constraints.values() - if c['definition'] is not None - )) + self.assertTrue( + any( + "st_convexhull(%s)" % column in c["definition"] + for c in constraints.values() + if c["definition"] is not None + ) + ) else: - self.assertIn([column], [c['columns'] for c in constraints.values()]) - - def alter_gis_model(self, migration_class, model_name, field_name, - blank=False, field_class=None, field_class_kwargs=None): + self.assertIn([column], [c["columns"] for c in constraints.values()]) + + def alter_gis_model( + self, + migration_class, + model_name, + field_name, + blank=False, + field_class=None, + field_class_kwargs=None, + ): args = [model_name, field_name] if field_class: - field_class_kwargs = field_class_kwargs or {'srid': 4326, 'blank': blank} + field_class_kwargs = field_class_kwargs or {"srid": 4326, "blank": blank} args.append(field_class(**field_class_kwargs)) operation = migration_class(*args) old_state = self.current_state.clone() - operation.state_forwards('gis', self.current_state) + operation.state_forwards("gis", self.current_state) with connection.schema_editor() as editor: - operation.database_forwards('gis', editor, old_state, self.current_state) + operation.database_forwards("gis", editor, old_state, self.current_state) class OperationTests(OperationTestCase): - def setUp(self): super().setUp() self.set_up_test_model() @@ -109,8 +121,10 @@ class OperationTests(OperationTestCase): """ Test the AddField operation with a geometry-enabled column. """ - self.alter_gis_model(migrations.AddField, 'Neighborhood', 'path', False, fields.LineStringField) - self.assertColumnExists('gis_neighborhood', 'path') + self.alter_gis_model( + migrations.AddField, "Neighborhood", "path", False, fields.LineStringField + ) + self.assertColumnExists("gis_neighborhood", "path") # Test GeometryColumns when available if HAS_GEOMETRY_COLUMNS: @@ -118,33 +132,37 @@ class OperationTests(OperationTestCase): # Test spatial indices when available if self.has_spatial_indexes: - self.assertSpatialIndexExists('gis_neighborhood', 'path') + self.assertSpatialIndexExists("gis_neighborhood", "path") @skipUnless(HAS_GEOMETRY_COLUMNS, "Backend doesn't support GeometryColumns.") def test_geom_col_name(self): self.assertEqual( GeometryColumns.geom_col_name(), - 'column_name' if connection.ops.oracle else 'f_geometry_column', + "column_name" if connection.ops.oracle else "f_geometry_column", ) - @skipUnlessDBFeature('supports_raster') + @skipUnlessDBFeature("supports_raster") def test_add_raster_field(self): """ Test the AddField operation with a raster-enabled column. """ - self.alter_gis_model(migrations.AddField, 'Neighborhood', 'heatmap', False, fields.RasterField) - self.assertColumnExists('gis_neighborhood', 'heatmap') + self.alter_gis_model( + migrations.AddField, "Neighborhood", "heatmap", False, fields.RasterField + ) + self.assertColumnExists("gis_neighborhood", "heatmap") # Test spatial indices when available if self.has_spatial_indexes: - self.assertSpatialIndexExists('gis_neighborhood', 'heatmap', raster=True) + self.assertSpatialIndexExists("gis_neighborhood", "heatmap", raster=True) def test_add_blank_geom_field(self): """ Should be able to add a GeometryField with blank=True. """ - self.alter_gis_model(migrations.AddField, 'Neighborhood', 'path', True, fields.LineStringField) - self.assertColumnExists('gis_neighborhood', 'path') + self.alter_gis_model( + migrations.AddField, "Neighborhood", "path", True, fields.LineStringField + ) + self.assertColumnExists("gis_neighborhood", "path") # Test GeometryColumns when available if HAS_GEOMETRY_COLUMNS: @@ -152,96 +170,108 @@ class OperationTests(OperationTestCase): # Test spatial indices when available if self.has_spatial_indexes: - self.assertSpatialIndexExists('gis_neighborhood', 'path') + self.assertSpatialIndexExists("gis_neighborhood", "path") - @skipUnlessDBFeature('supports_raster') + @skipUnlessDBFeature("supports_raster") def test_add_blank_raster_field(self): """ Should be able to add a RasterField with blank=True. """ - self.alter_gis_model(migrations.AddField, 'Neighborhood', 'heatmap', True, fields.RasterField) - self.assertColumnExists('gis_neighborhood', 'heatmap') + self.alter_gis_model( + migrations.AddField, "Neighborhood", "heatmap", True, fields.RasterField + ) + self.assertColumnExists("gis_neighborhood", "heatmap") # Test spatial indices when available if self.has_spatial_indexes: - self.assertSpatialIndexExists('gis_neighborhood', 'heatmap', raster=True) + self.assertSpatialIndexExists("gis_neighborhood", "heatmap", raster=True) def test_remove_geom_field(self): """ Test the RemoveField operation with a geometry-enabled column. """ - self.alter_gis_model(migrations.RemoveField, 'Neighborhood', 'geom') - self.assertColumnNotExists('gis_neighborhood', 'geom') + self.alter_gis_model(migrations.RemoveField, "Neighborhood", "geom") + self.assertColumnNotExists("gis_neighborhood", "geom") # Test GeometryColumns when available if HAS_GEOMETRY_COLUMNS: self.assertGeometryColumnsCount(0) - @skipUnlessDBFeature('supports_raster') + @skipUnlessDBFeature("supports_raster") def test_remove_raster_field(self): """ Test the RemoveField operation with a raster-enabled column. """ - self.alter_gis_model(migrations.RemoveField, 'Neighborhood', 'rast') - self.assertColumnNotExists('gis_neighborhood', 'rast') + self.alter_gis_model(migrations.RemoveField, "Neighborhood", "rast") + self.assertColumnNotExists("gis_neighborhood", "rast") def test_create_model_spatial_index(self): if not self.has_spatial_indexes: - self.skipTest('No support for Spatial indexes') + self.skipTest("No support for Spatial indexes") - self.assertSpatialIndexExists('gis_neighborhood', 'geom') + self.assertSpatialIndexExists("gis_neighborhood", "geom") if connection.features.supports_raster: - self.assertSpatialIndexExists('gis_neighborhood', 'rast', raster=True) + self.assertSpatialIndexExists("gis_neighborhood", "rast", raster=True) - @skipUnlessDBFeature('supports_3d_storage') + @skipUnlessDBFeature("supports_3d_storage") def test_add_3d_field_opclass(self): if not connection.ops.postgis: - self.skipTest('PostGIS-specific test.') + self.skipTest("PostGIS-specific test.") self.alter_gis_model( migrations.AddField, - 'Neighborhood', - 'point3d', + "Neighborhood", + "point3d", field_class=fields.PointField, - field_class_kwargs={'dim': 3}, + field_class_kwargs={"dim": 3}, ) - self.assertColumnExists('gis_neighborhood', 'point3d') - self.assertSpatialIndexExists('gis_neighborhood', 'point3d') + self.assertColumnExists("gis_neighborhood", "point3d") + self.assertSpatialIndexExists("gis_neighborhood", "point3d") with connection.cursor() as cursor: - index_name = 'gis_neighborhood_point3d_113bc868_id' + index_name = "gis_neighborhood_point3d_113bc868_id" cursor.execute(self.get_opclass_query, [index_name]) self.assertEqual( cursor.fetchall(), - [('gist_geometry_ops_nd', index_name)], + [("gist_geometry_ops_nd", index_name)], ) - @skipUnlessDBFeature('can_alter_geometry_field', 'supports_3d_storage') + @skipUnlessDBFeature("can_alter_geometry_field", "supports_3d_storage") def test_alter_geom_field_dim(self): - Neighborhood = self.current_state.apps.get_model('gis', 'Neighborhood') + Neighborhood = self.current_state.apps.get_model("gis", "Neighborhood") p1 = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0))) - Neighborhood.objects.create(name='TestDim', geom=MultiPolygon(p1, p1)) + Neighborhood.objects.create(name="TestDim", geom=MultiPolygon(p1, p1)) # Add 3rd dimension. self.alter_gis_model( - migrations.AlterField, 'Neighborhood', 'geom', False, - fields.MultiPolygonField, field_class_kwargs={'srid': 4326, 'dim': 3} + migrations.AlterField, + "Neighborhood", + "geom", + False, + fields.MultiPolygonField, + field_class_kwargs={"srid": 4326, "dim": 3}, ) self.assertTrue(Neighborhood.objects.first().geom.hasz) # Rewind to 2 dimensions. self.alter_gis_model( - migrations.AlterField, 'Neighborhood', 'geom', False, - fields.MultiPolygonField, field_class_kwargs={'srid': 4326, 'dim': 2} + migrations.AlterField, + "Neighborhood", + "geom", + False, + fields.MultiPolygonField, + field_class_kwargs={"srid": 4326, "dim": 2}, ) self.assertFalse(Neighborhood.objects.first().geom.hasz) - @skipUnlessDBFeature('supports_column_check_constraints', 'can_introspect_check_constraints') + @skipUnlessDBFeature( + "supports_column_check_constraints", "can_introspect_check_constraints" + ) def test_add_check_constraint(self): - Neighborhood = self.current_state.apps.get_model('gis', 'Neighborhood') + Neighborhood = self.current_state.apps.get_model("gis", "Neighborhood") poly = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0))) constraint = models.CheckConstraint( check=models.Q(geom=poly), - name='geom_within_constraint', + name="geom_within_constraint", ) Neighborhood._meta.constraints = [constraint] with connection.schema_editor() as editor: @@ -251,21 +281,24 @@ class OperationTests(OperationTestCase): cursor, Neighborhood._meta.db_table, ) - self.assertIn('geom_within_constraint', constraints) + self.assertIn("geom_within_constraint", constraints) -@skipIfDBFeature('supports_raster') +@skipIfDBFeature("supports_raster") class NoRasterSupportTests(OperationTestCase): def test_create_raster_model_on_db_without_raster_support(self): - msg = 'Raster fields require backends with raster support.' + msg = "Raster fields require backends with raster support." with self.assertRaisesMessage(ImproperlyConfigured, msg): self.set_up_test_model(force_raster_creation=True) def test_add_raster_field_on_db_without_raster_support(self): - msg = 'Raster fields require backends with raster support.' + msg = "Raster fields require backends with raster support." with self.assertRaisesMessage(ImproperlyConfigured, msg): self.set_up_test_model() self.alter_gis_model( - migrations.AddField, 'Neighborhood', 'heatmap', - False, fields.RasterField + migrations.AddField, + "Neighborhood", + "heatmap", + False, + fields.RasterField, ) diff --git a/tests/gis_tests/inspectapp/models.py b/tests/gis_tests/inspectapp/models.py index ca6566844e..da36e8c8b2 100644 --- a/tests/gis_tests/inspectapp/models.py +++ b/tests/gis_tests/inspectapp/models.py @@ -21,4 +21,4 @@ class Fields3D(models.Model): poly = models.PolygonField(dim=3) class Meta: - required_db_features = {'supports_3d_storage'} + required_db_features = {"supports_3d_storage"} diff --git a/tests/gis_tests/inspectapp/tests.py b/tests/gis_tests/inspectapp/tests.py index 35337897c0..952124db8c 100644 --- a/tests/gis_tests/inspectapp/tests.py +++ b/tests/gis_tests/inspectapp/tests.py @@ -20,94 +20,96 @@ class InspectDbTests(TestCase): """ out = StringIO() call_command( - 'inspectdb', - table_name_filter=lambda tn: tn == 'inspectapp_allogrfields', - stdout=out + "inspectdb", + table_name_filter=lambda tn: tn == "inspectapp_allogrfields", + stdout=out, ) output = out.getvalue() if connection.features.supports_geometry_field_introspection: - self.assertIn('geom = models.PolygonField()', output) - self.assertIn('point = models.PointField()', output) + self.assertIn("geom = models.PolygonField()", output) + self.assertIn("point = models.PointField()", output) else: - self.assertIn('geom = models.GeometryField(', output) - self.assertIn('point = models.GeometryField(', output) + self.assertIn("geom = models.GeometryField(", output) + self.assertIn("point = models.GeometryField(", output) @skipUnlessDBFeature("supports_3d_storage") def test_3d_columns(self): out = StringIO() call_command( - 'inspectdb', - table_name_filter=lambda tn: tn == 'inspectapp_fields3d', - stdout=out + "inspectdb", + table_name_filter=lambda tn: tn == "inspectapp_fields3d", + stdout=out, ) output = out.getvalue() if connection.features.supports_geometry_field_introspection: - self.assertIn('point = models.PointField(dim=3)', output) + self.assertIn("point = models.PointField(dim=3)", output) if connection.features.supports_geography: - self.assertIn('pointg = models.PointField(geography=True, dim=3)', output) + self.assertIn( + "pointg = models.PointField(geography=True, dim=3)", output + ) else: - self.assertIn('pointg = models.PointField(dim=3)', output) - self.assertIn('line = models.LineStringField(dim=3)', output) - self.assertIn('poly = models.PolygonField(dim=3)', output) + self.assertIn("pointg = models.PointField(dim=3)", output) + self.assertIn("line = models.LineStringField(dim=3)", output) + self.assertIn("poly = models.PolygonField(dim=3)", output) else: - self.assertIn('point = models.GeometryField(', output) - self.assertIn('pointg = models.GeometryField(', output) - self.assertIn('line = models.GeometryField(', output) - self.assertIn('poly = models.GeometryField(', output) + self.assertIn("point = models.GeometryField(", output) + self.assertIn("pointg = models.GeometryField(", output) + self.assertIn("line = models.GeometryField(", output) + self.assertIn("poly = models.GeometryField(", output) @modify_settings( - INSTALLED_APPS={'append': 'django.contrib.gis'}, + INSTALLED_APPS={"append": "django.contrib.gis"}, ) class OGRInspectTest(SimpleTestCase): maxDiff = 1024 def test_poly(self): - shp_file = os.path.join(TEST_DATA, 'test_poly', 'test_poly.shp') - model_def = ogrinspect(shp_file, 'MyModel') + shp_file = os.path.join(TEST_DATA, "test_poly", "test_poly.shp") + model_def = ogrinspect(shp_file, "MyModel") expected = [ - '# This is an auto-generated Django model module created by ogrinspect.', - 'from django.contrib.gis.db import models', - '', - '', - 'class MyModel(models.Model):', - ' float = models.FloatField()', - ' int = models.BigIntegerField()', - ' str = models.CharField(max_length=80)', - ' geom = models.PolygonField()', + "# This is an auto-generated Django model module created by ogrinspect.", + "from django.contrib.gis.db import models", + "", + "", + "class MyModel(models.Model):", + " float = models.FloatField()", + " int = models.BigIntegerField()", + " str = models.CharField(max_length=80)", + " geom = models.PolygonField()", ] - self.assertEqual(model_def, '\n'.join(expected)) + self.assertEqual(model_def, "\n".join(expected)) def test_poly_multi(self): - shp_file = os.path.join(TEST_DATA, 'test_poly', 'test_poly.shp') - model_def = ogrinspect(shp_file, 'MyModel', multi_geom=True) - self.assertIn('geom = models.MultiPolygonField()', model_def) + shp_file = os.path.join(TEST_DATA, "test_poly", "test_poly.shp") + model_def = ogrinspect(shp_file, "MyModel", multi_geom=True) + self.assertIn("geom = models.MultiPolygonField()", model_def) # Same test with a 25D-type geometry field - shp_file = os.path.join(TEST_DATA, 'gas_lines', 'gas_leitung.shp') - model_def = ogrinspect(shp_file, 'MyModel', multi_geom=True) - srid = '-1' if GDAL_VERSION < (2, 3) else '31253' - self.assertIn('geom = models.MultiLineStringField(srid=%s)' % srid, model_def) + shp_file = os.path.join(TEST_DATA, "gas_lines", "gas_leitung.shp") + model_def = ogrinspect(shp_file, "MyModel", multi_geom=True) + srid = "-1" if GDAL_VERSION < (2, 3) else "31253" + self.assertIn("geom = models.MultiLineStringField(srid=%s)" % srid, model_def) def test_date_field(self): - shp_file = os.path.join(TEST_DATA, 'cities', 'cities.shp') - model_def = ogrinspect(shp_file, 'City') + shp_file = os.path.join(TEST_DATA, "cities", "cities.shp") + model_def = ogrinspect(shp_file, "City") expected = [ - '# This is an auto-generated Django model module created by ogrinspect.', - 'from django.contrib.gis.db import models', - '', - '', - 'class City(models.Model):', - ' name = models.CharField(max_length=80)', - ' population = models.BigIntegerField()', - ' density = models.FloatField()', - ' created = models.DateField()', - ' geom = models.PointField()', + "# This is an auto-generated Django model module created by ogrinspect.", + "from django.contrib.gis.db import models", + "", + "", + "class City(models.Model):", + " name = models.CharField(max_length=80)", + " population = models.BigIntegerField()", + " density = models.FloatField()", + " created = models.DateField()", + " geom = models.PointField()", ] - self.assertEqual(model_def, '\n'.join(expected)) + self.assertEqual(model_def, "\n".join(expected)) def test_time_field(self): # Getting the database identifier used by OGR, if None returned @@ -119,48 +121,60 @@ class OGRInspectTest(SimpleTestCase): try: # Writing shapefiles via GDAL currently does not support writing OGRTime # fields, so we need to actually use a database - model_def = ogrinspect(ogr_db, 'Measurement', - layer_key=AllOGRFields._meta.db_table, - decimal=['f_decimal']) + model_def = ogrinspect( + ogr_db, + "Measurement", + layer_key=AllOGRFields._meta.db_table, + decimal=["f_decimal"], + ) except GDALException: self.skipTest("Unable to setup an OGR connection to your database") - self.assertTrue(model_def.startswith( - '# This is an auto-generated Django model module created by ogrinspect.\n' - 'from django.contrib.gis.db import models\n' - '\n' - '\n' - 'class Measurement(models.Model):\n' - )) + self.assertTrue( + model_def.startswith( + "# This is an auto-generated Django model module created by ogrinspect.\n" + "from django.contrib.gis.db import models\n" + "\n" + "\n" + "class Measurement(models.Model):\n" + ) + ) # The ordering of model fields might vary depending on several factors (version of GDAL, etc.) - if connection.vendor == 'sqlite': + if connection.vendor == "sqlite": # SpatiaLite introspection is somewhat lacking (#29461). - self.assertIn(' f_decimal = models.CharField(max_length=0)', model_def) + self.assertIn(" f_decimal = models.CharField(max_length=0)", model_def) else: - self.assertIn(' f_decimal = models.DecimalField(max_digits=0, decimal_places=0)', model_def) - self.assertIn(' f_int = models.IntegerField()', model_def) + self.assertIn( + " f_decimal = models.DecimalField(max_digits=0, decimal_places=0)", + model_def, + ) + self.assertIn(" f_int = models.IntegerField()", model_def) if not connection.ops.mariadb: # Probably a bug between GDAL and MariaDB on time fields. - self.assertIn(' f_datetime = models.DateTimeField()', model_def) - self.assertIn(' f_time = models.TimeField()', model_def) - if connection.vendor == 'sqlite': - self.assertIn(' f_float = models.CharField(max_length=0)', model_def) + self.assertIn(" f_datetime = models.DateTimeField()", model_def) + self.assertIn(" f_time = models.TimeField()", model_def) + if connection.vendor == "sqlite": + self.assertIn(" f_float = models.CharField(max_length=0)", model_def) else: - self.assertIn(' f_float = models.FloatField()', model_def) - max_length = 0 if connection.vendor == 'sqlite' else 10 - self.assertIn(' f_char = models.CharField(max_length=%s)' % max_length, model_def) - self.assertIn(' f_date = models.DateField()', model_def) + self.assertIn(" f_float = models.FloatField()", model_def) + max_length = 0 if connection.vendor == "sqlite" else 10 + self.assertIn( + " f_char = models.CharField(max_length=%s)" % max_length, model_def + ) + self.assertIn(" f_date = models.DateField()", model_def) # Some backends may have srid=-1 - self.assertIsNotNone(re.search(r' geom = models.PolygonField\(([^\)])*\)', model_def)) + self.assertIsNotNone( + re.search(r" geom = models.PolygonField\(([^\)])*\)", model_def) + ) def test_management_command(self): - shp_file = os.path.join(TEST_DATA, 'cities', 'cities.shp') + shp_file = os.path.join(TEST_DATA, "cities", "cities.shp") out = StringIO() - call_command('ogrinspect', shp_file, 'City', stdout=out) + call_command("ogrinspect", shp_file, "City", stdout=out) output = out.getvalue() - self.assertIn('class City(models.Model):', output) + self.assertIn("class City(models.Model):", output) def test_mapping_option(self): expected = ( @@ -174,10 +188,11 @@ class OGRInspectTest(SimpleTestCase): " 'density': 'Density',\n" " 'created': 'Created',\n" " 'geom': 'POINT',\n" - "}\n") - shp_file = os.path.join(TEST_DATA, 'cities', 'cities.shp') + "}\n" + ) + shp_file = os.path.join(TEST_DATA, "cities", "cities.shp") out = StringIO() - call_command('ogrinspect', shp_file, '--mapping', 'City', stdout=out) + call_command("ogrinspect", shp_file, "--mapping", "City", stdout=out) self.assertIn(expected, out.getvalue()) @@ -187,19 +202,23 @@ def get_ogr_db_string(): GDAL will create its own connection to the database, so we re-use the connection settings from the Django test. """ - db = connections.databases['default'] + db = connections.databases["default"] # Map from the django backend into the OGR driver name and database identifier # https://gdal.org/drivers/vector/ # # TODO: Support Oracle (OCI). drivers = { - 'django.contrib.gis.db.backends.postgis': ('PostgreSQL', "PG:dbname='%(db_name)s'", ' '), - 'django.contrib.gis.db.backends.mysql': ('MySQL', 'MYSQL:"%(db_name)s"', ','), - 'django.contrib.gis.db.backends.spatialite': ('SQLite', '%(db_name)s', '') + "django.contrib.gis.db.backends.postgis": ( + "PostgreSQL", + "PG:dbname='%(db_name)s'", + " ", + ), + "django.contrib.gis.db.backends.mysql": ("MySQL", 'MYSQL:"%(db_name)s"', ","), + "django.contrib.gis.db.backends.spatialite": ("SQLite", "%(db_name)s", ""), } - db_engine = db['ENGINE'] + db_engine = db["ENGINE"] if db_engine not in drivers: return None @@ -212,20 +231,21 @@ def get_ogr_db_string(): return None # SQLite/SpatiaLite in-memory databases - if db['NAME'] == ":memory:": + if db["NAME"] == ":memory:": return None # Build the params of the OGR database connection string - params = [db_str % {'db_name': db['NAME']}] + params = [db_str % {"db_name": db["NAME"]}] def add(key, template): value = db.get(key, None) # Don't add the parameter if it is not in django's settings if value: params.append(template % value) - add('HOST', "host='%s'") - add('PORT', "port='%s'") - add('USER', "user='%s'") - add('PASSWORD', "password='%s'") + + add("HOST", "host='%s'") + add("PORT", "port='%s'") + add("USER", "user='%s'") + add("PASSWORD", "password='%s'") return param_sep.join(params) diff --git a/tests/gis_tests/layermap/models.py b/tests/gis_tests/layermap/models.py index b488eedffe..953332539a 100644 --- a/tests/gis_tests/layermap/models.py +++ b/tests/gis_tests/layermap/models.py @@ -25,7 +25,7 @@ class CountyFeat(NamedModel): class City(NamedModel): - name_txt = models.TextField(default='') + name_txt = models.TextField(default="") name_short = models.CharField(max_length=5) population = models.IntegerField() density = models.DecimalField(max_digits=7, decimal_places=1) @@ -33,7 +33,7 @@ class City(NamedModel): point = models.PointField() class Meta: - app_label = 'layermap' + app_label = "layermap" class Interstate(NamedModel): @@ -41,7 +41,7 @@ class Interstate(NamedModel): path = models.LineStringField() class Meta: - app_label = 'layermap' + app_label = "layermap" # Same as `City` above, but for testing model inheritance. @@ -91,37 +91,37 @@ class DoesNotAllowNulls(models.Model): # Mapping dictionaries for the models above. co_mapping = { - 'name': 'Name', + "name": "Name", # ForeignKey's use another mapping dictionary for the _related_ Model (State in this case). - 'state': {'name': 'State'}, - 'mpoly': 'MULTIPOLYGON', # Will convert POLYGON features into MULTIPOLYGONS. + "state": {"name": "State"}, + "mpoly": "MULTIPOLYGON", # Will convert POLYGON features into MULTIPOLYGONS. } cofeat_mapping = { - 'name': 'Name', - 'poly': 'POLYGON', + "name": "Name", + "poly": "POLYGON", } city_mapping = { - 'name': 'Name', - 'population': 'Population', - 'density': 'Density', - 'dt': 'Created', - 'point': 'POINT', + "name": "Name", + "population": "Population", + "density": "Density", + "dt": "Created", + "point": "POINT", } inter_mapping = { - 'name': 'Name', - 'length': 'Length', - 'path': 'LINESTRING', + "name": "Name", + "length": "Length", + "path": "LINESTRING", } has_nulls_mapping = { - 'geom': 'POLYGON', - 'uuid': 'uuid', - 'datetime': 'datetime', - 'name': 'name', - 'integer': 'integer', - 'num': 'num', - 'boolean': 'boolean', + "geom": "POLYGON", + "uuid": "uuid", + "datetime": "datetime", + "name": "name", + "integer": "integer", + "num": "num", + "boolean": "boolean", } diff --git a/tests/gis_tests/layermap/tests.py b/tests/gis_tests/layermap/tests.py index 4e1d0f69ba..a7aafcc60c 100644 --- a/tests/gis_tests/layermap/tests.py +++ b/tests/gis_tests/layermap/tests.py @@ -7,47 +7,61 @@ from pathlib import Path from django.conf import settings from django.contrib.gis.gdal import DataSource from django.contrib.gis.utils.layermapping import ( - InvalidDecimal, InvalidString, LayerMapError, LayerMapping, + InvalidDecimal, + InvalidString, + LayerMapError, + LayerMapping, MissingForeignKey, ) from django.db import connection from django.test import TestCase, override_settings from .models import ( - City, County, CountyFeat, DoesNotAllowNulls, HasNulls, ICity1, ICity2, - Interstate, Invalid, State, city_mapping, co_mapping, cofeat_mapping, - has_nulls_mapping, inter_mapping, + City, + County, + CountyFeat, + DoesNotAllowNulls, + HasNulls, + ICity1, + ICity2, + Interstate, + Invalid, + State, + city_mapping, + co_mapping, + cofeat_mapping, + has_nulls_mapping, + inter_mapping, ) -shp_path = Path(__file__).resolve().parent.parent / 'data' -city_shp = shp_path / 'cities' / 'cities.shp' -co_shp = shp_path / 'counties' / 'counties.shp' -inter_shp = shp_path / 'interstates' / 'interstates.shp' -invalid_shp = shp_path / 'invalid' / 'emptypoints.shp' -has_nulls_geojson = shp_path / 'has_nulls' / 'has_nulls.geojson' +shp_path = Path(__file__).resolve().parent.parent / "data" +city_shp = shp_path / "cities" / "cities.shp" +co_shp = shp_path / "counties" / "counties.shp" +inter_shp = shp_path / "interstates" / "interstates.shp" +invalid_shp = shp_path / "invalid" / "emptypoints.shp" +has_nulls_geojson = shp_path / "has_nulls" / "has_nulls.geojson" # Dictionaries to hold what's expected in the county shapefile. -NAMES = ['Bexar', 'Galveston', 'Harris', 'Honolulu', 'Pueblo'] +NAMES = ["Bexar", "Galveston", "Harris", "Honolulu", "Pueblo"] NUMS = [1, 2, 1, 19, 1] # Number of polygons for each. -STATES = ['Texas', 'Texas', 'Texas', 'Hawaii', 'Colorado'] +STATES = ["Texas", "Texas", "Texas", "Hawaii", "Colorado"] class LayerMapTest(TestCase): - def test_init(self): "Testing LayerMapping initialization." # Model field that does not exist. bad1 = copy(city_mapping) - bad1['foobar'] = 'FooField' + bad1["foobar"] = "FooField" # Shapefile field that does not exist. bad2 = copy(city_mapping) - bad2['name'] = 'Nombre' + bad2["name"] = "Nombre" # Nonexistent geographic field type. bad3 = copy(city_mapping) - bad3['point'] = 'CURVE' + bad3["point"] = "CURVE" # Incrementing through the bad mapping dictionaries and # ensuring that a LayerMapError is raised. @@ -57,7 +71,7 @@ class LayerMapTest(TestCase): # A LookupError should be thrown for bogus encodings. with self.assertRaises(LookupError): - LayerMapping(City, city_shp, city_mapping, encoding='foobar') + LayerMapping(City, city_shp, city_mapping, encoding="foobar") def test_simple_layermap(self): "Test LayerMapping import of a simple point shapefile." @@ -73,10 +87,10 @@ class LayerMapTest(TestCase): ds = DataSource(city_shp) layer = ds[0] for feat in layer: - city = City.objects.get(name=feat['Name'].value) - self.assertEqual(feat['Population'].value, city.population) - self.assertEqual(Decimal(str(feat['Density'])), city.density) - self.assertEqual(feat['Created'].value, city.dt) + city = City.objects.get(name=feat["Name"].value) + self.assertEqual(feat["Population"].value, city.population) + self.assertEqual(Decimal(str(feat["Density"])), city.density) + self.assertEqual(feat["Created"].value, city.dt) # Comparing the geometries. pnt1, pnt2 = feat.geom, city.point @@ -110,14 +124,14 @@ class LayerMapTest(TestCase): # Only the first two features of this shapefile are valid. valid_feats = ds[0][:2] for feat in valid_feats: - istate = Interstate.objects.get(name=feat['Name'].value) + istate = Interstate.objects.get(name=feat["Name"].value) if feat.fid == 0: - self.assertEqual(Decimal(str(feat['Length'])), istate.length) + self.assertEqual(Decimal(str(feat["Length"])), istate.length) elif feat.fid == 1: # Everything but the first two decimal digits were truncated, # because the Interstate model's `length` field has decimal_places=2. - self.assertAlmostEqual(feat.get('Length'), float(istate.length), 2) + self.assertAlmostEqual(feat.get("Length"), float(istate.length), 2) for p1, p2 in zip(feat.geom, istate.path): self.assertAlmostEqual(p1[0], p2[0], 6) @@ -145,16 +159,20 @@ class LayerMapTest(TestCase): # Specifying the source spatial reference system via the `source_srs` keyword. lm = LayerMapping(County, co_shp, co_mapping, source_srs=4269) - lm = LayerMapping(County, co_shp, co_mapping, source_srs='NAD83') + lm = LayerMapping(County, co_shp, co_mapping, source_srs="NAD83") # Unique may take tuple or string parameters. - for arg in ('name', ('name', 'mpoly')): + for arg in ("name", ("name", "mpoly")): lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique=arg) # Now test for failures # Testing invalid params for the `unique` keyword. - for e, arg in ((TypeError, 5.0), (ValueError, 'foobar'), (ValueError, ('name', 'mpolygon'))): + for e, arg in ( + (TypeError, 5.0), + (ValueError, "foobar"), + (ValueError, ("name", "mpolygon")), + ): with self.assertRaises(e): LayerMapping(County, co_shp, co_mapping, transform=False, unique=arg) @@ -166,9 +184,9 @@ class LayerMapTest(TestCase): # Passing in invalid ForeignKey mapping parameters -- must be a dictionary # mapping for the model the ForeignKey points to. bad_fk_map1 = copy(co_mapping) - bad_fk_map1['state'] = 'name' + bad_fk_map1["state"] = "name" bad_fk_map2 = copy(co_mapping) - bad_fk_map2['state'] = {'nombre': 'State'} + bad_fk_map2["state"] = {"nombre": "State"} with self.assertRaises(TypeError): LayerMapping(County, co_shp, bad_fk_map1, transform=False) with self.assertRaises(LayerMapError): @@ -177,14 +195,14 @@ class LayerMapTest(TestCase): # There exist no State models for the ForeignKey mapping to work -- should raise # a MissingForeignKey exception (this error would be ignored if the `strict` # keyword is not set). - lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name') + lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique="name") with self.assertRaises(MissingForeignKey): lm.save(silent=True, strict=True) # Now creating the state models so the ForeignKey mapping may work. - State.objects.bulk_create([ - State(name='Colorado'), State(name='Hawaii'), State(name='Texas') - ]) + State.objects.bulk_create( + [State(name="Colorado"), State(name="Hawaii"), State(name="Texas")] + ) # If a mapping is specified as a collection, all OGR fields that # are not collections will be converted into them. For example, @@ -199,7 +217,7 @@ class LayerMapTest(TestCase): # appended to the geometry collection of the unique model. Thus, # all of the various islands in Honolulu county will be in in one # database record with a MULTIPOLYGON type. - lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name') + lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique="name") lm.save(silent=True, strict=True) # A reference that doesn't use the unique keyword; a new database record will @@ -216,15 +234,15 @@ class LayerMapTest(TestCase): def clear_counties(): County.objects.all().delete() - State.objects.bulk_create([ - State(name='Colorado'), State(name='Hawaii'), State(name='Texas') - ]) + State.objects.bulk_create( + [State(name="Colorado"), State(name="Hawaii"), State(name="Texas")] + ) # Initializing the LayerMapping object to use in these tests. - lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name') + lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique="name") # Bad feature id ranges should raise a type error. - bad_ranges = (5.0, 'foo', co_shp) + bad_ranges = (5.0, "foo", co_shp) for bad in bad_ranges: with self.assertRaises(TypeError): lm.save(fid_range=bad) @@ -239,7 +257,7 @@ class LayerMapTest(TestCase): # one model is returned because the `unique` keyword was set. qs = County.objects.all() self.assertEqual(1, qs.count()) - self.assertEqual('Galveston', qs[0].name) + self.assertEqual("Galveston", qs[0].name) # Features IDs 5 and beyond for Honolulu County, Hawaii, and # FID 0 is for Pueblo County, Colorado. @@ -250,13 +268,13 @@ class LayerMapTest(TestCase): # Only Pueblo & Honolulu counties should be present because of # the `unique` keyword. Have to set `order_by` on this QuerySet # or else MySQL will return a different ordering than the other dbs. - qs = County.objects.order_by('name') + qs = County.objects.order_by("name") self.assertEqual(2, qs.count()) hi, co = tuple(qs) - hi_idx, co_idx = tuple(map(NAMES.index, ('Honolulu', 'Pueblo'))) - self.assertEqual('Pueblo', co.name) + hi_idx, co_idx = tuple(map(NAMES.index, ("Honolulu", "Pueblo"))) + self.assertEqual("Pueblo", co.name) self.assertEqual(NUMS[co_idx], len(co.mpoly)) - self.assertEqual('Honolulu', hi.name) + self.assertEqual("Honolulu", hi.name) self.assertEqual(NUMS[hi_idx], len(hi.mpoly)) # Testing the `step` keyword -- should get the same counties @@ -270,11 +288,11 @@ class LayerMapTest(TestCase): def test_model_inheritance(self): "Tests LayerMapping on inherited models. See #12093." icity_mapping = { - 'name': 'Name', - 'population': 'Population', - 'density': 'Density', - 'point': 'POINT', - 'dt': 'Created', + "name": "Name", + "population": "Population", + "density": "Density", + "point": "POINT", + "dt": "Created", } # Parent model has geometry field. lm1 = LayerMapping(ICity1, city_shp, icity_mapping) @@ -289,14 +307,13 @@ class LayerMapTest(TestCase): def test_invalid_layer(self): "Tests LayerMapping on invalid geometries. See #15378." - invalid_mapping = {'point': 'POINT'} - lm = LayerMapping(Invalid, invalid_shp, invalid_mapping, - source_srs=4326) + invalid_mapping = {"point": "POINT"} + lm = LayerMapping(Invalid, invalid_shp, invalid_mapping, source_srs=4326) lm.save(silent=True) def test_charfield_too_short(self): mapping = copy(city_mapping) - mapping['name_short'] = 'Name' + mapping["name_short"] = "Name" lm = LayerMapping(City, city_shp, mapping) with self.assertRaises(InvalidString): lm.save(silent=True, strict=True) @@ -304,15 +321,15 @@ class LayerMapTest(TestCase): def test_textfield(self): "String content fits also in a TextField" mapping = copy(city_mapping) - mapping['name_txt'] = 'Name' + mapping["name_txt"] = "Name" lm = LayerMapping(City, city_shp, mapping) lm.save(silent=True, strict=True) self.assertEqual(City.objects.count(), 3) - self.assertEqual(City.objects.get(name='Houston').name_txt, "Houston") + self.assertEqual(City.objects.get(name="Houston").name_txt, "Houston") def test_encoded_name(self): - """ Test a layer containing utf-8-encoded name """ - city_shp = shp_path / 'ch-city' / 'ch-city.shp' + """Test a layer containing utf-8-encoded name""" + city_shp = shp_path / "ch-city" / "ch-city.shp" lm = LayerMapping(City, city_shp, city_mapping) lm.save(silent=True, strict=True) self.assertEqual(City.objects.count(), 1) @@ -320,10 +337,12 @@ class LayerMapTest(TestCase): def test_null_geom_with_unique(self): """LayerMapping may be created with a unique and a null geometry.""" - State.objects.bulk_create([State(name='Colorado'), State(name='Hawaii'), State(name='Texas')]) - hw = State.objects.get(name='Hawaii') - hu = County.objects.create(name='Honolulu', state=hw, mpoly=None) - lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name') + State.objects.bulk_create( + [State(name="Colorado"), State(name="Hawaii"), State(name="Texas")] + ) + hw = State.objects.get(name="Hawaii") + hu = County.objects.create(name="Honolulu", state=hw, mpoly=None) + lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique="name") lm.save(silent=True, strict=True) hu.refresh_from_db() self.assertIsNotNone(hu.mpoly) @@ -341,9 +360,9 @@ class LayerMapTest(TestCase): "Test LayerMapping import of GeoJSON with a null string value." lm = LayerMapping(HasNulls, has_nulls_geojson, has_nulls_mapping) lm.save() - self.assertEqual(HasNulls.objects.filter(name='None').count(), 0) + self.assertEqual(HasNulls.objects.filter(name="None").count(), 0) num_empty = 1 if connection.features.interprets_empty_strings_as_nulls else 0 - self.assertEqual(HasNulls.objects.filter(name='').count(), num_empty) + self.assertEqual(HasNulls.objects.filter(name="").count(), num_empty) self.assertEqual(HasNulls.objects.filter(name__isnull=True).count(), 1) def test_nullable_boolean_imported(self): @@ -358,15 +377,24 @@ class LayerMapTest(TestCase): """LayerMapping import of GeoJSON with a nullable date/time value.""" lm = LayerMapping(HasNulls, has_nulls_geojson, has_nulls_mapping) lm.save() - self.assertEqual(HasNulls.objects.filter(datetime__lt=datetime.date(1994, 8, 15)).count(), 1) - self.assertEqual(HasNulls.objects.filter(datetime='2018-11-29T03:02:52').count(), 1) + self.assertEqual( + HasNulls.objects.filter(datetime__lt=datetime.date(1994, 8, 15)).count(), 1 + ) + self.assertEqual( + HasNulls.objects.filter(datetime="2018-11-29T03:02:52").count(), 1 + ) self.assertEqual(HasNulls.objects.filter(datetime__isnull=True).count(), 1) def test_uuids_imported(self): """LayerMapping import of GeoJSON with UUIDs.""" lm = LayerMapping(HasNulls, has_nulls_geojson, has_nulls_mapping) lm.save() - self.assertEqual(HasNulls.objects.filter(uuid='1378c26f-cbe6-44b0-929f-eb330d4991f5').count(), 1) + self.assertEqual( + HasNulls.objects.filter( + uuid="1378c26f-cbe6-44b0-929f-eb330d4991f5" + ).count(), + 1, + ) def test_null_number_imported_not_allowed(self): """ @@ -385,7 +413,7 @@ class LayerMapTest(TestCase): class OtherRouter: def db_for_read(self, model, **hints): - return 'other' + return "other" def db_for_write(self, model, **hints): return self.db_for_read(model, **hints) @@ -402,9 +430,9 @@ class OtherRouter: @override_settings(DATABASE_ROUTERS=[OtherRouter()]) class LayerMapRouterTest(TestCase): - databases = {'default', 'other'} + databases = {"default", "other"} - @unittest.skipUnless(len(settings.DATABASES) > 1, 'multiple databases required') + @unittest.skipUnless(len(settings.DATABASES) > 1, "multiple databases required") def test_layermapping_default_db(self): lm = LayerMapping(City, city_shp, city_mapping) - self.assertEqual(lm.using, 'other') + self.assertEqual(lm.using, "other") diff --git a/tests/gis_tests/rasterapp/migrations/0001_setup_extensions.py b/tests/gis_tests/rasterapp/migrations/0001_setup_extensions.py index 35451b3a1e..227d6b668d 100644 --- a/tests/gis_tests/rasterapp/migrations/0001_setup_extensions.py +++ b/tests/gis_tests/rasterapp/migrations/0001_setup_extensions.py @@ -9,10 +9,12 @@ if connection.features.supports_raster: # PostGIS 3+ requires postgis_raster extension. if pg_version[1:] >= (3,): operations = [ - CreateExtension('postgis_raster'), + CreateExtension("postgis_raster"), ] else: operations = [] + else: + class Migration(migrations.Migration): operations = [] diff --git a/tests/gis_tests/rasterapp/migrations/0002_rastermodels.py b/tests/gis_tests/rasterapp/migrations/0002_rastermodels.py index 58b742c1b2..68b77f1d85 100644 --- a/tests/gis_tests/rasterapp/migrations/0002_rastermodels.py +++ b/tests/gis_tests/rasterapp/migrations/0002_rastermodels.py @@ -6,42 +6,67 @@ from django.db.models import deletion class Migration(migrations.Migration): dependencies = [ - ('rasterapp', '0001_setup_extensions'), + ("rasterapp", "0001_setup_extensions"), ] operations = [ migrations.CreateModel( - name='RasterModel', + name="RasterModel", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('rast', models.fields.RasterField( - blank=True, - null=True, - srid=4326, - verbose_name='A Verbose Raster Name', - )), - ('rastprojected', models.fields.RasterField( - null=True, - srid=3086, - verbose_name='A Projected Raster Table', - )), - ('geom', models.fields.PointField(null=True, srid=4326)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "rast", + models.fields.RasterField( + blank=True, + null=True, + srid=4326, + verbose_name="A Verbose Raster Name", + ), + ), + ( + "rastprojected", + models.fields.RasterField( + null=True, + srid=3086, + verbose_name="A Projected Raster Table", + ), + ), + ("geom", models.fields.PointField(null=True, srid=4326)), ], options={ - 'required_db_features': ['supports_raster'], + "required_db_features": ["supports_raster"], }, ), migrations.CreateModel( - name='RasterRelatedModel', + name="RasterRelatedModel", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('rastermodel', models.ForeignKey( - on_delete=deletion.CASCADE, - to='rasterapp.rastermodel', - )), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "rastermodel", + models.ForeignKey( + on_delete=deletion.CASCADE, + to="rasterapp.rastermodel", + ), + ), ], options={ - 'required_db_features': ['supports_raster'], + "required_db_features": ["supports_raster"], }, ), ] diff --git a/tests/gis_tests/rasterapp/models.py b/tests/gis_tests/rasterapp/models.py index e7769f19c4..db540bd488 100644 --- a/tests/gis_tests/rasterapp/models.py +++ b/tests/gis_tests/rasterapp/models.py @@ -2,12 +2,14 @@ from django.contrib.gis.db import models class RasterModel(models.Model): - rast = models.RasterField('A Verbose Raster Name', null=True, srid=4326, spatial_index=True, blank=True) - rastprojected = models.RasterField('A Projected Raster Table', srid=3086, null=True) + rast = models.RasterField( + "A Verbose Raster Name", null=True, srid=4326, spatial_index=True, blank=True + ) + rastprojected = models.RasterField("A Projected Raster Table", srid=3086, null=True) geom = models.PointField(null=True) class Meta: - required_db_features = ['supports_raster'] + required_db_features = ["supports_raster"] def __str__(self): return str(self.id) @@ -17,7 +19,7 @@ class RasterRelatedModel(models.Model): rastermodel = models.ForeignKey(RasterModel, models.CASCADE) class Meta: - required_db_features = ['supports_raster'] + required_db_features = ["supports_raster"] def __str__(self): return str(self.id) diff --git a/tests/gis_tests/rasterapp/test_rasterfield.py b/tests/gis_tests/rasterapp/test_rasterfield.py index 6981fcebc4..3f2ce770a9 100644 --- a/tests/gis_tests/rasterapp/test_rasterfield.py +++ b/tests/gis_tests/rasterapp/test_rasterfield.py @@ -16,21 +16,23 @@ from ..data.rasters.textrasters import JSON_RASTER from .models import RasterModel, RasterRelatedModel -@skipUnlessDBFeature('supports_raster') +@skipUnlessDBFeature("supports_raster") class RasterFieldTest(TransactionTestCase): - available_apps = ['gis_tests.rasterapp'] + available_apps = ["gis_tests.rasterapp"] def setUp(self): - rast = GDALRaster({ - "srid": 4326, - "origin": [0, 0], - "scale": [-1, 1], - "skew": [0, 0], - "width": 5, - "height": 5, - "nr_of_bands": 2, - "bands": [{"data": range(25)}, {"data": range(25, 50)}], - }) + rast = GDALRaster( + { + "srid": 4326, + "origin": [0, 0], + "scale": [-1, 1], + "skew": [0, 0], + "width": 5, + "height": 5, + "nr_of_bands": 2, + "bands": [{"data": range(25)}, {"data": range(25, 50)}], + } + ) model_instance = RasterModel.objects.create( rast=rast, rastprojected=rast, @@ -53,19 +55,21 @@ class RasterFieldTest(TransactionTestCase): def test_deserialize_with_pixeltype_flags(self): no_data = 3 - rast = GDALRaster({ - 'srid': 4326, - 'origin': [0, 0], - 'scale': [-1, 1], - 'skew': [0, 0], - 'width': 1, - 'height': 1, - 'nr_of_bands': 1, - 'bands': [{'data': [no_data], 'nodata_value': no_data}], - }) + rast = GDALRaster( + { + "srid": 4326, + "origin": [0, 0], + "scale": [-1, 1], + "skew": [0, 0], + "width": 1, + "height": 1, + "nr_of_bands": 1, + "bands": [{"data": [no_data], "nodata_value": no_data}], + } + ) r = RasterModel.objects.create(rast=rast) RasterModel.objects.filter(pk=r.pk).update( - rast=Func(F('rast'), function='ST_SetBandIsNoData'), + rast=Func(F("rast"), function="ST_SetBandIsNoData"), ) r.refresh_from_db() band = r.rast.bands[0].data() @@ -96,13 +100,33 @@ class RasterFieldTest(TransactionTestCase): # value is as expected. self.assertEqual( [ - 0.0, 1.0, 2.0, 3.0, 4.0, - 5.0, 6.0, 7.0, 8.0, 9.0, - 10.0, 11.0, 12.0, 13.0, 14.0, - 15.0, 16.0, 17.0, 18.0, 19.0, - 20.0, 21.0, 22.0, 23.0, 24.0 + 0.0, + 1.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, + 11.0, + 12.0, + 13.0, + 14.0, + 15.0, + 16.0, + 17.0, + 18.0, + 19.0, + 20.0, + 21.0, + 22.0, + 23.0, + 24.0, ], - band + band, ) def test_implicit_raster_transformation(self): @@ -113,7 +137,7 @@ class RasterFieldTest(TransactionTestCase): # Parse json raster rast = json.loads(JSON_RASTER) # Update srid to another value - rast['srid'] = 3086 + rast["srid"] = 3086 # Save model and get it from db r = RasterModel.objects.create(rast=rast) r.refresh_from_db() @@ -121,8 +145,12 @@ class RasterFieldTest(TransactionTestCase): self.assertEqual(r.rast.srs.srid, 4326) # Confirm geotransform is in lat/lon expected = [ - -87.9298551266551, 9.459646421449934e-06, 0.0, 23.94249275457565, - 0.0, -9.459646421449934e-06, + -87.9298551266551, + 9.459646421449934e-06, + 0.0, + 23.94249275457565, + 0.0, + -9.459646421449934e-06, ] for val, exp in zip(r.rast.geotransform, expected): self.assertAlmostEqual(exp, val) @@ -132,8 +160,7 @@ class RasterFieldTest(TransactionTestCase): RasterField should accept a positional verbose name argument. """ self.assertEqual( - RasterModel._meta.get_field('rast').verbose_name, - 'A Verbose Raster Name' + RasterModel._meta.get_field("rast").verbose_name, "A Verbose Raster Name" ) def test_all_gis_lookups_with_rasters(self): @@ -143,13 +170,11 @@ class RasterFieldTest(TransactionTestCase): unprojected coordinate systems. This test just checks that the lookup can be called, but doesn't check if the result makes logical sense. """ - from django.contrib.gis.db.backends.postgis.operations import ( - PostGISOperations, - ) + from django.contrib.gis.db.backends.postgis.operations import PostGISOperations # Create test raster and geom. rast = GDALRaster(json.loads(JSON_RASTER)) - stx_pnt = GEOSGeometry('POINT (-95.370401017314293 29.704867409475465)', 4326) + stx_pnt = GEOSGeometry("POINT (-95.370401017314293 29.704867409475465)", 4326) stx_pnt.transform(3086) lookups = [ @@ -157,64 +182,83 @@ class RasterFieldTest(TransactionTestCase): for name, lookup in BaseSpatialField.get_lookups().items() if issubclass(lookup, GISLookup) ] - self.assertNotEqual(lookups, [], 'No lookups found') + self.assertNotEqual(lookups, [], "No lookups found") # Loop through all the GIS lookups. for name, lookup in lookups: # Construct lookup filter strings. combo_keys = [ - field + name for field in [ - 'rast__', 'rast__', 'rastprojected__0__', 'rast__', - 'rastprojected__', 'geom__', 'rast__', + field + name + for field in [ + "rast__", + "rast__", + "rastprojected__0__", + "rast__", + "rastprojected__", + "geom__", + "rast__", ] ] if issubclass(lookup, DistanceLookupBase): # Set lookup values for distance lookups. combo_values = [ - (rast, 50, 'spheroid'), - (rast, 0, 50, 'spheroid'), + (rast, 50, "spheroid"), + (rast, 0, 50, "spheroid"), (rast, 0, D(km=1)), (stx_pnt, 0, 500), (stx_pnt, D(km=1000)), (rast, 500), (json.loads(JSON_RASTER), 500), ] - elif name == 'relate': + elif name == "relate": # Set lookup values for the relate lookup. combo_values = [ - (rast, 'T*T***FF*'), - (rast, 0, 'T*T***FF*'), - (rast, 0, 'T*T***FF*'), - (stx_pnt, 0, 'T*T***FF*'), - (stx_pnt, 'T*T***FF*'), - (rast, 'T*T***FF*'), - (json.loads(JSON_RASTER), 'T*T***FF*'), + (rast, "T*T***FF*"), + (rast, 0, "T*T***FF*"), + (rast, 0, "T*T***FF*"), + (stx_pnt, 0, "T*T***FF*"), + (stx_pnt, "T*T***FF*"), + (rast, "T*T***FF*"), + (json.loads(JSON_RASTER), "T*T***FF*"), ] - elif name == 'isvalid': + elif name == "isvalid": # The isvalid lookup doesn't make sense for rasters. continue elif PostGISOperations.gis_operators[name].func: # Set lookup values for all function based operators. combo_values = [ - rast, (rast, 0), (rast, 0), (stx_pnt, 0), stx_pnt, - rast, json.loads(JSON_RASTER) + rast, + (rast, 0), + (rast, 0), + (stx_pnt, 0), + stx_pnt, + rast, + json.loads(JSON_RASTER), ] else: # Override band lookup for these, as it's not supported. - combo_keys[2] = 'rastprojected__' + name + combo_keys[2] = "rastprojected__" + name # Set lookup values for all other operators. - combo_values = [rast, None, rast, stx_pnt, stx_pnt, rast, json.loads(JSON_RASTER)] + combo_values = [ + rast, + None, + rast, + stx_pnt, + stx_pnt, + rast, + json.loads(JSON_RASTER), + ] # Create query filter combinations. self.assertEqual( len(combo_keys), len(combo_values), - 'Number of lookup names and values should be the same', + "Number of lookup names and values should be the same", ) combos = [x for x in zip(combo_keys, combo_values) if x[1]] self.assertEqual( [(n, x) for n, x in enumerate(combos) if x in combos[:n]], [], - 'There are repeated test lookups', + "There are repeated test lookups", ) combos = [{k: v} for k, v in combos] @@ -236,14 +280,16 @@ class RasterFieldTest(TransactionTestCase): """ # Create test raster and geom. rast = GDALRaster(json.loads(JSON_RASTER)) - stx_pnt = GEOSGeometry('POINT (-95.370401017314293 29.704867409475465)', 4326) + stx_pnt = GEOSGeometry("POINT (-95.370401017314293 29.704867409475465)", 4326) stx_pnt.transform(3086) # Filter raster with different lookup raster formats. qs = RasterModel.objects.filter(rastprojected__dwithin=(rast, D(km=1))) self.assertEqual(qs.count(), 1) - qs = RasterModel.objects.filter(rastprojected__dwithin=(json.loads(JSON_RASTER), D(km=1))) + qs = RasterModel.objects.filter( + rastprojected__dwithin=(json.loads(JSON_RASTER), D(km=1)) + ) self.assertEqual(qs.count(), 1) qs = RasterModel.objects.filter(rastprojected__dwithin=(JSON_RASTER, D(km=1))) @@ -287,7 +333,10 @@ class RasterFieldTest(TransactionTestCase): self.assertEqual(qs.count(), 1) # Filter through conditional statements. - qs = RasterModel.objects.filter(Q(rast__dwithin=(rast, 40)) & Q(rastprojected__dwithin=(stx_pnt, D(km=10000)))) + qs = RasterModel.objects.filter( + Q(rast__dwithin=(rast, 40)) + & Q(rastprojected__dwithin=(stx_pnt, D(km=10000))) + ) self.assertEqual(qs.count(), 1) # Filter through different lookup. @@ -296,35 +345,43 @@ class RasterFieldTest(TransactionTestCase): def test_lookup_input_tuple_too_long(self): rast = GDALRaster(json.loads(JSON_RASTER)) - msg = 'Tuple too long for lookup bbcontains.' + msg = "Tuple too long for lookup bbcontains." with self.assertRaisesMessage(ValueError, msg): RasterModel.objects.filter(rast__bbcontains=(rast, 1, 2)) def test_lookup_input_band_not_allowed(self): rast = GDALRaster(json.loads(JSON_RASTER)) qs = RasterModel.objects.filter(rast__bbcontains=(rast, 1)) - msg = 'Band indices are not allowed for this operator, it works on bbox only.' + msg = "Band indices are not allowed for this operator, it works on bbox only." with self.assertRaisesMessage(ValueError, msg): qs.count() def test_isvalid_lookup_with_raster_error(self): qs = RasterModel.objects.filter(rast__isvalid=True) - msg = 'IsValid function requires a GeometryField in position 1, got RasterField.' + msg = ( + "IsValid function requires a GeometryField in position 1, got RasterField." + ) with self.assertRaisesMessage(TypeError, msg): qs.count() def test_result_of_gis_lookup_with_rasters(self): # Point is in the interior - qs = RasterModel.objects.filter(rast__contains=GEOSGeometry('POINT (-0.5 0.5)', 4326)) + qs = RasterModel.objects.filter( + rast__contains=GEOSGeometry("POINT (-0.5 0.5)", 4326) + ) self.assertEqual(qs.count(), 1) # Point is in the exterior - qs = RasterModel.objects.filter(rast__contains=GEOSGeometry('POINT (0.5 0.5)', 4326)) + qs = RasterModel.objects.filter( + rast__contains=GEOSGeometry("POINT (0.5 0.5)", 4326) + ) self.assertEqual(qs.count(), 0) # A point on the boundary is not contained properly - qs = RasterModel.objects.filter(rast__contains_properly=GEOSGeometry('POINT (0 0)', 4326)) + qs = RasterModel.objects.filter( + rast__contains_properly=GEOSGeometry("POINT (0 0)", 4326) + ) self.assertEqual(qs.count(), 0) # Raster is located left of the point - qs = RasterModel.objects.filter(rast__left=GEOSGeometry('POINT (1 0)', 4326)) + qs = RasterModel.objects.filter(rast__left=GEOSGeometry("POINT (1 0)", 4326)) self.assertEqual(qs.count(), 1) def test_lookup_with_raster_bbox(self): @@ -363,7 +420,7 @@ class RasterFieldTest(TransactionTestCase): with self.assertRaisesMessage(ValueError, msg): RasterModel.objects.filter(geom__intersects=obj) # Test with invalid string lookup parameter - obj = '00000' + obj = "00000" msg = "Couldn't create spatial object from lookup value '%s'." % obj with self.assertRaisesMessage(ValueError, msg): RasterModel.objects.filter(geom__intersects=obj) @@ -378,14 +435,22 @@ class RasterFieldTest(TransactionTestCase): with self.assertRaisesMessage(TypeError, msg): RasterModel.objects.annotate(distance_from_point=Distance("geom", rast)) with self.assertRaisesMessage(TypeError, msg): - RasterModel.objects.annotate(distance_from_point=Distance("rastprojected", rast)) - msg = "Distance function requires a GeometryField in position 1, got RasterField." + RasterModel.objects.annotate( + distance_from_point=Distance("rastprojected", rast) + ) + msg = ( + "Distance function requires a GeometryField in position 1, got RasterField." + ) with self.assertRaisesMessage(TypeError, msg): - RasterModel.objects.annotate(distance_from_point=Distance("rastprojected", point)).count() + RasterModel.objects.annotate( + distance_from_point=Distance("rastprojected", point) + ).count() def test_lhs_with_index_rhs_without_index(self): with CaptureQueriesContext(connection) as queries: - RasterModel.objects.filter(rast__0__contains=json.loads(JSON_RASTER)).exists() + RasterModel.objects.filter( + rast__0__contains=json.loads(JSON_RASTER) + ).exists() # It's easier to check the indexes in the generated SQL than to write # tests that cover all index combinations. - self.assertRegex(queries[-1]['sql'], r'WHERE ST_Contains\([^)]*, 1, [^)]*, 1\)') + self.assertRegex(queries[-1]["sql"], r"WHERE ST_Contains\([^)]*, 1, [^)]*, 1\)") diff --git a/tests/gis_tests/relatedapp/models.py b/tests/gis_tests/relatedapp/models.py index 6903617a2a..94a0a28780 100644 --- a/tests/gis_tests/relatedapp/models.py +++ b/tests/gis_tests/relatedapp/models.py @@ -36,7 +36,7 @@ class Parcel(SimpleModel): city = models.ForeignKey(City, models.CASCADE) center1 = models.PointField() # Throwing a curveball w/`db_column` here. - center2 = models.PointField(srid=2276, db_column='mycenter') + center2 = models.PointField(srid=2276, db_column="mycenter") border1 = models.PolygonField() border2 = models.PolygonField(srid=2276) @@ -56,7 +56,7 @@ class Article(SimpleModel): class Book(SimpleModel): title = models.CharField(max_length=100) - author = models.ForeignKey(Author, models.SET_NULL, related_name='books', null=True) + author = models.ForeignKey(Author, models.SET_NULL, related_name="books", null=True) class Event(SimpleModel): diff --git a/tests/gis_tests/relatedapp/tests.py b/tests/gis_tests/relatedapp/tests.py index b834e13d48..48f9c12b69 100644 --- a/tests/gis_tests/relatedapp/tests.py +++ b/tests/gis_tests/relatedapp/tests.py @@ -5,25 +5,23 @@ from django.test import TestCase, skipUnlessDBFeature from django.test.utils import override_settings from django.utils import timezone -from .models import ( - Article, Author, Book, City, DirectoryEntry, Event, Location, Parcel, -) +from .models import Article, Author, Book, City, DirectoryEntry, Event, Location, Parcel class RelatedGeoModelTest(TestCase): - fixtures = ['initial'] + fixtures = ["initial"] def test02_select_related(self): "Testing `select_related` on geographic models (see #7126)." - qs1 = City.objects.order_by('id') - qs2 = City.objects.order_by('id').select_related() - qs3 = City.objects.order_by('id').select_related('location') + qs1 = City.objects.order_by("id") + qs2 = City.objects.order_by("id").select_related() + qs3 = City.objects.order_by("id").select_related("location") # Reference data for what's in the fixtures. cities = ( - ('Aurora', 'TX', -97.516111, 33.058333), - ('Roswell', 'NM', -104.528056, 33.387222), - ('Kecksburg', 'PA', -79.460734, 40.18476), + ("Aurora", "TX", -97.516111, 33.058333), + ("Roswell", "NM", -104.528056, 33.387222), + ("Kecksburg", "PA", -79.460734, 40.18476), ) for qs in (qs1, qs2, qs3): @@ -38,14 +36,18 @@ class RelatedGeoModelTest(TestCase): def test_related_extent_aggregate(self): "Testing the `Extent` aggregate on related geographic models." # This combines the Extent and Union aggregates into one query - aggs = City.objects.aggregate(Extent('location__point')) + aggs = City.objects.aggregate(Extent("location__point")) # One for all locations, one that excludes New Mexico (Roswell). all_extent = (-104.528056, 29.763374, -79.460734, 40.18476) txpa_extent = (-97.516111, 29.763374, -79.460734, 40.18476) - e1 = City.objects.aggregate(Extent('location__point'))['location__point__extent'] - e2 = City.objects.exclude(state='NM').aggregate(Extent('location__point'))['location__point__extent'] - e3 = aggs['location__point__extent'] + e1 = City.objects.aggregate(Extent("location__point"))[ + "location__point__extent" + ] + e2 = City.objects.exclude(state="NM").aggregate(Extent("location__point"))[ + "location__point__extent" + ] + e3 = aggs["location__point__extent"] # The tolerance value is to four decimal places because of differences # between the Oracle and PostGIS spatial backends on the extent calculation. @@ -59,19 +61,19 @@ class RelatedGeoModelTest(TestCase): """ Test annotation with Extent GeoAggregate. """ - cities = City.objects.annotate(points_extent=Extent('location__point')).order_by('name') + cities = City.objects.annotate( + points_extent=Extent("location__point") + ).order_by("name") tol = 4 self.assertAlmostEqual( - cities[0].points_extent, - (-97.516111, 33.058333, -97.516111, 33.058333), - tol + cities[0].points_extent, (-97.516111, 33.058333, -97.516111, 33.058333), tol ) - @skipUnlessDBFeature('supports_union_aggr') + @skipUnlessDBFeature("supports_union_aggr") def test_related_union_aggregate(self): "Testing the `Union` aggregate on related geographic models." # This combines the Extent and Union aggregates into one query - aggs = City.objects.aggregate(Union('location__point')) + aggs = City.objects.aggregate(Union("location__point")) # These are the points that are components of the aggregate geographic # union that is returned. Each point # corresponds to City PK. @@ -87,11 +89,11 @@ class RelatedGeoModelTest(TestCase): ref_u1 = MultiPoint(p1, p2, p4, p5, p3, srid=4326) ref_u2 = MultiPoint(p2, p3, srid=4326) - u1 = City.objects.aggregate(Union('location__point'))['location__point__union'] + u1 = City.objects.aggregate(Union("location__point"))["location__point__union"] u2 = City.objects.exclude( - name__in=('Roswell', 'Houston', 'Dallas', 'Fort Worth'), - ).aggregate(Union('location__point'))['location__point__union'] - u3 = aggs['location__point__union'] + name__in=("Roswell", "Houston", "Dallas", "Fort Worth"), + ).aggregate(Union("location__point"))["location__point__union"] + u3 = aggs["location__point__union"] self.assertEqual(type(u1), MultiPoint) self.assertEqual(type(u3), MultiPoint) @@ -111,11 +113,11 @@ class RelatedGeoModelTest(TestCase): # Constructing a dummy parcel border and getting the City instance for # assigning the FK. b1 = GEOSGeometry( - 'POLYGON((-97.501205 33.052520,-97.501205 33.052576,' - '-97.501150 33.052576,-97.501150 33.052520,-97.501205 33.052520))', - srid=4326 + "POLYGON((-97.501205 33.052520,-97.501205 33.052576," + "-97.501150 33.052576,-97.501150 33.052520,-97.501205 33.052520))", + srid=4326, ) - pcity = City.objects.get(name='Aurora') + pcity = City.objects.get(name="Aurora") # First parcel has incorrect center point that is equal to the City; # it also has a second border that is different from the first as a @@ -123,7 +125,9 @@ class RelatedGeoModelTest(TestCase): c1 = pcity.location.point c2 = c1.transform(2276, clone=True) b2 = c2.buffer(100) - Parcel.objects.create(name='P1', city=pcity, center1=c1, center2=c2, border1=b1, border2=b2) + Parcel.objects.create( + name="P1", city=pcity, center1=c1, center2=c2, border1=b1, border2=b2 + ) # Now creating a second Parcel where the borders are the same, just # in different coordinate systems. The center points are also the @@ -131,20 +135,26 @@ class RelatedGeoModelTest(TestCase): # actually correspond to the centroid of the border. c1 = b1.centroid c2 = c1.transform(2276, clone=True) - b2 = b1 if connection.features.supports_transform else b1.transform(2276, clone=True) - Parcel.objects.create(name='P2', city=pcity, center1=c1, center2=c2, border1=b1, border2=b2) + b2 = ( + b1 + if connection.features.supports_transform + else b1.transform(2276, clone=True) + ) + Parcel.objects.create( + name="P2", city=pcity, center1=c1, center2=c2, border1=b1, border2=b2 + ) # Should return the second Parcel, which has the center within the # border. - qs = Parcel.objects.filter(center1__within=F('border1')) + qs = Parcel.objects.filter(center1__within=F("border1")) self.assertEqual(1, len(qs)) - self.assertEqual('P2', qs[0].name) + self.assertEqual("P2", qs[0].name) # This time center2 is in a different coordinate system and needs to be # wrapped in transformation SQL. - qs = Parcel.objects.filter(center2__within=F('border1')) + qs = Parcel.objects.filter(center2__within=F("border1")) if connection.features.supports_transform: - self.assertEqual('P2', qs.get().name) + self.assertEqual("P2", qs.get().name) else: msg = "This backend doesn't support the Transform function." with self.assertRaisesMessage(NotSupportedError, msg): @@ -152,14 +162,14 @@ class RelatedGeoModelTest(TestCase): # Should return the first Parcel, which has the center point equal # to the point in the City ForeignKey. - qs = Parcel.objects.filter(center1=F('city__location__point')) + qs = Parcel.objects.filter(center1=F("city__location__point")) self.assertEqual(1, len(qs)) - self.assertEqual('P1', qs[0].name) + self.assertEqual("P1", qs[0].name) # This time the city column should be wrapped in transformation SQL. - qs = Parcel.objects.filter(border2__contains=F('city__location__point')) + qs = Parcel.objects.filter(border2__contains=F("city__location__point")) if connection.features.supports_transform: - self.assertEqual('P1', qs.get().name) + self.assertEqual("P1", qs.get().name) else: msg = "This backend doesn't support the Transform function." with self.assertRaisesMessage(NotSupportedError, msg): @@ -176,21 +186,21 @@ class RelatedGeoModelTest(TestCase): for m, d, t in zip(gqs, gvqs, gvlqs): # The values should be Geometry objects and not raw strings returned # by the spatial database. - self.assertIsInstance(d['point'], GEOSGeometry) + self.assertIsInstance(d["point"], GEOSGeometry) self.assertIsInstance(t[1], GEOSGeometry) - self.assertEqual(m.point, d['point']) + self.assertEqual(m.point, d["point"]) self.assertEqual(m.point, t[1]) @override_settings(USE_TZ=True) def test_07b_values(self): "Testing values() and values_list() with aware datetime. See #21565." Event.objects.create(name="foo", when=timezone.now()) - list(Event.objects.values_list('when')) + list(Event.objects.values_list("when")) def test08_defer_only(self): "Testing defer() and only() on Geographic models." qs = Location.objects.all() - def_qs = Location.objects.defer('point') + def_qs = Location.objects.defer("point") for loc, def_loc in zip(qs, def_qs): self.assertEqual(loc.point, def_loc.point) @@ -202,31 +212,33 @@ class RelatedGeoModelTest(TestCase): # ID column is selected instead of ID column for the city. city_ids = (1, 2, 3, 4, 5) loc_ids = (1, 2, 3, 5, 4) - ids_qs = City.objects.order_by('id').values('id', 'location__id') + ids_qs = City.objects.order_by("id").values("id", "location__id") for val_dict, c_id, l_id in zip(ids_qs, city_ids, loc_ids): - self.assertEqual(val_dict['id'], c_id) - self.assertEqual(val_dict['location__id'], l_id) + self.assertEqual(val_dict["id"], c_id) + self.assertEqual(val_dict["location__id"], l_id) def test10_combine(self): "Testing the combination of two QuerySets (#10807)." - buf1 = City.objects.get(name='Aurora').location.point.buffer(0.1) - buf2 = City.objects.get(name='Kecksburg').location.point.buffer(0.1) + buf1 = City.objects.get(name="Aurora").location.point.buffer(0.1) + buf2 = City.objects.get(name="Kecksburg").location.point.buffer(0.1) qs1 = City.objects.filter(location__point__within=buf1) qs2 = City.objects.filter(location__point__within=buf2) combined = qs1 | qs2 names = [c.name for c in combined] self.assertEqual(2, len(names)) - self.assertIn('Aurora', names) - self.assertIn('Kecksburg', names) + self.assertIn("Aurora", names) + self.assertIn("Kecksburg", names) - @skipUnlessDBFeature('allows_group_by_lob') + @skipUnlessDBFeature("allows_group_by_lob") def test12a_count(self): "Testing `Count` aggregate on geo-fields." # The City, 'Fort Worth' uses the same location as Dallas. - dallas = City.objects.get(name='Dallas') + dallas = City.objects.get(name="Dallas") # Count annotation should be 2 for the Dallas location now. - loc = Location.objects.annotate(num_cities=Count('city')).get(id=dallas.location.id) + loc = Location.objects.annotate(num_cities=Count("city")).get( + id=dallas.location.id + ) self.assertEqual(2, loc.num_cities) def test12b_count(self): @@ -234,25 +246,33 @@ class RelatedGeoModelTest(TestCase): # Should only be one author (Trevor Paglen) returned by this query, and # the annotation should have 3 for the number of books, see #11087. # Also testing with a values(), see #11489. - qs = Author.objects.annotate(num_books=Count('books')).filter(num_books__gt=1) - vqs = Author.objects.values('name').annotate(num_books=Count('books')).filter(num_books__gt=1) + qs = Author.objects.annotate(num_books=Count("books")).filter(num_books__gt=1) + vqs = ( + Author.objects.values("name") + .annotate(num_books=Count("books")) + .filter(num_books__gt=1) + ) self.assertEqual(1, len(qs)) self.assertEqual(3, qs[0].num_books) self.assertEqual(1, len(vqs)) - self.assertEqual(3, vqs[0]['num_books']) + self.assertEqual(3, vqs[0]["num_books"]) - @skipUnlessDBFeature('allows_group_by_lob') + @skipUnlessDBFeature("allows_group_by_lob") def test13c_count(self): "Testing `Count` aggregate with `.values()`. See #15305." - qs = Location.objects.filter(id=5).annotate(num_cities=Count('city')).values('id', 'point', 'num_cities') + qs = ( + Location.objects.filter(id=5) + .annotate(num_cities=Count("city")) + .values("id", "point", "num_cities") + ) self.assertEqual(1, len(qs)) - self.assertEqual(2, qs[0]['num_cities']) - self.assertIsInstance(qs[0]['point'], GEOSGeometry) + self.assertEqual(2, qs[0]["num_cities"]) + self.assertIsInstance(qs[0]["point"], GEOSGeometry) def test13_select_related_null_fk(self): "Testing `select_related` on a nullable ForeignKey." - Book.objects.create(title='Without Author') - b = Book.objects.select_related('author').get(title='Without Author') + Book.objects.create(title="Without Author") + b = Book.objects.select_related("author").get(title="Without Author") # Should be `None`, and not a 'dummy' model. self.assertIsNone(b.author) @@ -266,11 +286,13 @@ class RelatedGeoModelTest(TestCase): # "relatedapp_location" ON ("relatedapp_city"."location_id" = "relatedapp_location"."id") # WHERE "relatedapp_city"."state" = 'TX'; ref_geom = GEOSGeometry( - 'MULTIPOINT(-97.516111 33.058333,-96.801611 32.782057,' - '-95.363151 29.763374,-96.801611 32.782057)' + "MULTIPOINT(-97.516111 33.058333,-96.801611 32.782057," + "-95.363151 29.763374,-96.801611 32.782057)" ) - coll = City.objects.filter(state='TX').aggregate(Collect('location__point'))['location__point__collect'] + coll = City.objects.filter(state="TX").aggregate(Collect("location__point"))[ + "location__point__collect" + ] # Even though Dallas and Ft. Worth share same point, Collect doesn't # consolidate -- that's why 4 points in MultiPoint. self.assertEqual(4, len(coll)) @@ -278,7 +300,7 @@ class RelatedGeoModelTest(TestCase): def test15_invalid_select_related(self): "Testing doing select_related on the related name manager of a unique FK. See #13934." - qs = Article.objects.select_related('author__article') + qs = Article.objects.select_related("author__article") # This triggers TypeError when `get_default_columns` has no `local_only` # keyword. The TypeError is swallowed if QuerySet is actually # evaluated as list generation swallows TypeError in CPython. @@ -286,8 +308,12 @@ class RelatedGeoModelTest(TestCase): def test16_annotated_date_queryset(self): "Ensure annotated date querysets work if spatial backend is used. See #14648." - birth_years = [dt.year for dt in - list(Author.objects.annotate(num_books=Count('books')).dates('dob', 'year'))] + birth_years = [ + dt.year + for dt in list( + Author.objects.annotate(num_books=Count("books")).dates("dob", "year") + ) + ] birth_years.sort() self.assertEqual([1950, 1974], birth_years) diff --git a/tests/gis_tests/test_data.py b/tests/gis_tests/test_data.py index ab6e13f558..0a94907320 100644 --- a/tests/gis_tests/test_data.py +++ b/tests/gis_tests/test_data.py @@ -8,7 +8,7 @@ import os from django.utils.functional import cached_property # Path where reference test data is located. -TEST_DATA = os.path.join(os.path.dirname(__file__), 'data') +TEST_DATA = os.path.join(os.path.dirname(__file__), "data") def tuplize(seq): @@ -24,16 +24,14 @@ def strconvert(d): def get_ds_file(name, ext): - return os.path.join(TEST_DATA, - name, - name + '.%s' % ext - ) + return os.path.join(TEST_DATA, name, name + ".%s" % ext) class TestObj: """ Base testing object, turns keyword args into attributes. """ + def __init__(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) @@ -43,7 +41,8 @@ class TestDS(TestObj): """ Object for testing GDAL data sources. """ - def __init__(self, name, *, ext='shp', **kwargs): + + def __init__(self, name, *, ext="shp", **kwargs): # Shapefile is default extension, unless specified otherwise. self.name = name self.ds = get_ds_file(name, ext) @@ -55,6 +54,7 @@ class TestGeom(TestObj): Testing object used for wrapping reference geometry data in GEOS/GDAL tests. """ + def __init__(self, *, coords=None, centroid=None, ext_ring_cs=None, **kwargs): # Converting lists to tuples of certain keyword args # so coordinate test cases will match (JSON has no @@ -71,6 +71,7 @@ class TestGeomSet: """ Each attribute of this object is a list of `TestGeom` instances. """ + def __init__(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, [TestGeom(**strconvert(kw)) for kw in value]) @@ -81,9 +82,10 @@ class TestDataMixin: Mixin used for GEOS/GDAL test cases that defines a `geometries` property, which returns and/or loads the reference geometry data. """ + @cached_property def geometries(self): # Load up the test geometry data from fixture into global. - with open(os.path.join(TEST_DATA, 'geometries.json')) as f: + with open(os.path.join(TEST_DATA, "geometries.json")) as f: geometries = json.load(f) return TestGeomSet(**strconvert(geometries)) diff --git a/tests/gis_tests/test_fields.py b/tests/gis_tests/test_fields.py index c9e6833767..933514fee7 100644 --- a/tests/gis_tests/test_fields.py +++ b/tests/gis_tests/test_fields.py @@ -6,7 +6,6 @@ from django.test import SimpleTestCase class FieldsTests(SimpleTestCase): - def test_area_field_deepcopy(self): field = AreaField(None) self.assertEqual(copy.deepcopy(field), field) @@ -20,21 +19,38 @@ class GeometryFieldTests(SimpleTestCase): def test_deconstruct_empty(self): field = GeometryField() *_, kwargs = field.deconstruct() - self.assertEqual(kwargs, {'srid': 4326}) + self.assertEqual(kwargs, {"srid": 4326}) def test_deconstruct_values(self): field = GeometryField( srid=4067, dim=3, geography=True, - extent=(50199.4814, 6582464.0358, -50000.0, 761274.6247, 7799839.8902, 50000.0), + extent=( + 50199.4814, + 6582464.0358, + -50000.0, + 761274.6247, + 7799839.8902, + 50000.0, + ), tolerance=0.01, ) *_, kwargs = field.deconstruct() - self.assertEqual(kwargs, { - 'srid': 4067, - 'dim': 3, - 'geography': True, - 'extent': (50199.4814, 6582464.0358, -50000.0, 761274.6247, 7799839.8902, 50000.0), - 'tolerance': 0.01, - }) + self.assertEqual( + kwargs, + { + "srid": 4067, + "dim": 3, + "geography": True, + "extent": ( + 50199.4814, + 6582464.0358, + -50000.0, + 761274.6247, + 7799839.8902, + 50000.0, + ), + "tolerance": 0.01, + }, + ) diff --git a/tests/gis_tests/test_geoforms.py b/tests/gis_tests/test_geoforms.py index a2a3ccae7b..949ad46a41 100644 --- a/tests/gis_tests/test_geoforms.py +++ b/tests/gis_tests/test_geoforms.py @@ -9,11 +9,10 @@ from django.utils.html import escape class GeometryFieldTest(SimpleTestCase): - def test_init(self): "Testing GeometryField initialization with defaults." fld = forms.GeometryField() - for bad_default in ('blah', 3, 'FoO', None, 0): + for bad_default in ("blah", 3, "FoO", None, 0): with self.subTest(bad_default=bad_default): with self.assertRaises(ValidationError): fld.clean(bad_default) @@ -23,7 +22,7 @@ class GeometryFieldTest(SimpleTestCase): # Input that doesn't specify the SRID is assumed to be in the SRID # of the input field. fld = forms.GeometryField(srid=4326) - geom = fld.clean('POINT(5 23)') + geom = fld.clean("POINT(5 23)") self.assertEqual(4326, geom.srid) # Making the field in a different SRID from that of the geometry, and # asserting it transforms. @@ -31,9 +30,13 @@ class GeometryFieldTest(SimpleTestCase): # Different PROJ versions use different transformations, all are # correct as having a 1 meter accuracy. tol = 1 - xform_geom = GEOSGeometry('POINT (951640.547328465 4219369.26171664)', srid=32140) + xform_geom = GEOSGeometry( + "POINT (951640.547328465 4219369.26171664)", srid=32140 + ) # The cleaned geometry is transformed to 32140 (the widget map_srid is 3857). - cleaned_geom = fld.clean('SRID=3857;POINT (-10615777.40976205 3473169.895707852)') + cleaned_geom = fld.clean( + "SRID=3857;POINT (-10615777.40976205 3473169.895707852)" + ) self.assertEqual(cleaned_geom.srid, 32140) self.assertTrue(xform_geom.equals_exact(cleaned_geom, tol)) @@ -52,22 +55,31 @@ class GeometryFieldTest(SimpleTestCase): "Testing GeometryField's handling of different geometry types." # By default, all geometry types are allowed. fld = forms.GeometryField() - for wkt in ('POINT(5 23)', 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'LINESTRING(0 0, 1 1)'): + for wkt in ( + "POINT(5 23)", + "MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))", + "LINESTRING(0 0, 1 1)", + ): with self.subTest(wkt=wkt): # to_python() uses the SRID of OpenLayersWidget if the # converted value doesn't have an SRID. - self.assertEqual(GEOSGeometry(wkt, srid=fld.widget.map_srid), fld.clean(wkt)) + self.assertEqual( + GEOSGeometry(wkt, srid=fld.widget.map_srid), fld.clean(wkt) + ) - pnt_fld = forms.GeometryField(geom_type='POINT') - self.assertEqual(GEOSGeometry('POINT(5 23)', srid=pnt_fld.widget.map_srid), pnt_fld.clean('POINT(5 23)')) + pnt_fld = forms.GeometryField(geom_type="POINT") + self.assertEqual( + GEOSGeometry("POINT(5 23)", srid=pnt_fld.widget.map_srid), + pnt_fld.clean("POINT(5 23)"), + ) # a WKT for any other geom_type will be properly transformed by `to_python` self.assertEqual( - GEOSGeometry('LINESTRING(0 0, 1 1)', srid=pnt_fld.widget.map_srid), - pnt_fld.to_python('LINESTRING(0 0, 1 1)') + GEOSGeometry("LINESTRING(0 0, 1 1)", srid=pnt_fld.widget.map_srid), + pnt_fld.to_python("LINESTRING(0 0, 1 1)"), ) # but rejected by `clean` with self.assertRaises(ValidationError): - pnt_fld.clean('LINESTRING(0 0, 1 1)') + pnt_fld.clean("LINESTRING(0 0, 1 1)") def test_to_python(self): """ @@ -75,14 +87,14 @@ class GeometryFieldTest(SimpleTestCase): a ValidationError. """ good_inputs = [ - 'POINT(5 23)', - 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', - 'LINESTRING(0 0, 1 1)', + "POINT(5 23)", + "MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))", + "LINESTRING(0 0, 1 1)", ] bad_inputs = [ - 'POINT(5)', - 'MULTI POLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', - 'BLAH(0 0, 1 1)', + "POINT(5)", + "MULTI POLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))", + "BLAH(0 0, 1 1)", '{"type": "FeatureCollection", "features": [' '{"geometry": {"type": "Point", "coordinates": [508375, 148905]}, "type": "Feature"}]}', ] @@ -90,7 +102,10 @@ class GeometryFieldTest(SimpleTestCase): # to_python returns the same GEOSGeometry for a WKT for geo_input in good_inputs: with self.subTest(geo_input=geo_input): - self.assertEqual(GEOSGeometry(geo_input, srid=fld.widget.map_srid), fld.to_python(geo_input)) + self.assertEqual( + GEOSGeometry(geo_input, srid=fld.widget.map_srid), + fld.to_python(geo_input), + ) # but raises a ValidationError for any other string for geo_input in bad_inputs: with self.subTest(geo_input=geo_input): @@ -100,21 +115,23 @@ class GeometryFieldTest(SimpleTestCase): def test_to_python_different_map_srid(self): f = forms.GeometryField(widget=OpenLayersWidget) json = '{ "type": "Point", "coordinates": [ 5.0, 23.0 ] }' - self.assertEqual(GEOSGeometry('POINT(5 23)', srid=f.widget.map_srid), f.to_python(json)) + self.assertEqual( + GEOSGeometry("POINT(5 23)", srid=f.widget.map_srid), f.to_python(json) + ) def test_field_with_text_widget(self): class PointForm(forms.Form): pt = forms.PointField(srid=4326, widget=forms.TextInput) form = PointForm() - cleaned_pt = form.fields['pt'].clean('POINT(5 23)') - self.assertEqual(cleaned_pt, GEOSGeometry('POINT(5 23)', srid=4326)) + cleaned_pt = form.fields["pt"].clean("POINT(5 23)") + self.assertEqual(cleaned_pt, GEOSGeometry("POINT(5 23)", srid=4326)) self.assertEqual(4326, cleaned_pt.srid) - with self.assertRaisesMessage(ValidationError, 'Invalid geometry value.'): - form.fields['pt'].clean('POINT(5)') + with self.assertRaisesMessage(ValidationError, "Invalid geometry value."): + form.fields["pt"].clean("POINT(5)") - point = GEOSGeometry('SRID=4326;POINT(5 23)') - form = PointForm(data={'pt': 'POINT(5 23)'}, initial={'pt': point}) + point = GEOSGeometry("SRID=4326;POINT(5 23)") + form = PointForm(data={"pt": "POINT(5 23)"}, initial={"pt": point}) self.assertFalse(form.has_changed()) def test_field_string_value(self): @@ -122,36 +139,39 @@ class GeometryFieldTest(SimpleTestCase): Initialization of a geometry field with a valid/empty/invalid string. Only the invalid string should trigger an error log entry. """ + class PointForm(forms.Form): pt1 = forms.PointField(srid=4326) pt2 = forms.PointField(srid=4326) pt3 = forms.PointField(srid=4326) - form = PointForm({ - 'pt1': 'SRID=4326;POINT(7.3 44)', # valid - 'pt2': '', # empty - 'pt3': 'PNT(0)', # invalid - }) + form = PointForm( + { + "pt1": "SRID=4326;POINT(7.3 44)", # valid + "pt2": "", # empty + "pt3": "PNT(0)", # invalid + } + ) - with self.assertLogs('django.contrib.gis', 'ERROR') as logger_calls: + with self.assertLogs("django.contrib.gis", "ERROR") as logger_calls: output = str(form) # The first point can't use assertInHTML() due to non-deterministic # ordering of the rendered dictionary. - pt1_serialized = re.search(r'<textarea [^>]*>({[^<]+})<', output)[1] - pt1_json = pt1_serialized.replace('"', '"') - pt1_expected = GEOSGeometry(form.data['pt1']).transform(3857, clone=True) + pt1_serialized = re.search(r"<textarea [^>]*>({[^<]+})<", output)[1] + pt1_json = pt1_serialized.replace(""", '"') + pt1_expected = GEOSGeometry(form.data["pt1"]).transform(3857, clone=True) self.assertJSONEqual(pt1_json, pt1_expected.json) self.assertInHTML( '<textarea id="id_pt2" class="vSerializedField required" cols="150"' ' rows="10" name="pt2"></textarea>', - output + output, ) self.assertInHTML( '<textarea id="id_pt3" class="vSerializedField required" cols="150"' ' rows="10" name="pt3"></textarea>', - output + output, ) # Only the invalid PNT(0) triggers an error log entry. # Deserialization is called in form clean and in widget rendering. @@ -159,62 +179,74 @@ class GeometryFieldTest(SimpleTestCase): self.assertEqual( logger_calls.records[0].getMessage(), "Error creating geometry from value 'PNT(0)' (String input " - "unrecognized as WKT EWKT, and HEXEWKB.)" + "unrecognized as WKT EWKT, and HEXEWKB.)", ) class SpecializedFieldTest(SimpleTestCase): def setUp(self): self.geometries = { - 'point': GEOSGeometry("SRID=4326;POINT(9.052734375 42.451171875)"), - 'multipoint': GEOSGeometry("SRID=4326;MULTIPOINT(" - "(13.18634033203125 14.504356384277344)," - "(13.207969665527 14.490966796875)," - "(13.177070617675 14.454917907714))"), - 'linestring': GEOSGeometry("SRID=4326;LINESTRING(" - "-8.26171875 -0.52734375," - "-7.734375 4.21875," - "6.85546875 3.779296875," - "5.44921875 -3.515625)"), - 'multilinestring': GEOSGeometry("SRID=4326;MULTILINESTRING(" - "(-16.435546875 -2.98828125," - "-17.2265625 2.98828125," - "-0.703125 3.515625," - "-1.494140625 -3.33984375)," - "(-8.0859375 -5.9765625," - "8.525390625 -8.7890625," - "12.392578125 -0.87890625," - "10.01953125 7.646484375))"), - 'polygon': GEOSGeometry("SRID=4326;POLYGON(" - "(-1.669921875 6.240234375," - "-3.8671875 -0.615234375," - "5.9765625 -3.955078125," - "18.193359375 3.955078125," - "9.84375 9.4921875," - "-1.669921875 6.240234375))"), - 'multipolygon': GEOSGeometry("SRID=4326;MULTIPOLYGON(" - "((-17.578125 13.095703125," - "-17.2265625 10.8984375," - "-13.974609375 10.1953125," - "-13.359375 12.744140625," - "-15.732421875 13.7109375," - "-17.578125 13.095703125))," - "((-8.525390625 5.537109375," - "-8.876953125 2.548828125," - "-5.888671875 1.93359375," - "-5.09765625 4.21875," - "-6.064453125 6.240234375," - "-8.525390625 5.537109375)))"), - 'geometrycollection': GEOSGeometry("SRID=4326;GEOMETRYCOLLECTION(" - "POINT(5.625 -0.263671875)," - "POINT(6.767578125 -3.603515625)," - "POINT(8.525390625 0.087890625)," - "POINT(8.0859375 -2.13134765625)," - "LINESTRING(" - "6.273193359375 -1.175537109375," - "5.77880859375 -1.812744140625," - "7.27294921875 -2.230224609375," - "7.657470703125 -1.25244140625))"), + "point": GEOSGeometry("SRID=4326;POINT(9.052734375 42.451171875)"), + "multipoint": GEOSGeometry( + "SRID=4326;MULTIPOINT(" + "(13.18634033203125 14.504356384277344)," + "(13.207969665527 14.490966796875)," + "(13.177070617675 14.454917907714))" + ), + "linestring": GEOSGeometry( + "SRID=4326;LINESTRING(" + "-8.26171875 -0.52734375," + "-7.734375 4.21875," + "6.85546875 3.779296875," + "5.44921875 -3.515625)" + ), + "multilinestring": GEOSGeometry( + "SRID=4326;MULTILINESTRING(" + "(-16.435546875 -2.98828125," + "-17.2265625 2.98828125," + "-0.703125 3.515625," + "-1.494140625 -3.33984375)," + "(-8.0859375 -5.9765625," + "8.525390625 -8.7890625," + "12.392578125 -0.87890625," + "10.01953125 7.646484375))" + ), + "polygon": GEOSGeometry( + "SRID=4326;POLYGON(" + "(-1.669921875 6.240234375," + "-3.8671875 -0.615234375," + "5.9765625 -3.955078125," + "18.193359375 3.955078125," + "9.84375 9.4921875," + "-1.669921875 6.240234375))" + ), + "multipolygon": GEOSGeometry( + "SRID=4326;MULTIPOLYGON(" + "((-17.578125 13.095703125," + "-17.2265625 10.8984375," + "-13.974609375 10.1953125," + "-13.359375 12.744140625," + "-15.732421875 13.7109375," + "-17.578125 13.095703125))," + "((-8.525390625 5.537109375," + "-8.876953125 2.548828125," + "-5.888671875 1.93359375," + "-5.09765625 4.21875," + "-6.064453125 6.240234375," + "-8.525390625 5.537109375)))" + ), + "geometrycollection": GEOSGeometry( + "SRID=4326;GEOMETRYCOLLECTION(" + "POINT(5.625 -0.263671875)," + "POINT(6.767578125 -3.603515625)," + "POINT(8.525390625 0.087890625)," + "POINT(8.0859375 -2.13134765625)," + "LINESTRING(" + "6.273193359375 -1.175537109375," + "5.77880859375 -1.812744140625," + "7.27294921875 -2.230224609375," + "7.657470703125 -1.25244140625))" + ), } def assertMapWidget(self, form_instance): @@ -224,15 +256,15 @@ class SpecializedFieldTest(SimpleTestCase): """ self.assertTrue(form_instance.is_valid()) rendered = form_instance.as_p() - self.assertIn('new MapWidget(options);', rendered) - self.assertIn('map_srid: 3857,', rendered) - self.assertIn('gis/js/OLMapWidget.js', str(form_instance.media)) + self.assertIn("new MapWidget(options);", rendered) + self.assertIn("map_srid: 3857,", rendered) + self.assertIn("gis/js/OLMapWidget.js", str(form_instance.media)) def assertTextarea(self, geom, rendered): """Makes sure the wkt and a textarea are in the content""" - self.assertIn('<textarea ', rendered) - self.assertIn('required', rendered) + self.assertIn("<textarea ", rendered) + self.assertIn("required", rendered) ogr = geom.ogr ogr.transform(3857) self.assertIn(escape(ogr.json), rendered) @@ -243,109 +275,121 @@ class SpecializedFieldTest(SimpleTestCase): class PointForm(forms.Form): p = forms.PointField() - geom = self.geometries['point'] - form = PointForm(data={'p': geom}) + geom = self.geometries["point"] + form = PointForm(data={"p": geom}) self.assertTextarea(geom, form.as_p()) self.assertMapWidget(form) self.assertFalse(PointForm().is_valid()) - invalid = PointForm(data={'p': 'some invalid geom'}) + invalid = PointForm(data={"p": "some invalid geom"}) self.assertFalse(invalid.is_valid()) - self.assertIn('Invalid geometry value', str(invalid.errors)) + self.assertIn("Invalid geometry value", str(invalid.errors)) - for invalid in [geo for key, geo in self.geometries.items() if key != 'point']: - self.assertFalse(PointForm(data={'p': invalid.wkt}).is_valid()) + for invalid in [geo for key, geo in self.geometries.items() if key != "point"]: + self.assertFalse(PointForm(data={"p": invalid.wkt}).is_valid()) def test_multipointfield(self): class PointForm(forms.Form): p = forms.MultiPointField() - geom = self.geometries['multipoint'] - form = PointForm(data={'p': geom}) + geom = self.geometries["multipoint"] + form = PointForm(data={"p": geom}) self.assertTextarea(geom, form.as_p()) self.assertMapWidget(form) self.assertFalse(PointForm().is_valid()) - for invalid in [geo for key, geo in self.geometries.items() if key != 'multipoint']: - self.assertFalse(PointForm(data={'p': invalid.wkt}).is_valid()) + for invalid in [ + geo for key, geo in self.geometries.items() if key != "multipoint" + ]: + self.assertFalse(PointForm(data={"p": invalid.wkt}).is_valid()) def test_linestringfield(self): class LineStringForm(forms.Form): f = forms.LineStringField() - geom = self.geometries['linestring'] - form = LineStringForm(data={'f': geom}) + geom = self.geometries["linestring"] + form = LineStringForm(data={"f": geom}) self.assertTextarea(geom, form.as_p()) self.assertMapWidget(form) self.assertFalse(LineStringForm().is_valid()) - for invalid in [geo for key, geo in self.geometries.items() if key != 'linestring']: - self.assertFalse(LineStringForm(data={'p': invalid.wkt}).is_valid()) + for invalid in [ + geo for key, geo in self.geometries.items() if key != "linestring" + ]: + self.assertFalse(LineStringForm(data={"p": invalid.wkt}).is_valid()) def test_multilinestringfield(self): class LineStringForm(forms.Form): f = forms.MultiLineStringField() - geom = self.geometries['multilinestring'] - form = LineStringForm(data={'f': geom}) + geom = self.geometries["multilinestring"] + form = LineStringForm(data={"f": geom}) self.assertTextarea(geom, form.as_p()) self.assertMapWidget(form) self.assertFalse(LineStringForm().is_valid()) - for invalid in [geo for key, geo in self.geometries.items() if key != 'multilinestring']: - self.assertFalse(LineStringForm(data={'p': invalid.wkt}).is_valid()) + for invalid in [ + geo for key, geo in self.geometries.items() if key != "multilinestring" + ]: + self.assertFalse(LineStringForm(data={"p": invalid.wkt}).is_valid()) def test_polygonfield(self): class PolygonForm(forms.Form): p = forms.PolygonField() - geom = self.geometries['polygon'] - form = PolygonForm(data={'p': geom}) + geom = self.geometries["polygon"] + form = PolygonForm(data={"p": geom}) self.assertTextarea(geom, form.as_p()) self.assertMapWidget(form) self.assertFalse(PolygonForm().is_valid()) - for invalid in [geo for key, geo in self.geometries.items() if key != 'polygon']: - self.assertFalse(PolygonForm(data={'p': invalid.wkt}).is_valid()) + for invalid in [ + geo for key, geo in self.geometries.items() if key != "polygon" + ]: + self.assertFalse(PolygonForm(data={"p": invalid.wkt}).is_valid()) def test_multipolygonfield(self): class PolygonForm(forms.Form): p = forms.MultiPolygonField() - geom = self.geometries['multipolygon'] - form = PolygonForm(data={'p': geom}) + geom = self.geometries["multipolygon"] + form = PolygonForm(data={"p": geom}) self.assertTextarea(geom, form.as_p()) self.assertMapWidget(form) self.assertFalse(PolygonForm().is_valid()) - for invalid in [geo for key, geo in self.geometries.items() if key != 'multipolygon']: - self.assertFalse(PolygonForm(data={'p': invalid.wkt}).is_valid()) + for invalid in [ + geo for key, geo in self.geometries.items() if key != "multipolygon" + ]: + self.assertFalse(PolygonForm(data={"p": invalid.wkt}).is_valid()) def test_geometrycollectionfield(self): class GeometryForm(forms.Form): g = forms.GeometryCollectionField() - geom = self.geometries['geometrycollection'] - form = GeometryForm(data={'g': geom}) + geom = self.geometries["geometrycollection"] + form = GeometryForm(data={"g": geom}) self.assertTextarea(geom, form.as_p()) self.assertMapWidget(form) self.assertFalse(GeometryForm().is_valid()) - for invalid in [geo for key, geo in self.geometries.items() if key != 'geometrycollection']: - self.assertFalse(GeometryForm(data={'g': invalid.wkt}).is_valid()) + for invalid in [ + geo for key, geo in self.geometries.items() if key != "geometrycollection" + ]: + self.assertFalse(GeometryForm(data={"g": invalid.wkt}).is_valid()) class OSMWidgetTest(SimpleTestCase): def setUp(self): self.geometries = { - 'point': GEOSGeometry("SRID=4326;POINT(9.052734375 42.451171875)"), + "point": GEOSGeometry("SRID=4326;POINT(9.052734375 42.451171875)"), } def test_osm_widget(self): class PointForm(forms.Form): p = forms.PointField(widget=forms.OSMWidget) - geom = self.geometries['point'] - form = PointForm(data={'p': geom}) + geom = self.geometries["point"] + form = PointForm(data={"p": geom}) rendered = form.as_p() self.assertIn("ol.source.OSM()", rendered) @@ -358,11 +402,13 @@ class OSMWidgetTest(SimpleTestCase): class PointForm(forms.Form): p = forms.PointField( - widget=forms.OSMWidget(attrs={ - 'default_lon': 20, - 'default_lat': 30, - 'default_zoom': 17, - }), + widget=forms.OSMWidget( + attrs={ + "default_lon": 20, + "default_lat": 30, + "default_zoom": 17, + } + ), ) form = PointForm() @@ -374,48 +420,49 @@ class OSMWidgetTest(SimpleTestCase): class GeometryWidgetTests(SimpleTestCase): - def test_get_context_attrs(self): # The Widget.get_context() attrs argument overrides self.attrs. - widget = BaseGeometryWidget(attrs={'geom_type': 'POINT'}) - context = widget.get_context('point', None, attrs={'geom_type': 'POINT2'}) - self.assertEqual(context['geom_type'], 'POINT2') + widget = BaseGeometryWidget(attrs={"geom_type": "POINT"}) + context = widget.get_context("point", None, attrs={"geom_type": "POINT2"}) + self.assertEqual(context["geom_type"], "POINT2") # Widget.get_context() returns expected name for geom_type. - widget = BaseGeometryWidget(attrs={'geom_type': 'POLYGON'}) - context = widget.get_context('polygon', None, None) - self.assertEqual(context['geom_type'], 'Polygon') + widget = BaseGeometryWidget(attrs={"geom_type": "POLYGON"}) + context = widget.get_context("polygon", None, None) + self.assertEqual(context["geom_type"], "Polygon") # Widget.get_context() returns 'Geometry' instead of 'Unknown'. - widget = BaseGeometryWidget(attrs={'geom_type': 'GEOMETRY'}) - context = widget.get_context('geometry', None, None) - self.assertEqual(context['geom_type'], 'Geometry') + widget = BaseGeometryWidget(attrs={"geom_type": "GEOMETRY"}) + context = widget.get_context("geometry", None, None) + self.assertEqual(context["geom_type"], "Geometry") def test_subwidgets(self): widget = forms.BaseGeometryWidget() self.assertEqual( - list(widget.subwidgets('name', 'value')), - [{ - 'is_hidden': False, - 'attrs': { - 'map_srid': 4326, - 'map_width': 600, - 'geom_type': 'GEOMETRY', - 'map_height': 400, - 'display_raw': False, - }, - 'name': 'name', - 'template_name': '', - 'value': 'value', - 'required': False, - }] + list(widget.subwidgets("name", "value")), + [ + { + "is_hidden": False, + "attrs": { + "map_srid": 4326, + "map_width": 600, + "geom_type": "GEOMETRY", + "map_height": 400, + "display_raw": False, + }, + "name": "name", + "template_name": "", + "value": "value", + "required": False, + } + ], ) def test_custom_serialization_widget(self): class CustomGeometryWidget(forms.BaseGeometryWidget): - template_name = 'gis/openlayers.html' + template_name = "gis/openlayers.html" deserialize_called = 0 def serialize(self, value): - return value.json if value else '' + return value.json if value else "" def deserialize(self, value): self.deserialize_called += 1 @@ -425,15 +472,15 @@ class GeometryWidgetTests(SimpleTestCase): p = forms.PointField(widget=CustomGeometryWidget) point = GEOSGeometry("SRID=4326;POINT(9.052734375 42.451171875)") - form = PointForm(data={'p': point}) + form = PointForm(data={"p": point}) self.assertIn(escape(point.json), form.as_p()) CustomGeometryWidget.called = 0 - widget = form.fields['p'].widget + widget = form.fields["p"].widget # Force deserialize use due to a string value - self.assertIn(escape(point.json), widget.render('p', point.json)) + self.assertIn(escape(point.json), widget.render("p", point.json)) self.assertEqual(widget.deserialize_called, 1) - form = PointForm(data={'p': point.json}) + form = PointForm(data={"p": point.json}) self.assertTrue(form.is_valid()) - self.assertEqual(form.cleaned_data['p'].srid, 4326) + self.assertEqual(form.cleaned_data["p"].srid, 4326) diff --git a/tests/gis_tests/test_geoip2.py b/tests/gis_tests/test_geoip2.py index 91bba2e8ff..163ba4887d 100644 --- a/tests/gis_tests/test_geoip2.py +++ b/tests/gis_tests/test_geoip2.py @@ -17,11 +17,11 @@ if HAS_GEOIP2: # 'GeoLite2-City.mmdb'. @skipUnless( HAS_GEOIP2 and getattr(settings, "GEOIP_PATH", None), - "GeoIP is required along with the GEOIP_PATH setting." + "GeoIP is required along with the GEOIP_PATH setting.", ) class GeoIPTest(SimpleTestCase): - addr = '129.237.192.1' - fqdn = 'ku.edu' + addr = "129.237.192.1" + fqdn = "ku.edu" def test01_init(self): "GeoIP initialization." @@ -40,15 +40,15 @@ class GeoIPTest(SimpleTestCase): self.assertTrue(g._city) # Only passing in the location of one database. - city = os.path.join(path, 'GeoLite2-City.mmdb') - cntry = os.path.join(path, 'GeoLite2-Country.mmdb') - g4 = GeoIP2(city, country='') + city = os.path.join(path, "GeoLite2-City.mmdb") + cntry = os.path.join(path, "GeoLite2-Country.mmdb") + g4 = GeoIP2(city, country="") self.assertIsNone(g4._country) - g5 = GeoIP2(cntry, city='') + g5 = GeoIP2(cntry, city="") self.assertIsNone(g5._city) # Improper parameters. - bad_params = (23, 'foo', 15.23) + bad_params = (23, "foo", 15.23) for bad in bad_params: with self.assertRaises(GeoIP2Exception): GeoIP2(cache=bad) @@ -60,19 +60,19 @@ class GeoIPTest(SimpleTestCase): GeoIP2(bad, 0) def test_no_database_file(self): - invalid_path = os.path.join(os.path.dirname(__file__), 'data') - msg = 'Could not load a database from %s.' % invalid_path + invalid_path = os.path.join(os.path.dirname(__file__), "data") + msg = "Could not load a database from %s." % invalid_path with self.assertRaisesMessage(GeoIP2Exception, msg): GeoIP2(invalid_path) def test02_bad_query(self): "GeoIP query parameter checking." - cntry_g = GeoIP2(city='<foo>') + cntry_g = GeoIP2(city="<foo>") # No city database available, these calls should fail. with self.assertRaises(GeoIP2Exception): - cntry_g.city('tmc.edu') + cntry_g.city("tmc.edu") with self.assertRaises(GeoIP2Exception): - cntry_g.coords('tmc.edu') + cntry_g.coords("tmc.edu") # Non-string query should raise TypeError with self.assertRaises(TypeError): @@ -80,92 +80,105 @@ class GeoIPTest(SimpleTestCase): with self.assertRaises(TypeError): cntry_g.country_name(GeoIP2) - @mock.patch('socket.gethostbyname') + @mock.patch("socket.gethostbyname") def test03_country(self, gethostbyname): "GeoIP country querying methods." - gethostbyname.return_value = '128.249.1.1' - g = GeoIP2(city='<foo>') + gethostbyname.return_value = "128.249.1.1" + g = GeoIP2(city="<foo>") for query in (self.fqdn, self.addr): self.assertEqual( - 'US', + "US", g.country_code(query), - 'Failed for func country_code and query %s' % query + "Failed for func country_code and query %s" % query, ) self.assertEqual( - 'United States', + "United States", g.country_name(query), - 'Failed for func country_name and query %s' % query + "Failed for func country_name and query %s" % query, ) self.assertEqual( - {'country_code': 'US', 'country_name': 'United States'}, - g.country(query) + {"country_code": "US", "country_name": "United States"}, + g.country(query), ) - @mock.patch('socket.gethostbyname') + @mock.patch("socket.gethostbyname") def test04_city(self, gethostbyname): "GeoIP city querying methods." - gethostbyname.return_value = '129.237.192.1' - g = GeoIP2(country='<foo>') + gethostbyname.return_value = "129.237.192.1" + g = GeoIP2(country="<foo>") for query in (self.fqdn, self.addr): # Country queries should still work. self.assertEqual( - 'US', + "US", g.country_code(query), - 'Failed for func country_code and query %s' % query + "Failed for func country_code and query %s" % query, ) self.assertEqual( - 'United States', + "United States", g.country_name(query), - 'Failed for func country_name and query %s' % query + "Failed for func country_name and query %s" % query, ) self.assertEqual( - {'country_code': 'US', 'country_name': 'United States'}, - g.country(query) + {"country_code": "US", "country_name": "United States"}, + g.country(query), ) # City information dictionary. d = g.city(query) - self.assertEqual('NA', d['continent_code']) - self.assertEqual('North America', d['continent_name']) - self.assertEqual('US', d['country_code']) - self.assertEqual('Lawrence', d['city']) - self.assertEqual('KS', d['region']) - self.assertEqual('America/Chicago', d['time_zone']) - self.assertFalse(d['is_in_european_union']) + self.assertEqual("NA", d["continent_code"]) + self.assertEqual("North America", d["continent_name"]) + self.assertEqual("US", d["country_code"]) + self.assertEqual("Lawrence", d["city"]) + self.assertEqual("KS", d["region"]) + self.assertEqual("America/Chicago", d["time_zone"]) + self.assertFalse(d["is_in_european_union"]) geom = g.geos(query) self.assertIsInstance(geom, GEOSGeometry) - for e1, e2 in (geom.tuple, g.coords(query), g.lon_lat(query), g.lat_lon(query)): + for e1, e2 in ( + geom.tuple, + g.coords(query), + g.lon_lat(query), + g.lat_lon(query), + ): self.assertIsInstance(e1, float) self.assertIsInstance(e2, float) def test06_ipv6_query(self): "GeoIP can lookup IPv6 addresses." g = GeoIP2() - d = g.city('2002:81ed:c9a5::81ed:c9a5') # IPv6 address for www.nhm.ku.edu - self.assertEqual('US', d['country_code']) - self.assertEqual('Lawrence', d['city']) - self.assertEqual('KS', d['region']) + d = g.city("2002:81ed:c9a5::81ed:c9a5") # IPv6 address for www.nhm.ku.edu + self.assertEqual("US", d["country_code"]) + self.assertEqual("Lawrence", d["city"]) + self.assertEqual("KS", d["region"]) def test_repr(self): path = settings.GEOIP_PATH g = GeoIP2(path=path) meta = g._reader.metadata() - version = '%s.%s' % (meta.binary_format_major_version, meta.binary_format_minor_version) + version = "%s.%s" % ( + meta.binary_format_major_version, + meta.binary_format_minor_version, + ) country_path = g._country_file city_path = g._city_file - expected = '<GeoIP2 [v%(version)s] _country_file="%(country)s", _city_file="%(city)s">' % { - 'version': version, - 'country': country_path, - 'city': city_path, - } + expected = ( + '<GeoIP2 [v%(version)s] _country_file="%(country)s", _city_file="%(city)s">' + % { + "version": version, + "country": country_path, + "city": city_path, + } + ) self.assertEqual(repr(g), expected) - @mock.patch('socket.gethostbyname', return_value='expected') + @mock.patch("socket.gethostbyname", return_value="expected") def test_check_query(self, gethostbyname): g = GeoIP2() - self.assertEqual(g._check_query('127.0.0.1'), '127.0.0.1') - self.assertEqual(g._check_query('2002:81ed:c9a5::81ed:c9a5'), '2002:81ed:c9a5::81ed:c9a5') - self.assertEqual(g._check_query('invalid-ip-address'), 'expected') + self.assertEqual(g._check_query("127.0.0.1"), "127.0.0.1") + self.assertEqual( + g._check_query("2002:81ed:c9a5::81ed:c9a5"), "2002:81ed:c9a5::81ed:c9a5" + ) + self.assertEqual(g._check_query("invalid-ip-address"), "expected") diff --git a/tests/gis_tests/test_gis_tests_utils.py b/tests/gis_tests/test_gis_tests_utils.py index 96eea8a9c2..31b2210805 100644 --- a/tests/gis_tests/test_gis_tests_utils.py +++ b/tests/gis_tests/test_gis_tests_utils.py @@ -11,28 +11,29 @@ def test_mutation(raises=True): output_field = models.IntegerField() def __init__(self): - self.attribute = 'initial' - super().__init__('initial', ['initial']) + self.attribute = "initial" + super().__init__("initial", ["initial"]) def as_sql(self, *args, **kwargs): mutation_func(self) - return '', () + return "", () if raises: - msg = 'TestFunc Func was mutated during compilation.' + msg = "TestFunc Func was mutated during compilation." with test_case_instance.assertRaisesMessage(AssertionError, msg): - getattr(TestFunc(), 'as_' + connection.vendor)(None, None) + getattr(TestFunc(), "as_" + connection.vendor)(None, None) else: - getattr(TestFunc(), 'as_' + connection.vendor)(None, None) + getattr(TestFunc(), "as_" + connection.vendor)(None, None) return test + return wrapper class FuncTestMixinTests(FuncTestMixin, SimpleTestCase): @test_mutation() def test_mutated_attribute(func): - func.attribute = 'mutated' + func.attribute = "mutated" @test_mutation() def test_mutated_expressions(func): @@ -40,11 +41,11 @@ class FuncTestMixinTests(FuncTestMixin, SimpleTestCase): @test_mutation() def test_mutated_expression(func): - func.source_expressions[0].name = 'mutated' + func.source_expressions[0].name = "mutated" @test_mutation() def test_mutated_expression_deep(func): - func.source_expressions[1].value[0] = 'mutated' + func.source_expressions[1].value[0] = "mutated" @test_mutation(raises=False) def test_not_mutated(func): diff --git a/tests/gis_tests/test_measure.py b/tests/gis_tests/test_measure.py index bc2249a816..12c7924b11 100644 --- a/tests/gis_tests/test_measure.py +++ b/tests/gis_tests/test_measure.py @@ -46,7 +46,7 @@ class DistanceTest(unittest.TestCase): def test_access_invalid(self): "Testing access in invalid units" d = D(m=100) - self.assertFalse(hasattr(d, 'banana')) + self.assertFalse(hasattr(d, "banana")) def test_addition(self): "Test addition & subtraction" @@ -109,13 +109,13 @@ class DistanceTest(unittest.TestCase): d2 = D(km=1) d3 = d1 + d2 - self.assertEqual(d3._default_unit, 'm') + self.assertEqual(d3._default_unit, "m") d4 = d2 + d1 - self.assertEqual(d4._default_unit, 'km') + self.assertEqual(d4._default_unit, "km") d5 = d1 * 2 - self.assertEqual(d5._default_unit, 'm') + self.assertEqual(d5._default_unit, "m") d6 = d1 / 2 - self.assertEqual(d6._default_unit, 'm') + self.assertEqual(d6._default_unit, "m") def test_comparisons(self): "Testing comparisons" @@ -133,10 +133,10 @@ class DistanceTest(unittest.TestCase): d1 = D(m=100) d2 = D(km=3.5) - self.assertEqual(str(d1), '100.0 m') - self.assertEqual(str(d2), '3.5 km') - self.assertEqual(repr(d1), 'Distance(m=100.0)') - self.assertEqual(repr(d2), 'Distance(km=3.5)') + self.assertEqual(str(d1), "100.0 m") + self.assertEqual(str(d2), "3.5 km") + self.assertEqual(repr(d1), "Distance(m=100.0)") + self.assertEqual(repr(d2), "Distance(km=3.5)") def test_furlong(self): d = D(m=201.168) @@ -144,9 +144,15 @@ class DistanceTest(unittest.TestCase): def test_unit_att_name(self): "Testing the `unit_attname` class method" - unit_tuple = [('Yard', 'yd'), ('Nautical Mile', 'nm'), ('German legal metre', 'german_m'), - ('Indian yard', 'indian_yd'), ('Chain (Sears)', 'chain_sears'), ('Chain', 'chain'), - ('Furrow Long', 'furlong')] + unit_tuple = [ + ("Yard", "yd"), + ("Nautical Mile", "nm"), + ("German legal metre", "german_m"), + ("Indian yard", "indian_yd"), + ("Chain (Sears)", "chain_sears"), + ("Chain", "chain"), + ("Furrow Long", "furlong"), + ] for nm, att in unit_tuple: with self.subTest(nm=nm): self.assertEqual(att, D.unit_attname(nm)) @@ -188,7 +194,7 @@ class AreaTest(unittest.TestCase): def test_access_invalid_a(self): "Testing access in invalid units" a = A(sq_m=100) - self.assertFalse(hasattr(a, 'banana')) + self.assertFalse(hasattr(a, "banana")) def test_addition(self): "Test addition & subtraction" @@ -251,13 +257,13 @@ class AreaTest(unittest.TestCase): a2 = A(sq_km=1) a3 = a1 + a2 - self.assertEqual(a3._default_unit, 'sq_m') + self.assertEqual(a3._default_unit, "sq_m") a4 = a2 + a1 - self.assertEqual(a4._default_unit, 'sq_km') + self.assertEqual(a4._default_unit, "sq_km") a5 = a1 * 2 - self.assertEqual(a5._default_unit, 'sq_m') + self.assertEqual(a5._default_unit, "sq_m") a6 = a1 / 2 - self.assertEqual(a6._default_unit, 'sq_m') + self.assertEqual(a6._default_unit, "sq_m") def test_comparisons(self): "Testing comparisons" @@ -275,10 +281,10 @@ class AreaTest(unittest.TestCase): a1 = A(sq_m=100) a2 = A(sq_km=3.5) - self.assertEqual(str(a1), '100.0 sq_m') - self.assertEqual(str(a2), '3.5 sq_km') - self.assertEqual(repr(a1), 'Area(sq_m=100.0)') - self.assertEqual(repr(a2), 'Area(sq_km=3.5)') + self.assertEqual(str(a1), "100.0 sq_m") + self.assertEqual(str(a2), "3.5 sq_km") + self.assertEqual(repr(a1), "Area(sq_m=100.0)") + self.assertEqual(repr(a2), "Area(sq_km=3.5)") def test_hash(self): a1 = A(sq_m=100) diff --git a/tests/gis_tests/test_ptr.py b/tests/gis_tests/test_ptr.py index 1d80e24f92..a09679f5b9 100644 --- a/tests/gis_tests/test_ptr.py +++ b/tests/gis_tests/test_ptr.py @@ -6,7 +6,6 @@ from django.test import SimpleTestCase class CPointerBaseTests(SimpleTestCase): - def test(self): destructor_mock = mock.Mock() @@ -41,10 +40,10 @@ class CPointerBaseTests(SimpleTestCase): # results in a TypeError when trying to assign it to the `ptr` property. # Thus, memory addresses (integers) and pointers of the incorrect type # (in `bad_ptrs`) aren't allowed. - bad_ptrs = (5, ctypes.c_char_p(b'foobar')) + bad_ptrs = (5, ctypes.c_char_p(b"foobar")) for bad_ptr in bad_ptrs: for fg in (fg1, fg2): - with self.assertRaisesMessage(TypeError, 'Incompatible pointer type'): + with self.assertRaisesMessage(TypeError, "Incompatible pointer type"): fg.ptr = bad_ptr # Object can be deleted without a destructor set. @@ -60,7 +59,7 @@ class CPointerBaseTests(SimpleTestCase): # The destructor is called if set. fg = FakeGeom2() - ptr = fg.ptr_type(ctypes.c_float(1.)) + ptr = fg.ptr_type(ctypes.c_float(1.0)) fg.ptr = ptr del fg destructor_mock.assert_called_with(ptr) diff --git a/tests/gis_tests/test_spatialrefsys.py b/tests/gis_tests/test_spatialrefsys.py index 548be7f54c..e23ea2ba2a 100644 --- a/tests/gis_tests/test_spatialrefsys.py +++ b/tests/gis_tests/test_spatialrefsys.py @@ -4,20 +4,27 @@ from django.db import connection from django.test import TestCase, skipUnlessDBFeature from django.utils.functional import cached_property -test_srs = ({ - 'srid': 4326, - 'auth_name': ('EPSG', True), - 'auth_srid': 4326, - # Only the beginning, because there are differences depending on installed libs - 'srtext': 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84"', - # +ellps=WGS84 has been removed in the 4326 proj string in proj-4.8 - 'proj_re': r'\+proj=longlat (\+ellps=WGS84 )?(\+datum=WGS84 |\+towgs84=0,0,0,0,0,0,0 )\+no_defs ?', - 'spheroid': 'WGS 84', 'name': 'WGS 84', - 'geographic': True, 'projected': False, 'spatialite': True, - # From proj's "cs2cs -le" and Wikipedia (semi-minor only) - 'ellipsoid': (6378137.0, 6356752.3, 298.257223563), - 'eprec': (1, 1, 9), - 'wkt': re.sub(r'[\s+]', '', """ +test_srs = ( + { + "srid": 4326, + "auth_name": ("EPSG", True), + "auth_srid": 4326, + # Only the beginning, because there are differences depending on installed libs + "srtext": 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84"', + # +ellps=WGS84 has been removed in the 4326 proj string in proj-4.8 + "proj_re": r"\+proj=longlat (\+ellps=WGS84 )?(\+datum=WGS84 |\+towgs84=0,0,0,0,0,0,0 )\+no_defs ?", + "spheroid": "WGS 84", + "name": "WGS 84", + "geographic": True, + "projected": False, + "spatialite": True, + # From proj's "cs2cs -le" and Wikipedia (semi-minor only) + "ellipsoid": (6378137.0, 6356752.3, 298.257223563), + "eprec": (1, 1, 9), + "wkt": re.sub( + r"[\s+]", + "", + """ GEOGCS["WGS 84", DATUM["WGS_1984", SPHEROID["WGS 84",6378137,298.257223563, @@ -28,37 +35,42 @@ test_srs = ({ UNIT["degree",0.01745329251994328, AUTHORITY["EPSG","9122"]], AUTHORITY["EPSG","4326"]] - """) -}, { - 'srid': 32140, - 'auth_name': ('EPSG', False), - 'auth_srid': 32140, - 'srtext': ( - 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",' - 'DATUM["North_American_Datum_1983",SPHEROID["GRS 1980"' - ), - 'proj_re': r'\+proj=lcc (\+lat_1=30.28333333333333? |\+lat_2=28.38333333333333? |\+lat_0=27.83333333333333? |' - r'\+lon_0=-99 ){4}\+x_0=600000 \+y_0=4000000 (\+ellps=GRS80 )?' - r'(\+datum=NAD83 |\+towgs84=0,0,0,0,0,0,0 )?\+units=m \+no_defs ?', - 'spheroid': 'GRS 1980', 'name': 'NAD83 / Texas South Central', - 'geographic': False, 'projected': True, 'spatialite': False, - # From proj's "cs2cs -le" and Wikipedia (semi-minor only) - 'ellipsoid': (6378137.0, 6356752.31414, 298.257222101), - 'eprec': (1, 5, 10), -}) + """, + ), + }, + { + "srid": 32140, + "auth_name": ("EPSG", False), + "auth_srid": 32140, + "srtext": ( + 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",' + 'DATUM["North_American_Datum_1983",SPHEROID["GRS 1980"' + ), + "proj_re": r"\+proj=lcc (\+lat_1=30.28333333333333? |\+lat_2=28.38333333333333? |\+lat_0=27.83333333333333? |" + r"\+lon_0=-99 ){4}\+x_0=600000 \+y_0=4000000 (\+ellps=GRS80 )?" + r"(\+datum=NAD83 |\+towgs84=0,0,0,0,0,0,0 )?\+units=m \+no_defs ?", + "spheroid": "GRS 1980", + "name": "NAD83 / Texas South Central", + "geographic": False, + "projected": True, + "spatialite": False, + # From proj's "cs2cs -le" and Wikipedia (semi-minor only) + "ellipsoid": (6378137.0, 6356752.31414, 298.257222101), + "eprec": (1, 5, 10), + }, +) @skipUnlessDBFeature("has_spatialrefsys_table") class SpatialRefSysTest(TestCase): - @cached_property def SpatialRefSys(self): return connection.ops.connection.ops.spatial_ref_sys() def test_get_units(self): - epsg_4326 = next(f for f in test_srs if f['srid'] == 4326) - unit, unit_name = self.SpatialRefSys().get_units(epsg_4326['wkt']) - self.assertEqual(unit_name, 'degree') + epsg_4326 = next(f for f in test_srs if f["srid"] == 4326) + unit, unit_name = self.SpatialRefSys().get_units(epsg_4326["wkt"]) + self.assertEqual(unit_name, "degree") self.assertAlmostEqual(unit, 0.01745329251994328) def test_retrieve(self): @@ -66,40 +78,40 @@ class SpatialRefSysTest(TestCase): Test retrieval of SpatialRefSys model objects. """ for sd in test_srs: - srs = self.SpatialRefSys.objects.get(srid=sd['srid']) - self.assertEqual(sd['srid'], srs.srid) + srs = self.SpatialRefSys.objects.get(srid=sd["srid"]) + self.assertEqual(sd["srid"], srs.srid) # Some of the authority names are borked on Oracle, e.g., SRID=32140. # also, Oracle Spatial seems to add extraneous info to fields, hence the # the testing with the 'startswith' flag. - auth_name, oracle_flag = sd['auth_name'] + auth_name, oracle_flag = sd["auth_name"] # Compare case-insensitively because srs.auth_name is lowercase # ("epsg") on Spatialite. if not connection.ops.oracle or oracle_flag: self.assertIs(srs.auth_name.upper().startswith(auth_name), True) - self.assertEqual(sd['auth_srid'], srs.auth_srid) + self.assertEqual(sd["auth_srid"], srs.auth_srid) # No PROJ and different srtext on Oracle. if not connection.ops.oracle: - self.assertTrue(srs.wkt.startswith(sd['srtext'])) - self.assertRegex(srs.proj4text, sd['proj_re']) + self.assertTrue(srs.wkt.startswith(sd["srtext"])) + self.assertRegex(srs.proj4text, sd["proj_re"]) def test_osr(self): """ Test getting OSR objects from SpatialRefSys model objects. """ for sd in test_srs: - sr = self.SpatialRefSys.objects.get(srid=sd['srid']) - self.assertTrue(sr.spheroid.startswith(sd['spheroid'])) - self.assertEqual(sd['geographic'], sr.geographic) - self.assertEqual(sd['projected'], sr.projected) - self.assertIs(sr.name.startswith(sd['name']), True) + sr = self.SpatialRefSys.objects.get(srid=sd["srid"]) + self.assertTrue(sr.spheroid.startswith(sd["spheroid"])) + self.assertEqual(sd["geographic"], sr.geographic) + self.assertEqual(sd["projected"], sr.projected) + self.assertIs(sr.name.startswith(sd["name"]), True) # Testing the SpatialReference object directly. if not connection.ops.oracle: srs = sr.srs - self.assertRegex(srs.proj, sd['proj_re']) - self.assertTrue(srs.wkt.startswith(sd['srtext'])) + self.assertRegex(srs.proj, sd["proj_re"]) + self.assertTrue(srs.wkt.startswith(sd["srtext"])) def test_ellipsoid(self): """ @@ -107,17 +119,17 @@ class SpatialRefSysTest(TestCase): """ for sd in test_srs: # Getting the ellipsoid and precision parameters. - ellps1 = sd['ellipsoid'] - prec = sd['eprec'] + ellps1 = sd["ellipsoid"] + prec = sd["eprec"] # Getting our spatial reference and its ellipsoid - srs = self.SpatialRefSys.objects.get(srid=sd['srid']) + srs = self.SpatialRefSys.objects.get(srid=sd["srid"]) ellps2 = srs.ellipsoid for i in range(3): self.assertAlmostEqual(ellps1[i], ellps2[i], prec[i]) - @skipUnlessDBFeature('supports_add_srs_entry') + @skipUnlessDBFeature("supports_add_srs_entry") def test_add_entry(self): """ Test adding a new entry in the SpatialRefSys model using the @@ -126,10 +138,8 @@ class SpatialRefSysTest(TestCase): from django.contrib.gis.utils import add_srs_entry add_srs_entry(3857) - self.assertTrue( - self.SpatialRefSys.objects.filter(srid=3857).exists() - ) + self.assertTrue(self.SpatialRefSys.objects.filter(srid=3857).exists()) srs = self.SpatialRefSys.objects.get(srid=3857) self.assertTrue( - self.SpatialRefSys.get_spheroid(srs.wkt).startswith('SPHEROID[') + self.SpatialRefSys.get_spheroid(srs.wkt).startswith("SPHEROID[") ) diff --git a/tests/gis_tests/tests.py b/tests/gis_tests/tests.py index 2bb95ec3d4..d1c93592a8 100644 --- a/tests/gis_tests/tests.py +++ b/tests/gis_tests/tests.py @@ -4,19 +4,19 @@ from django.core.exceptions import ImproperlyConfigured from django.db import ProgrammingError try: - from django.contrib.gis.db.backends.postgis.operations import ( - PostGISOperations, - ) + from django.contrib.gis.db.backends.postgis.operations import PostGISOperations + HAS_POSTGRES = True except ImportError: HAS_POSTGRES = False if HAS_POSTGRES: + class FakeConnection: def __init__(self): self.settings_dict = { - 'NAME': 'test', + "NAME": "test", } class FakePostGISOperations(PostGISOperations): @@ -25,15 +25,15 @@ if HAS_POSTGRES: self.connection = FakeConnection() def _get_postgis_func(self, func): - if func == 'postgis_lib_version': + if func == "postgis_lib_version": if self.version is None: raise ProgrammingError else: return self.version - elif func == 'version': + elif func == "version": pass else: - raise NotImplementedError('This function was not expected to be called') + raise NotImplementedError("This function was not expected to be called") @unittest.skipUnless(HAS_POSTGRES, "The psycopg2 driver is needed for these tests") @@ -43,34 +43,34 @@ class TestPostGISVersionCheck(unittest.TestCase): """ def test_get_version(self): - expect = '1.0.0' + expect = "1.0.0" ops = FakePostGISOperations(expect) actual = ops.postgis_lib_version() self.assertEqual(expect, actual) def test_version_classic_tuple(self): - expect = ('1.2.3', 1, 2, 3) + expect = ("1.2.3", 1, 2, 3) ops = FakePostGISOperations(expect[0]) actual = ops.postgis_version_tuple() self.assertEqual(expect, actual) def test_version_dev_tuple(self): - expect = ('1.2.3dev', 1, 2, 3) + expect = ("1.2.3dev", 1, 2, 3) ops = FakePostGISOperations(expect[0]) actual = ops.postgis_version_tuple() self.assertEqual(expect, actual) def test_version_loose_tuple(self): - expect = ('1.2.3b1.dev0', 1, 2, 3) + expect = ("1.2.3b1.dev0", 1, 2, 3) ops = FakePostGISOperations(expect[0]) actual = ops.postgis_version_tuple() self.assertEqual(expect, actual) def test_valid_version_numbers(self): versions = [ - ('1.3.0', 1, 3, 0), - ('2.1.1', 2, 1, 1), - ('2.2.0dev', 2, 2, 0), + ("1.3.0", 1, 3, 0), + ("2.1.1", 2, 1, 1), + ("2.2.0dev", 2, 2, 0), ] for version in versions: diff --git a/tests/gis_tests/utils.py b/tests/gis_tests/utils.py index 35c7d366ee..2fbfd438eb 100644 --- a/tests/gis_tests/utils.py +++ b/tests/gis_tests/utils.py @@ -12,25 +12,30 @@ def skipUnlessGISLookup(*gis_lookups): """ Skip a test unless a database supports all of gis_lookups. """ + def decorator(test_func): @wraps(test_func) def skip_wrapper(*args, **kwargs): if any(key not in connection.ops.gis_operators for key in gis_lookups): raise unittest.SkipTest( - "Database doesn't support all the lookups: %s" % ", ".join(gis_lookups) + "Database doesn't support all the lookups: %s" + % ", ".join(gis_lookups) ) return test_func(*args, **kwargs) + return skip_wrapper + return decorator -_default_db = settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'].rsplit('.')[-1] +_default_db = settings.DATABASES[DEFAULT_DB_ALIAS]["ENGINE"].rsplit(".")[-1] # MySQL spatial indices can't handle NULL geometries. -gisfield_may_be_null = _default_db != 'mysql' +gisfield_may_be_null = _default_db != "mysql" class FuncTestMixin: """Assert that Func expressions aren't mutated during their as_sql().""" + def setUp(self): def as_sql_wrapper(original_as_sql): def inner(*args, **kwargs): @@ -40,9 +45,12 @@ class FuncTestMixin: func.output_field __dict__original = copy.deepcopy(func.__dict__) result = original_as_sql(*args, **kwargs) - msg = '%s Func was mutated during compilation.' % func.__class__.__name__ + msg = ( + "%s Func was mutated during compilation." % func.__class__.__name__ + ) self.assertEqual(func.__dict__, __dict__original, msg) return result + return inner def __getattribute__(self, name): @@ -51,12 +59,14 @@ class FuncTestMixin: try: as_sql = __getattribute__original(self, vendor_impl) except AttributeError: - as_sql = __getattribute__original(self, 'as_sql') + as_sql = __getattribute__original(self, "as_sql") return as_sql_wrapper(as_sql) - vendor_impl = 'as_' + connection.vendor + vendor_impl = "as_" + connection.vendor __getattribute__original = Func.__getattribute__ - self.func_patcher = mock.patch.object(Func, '__getattribute__', __getattribute__) + self.func_patcher = mock.patch.object( + Func, "__getattribute__", __getattribute__ + ) self.func_patcher.start() super().setUp() |