From e3cfba0029516aafe40f963378e234df2c0d33bb Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 3 Apr 2021 14:47:27 +0200 Subject: [PATCH] Fixed #32544 -- Confirmed support for GDAL 3.2 and GEOS 3.9. --- django/contrib/gis/gdal/libgdal.py | 7 +++- docs/ref/contrib/gis/install/geolibs.txt | 12 +++--- docs/releases/3.2.1.txt | 2 +- tests/gis_tests/gdal_tests/test_geom.py | 24 +++++------ tests/gis_tests/geoapp/test_functions.py | 52 ++++++++++++++---------- tests/gis_tests/geos_tests/test_geos.py | 24 +++++------ 6 files changed, 67 insertions(+), 54 deletions(-) diff --git a/django/contrib/gis/gdal/libgdal.py b/django/contrib/gis/gdal/libgdal.py index 79408d4858..2580791856 100644 --- a/django/contrib/gis/gdal/libgdal.py +++ b/django/contrib/gis/gdal/libgdal.py @@ -20,12 +20,15 @@ if lib_path: lib_names = None elif os.name == 'nt': # Windows NT shared libraries - lib_names = ['gdal301', 'gdal300', 'gdal204', 'gdal203', 'gdal202', 'gdal201', 'gdal20'] + lib_names = [ + 'gdal302', 'gdal301', 'gdal300', + 'gdal204', 'gdal203', 'gdal202', 'gdal201', 'gdal20', + ] elif os.name == 'posix': # *NIX library names. lib_names = [ 'gdal', 'GDAL', - 'gdal3.1.0', 'gdal3.0.0', + 'gdal3.2.0', 'gdal3.1.0', 'gdal3.0.0', 'gdal2.4.0', 'gdal2.3.0', 'gdal2.2.0', 'gdal2.1.0', 'gdal2.0.0', ] else: diff --git a/docs/ref/contrib/gis/install/geolibs.txt b/docs/ref/contrib/gis/install/geolibs.txt index 46840884bf..818e5abaa4 100644 --- a/docs/ref/contrib/gis/install/geolibs.txt +++ b/docs/ref/contrib/gis/install/geolibs.txt @@ -5,16 +5,16 @@ Installing Geospatial libraries GeoDjango uses and/or provides interfaces for the following open source geospatial libraries: -======================== ==================================== ================================ =================================== +======================== ==================================== ================================ ====================================== Program Description Required Supported Versions -======================== ==================================== ================================ =================================== -:doc:`GEOS <../geos>` Geometry Engine Open Source Yes 3.8, 3.7, 3.6, 3.5 +======================== ==================================== ================================ ====================================== +:doc:`GEOS <../geos>` Geometry Engine Open Source Yes 3.9, 3.8, 3.7, 3.6, 3.5 `PROJ`_ Cartographic Projections library Yes (PostgreSQL and SQLite only) 7.x. 6.x, 5.x, 4.x -:doc:`GDAL <../gdal>` Geospatial Data Abstraction Library Yes 3.1, 3.0, 2.4, 2.3, 2.2, 2.1, 2.0 +:doc:`GDAL <../gdal>` Geospatial Data Abstraction Library Yes 3.2, 3.1, 3.0, 2.4, 2.3, 2.2, 2.1, 2.0 :doc:`GeoIP <../geoip2>` IP-based geolocation library No 2 `PostGIS`__ Spatial extensions for PostgreSQL Yes (PostgreSQL only) 3.0, 2.5, 2.4 `SpatiaLite`__ Spatial extensions for SQLite Yes (SQLite only) 4.3 -======================== ==================================== ================================ =================================== +======================== ==================================== ================================ ====================================== Note that older or more recent versions of these libraries *may* also work totally fine with GeoDjango. Your mileage may vary. @@ -25,6 +25,7 @@ totally fine with GeoDjango. Your mileage may vary. GEOS 3.6.0 2016-10-25 GEOS 3.7.0 2018-09-10 GEOS 3.8.0 2019-10-10 + GEOS 3.9.0 2020-12-14 GDAL 2.0.0 2015-06 GDAL 2.1.0 2016-04 GDAL 2.2.0 2017-05 @@ -32,6 +33,7 @@ totally fine with GeoDjango. Your mileage may vary. GDAL 2.4.0 2018-12 GDAL 3.0.0 2019-05 GDAL 3.1.0 2020-05-07 + GDAL 3.2.0 2020-11-02 PostGIS 2.4.0 2017-09-30 PostGIS 2.5.0 2018-09-23 PostGIS 3.0.0 2019-10-20 diff --git a/docs/releases/3.2.1.txt b/docs/releases/3.2.1.txt index f8425355d8..d6efa42575 100644 --- a/docs/releases/3.2.1.txt +++ b/docs/releases/3.2.1.txt @@ -9,4 +9,4 @@ Django 3.2.1 fixes several bugs in 3.2.0. Bugfixes ======== -* ... +* Corrected detection of GDAL 3.2 on Windows (:ticket:`32544`). diff --git a/tests/gis_tests/gdal_tests/test_geom.py b/tests/gis_tests/gdal_tests/test_geom.py index a9571d583f..68f43e2404 100644 --- a/tests/gis_tests/gdal_tests/test_geom.py +++ b/tests/gis_tests/gdal_tests/test_geom.py @@ -378,10 +378,10 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): b = OGRGeometry(self.geometries.topology_geoms[i].wkt_b) d1 = OGRGeometry(self.geometries.diff_geoms[i].wkt) d2 = a.difference(b) - self.assertEqual(d1, d2) - self.assertEqual(d1, a - b) # __sub__ is difference operator + self.assertTrue(d1.geos.equals(d2.geos)) + self.assertTrue(d1.geos.equals((a - b).geos)) # __sub__ is difference operator a -= b # testing __isub__ - self.assertEqual(d1, a) + self.assertTrue(d1.geos.equals(a.geos)) def test_intersection(self): "Testing intersects() and intersection()." @@ -391,10 +391,10 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): i1 = OGRGeometry(self.geometries.intersect_geoms[i].wkt) self.assertTrue(a.intersects(b)) i2 = a.intersection(b) - self.assertEqual(i1, i2) - self.assertEqual(i1, a & b) # __and__ is intersection operator + self.assertTrue(i1.geos.equals(i2.geos)) + self.assertTrue(i1.geos.equals((a & b).geos)) # __and__ is intersection operator a &= b # testing __iand__ - self.assertEqual(i1, a) + self.assertTrue(i1.geos.equals(a.geos)) def test_symdifference(self): "Testing sym_difference()." @@ -403,10 +403,10 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): b = OGRGeometry(self.geometries.topology_geoms[i].wkt_b) d1 = OGRGeometry(self.geometries.sdiff_geoms[i].wkt) d2 = a.sym_difference(b) - self.assertEqual(d1, d2) - self.assertEqual(d1, a ^ b) # __xor__ is symmetric difference operator + self.assertTrue(d1.geos.equals(d2.geos)) + self.assertTrue(d1.geos.equals((a ^ b).geos)) # __xor__ is symmetric difference operator a ^= b # testing __ixor__ - self.assertEqual(d1, a) + self.assertTrue(d1.geos.equals(a.geos)) def test_union(self): "Testing union()." @@ -415,10 +415,10 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin): b = OGRGeometry(self.geometries.topology_geoms[i].wkt_b) u1 = OGRGeometry(self.geometries.union_geoms[i].wkt) u2 = a.union(b) - self.assertEqual(u1, u2) - self.assertEqual(u1, a | b) # __or__ is union operator + self.assertTrue(u1.geos.equals(u2.geos)) + self.assertTrue(u1.geos.equals((a | b).geos)) # __or__ is union operator a |= b # testing __ior__ - self.assertEqual(u1, a) + self.assertTrue(u1.geos.equals(a.geos)) def test_add(self): "Testing GeometryCollection.add()." diff --git a/tests/gis_tests/geoapp/test_functions.py b/tests/gis_tests/geoapp/test_functions.py index 9cf444935e..22d40a4400 100644 --- a/tests/gis_tests/geoapp/test_functions.py +++ b/tests/gis_tests/geoapp/test_functions.py @@ -89,12 +89,22 @@ class GISFunctionsTests(FuncTestMixin, TestCase): # 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] - self.assertJSONEqual( - City.objects.annotate( - geojson=functions.AsGeoJSON('point', bbox=True, crs=True, precision=5) - ).get(name='Chicago').geojson, - chicago_json, - ) + try: + self.assertJSONEqual( + City.objects.annotate( + 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 + self.assertJSONEqual( + City.objects.annotate( + geojson=functions.AsGeoJSON('point', bbox=True, crs=True, precision=5) + ).get(name='Chicago').geojson, + chicago_json, + ) @skipUnlessDBFeature("has_AsGML_function") def test_asgml(self): @@ -295,11 +305,10 @@ class GISFunctionsTests(FuncTestMixin, TestCase): geom = Point(5, 23, srid=4326) qs = Country.objects.annotate(inter=functions.Intersection('mpoly', geom)) for c in qs: - expected = ( - None if connection.features.empty_intersection_returns_none - else c.mpoly.intersection(geom) - ) - self.assertEqual(c.inter, expected) + if connection.features.empty_intersection_returns_none: + self.assertIsNone(c.inter) + else: + self.assertIs(c.inter.empty, True) @skipUnlessDBFeature("has_IsValid_function") def test_isvalid(self): @@ -352,7 +361,7 @@ class GISFunctionsTests(FuncTestMixin, TestCase): 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.assertEqual(invalid.repaired, 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') def test_make_valid_multipolygon(self): @@ -365,11 +374,11 @@ class GISFunctionsTests(FuncTestMixin, TestCase): repaired=functions.MakeValid('poly'), ).get() self.assertIs(invalid.repaired.valid, True) - self.assertEqual(invalid.repaired, fromstr( + 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') @@ -528,14 +537,14 @@ class GISFunctionsTests(FuncTestMixin, TestCase): def test_transform(self): # Pre-transformed points for Houston and Pueblo. ptown = fromstr('POINT(992363.390841912 481455.395105533)', srid=2774) - prec = 3 # Precision is low due to version variations in PROJ and GDAL. # 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') self.assertEqual(2774, h.pt.srid) - self.assertAlmostEqual(ptown.x, h.pt.x, prec) - self.assertAlmostEqual(ptown.y, h.pt.y, prec) + # Precision is low due to version variations in PROJ and GDAL. + self.assertLess(ptown.x - h.pt.x, 1) + self.assertLess(ptown.y - h.pt.y, 1) @skipUnlessDBFeature("has_Translate_function") def test_translate(self): @@ -569,11 +578,10 @@ class GISFunctionsTests(FuncTestMixin, TestCase): return for c in qs: self.assertTrue(c.mpoly.difference(geom).equals(c.difference)) - expected_intersection = ( - None if connection.features.empty_intersection_returns_none - else c.mpoly.intersection(geom) - ) - self.assertEqual(c.intersection, expected_intersection) + if connection.features.empty_intersection_returns_none: + self.assertIsNone(c.intersection) + else: + self.assertIs(c.intersection.empty, True) self.assertTrue(c.mpoly.sym_difference(geom).equals(c.sym_difference)) self.assertTrue(c.mpoly.union(geom).equals(c.union)) diff --git a/tests/gis_tests/geos_tests/test_geos.py b/tests/gis_tests/geos_tests/test_geos.py index 552ec8396d..ab809b6630 100644 --- a/tests/gis_tests/geos_tests/test_geos.py +++ b/tests/gis_tests/geos_tests/test_geos.py @@ -638,10 +638,10 @@ class GEOSTest(SimpleTestCase, TestDataMixin): i1 = fromstr(self.geometries.intersect_geoms[i].wkt) self.assertIs(a.intersects(b), True) i2 = a.intersection(b) - self.assertEqual(i1, i2) - self.assertEqual(i1, a & b) # __and__ is intersection operator + self.assertTrue(i1.equals(i2)) + self.assertTrue(i1.equals(a & b)) # __and__ is intersection operator a &= b # testing __iand__ - self.assertEqual(i1, a) + self.assertTrue(i1.equals(a)) def test_union(self): "Testing union()." @@ -650,10 +650,10 @@ class GEOSTest(SimpleTestCase, TestDataMixin): b = fromstr(self.geometries.topology_geoms[i].wkt_b) u1 = fromstr(self.geometries.union_geoms[i].wkt) u2 = a.union(b) - self.assertEqual(u1, u2) - self.assertEqual(u1, a | b) # __or__ is union operator + self.assertTrue(u1.equals(u2)) + self.assertTrue(u1.equals(a | b)) # __or__ is union operator a |= b # testing __ior__ - self.assertEqual(u1, a) + self.assertTrue(u1.equals(a)) def test_unary_union(self): "Testing unary_union." @@ -671,10 +671,10 @@ class GEOSTest(SimpleTestCase, TestDataMixin): b = fromstr(self.geometries.topology_geoms[i].wkt_b) d1 = fromstr(self.geometries.diff_geoms[i].wkt) d2 = a.difference(b) - self.assertEqual(d1, d2) - self.assertEqual(d1, a - b) # __sub__ is difference operator + self.assertTrue(d1.equals(d2)) + self.assertTrue(d1.equals(a - b)) # __sub__ is difference operator a -= b # testing __isub__ - self.assertEqual(d1, a) + self.assertTrue(d1.equals(a)) def test_symdifference(self): "Testing sym_difference()." @@ -683,10 +683,10 @@ class GEOSTest(SimpleTestCase, TestDataMixin): b = fromstr(self.geometries.topology_geoms[i].wkt_b) d1 = fromstr(self.geometries.sdiff_geoms[i].wkt) d2 = a.sym_difference(b) - self.assertEqual(d1, d2) - self.assertEqual(d1, a ^ b) # __xor__ is symmetric difference operator + self.assertTrue(d1.equals(d2)) + self.assertTrue(d1.equals(a ^ b)) # __xor__ is symmetric difference operator a ^= b # testing __ixor__ - self.assertEqual(d1, a) + self.assertTrue(d1.equals(a)) def test_buffer(self): bg = self.geometries.buffer_geoms[0]