From 601ffb0da3a8c3900164b356c585650c00d238b0 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Thu, 29 Jun 2023 21:45:36 +0200 Subject: [PATCH] Fixed #34685 -- Dropped support for GEOS 3.6 and 3.7. --- django/contrib/gis/geos/coordseq.py | 12 +----------- django/contrib/gis/geos/geometry.py | 4 +--- django/contrib/gis/geos/prototypes/io.py | 10 ---------- docs/ref/contrib/gis/install/geolibs.txt | 4 +--- docs/releases/5.0.txt | 2 ++ tests/gis_tests/geos_tests/test_geos.py | 21 ++------------------- 6 files changed, 7 insertions(+), 46 deletions(-) diff --git a/django/contrib/gis/geos/coordseq.py b/django/contrib/gis/geos/coordseq.py index 07a3b7d213..0ff4ecb2e5 100644 --- a/django/contrib/gis/geos/coordseq.py +++ b/django/contrib/gis/geos/coordseq.py @@ -8,7 +8,7 @@ from ctypes import byref, c_byte, c_double, c_uint from django.contrib.gis.geos import prototypes as capi from django.contrib.gis.geos.base import GEOSBase from django.contrib.gis.geos.error import GEOSException -from django.contrib.gis.geos.libgeos import CS_PTR, geos_version_tuple +from django.contrib.gis.geos.libgeos import CS_PTR from django.contrib.gis.shortcuts import numpy @@ -202,16 +202,6 @@ class GEOSCoordSeq(GEOSBase): @property def is_counterclockwise(self): """Return whether this coordinate sequence is counterclockwise.""" - if geos_version_tuple() < (3, 7): - # A modified shoelace algorithm to determine polygon orientation. - # See https://en.wikipedia.org/wiki/Shoelace_formula. - area = 0.0 - n = len(self) - for i in range(n): - j = (i + 1) % n - area += self[i][0] * self[j][1] - area -= self[j][0] * self[i][1] - return area > 0.0 ret = c_byte() if not capi.cs_is_ccw(self.ptr, byref(ret)): raise GEOSException( diff --git a/django/contrib/gis/geos/geometry.py b/django/contrib/gis/geos/geometry.py index 63e47aa130..38e5fe554c 100644 --- a/django/contrib/gis/geos/geometry.py +++ b/django/contrib/gis/geos/geometry.py @@ -11,7 +11,7 @@ from django.contrib.gis.geos import prototypes as capi from django.contrib.gis.geos.base import GEOSBase from django.contrib.gis.geos.coordseq import GEOSCoordSeq from django.contrib.gis.geos.error import GEOSException -from django.contrib.gis.geos.libgeos import GEOM_PTR, geos_version_tuple +from django.contrib.gis.geos.libgeos import GEOM_PTR from django.contrib.gis.geos.mutable_list import ListMixin from django.contrib.gis.geos.prepared import PreparedGeometry from django.contrib.gis.geos.prototypes.io import ewkb_w, wkb_r, wkb_w, wkt_r, wkt_w @@ -238,8 +238,6 @@ class GEOSGeometryBase(GEOSBase): Attempt to create a valid representation of a given invalid geometry without losing any of the input vertices. """ - if geos_version_tuple() < (3, 8): - raise GEOSException("GEOSGeometry.make_valid() requires GEOS >= 3.8.0.") return GEOSGeometry(capi.geos_makevalid(self.ptr), srid=self.srid) # #### Unary predicates #### diff --git a/django/contrib/gis/geos/prototypes/io.py b/django/contrib/gis/geos/prototypes/io.py index 7eb6eb8244..f291ec08b6 100644 --- a/django/contrib/gis/geos/prototypes/io.py +++ b/django/contrib/gis/geos/prototypes/io.py @@ -259,24 +259,14 @@ class WKBWriter(IOBase): def write(self, geom): "Return the WKB representation of the given geometry." - from django.contrib.gis.geos import Polygon - geom = self._handle_empty_point(geom) wkb = wkb_writer_write(self.ptr, geom.ptr, byref(c_size_t())) - if self.geos_version < (3, 6, 1) and isinstance(geom, Polygon) and geom.empty: - # Fix GEOS output for empty polygon. - # See https://trac.osgeo.org/geos/ticket/680. - wkb = wkb[:-8] + b"\0" * 4 return memoryview(wkb) def write_hex(self, geom): "Return the HEXEWKB representation of the given geometry." - from django.contrib.gis.geos.polygon import Polygon - geom = self._handle_empty_point(geom) wkb = wkb_writer_write_hex(self.ptr, geom.ptr, byref(c_size_t())) - if self.geos_version < (3, 6, 1) and isinstance(geom, Polygon) and geom.empty: - wkb = wkb[:-16] + b"0" * 8 return wkb # ### WKBWriter Properties ### diff --git a/docs/ref/contrib/gis/install/geolibs.txt b/docs/ref/contrib/gis/install/geolibs.txt index 7478b1bff1..1475d512b0 100644 --- a/docs/ref/contrib/gis/install/geolibs.txt +++ b/docs/ref/contrib/gis/install/geolibs.txt @@ -8,7 +8,7 @@ geospatial libraries: ======================== ==================================== ================================ ====================================== Program Description Required Supported Versions ======================== ==================================== ================================ ====================================== -:doc:`GEOS <../geos>` Geometry Engine Open Source Yes 3.11, 3.10, 3.9, 3.8, 3.7, 3.6 +:doc:`GEOS <../geos>` Geometry Engine Open Source Yes 3.11, 3.10, 3.9, 3.8 `PROJ`_ Cartographic Projections library Yes (PostgreSQL and SQLite only) 9.x, 8.x, 7.x, 6.x, 5.x :doc:`GDAL <../gdal>` Geospatial Data Abstraction Library Yes 3.6, 3.5, 3.4, 3.3, 3.2, 3.1, 3.0, 2.4 :doc:`GeoIP <../geoip2>` IP-based geolocation library No 2 @@ -21,8 +21,6 @@ totally fine with GeoDjango. Your mileage may vary. .. Libs release dates: - 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 GEOS 3.10.0 2021-10-20 diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt index 1227098459..60de6f81e7 100644 --- a/docs/releases/5.0.txt +++ b/docs/releases/5.0.txt @@ -422,6 +422,8 @@ backends. * Support for GDAL 2.2 and 2.3 is removed. +* Support for GEOS 3.6 and 3.7 is removed. + Using ``create_defaults__exact`` may now be required with ``QuerySet.update_or_create()`` ----------------------------------------------------------------------------------------- diff --git a/tests/gis_tests/geos_tests/test_geos.py b/tests/gis_tests/geos_tests/test_geos.py index de2fcf5529..9f1ba6d45f 100644 --- a/tests/gis_tests/geos_tests/test_geos.py +++ b/tests/gis_tests/geos_tests/test_geos.py @@ -5,7 +5,7 @@ import pickle import random from binascii import a2b_hex from io import BytesIO -from unittest import mock, skipIf +from unittest import mock from django.contrib.gis import gdal from django.contrib.gis.geos import ( @@ -393,7 +393,7 @@ class GEOSTest(SimpleTestCase, TestDataMixin): line.ewkt, "SRID=4326;LINESTRING (151.2607 -33.887, 144.963 -37.8143)" ) - def _test_is_counterclockwise(self): + def test_is_counterclockwise(self): lr = LinearRing((0, 0), (1, 0), (0, 1), (0, 0)) self.assertIs(lr.is_counterclockwise, True) lr.reverse() @@ -402,11 +402,6 @@ class GEOSTest(SimpleTestCase, TestDataMixin): with self.assertRaisesMessage(ValueError, msg): LinearRing().is_counterclockwise - @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") def test_is_counterclockwise_geos_error(self): with mock.patch("django.contrib.gis.geos.prototypes.cs_is_ccw") as mocked: mocked.return_value = 0 @@ -415,10 +410,6 @@ class GEOSTest(SimpleTestCase, TestDataMixin): 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") - def test_is_counterclockwise_fallback(self): - self._test_is_counterclockwise() - def test_multilinestring(self): "Testing MultiLineString objects." prev = fromstr("POINT(0 0)") @@ -1543,7 +1534,6 @@ class GEOSTest(SimpleTestCase, TestDataMixin): self.assertEqual(multipoint_2, normalized) self.assertNotEqual(multipoint, normalized) - @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))") self.assertIs(poly.valid, False) @@ -1555,13 +1545,6 @@ 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") - 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))") - with self.assertRaisesMessage(GEOSException, msg): - poly.make_valid() - def test_empty_point(self): p = Point(srid=4326) self.assertEqual(p.ogr.ewkt, p.ewkt)