diff --git a/django/contrib/gis/geos/prototypes/io.py b/django/contrib/gis/geos/prototypes/io.py index 20ef8b37ce..efe9ec159f 100644 --- a/django/contrib/gis/geos/prototypes/io.py +++ b/django/contrib/gis/geos/prototypes/io.py @@ -14,6 +14,7 @@ from django.contrib.gis.geos.prototypes.errcheck import ( ) from django.contrib.gis.geos.prototypes.geom import c_uchar_p, geos_char_p from django.utils.encoding import force_bytes +from django.utils.functional import SimpleLazyObject # ### The WKB/WKT Reader/Writer structures and pointers ### @@ -178,17 +179,27 @@ class _WKBReader(IOBase): raise TypeError +def default_trim_value(): + """ + GEOS changed the default value in 3.12.0. Can be replaced by True when + 3.12.0 becomes the minimum supported version. + """ + return geos_version_tuple() >= (3, 12) + + +DEFAULT_TRIM_VALUE = SimpleLazyObject(default_trim_value) + + # ### WKB/WKT Writer Classes ### class WKTWriter(IOBase): _constructor = wkt_writer_create ptr_type = WKT_WRITE_PTR destructor = wkt_writer_destroy - - _trim = False _precision = None def __init__(self, dim=2, trim=False, precision=None): super().__init__() + self._trim = DEFAULT_TRIM_VALUE self.trim = trim if precision is not None: self.precision = precision diff --git a/docs/ref/contrib/gis/install/geolibs.txt b/docs/ref/contrib/gis/install/geolibs.txt index 3615e50fdc..87e78485c4 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 +:doc:`GEOS <../geos>` Geometry Engine Open Source Yes 3.12, 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.7, 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 @@ -25,6 +25,7 @@ totally fine with GeoDjango. Your mileage may vary. GEOS 3.9.0 2020-12-14 GEOS 3.10.0 2021-10-20 GEOS 3.11.0 2022-07-01 + GEOS 3.12.0 2023-06-27 GDAL 2.4.0 2018-12 GDAL 3.0.0 2019-05 GDAL 3.1.0 2020-05-07 diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt index 5a758f8791..b468e19836 100644 --- a/docs/releases/5.0.txt +++ b/docs/releases/5.0.txt @@ -190,6 +190,8 @@ Minor features * Added support for GDAL 3.7. +* Added support for GEOS 3.12. + :mod:`django.contrib.messages` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/gis_tests/geoapp/models.py b/tests/gis_tests/geoapp/models.py index 42619e8a75..2c13c827c6 100644 --- a/tests/gis_tests/geoapp/models.py +++ b/tests/gis_tests/geoapp/models.py @@ -73,6 +73,13 @@ class Feature(NamedModel): geom = models.GeometryField() +class ThreeDimensionalFeature(NamedModel): + geom = models.GeometryField(dim=3) + + class Meta: + required_db_features = {"supports_3d_storage"} + + class MinusOneSRID(models.Model): geom = models.PointField(srid=-1) # Minus one SRID. diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py index e7971c179b..96bf6e1a0b 100644 --- a/tests/gis_tests/geoapp/tests.py +++ b/tests/gis_tests/geoapp/tests.py @@ -31,6 +31,7 @@ from .models import ( NonConcreteModel, PennsylvaniaCity, State, + ThreeDimensionalFeature, Track, ) @@ -252,7 +253,13 @@ class GeoModelTest(TestCase): ] for klass in geometry_classes: g = klass(srid=4326) - feature = Feature.objects.create(name="Empty %s" % klass.__name__, geom=g) + model_class = Feature + if g.hasz: + if not connection.features.supports_3d_storage: + continue + else: + model_class = ThreeDimensionalFeature + feature = model_class.objects.create(name=f"Empty {klass.__name__}", geom=g) feature.refresh_from_db() if klass is LinearRing: # LinearRing isn't representable in WKB, so GEOSGeomtry.wkb diff --git a/tests/gis_tests/relatedapp/tests.py b/tests/gis_tests/relatedapp/tests.py index 7433fc3c15..303d357705 100644 --- a/tests/gis_tests/relatedapp/tests.py +++ b/tests/gis_tests/relatedapp/tests.py @@ -323,14 +323,15 @@ class RelatedGeoModelTest(TestCase): ) city = qs.get(name="Aurora") self.assertEqual( - city.parcel_center.wkt, "MULTIPOINT (1.7128 -2.006, 4.7128 5.006)" + city.parcel_center.wkt, + GEOSGeometry("MULTIPOINT (1.7128 -2.006, 4.7128 5.006)"), ) self.assertIsNone(city.parcel_center_nonexistent) self.assertIn( city.parcel_center_single.wkt, [ - "MULTIPOINT (1.7128 -2.006)", - "POINT (1.7128 -2.006)", # SpatiaLite collapse to POINT. + GEOSGeometry("MULTIPOINT (1.7128 -2.006)"), + GEOSGeometry("POINT (1.7128 -2.006)"), # SpatiaLite collapse to POINT. ], ) @@ -410,8 +411,8 @@ class RelatedGeoModelTest(TestCase): self.assertIn( city.parcel_point_union.wkt, [ - "MULTIPOINT (12.75 10.05, 3.7128 -5.006)", - "MULTIPOINT (3.7128 -5.006, 12.75 10.05)", + GEOSGeometry("MULTIPOINT (12.75 10.05, 3.7128 -5.006)"), + GEOSGeometry("MULTIPOINT (3.7128 -5.006, 12.75 10.05)"), ], ) self.assertIsNone(city.parcel_point_nonexistent)