summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Smith <smithdc@gmail.com>2024-01-17 06:48:45 +0000
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2024-01-18 10:48:40 +0100
commitcfacd69ab81c37564799a7b8a0a765e3c1f40941 (patch)
tree2633d6af5f3690a7e5bd7ef98b18a136377b2d07
parent12c71bff8300f5b1252dab099f4753dc86903fbd (diff)
Refs #35058 -- Added is_3d and set_3d() to OGRGeometry.
-rw-r--r--django/contrib/gis/gdal/geometries.py27
-rw-r--r--django/contrib/gis/gdal/prototypes/geom.py3
-rw-r--r--django/contrib/gis/utils/layermapping.py4
-rw-r--r--docs/ref/contrib/gis/gdal.txt21
-rw-r--r--docs/releases/5.1.txt6
-rw-r--r--tests/gis_tests/gdal_tests/test_geom.py13
6 files changed, 65 insertions, 9 deletions
diff --git a/django/contrib/gis/gdal/geometries.py b/django/contrib/gis/gdal/geometries.py
index 5094413642..516c6c7113 100644
--- a/django/contrib/gis/gdal/geometries.py
+++ b/django/contrib/gis/gdal/geometries.py
@@ -268,6 +268,20 @@ class OGRGeometry(GDALBase):
"Return the envelope as a 4-tuple, instead of as an Envelope object."
return self.envelope.tuple
+ @property
+ def is_3d(self):
+ """Return True if the geometry has Z coordinates."""
+ return capi.is_3d(self.ptr)
+
+ def set_3d(self, value):
+ """Set if this geometry has Z coordinates."""
+ if value is True:
+ capi.set_3d(self.ptr, 1)
+ elif value is False:
+ capi.set_3d(self.ptr, 0)
+ else:
+ raise ValueError(f"Input to 'set_3d' must be a boolean, got '{value!r}'.")
+
# #### SpatialReference-related Properties ####
# The SRS property
@@ -546,16 +560,15 @@ class Point(OGRGeometry):
@property
def z(self):
"Return the Z coordinate for this Point."
- if self.coord_dim == 3:
+ if self.is_3d:
return capi.getz(self.ptr, 0)
@property
def tuple(self):
"Return the tuple of this point."
- if self.coord_dim == 2:
- return (self.x, self.y)
- elif self.coord_dim == 3:
+ if self.is_3d:
return (self.x, self.y, self.z)
+ return self.x, self.y
coords = tuple
@@ -566,13 +579,13 @@ class LineString(OGRGeometry):
if 0 <= index < self.point_count:
x, y, z = c_double(), c_double(), c_double()
capi.get_point(self.ptr, index, byref(x), byref(y), byref(z))
+ if self.is_3d:
+ return x.value, y.value, z.value
dim = self.coord_dim
if dim == 1:
return (x.value,)
elif dim == 2:
return (x.value, y.value)
- elif dim == 3:
- return (x.value, y.value, z.value)
else:
raise IndexError(
"Index out of range when accessing points of a line string: %s." % index
@@ -609,7 +622,7 @@ class LineString(OGRGeometry):
@property
def z(self):
"Return the Z coordinates in a list."
- if self.coord_dim == 3:
+ if self.is_3d:
return self._listarr(capi.getz)
diff --git a/django/contrib/gis/gdal/prototypes/geom.py b/django/contrib/gis/gdal/prototypes/geom.py
index d5fb1a5c99..aaada31d5b 100644
--- a/django/contrib/gis/gdal/prototypes/geom.py
+++ b/django/contrib/gis/gdal/prototypes/geom.py
@@ -4,6 +4,7 @@ from django.contrib.gis.gdal.envelope import OGREnvelope
from django.contrib.gis.gdal.libgdal import GDAL_VERSION, lgdal
from django.contrib.gis.gdal.prototypes.errcheck import check_envelope
from django.contrib.gis.gdal.prototypes.generation import (
+ bool_output,
const_string_output,
double_output,
geom_output,
@@ -79,6 +80,8 @@ geom_diff = geom_output(lgdal.OGR_G_Difference, [c_void_p, c_void_p])
geom_intersection = geom_output(lgdal.OGR_G_Intersection, [c_void_p, c_void_p])
geom_sym_diff = geom_output(lgdal.OGR_G_SymmetricDifference, [c_void_p, c_void_p])
geom_union = geom_output(lgdal.OGR_G_Union, [c_void_p, c_void_p])
+is_3d = bool_output(lgdal.OGR_G_Is3D, [c_void_p])
+set_3d = void_output(lgdal.OGR_G_Set3D, [c_void_p, c_int], errcheck=False)
# Geometry modification routines.
add_geom = void_output(lgdal.OGR_G_AddGeometry, [c_void_p, c_void_p])
diff --git a/django/contrib/gis/utils/layermapping.py b/django/contrib/gis/utils/layermapping.py
index 2dcf839603..3e33c27772 100644
--- a/django/contrib/gis/utils/layermapping.py
+++ b/django/contrib/gis/utils/layermapping.py
@@ -489,8 +489,8 @@ class LayerMapping:
the mapped shapefile only contains Polygons).
"""
# Downgrade a 3D geom to a 2D one, if necessary.
- if self.coord_dim != geom.coord_dim:
- geom.coord_dim = self.coord_dim
+ if self.coord_dim == 2 and geom.is_3d:
+ geom.set_3d(False)
if self.make_multi(geom.geom_type, model_field):
# Constructing a multi-geometry type to contain the single geometry
diff --git a/docs/ref/contrib/gis/gdal.txt b/docs/ref/contrib/gis/gdal.txt
index c0221a17a9..c98c76cd10 100644
--- a/docs/ref/contrib/gis/gdal.txt
+++ b/docs/ref/contrib/gis/gdal.txt
@@ -556,6 +556,27 @@ coordinate transformation:
Returns or sets the coordinate dimension of this geometry. For example, the
value would be 2 for two-dimensional geometries.
+ .. attribute:: is_3d
+
+ .. versionadded:: 5.1
+
+ A boolean indicating if this geometry has Z coordinates.
+
+ .. method:: set_3d(value)
+
+ .. versionadded:: 5.1
+
+ A method that adds or removes the Z coordinate dimension.
+
+ .. code-block:: pycon
+
+ >>> p = OGRGeometry("POINT (1 2 3)")
+ >>> p.is_3d
+ True
+ >>> p.set_3d(False)
+ >>> p.wkt
+ "POINT (1 2)"
+
.. attribute:: geom_count
Returns the number of elements in this geometry:
diff --git a/docs/releases/5.1.txt b/docs/releases/5.1.txt
index f8393303be..be609cea7f 100644
--- a/docs/releases/5.1.txt
+++ b/docs/releases/5.1.txt
@@ -72,6 +72,12 @@ Minor features
* :class:`~django.contrib.gis.measure.Area` now supports the ``ha`` unit.
+* The new :attr:`.OGRGeometry.is_3d` attribute allows checking if a geometry
+ has a ``Z`` coordinate dimension.
+
+* The new :meth:`.OGRGeometry.set_3d` method allows addition and removal of the
+ ``Z`` coordinate dimension.
+
:mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/gis_tests/gdal_tests/test_geom.py b/tests/gis_tests/gdal_tests/test_geom.py
index 372a385443..eba90504b2 100644
--- a/tests/gis_tests/gdal_tests/test_geom.py
+++ b/tests/gis_tests/gdal_tests/test_geom.py
@@ -714,3 +714,16 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin):
msg = f"Unsupported geometry type: {type_}"
with self.assertRaisesMessage(TypeError, msg):
OGRGeometry(f"{geom_type} EMPTY")
+
+ def test_is_3d_and_set_3d(self):
+ geom = OGRGeometry("POINT (1 2)")
+ self.assertIs(geom.is_3d, False)
+ geom.set_3d(True)
+ self.assertIs(geom.is_3d, True)
+ self.assertEqual(geom.wkt, "POINT (1 2 0)")
+ geom.set_3d(False)
+ self.assertIs(geom.is_3d, False)
+ self.assertEqual(geom.wkt, "POINT (1 2)")
+ msg = "Input to 'set_3d' must be a boolean, got 'None'"
+ with self.assertRaisesMessage(ValueError, msg):
+ geom.set_3d(None)