From f605e85af9763a3940369bb79462f2cb466288f6 Mon Sep 17 00:00:00 2001
From: Mariusz Felisiak <felisiak.mariusz@gmail.com>
Date: Thu, 20 Jan 2022 18:54:29 +0100
Subject: [PATCH] Fixed #33453 -- Dropped support for GDAL 2.1.

---
 django/contrib/gis/gdal/field.py          |  1 -
 django/contrib/gis/gdal/libgdal.py        |  4 ++--
 django/contrib/gis/gdal/prototypes/ds.py  |  7 ++-----
 django/contrib/gis/gdal/prototypes/srs.py |  1 -
 docs/ref/contrib/gis/gdal.txt             |  3 +--
 docs/ref/contrib/gis/install/geolibs.txt  |  9 ++++-----
 docs/releases/4.1.txt                     |  5 +++++
 tests/gis_tests/inspectapp/tests.py       | 11 +++++------
 8 files changed, 19 insertions(+), 22 deletions(-)

diff --git a/django/contrib/gis/gdal/field.py b/django/contrib/gis/gdal/field.py
index 8fc731acc6..e803886182 100644
--- a/django/contrib/gis/gdal/field.py
+++ b/django/contrib/gis/gdal/field.py
@@ -225,7 +225,6 @@ OGRFieldTypes = {
     9: OFTDate,
     10: OFTTime,
     11: OFTDateTime,
-    # New 64-bit integer types in GDAL 2
     12: OFTInteger64,
     13: OFTInteger64List,
 }
diff --git a/django/contrib/gis/gdal/libgdal.py b/django/contrib/gis/gdal/libgdal.py
index 05b5732a16..3ff82ef086 100644
--- a/django/contrib/gis/gdal/libgdal.py
+++ b/django/contrib/gis/gdal/libgdal.py
@@ -22,14 +22,14 @@ elif os.name == 'nt':
     # Windows NT shared libraries
     lib_names = [
         'gdal303', 'gdal302', 'gdal301', 'gdal300',
-        'gdal204', 'gdal203', 'gdal202', 'gdal201', 'gdal20',
+        'gdal204', 'gdal203', 'gdal202',
     ]
 elif os.name == 'posix':
     # *NIX library names.
     lib_names = [
         'gdal', 'GDAL',
         'gdal3.3.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',
+        'gdal2.4.0', 'gdal2.3.0', 'gdal2.2.0',
     ]
 else:
     raise ImproperlyConfigured('GDAL is unsupported on OS "%s".' % os.name)
diff --git a/django/contrib/gis/gdal/prototypes/ds.py b/django/contrib/gis/gdal/prototypes/ds.py
index 907589cf86..0729ab3386 100644
--- a/django/contrib/gis/gdal/prototypes/ds.py
+++ b/django/contrib/gis/gdal/prototypes/ds.py
@@ -6,7 +6,7 @@
 from ctypes import POINTER, c_char_p, c_double, c_int, c_long, c_void_p
 
 from django.contrib.gis.gdal.envelope import OGREnvelope
