summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Smith <smithdc@gmail.com>2024-09-23 18:21:02 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2025-11-25 10:10:06 +0100
commitd86802f13fd821f006371b95d888d294297874fd (patch)
tree7487556b06689ca860f4ee0e1fbdc26caf125973
parente3d8f557ff3bab2b37a55db83f71ceb90bc4ebc8 (diff)
Fixed #35783 -- Added NumDimensions GIS database function and __num_dimensions lookup.
-rw-r--r--django/contrib/gis/db/backends/base/operations.py1
-rw-r--r--django/contrib/gis/db/backends/mysql/operations.py1
-rw-r--r--django/contrib/gis/db/backends/oracle/operations.py1
-rw-r--r--django/contrib/gis/db/backends/postgis/operations.py1
-rw-r--r--django/contrib/gis/db/backends/spatialite/operations.py1
-rw-r--r--django/contrib/gis/db/models/functions.py7
-rw-r--r--docs/ref/contrib/gis/db-api.txt2
-rw-r--r--docs/ref/contrib/gis/functions.txt13
-rw-r--r--docs/ref/contrib/gis/geoquerysets.txt22
-rw-r--r--docs/releases/6.1.txt5
-rw-r--r--tests/gis_tests/geoapp/test_functions.py25
11 files changed, 78 insertions, 1 deletions
diff --git a/django/contrib/gis/db/backends/base/operations.py b/django/contrib/gis/db/backends/base/operations.py
index b3bf4383ad..e468c6f610 100644
--- a/django/contrib/gis/db/backends/base/operations.py
+++ b/django/contrib/gis/db/backends/base/operations.py
@@ -62,6 +62,7 @@ class BaseSpatialOperations:
"LineLocatePoint",
"MakeValid",
"MemSize",
+ "NumDimensions",
"NumGeometries",
"NumPoints",
"Perimeter",
diff --git a/django/contrib/gis/db/backends/mysql/operations.py b/django/contrib/gis/db/backends/mysql/operations.py
index b82bd16abb..f48e05b67d 100644
--- a/django/contrib/gis/db/backends/mysql/operations.py
+++ b/django/contrib/gis/db/backends/mysql/operations.py
@@ -98,6 +98,7 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations):
"LineLocatePoint",
"MakeValid",
"MemSize",
+ "NumDimensions",
"Perimeter",
"PointOnSurface",
"Reverse",
diff --git a/django/contrib/gis/db/backends/oracle/operations.py b/django/contrib/gis/db/backends/oracle/operations.py
index 467fc530b3..1c09281809 100644
--- a/django/contrib/gis/db/backends/oracle/operations.py
+++ b/django/contrib/gis/db/backends/oracle/operations.py
@@ -132,6 +132,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
"LineLocatePoint",
"MakeValid",
"MemSize",
+ "NumDimensions",
"Rotate",
"Scale",
"SnapToGrid",
diff --git a/django/contrib/gis/db/backends/postgis/operations.py b/django/contrib/gis/db/backends/postgis/operations.py
index 5a403fd7fe..e0a6dee7be 100644
--- a/django/contrib/gis/db/backends/postgis/operations.py
+++ b/django/contrib/gis/db/backends/postgis/operations.py
@@ -176,6 +176,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
"BoundingCircle": "ST_MinimumBoundingCircle",
"FromWKB": "ST_GeomFromWKB",
"FromWKT": "ST_GeomFromText",
+ "NumDimensions": "ST_NDims",
"NumPoints": "ST_NPoints",
"GeometryType": "GeometryType",
}
diff --git a/django/contrib/gis/db/backends/spatialite/operations.py b/django/contrib/gis/db/backends/spatialite/operations.py
index 54ec249f07..51a04d5c50 100644
--- a/django/contrib/gis/db/backends/spatialite/operations.py
+++ b/django/contrib/gis/db/backends/spatialite/operations.py
@@ -76,6 +76,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
"IsEmpty": "ST_IsEmpty",
"Length": "ST_Length",
"LineLocatePoint": "ST_Line_Locate_Point",
+ "NumDimensions": "ST_NDims",
"NumPoints": "ST_NPoints",
"Reverse": "ST_Reverse",
"Scale": "ScaleCoords",
diff --git a/django/contrib/gis/db/models/functions.py b/django/contrib/gis/db/models/functions.py
index b6ff35858f..54505b136a 100644
--- a/django/contrib/gis/db/models/functions.py
+++ b/django/contrib/gis/db/models/functions.py
@@ -521,6 +521,13 @@ class MemSize(GeoFunc):
arity = 1
+@BaseSpatialField.register_lookup
+class NumDimensions(GeoFuncMixin, Transform):
+ lookup_name = "num_dimensions"
+ output_field = IntegerField()
+ arity = 1
+
+
class NumGeometries(GeoFunc):
output_field = IntegerField()
arity = 1
diff --git a/docs/ref/contrib/gis/db-api.txt b/docs/ref/contrib/gis/db-api.txt
index b51001ecd1..8f062e503d 100644
--- a/docs/ref/contrib/gis/db-api.txt
+++ b/docs/ref/contrib/gis/db-api.txt
@@ -365,6 +365,7 @@ Lookup Type PostGIS Oracle MariaDB MySQL [#]_
:lookup:`intersects` X X X X X B
:lookup:`isempty` X X
:lookup:`isvalid` X X X (≥ 12.0.1) X X
+:lookup:`num_dimensions` X X
:lookup:`overlaps` X X X X X B
:lookup:`relate` X X X X C
:lookup:`same_as` X X X X X B
@@ -420,6 +421,7 @@ Function PostGIS Oracle MariaDB MySQL
:class:`LineLocatePoint` X X
:class:`MakeValid` X X (LWGEOM/RTTOPO)
:class:`MemSize` X
+:class:`NumDimensions` X X
:class:`NumGeometries` X X X X X
:class:`NumPoints` X X X X X
:class:`Perimeter` X X X
diff --git a/docs/ref/contrib/gis/functions.txt b/docs/ref/contrib/gis/functions.txt
index ab540627eb..2c4d344867 100644
--- a/docs/ref/contrib/gis/functions.txt
+++ b/docs/ref/contrib/gis/functions.txt
@@ -669,6 +669,19 @@ Accepts a geographic field or expression and returns its geometry type.
Accepts a single geographic field or expression and returns the memory size
(number of bytes) that the geometry field takes.
+``NumDimensions``
+-----------------
+
+.. versionadded:: 6.1
+
+.. class:: NumDimensions(expression, **extra)
+
+*Availability*: `PostGIS <https://postgis.net/docs/ST_NDims.html>`__,
+SpatiaLite
+
+Accepts a single geometry field or expression and returns the number of
+dimensions used by the geometry.
+
``NumGeometries``
-----------------
diff --git a/docs/ref/contrib/gis/geoquerysets.txt b/docs/ref/contrib/gis/geoquerysets.txt
index 6109bafb4f..70ac9d4d28 100644
--- a/docs/ref/contrib/gis/geoquerysets.txt
+++ b/docs/ref/contrib/gis/geoquerysets.txt
@@ -425,6 +425,28 @@ Oracle ``SDO_GEOMETRY.GET_GTYPE(geom)``
SpatiaLite ``GeometryType(geom)``
========== ==========================
+.. fieldlookup:: num_dimensions
+
+``num_dimensions``
+------------------
+
+.. versionadded:: 6.1
+
+*Availability*: `PostGIS <https://postgis.net/docs/ST_NDims.html>`__,
+SpatiaLite
+
+Returns the number of dimensions used by the geometry.
+
+Example::
+
+ Zipcode.objects.filter(geom__num_dimensions=3)
+
+=================== ==================
+Backend SQL Equivalent
+=================== ==================
+PostGIS, SpatiaLite ``ST_NDims(geom)``
+=================== ==================
+
.. fieldlookup:: overlaps
``overlaps``
diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt
index ae588a3fdb..0d1b6ccdca 100644
--- a/docs/releases/6.1.txt
+++ b/docs/releases/6.1.txt
@@ -118,6 +118,11 @@ Minor features
:class:`IsEmpty() <django.contrib.gis.db.models.functions.IsEmpty>`
database function are now supported on SpatiaLite.
+* The new :lookup:`num_dimensions` lookup and :class:`NumDimensions()
+ <django.contrib.gis.db.models.functions.NumDimensions>` database function
+ allow filtering geometries by the number of dimensions on PostGIS and
+ SpatiaLite.
+
:mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/gis_tests/geoapp/test_functions.py b/tests/gis_tests/geoapp/test_functions.py
index 917e63cd57..a6b072542c 100644
--- a/tests/gis_tests/geoapp/test_functions.py
+++ b/tests/gis_tests/geoapp/test_functions.py
@@ -17,7 +17,7 @@ from django.contrib.gis.geos import (
from django.contrib.gis.geos.libgeos import geos_version_tuple
from django.contrib.gis.measure import Area
from django.db import NotSupportedError, connection
-from django.db.models import IntegerField, Sum, Value
+from django.db.models import F, IntegerField, Sum, Value
from django.test import TestCase, skipUnlessDBFeature
from ..utils import FuncTestMixin
@@ -28,6 +28,7 @@ from .models import (
Feature,
ManyPointModel,
State,
+ ThreeDimensionalFeature,
Track,
)
@@ -608,6 +609,28 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
else:
self.assertEqual(1, city.num_geom)
+ @skipUnlessDBFeature("has_NumDimensions_function")
+ def test_num_dimensions(self):
+ for c in Country.objects.annotate(num_dims=functions.NumDimensions("mpoly")):
+ self.assertEqual(2, c.num_dims)
+
+ ThreeDimensionalFeature.objects.create(
+ name="London", geom=Point(-0.126418, 51.500832, 0)
+ )
+ qs = ThreeDimensionalFeature.objects.annotate(
+ num_dims=functions.NumDimensions("geom")
+ )
+ self.assertEqual(qs[0].num_dims, 3)
+
+ qs = ThreeDimensionalFeature.objects.annotate(
+ num_dims=F("geom__num_dimensions")
+ )
+ self.assertEqual(qs[0].num_dims, 3)
+
+ msg = "'NumDimensions' takes exactly 1 argument (2 given)"
+ with self.assertRaisesMessage(TypeError, msg):
+ Country.objects.annotate(num_dims=functions.NumDimensions("point", "error"))
+
@skipUnlessDBFeature("has_NumPoints_function")
def test_num_points(self):
coords = [(-95.363151, 29.763374), (-95.448601, 29.713803)]