1
0
mirror of https://github.com/django/django.git synced 2025-03-12 18:30:48 +00:00

Refs #36036 -- Added support for GEOSHasM.

This commit is contained in:
Andrew Harris 2025-01-10 10:11:03 -05:00 committed by Sarah Boyce
parent 198b30168d
commit 5f30fd2358
7 changed files with 70 additions and 8 deletions

View File

@ -252,9 +252,16 @@ class GEOSGeometryBase(GEOSBase):
@property @property
def hasz(self): def hasz(self):
"Return whether the geometry has a 3D dimension." "Return whether the geometry has a Z dimension."
return capi.geos_hasz(self.ptr) return capi.geos_hasz(self.ptr)
@property
def hasm(self):
"Return whether the geometry has a M dimension."
if geos_version_tuple() < (3, 12):
raise GEOSException("GEOSGeometry.hasm requires GEOS >= 3.12.0.")
return capi.geos_hasm(self.ptr)
@property @property
def ring(self): def ring(self):
"Return whether or not the geometry is a ring." "Return whether or not the geometry is a ring."

View File

@ -52,6 +52,7 @@ from django.contrib.gis.geos.prototypes.predicates import ( # NOQA
geos_equals, geos_equals,
geos_equalsexact, geos_equalsexact,
geos_equalsidentical, geos_equalsidentical,
geos_hasm,
geos_hasz, geos_hasz,
geos_intersects, geos_intersects,
geos_isclosed, geos_isclosed,

View File

@ -24,6 +24,7 @@ class BinaryPredicate(UnaryPredicate):
# ## Unary Predicates ## # ## Unary Predicates ##
geos_hasz = UnaryPredicate("GEOSHasZ") geos_hasz = UnaryPredicate("GEOSHasZ")
geos_hasm = UnaryPredicate("GEOSHasM")
geos_isclosed = UnaryPredicate("GEOSisClosed") geos_isclosed = UnaryPredicate("GEOSisClosed")
geos_isempty = UnaryPredicate("GEOSisEmpty") geos_isempty = UnaryPredicate("GEOSisEmpty")
geos_isring = UnaryPredicate("GEOSisRing") geos_isring = UnaryPredicate("GEOSisRing")

View File

@ -312,7 +312,14 @@ Properties
.. attribute:: GEOSGeometry.hasz .. attribute:: GEOSGeometry.hasz
Returns a boolean indicating whether the geometry is three-dimensional. Returns a boolean indicating whether the geometry has the Z dimension.
.. attribute:: GEOSGeometry.hasm
.. versionadded:: 6.0
Returns a boolean indicating whether the geometry has the M dimension.
Requires GEOS 3.12.
.. attribute:: GEOSGeometry.ring .. attribute:: GEOSGeometry.ring

View File

@ -65,7 +65,8 @@ Minor features
:mod:`django.contrib.gis` :mod:`django.contrib.gis`
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
* ... * The new :attr:`.GEOSGeometry.hasm` property checks whether the geometry has
the M dimension.
:mod:`django.contrib.messages` :mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -1,5 +1,6 @@
import json import json
import pickle import pickle
from unittest import mock, skipIf
from django.contrib.gis.gdal import ( from django.contrib.gis.gdal import (
CoordTransform, CoordTransform,
@ -10,6 +11,7 @@ from django.contrib.gis.gdal import (
) )
from django.contrib.gis.gdal.geometries import CircularString, CurvePolygon from django.contrib.gis.gdal.geometries import CircularString, CurvePolygon
from django.contrib.gis.geos import GEOSException from django.contrib.gis.geos import GEOSException
from django.contrib.gis.geos.libgeos import geos_version_tuple
from django.template import Context from django.template import Context
from django.template.engine import Engine from django.template.engine import Engine
from django.test import SimpleTestCase from django.test import SimpleTestCase
@ -871,12 +873,19 @@ class OGRGeomTest(SimpleTestCase, TestDataMixin):
self.assertEqual(geom.geom_type.name, "PointM") self.assertEqual(geom.geom_type.name, "PointM")
self.assertEqual(geom.geom_type.num, 2001) self.assertEqual(geom.geom_type.num, 2001)
@skipIf(geos_version_tuple() < (3, 12), "GEOS >= 3.12.0 is required")
def test_point_m_dimension_geos(self): def test_point_m_dimension_geos(self):
"""GEOSGeometry does not yet support the M dimension.""" geo_zm = OGRGeometry("POINT ZM (1 2 3 4)")
geom = OGRGeometry("POINT ZM (1 2 3 4)") self.assertEqual(geo_zm.geos.wkt, "POINT ZM (1 2 3 4)")
self.assertEqual(geom.geos.wkt, "POINT Z (1 2 3)") geo_m = OGRGeometry("POINT M (1 2 3)")
geom = OGRGeometry("POINT M (1 2 3)") self.assertEqual(geo_m.geos.wkt, "POINT M (1 2 3)")
self.assertEqual(geom.geos.wkt, "POINT (1 2)")
@mock.patch("django.contrib.gis.geos.libgeos.geos_version", lambda: b"3.11.0")
def test_point_m_dimension_geos_version(self):
geo_zm = OGRGeometry("POINT ZM (1 2 3 4)")
self.assertEqual(geo_zm.geos.wkt, "POINT Z (1 2 3)")
geo_m = OGRGeometry("POINT M (1 2 3)")
self.assertEqual(geo_m.geos.wkt, "POINT (1 2)")
def test_centroid(self): def test_centroid(self):
point = OGRGeometry("POINT (1 2 3)") point = OGRGeometry("POINT (1 2 3)")

View File

@ -86,6 +86,22 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
# Redundant sanity check. # Redundant sanity check.
self.assertEqual(4326, GEOSGeometry(hexewkb_2d).srid) self.assertEqual(4326, GEOSGeometry(hexewkb_2d).srid)
@skipIf(geos_version_tuple() < (3, 12), "GEOS >= 3.12.0 is required")
def test_4d_hexewkb(self):
ogc_hex_4d = (
b"01010000C00000000000000000000000000000"
b"F03F00000000000000400000000000000000"
)
hexewkb_4d = (
b"01010000E0E61000000000000000000000000000000000"
b"F03F00000000000000400000000000000000"
)
pnt_4d = Point(0, 1, 2, 0, srid=4326)
self.assertEqual(ogc_hex_4d, pnt_4d.hex)
self.assertEqual(hexewkb_4d, pnt_4d.hexewkb)
self.assertIs(GEOSGeometry(hexewkb_4d).hasm, True)
self.assertEqual(memoryview(a2b_hex(hexewkb_4d)), pnt_4d.ewkb)
def test_kml(self): def test_kml(self):
"Testing KML output." "Testing KML output."
for tg in self.geometries.wkt_out: for tg in self.geometries.wkt_out:
@ -311,6 +327,20 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
with self.assertRaisesMessage(GEOSException, msg): with self.assertRaisesMessage(GEOSException, msg):
g1.equals_identical(g2) g1.equals_identical(g2)
@skipIf(geos_version_tuple() < (3, 12), "GEOS >= 3.12.0 is required")
def test_hasm(self):
pnt_xym = fromstr("POINT M (5 23 8)")
self.assertTrue(pnt_xym.hasm)
pnt_xyzm = fromstr("POINT (5 23 8 0)")
self.assertTrue(pnt_xyzm.hasm)
@mock.patch("django.contrib.gis.geos.libgeos.geos_version", lambda: b"3.11.0")
def test_hasm_geos_version(self):
p = fromstr("POINT (1 2 3)")
msg = "GEOSGeometry.hasm requires GEOS >= 3.12.0."
with self.assertRaisesMessage(GEOSException, msg):
p.hasm
def test_points(self): def test_points(self):
"Testing Point objects." "Testing Point objects."
prev = fromstr("POINT(0 0)") prev = fromstr("POINT(0 0)")
@ -1255,6 +1285,12 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertEqual(g2.hex, g2.ogr.hex) self.assertEqual(g2.hex, g2.ogr.hex)
self.assertEqual("WGS 84", g2.srs.name) self.assertEqual("WGS 84", g2.srs.name)
@skipIf(geos_version_tuple() < (3, 12), "GEOS >= 3.12.0 is required")
def test_gdal_4d(self):
g1_4d = fromstr("POINT(5 23 8 0)")
self.assertIsInstance(g1_4d.ogr, gdal.OGRGeometry)
self.assertEqual(g1_4d.ogr.m, 0)
def test_copy(self): def test_copy(self):
"Testing use with the Python `copy` module." "Testing use with the Python `copy` module."
import copy import copy