-from django.contrib.gis.gdal.libgdal import GDAL_VERSION, lgdal
+from django.contrib.gis.gdal.libgdal import lgdal
 from django.contrib.gis.gdal.prototypes.generation import (
     bool_output, const_string_output, double_output, geom_output, int64_output,
     int_output, srs_output, void_output, voidptr_output,
@@ -69,10 +69,7 @@ get_field_as_datetime = int_output(
 get_field_as_double = double_output(lgdal.OGR_F_GetFieldAsDouble, [c_void_p, c_int])
 get_field_as_integer = int_output(lgdal.OGR_F_GetFieldAsInteger, [c_void_p, c_int])
 get_field_as_integer64 = int64_output(lgdal.OGR_F_GetFieldAsInteger64, [c_void_p, c_int])
-if GDAL_VERSION >= (2, 2):
-    is_field_set = bool_output(lgdal.OGR_F_IsFieldSetAndNotNull, [c_void_p, c_int])
-else:
-    is_field_set = bool_output(lgdal.OGR_F_IsFieldSet, [c_void_p, c_int])
+is_field_set = bool_output(lgdal.OGR_F_IsFieldSetAndNotNull, [c_void_p, c_int])
 get_field_as_string = const_string_output(lgdal.OGR_F_GetFieldAsString, [c_void_p, c_int])
 get_field_index = int_output(lgdal.OGR_F_GetFieldIndex, [c_void_p, c_char_p])
 
diff --git a/django/contrib/gis/gdal/prototypes/srs.py b/django/contrib/gis/gdal/prototypes/srs.py
index 5d080d8588..c392b7bf2a 100644
--- a/django/contrib/gis/gdal/prototypes/srs.py
+++ b/django/contrib/gis/gdal/prototypes/srs.py
@@ -65,7 +65,6 @@ to_pretty_wkt = string_output(
     [c_void_p, POINTER(c_char_p), c_int], offset=-2, decoding='utf-8'
 )
 
-# Memory leak fixed in GDAL 1.5; still exists in 1.4.
 to_xml = string_output(lgdal.OSRExportToXML, [c_void_p, POINTER(c_char_p), c_char_p], offset=-2, decoding='utf-8')
 
 # String attribute retrieval routines.
diff --git a/docs/ref/contrib/gis/gdal.txt b/docs/ref/contrib/gis/gdal.txt
index a6ddcdd4ce..c7174a3a5a 100644
--- a/docs/ref/contrib/gis/gdal.txt
+++ b/docs/ref/contrib/gis/gdal.txt
@@ -1521,8 +1521,7 @@ blue.
         to mark pixels that are not valid data. Such pixels should generally not
         be displayed, nor contribute to analysis operations.
 
-        To delete an existing "no data" value, set this property to ``None``
-        (requires GDAL ≥ 2.1).
+        To delete an existing "no data" value, set this property to ``None``.
 
     .. method:: datatype(as_string=False)
 
diff --git a/docs/ref/contrib/gis/install/geolibs.txt b/docs/ref/contrib/gis/install/geolibs.txt
index 7e0a0d4ee5..c7436d2dfc 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.10, 3.9, 3.8, 3.7, 3.6
 `PROJ`_                   Cartographic Projections library      Yes (PostgreSQL and SQLite only)  8.x, 7.x, 6.x, 5.x, 4.x
-:doc:`GDAL <../gdal>`     Geospatial Data Abstraction Library   Yes                               3.3, 3.2, 3.1, 3.0, 2.4, 2.3, 2.2, 2.1
+:doc:`GDAL <../gdal>`     Geospatial Data Abstraction Library   Yes                               3.3, 3.2, 3.1, 3.0, 2.4, 2.3, 2.2
 :doc:`GeoIP <../geoip2>`  IP-based geolocation library          No                                2
 `PostGIS`__               Spatial extensions for PostgreSQL     Yes (PostgreSQL only)             3.2, 3.1, 3.0, 2.5, 2.4
 `SpatiaLite`__            Spatial extensions for SQLite         Yes (SQLite only)                 5.0, 4.3
-========================  ====================================  ================================  ======================================
+========================  ====================================  ================================  =================================
 
 Note that older or more recent versions of these libraries *may* also work
 totally fine with GeoDjango. Your mileage may vary.
@@ -26,7 +26,6 @@ totally fine with GeoDjango. Your mileage may vary.
     GEOS 3.8.0 2019-10-10
     GEOS 3.9.0 2020-12-14
     GEOS 3.10.0 2021-10-20
-    GDAL 2.1.0 2016-04
     GDAL 2.2.0 2017-05
     GDAL 2.3.0 2018-05
     GDAL 2.4.0 2018-12
diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt
index 8fc23e6141..c2a13a86b4 100644
--- a/docs/releases/4.1.txt
+++ b/docs/releases/4.1.txt
@@ -310,6 +310,11 @@ backends.
   ``DatabaseOperations.insert_statement()`` method is replaced by
   ``on_conflict`` that accepts ``django.db.models.constants.OnConflict``.
 
+:mod:`django.contrib.gis`
+-------------------------
+
+* Support for GDAL 2.1 is removed.
+
 Dropped support for MariaDB 10.2
 --------------------------------
 
diff --git a/tests/gis_tests/inspectapp/tests.py b/tests/gis_tests/inspectapp/tests.py
index 932d5bd9ab..35337897c0 100644
--- a/tests/gis_tests/inspectapp/tests.py
+++ b/tests/gis_tests/inspectapp/tests.py
@@ -60,7 +60,6 @@ class InspectDbTests(TestCase):
     INSTALLED_APPS={'append': 'django.contrib.gis'},
 )
 class OGRInspectTest(SimpleTestCase):
-    expected_srid = 'srid=-1' if GDAL_VERSION < (2, 2) else ''
     maxDiff = 1024
 
     def test_poly(self):
@@ -76,7 +75,7 @@ class OGRInspectTest(SimpleTestCase):
             '    float = models.FloatField()',
             '    int = models.BigIntegerField()',
             '    str = models.CharField(max_length=80)',
-            '    geom = models.PolygonField(%s)' % self.expected_srid,
+            '    geom = models.PolygonField()',
         ]
 
         self.assertEqual(model_def, '\n'.join(expected))
@@ -84,7 +83,7 @@ class OGRInspectTest(SimpleTestCase):
     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(%s)' % self.expected_srid, model_def)
+        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)
@@ -105,7 +104,7 @@ class OGRInspectTest(SimpleTestCase):
             '    population = models.BigIntegerField()',
             '    density = models.FloatField()',
             '    created = models.DateField()',
-            '    geom = models.PointField(%s)' % self.expected_srid,
+            '    geom = models.PointField()',
         ]
 
         self.assertEqual(model_def, '\n'.join(expected))
@@ -165,7 +164,7 @@ class OGRInspectTest(SimpleTestCase):
 
     def test_mapping_option(self):
         expected = (
-            "    geom = models.PointField(%s)\n"
+            "    geom = models.PointField()\n"
             "\n"
             "\n"
             "# Auto-generated `LayerMapping` dictionary for City model\n"
@@ -175,7 +174,7 @@ class OGRInspectTest(SimpleTestCase):
             "    'density': 'Density',\n"
             "    'created': 'Created',\n"
             "    'geom': 'POINT',\n"
-            "}\n" % self.expected_srid)
+            "}\n")
         shp_file = os.path.join(TEST_DATA, 'cities', 'cities.shp')
         out = StringIO()
         call_command('ogrinspect', shp_file, '--mapping', 'City', stdout=out)