Browse Source

Fixed #32544 -- Confirmed support for GDAL 3.2 and GEOS 3.9.

Claude Paroz 4 years ago
parent
commit
e3cfba0029

+ 5 - 2
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:

+ 7 - 5
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

+ 1 - 1
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`).

+ 12 - 12
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()."

+ 30 - 22
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))
 

+ 12 - 12
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]