diff --git a/django/contrib/gis/forms/widgets.py b/django/contrib/gis/forms/widgets.py index aba16f874a..53853b84f3 100644 --- a/django/contrib/gis/forms/widgets.py +++ b/django/contrib/gis/forms/widgets.py @@ -35,7 +35,7 @@ class BaseGeometryWidget(Widget): def deserialize(self, value): try: - return GEOSGeometry(value, self.map_srid) + return GEOSGeometry(value) except (GEOSException, ValueError) as err: logger.error("Error creating geometry from value '%s' (%s)", value, err) return None @@ -48,7 +48,7 @@ class BaseGeometryWidget(Widget): if value: # Check that srid of value and map match - if value.srid != self.map_srid: + if value.srid and value.srid != self.map_srid: try: ogr = value.ogr ogr.transform(self.map_srid) diff --git a/django/contrib/gis/geos/geometry.py b/django/contrib/gis/geos/geometry.py index 55cc2614ad..8e0719f4fa 100644 --- a/django/contrib/gis/geos/geometry.py +++ b/django/contrib/gis/geos/geometry.py @@ -46,6 +46,7 @@ class GEOSGeometry(GEOSBase, ListMixin): The `srid` keyword is used to specify the Source Reference Identifier (SRID) number for this Geometry. If not set, the SRID will be None. """ + input_srid = None if isinstance(geo_input, bytes): geo_input = force_text(geo_input) if isinstance(geo_input, str): @@ -53,7 +54,7 @@ class GEOSGeometry(GEOSBase, ListMixin): if wkt_m: # Handling WKT input. if wkt_m.group('srid'): - srid = int(wkt_m.group('srid')) + input_srid = int(wkt_m.group('srid')) g = wkt_r().read(force_bytes(wkt_m.group('wkt'))) elif hex_regex.match(geo_input): # Handling HEXEWKB input. @@ -75,14 +76,17 @@ class GEOSGeometry(GEOSBase, ListMixin): # Invalid geometry type. raise TypeError('Improper geometry input type: %s' % type(geo_input)) - if g: - # Setting the pointer object with a valid pointer. - self.ptr = g - else: + if not g: raise GEOSException('Could not initialize GEOS Geometry with given input.') + input_srid = input_srid or capi.geos_get_srid(g) or None + if input_srid and srid and input_srid != srid: + raise ValueError('Input geometry already has SRID: %d.' % input_srid) + + # Setting the pointer object with a valid pointer. + self.ptr = g # Post-initialization setup. - self._post_init(srid) + self._post_init(input_srid or srid) def _post_init(self, srid): "Perform post-initialization setup." diff --git a/docs/ref/contrib/gis/geos.txt b/docs/ref/contrib/gis/geos.txt index 68c4a2ac30..0c8139d218 100644 --- a/docs/ref/contrib/gis/geos.txt +++ b/docs/ref/contrib/gis/geos.txt @@ -194,6 +194,27 @@ This is the base class for all GEOS geometry objects. It initializes on the given ``geo_input`` argument, and then assumes the proper geometry subclass (e.g., ``GEOSGeometry('POINT(1 1)')`` will create a :class:`Point` object). +The ``srid`` parameter, if given, is set as the SRID of the created geometry if +``geo_input`` doesn't have an SRID. If different SRIDs are provided through the +``geo_input`` and ``srid`` parameters, ``ValueError`` is raised:: + + >>> from django.contrib.gis.geos import GEOSGeometry + >>> GEOSGeometry('POINT EMPTY', srid=4326).ewkt + 'SRID=4326;POINT EMPTY' + >>> GEOSGeometry('SRID=4326;POINT EMPTY', srid=4326).ewkt + 'SRID=4326;POINT EMPTY' + >>> GEOSGeometry('SRID=1;POINT EMPTY', srid=4326) + Traceback (most recent call last): + ... + ValueError: Input geometry already has SRID: 1. + +.. versionchanged:: 2.0 + + In older versions, the ``srid`` parameter is handled differently for WKT + and WKB input. For WKT, ``srid`` is used only if the input geometry doesn't + have an SRID. For WKB, ``srid`` (if given) replaces the SRID of the input + geometry. + The following input formats, along with their corresponding Python types, are accepted: diff --git a/tests/gis_tests/geos_tests/test_geos.py b/tests/gis_tests/geos_tests/test_geos.py index 53e42e4b2f..72d035cc22 100644 --- a/tests/gis_tests/geos_tests/test_geos.py +++ b/tests/gis_tests/geos_tests/test_geos.py @@ -708,6 +708,14 @@ class GEOSTest(SimpleTestCase, TestDataMixin): pnt_wo_srid = Point(1, 1) pnt_wo_srid.srid = pnt_wo_srid.srid + # Input geometries that have an SRID. + self.assertEqual(GEOSGeometry(pnt.ewkt, srid=pnt.srid).srid, pnt.srid) + self.assertEqual(GEOSGeometry(pnt.ewkb, srid=pnt.srid).srid, pnt.srid) + with self.assertRaisesMessage(ValueError, 'Input geometry already has SRID: %d.' % pnt.srid): + GEOSGeometry(pnt.ewkt, srid=1) + with self.assertRaisesMessage(ValueError, 'Input geometry already has SRID: %d.' % pnt.srid): + GEOSGeometry(pnt.ewkb, srid=1) + @skipUnless(HAS_GDAL, "GDAL is required.") def test_custom_srid(self): """Test with a null srid and a srid unknown to GDAL."""