From c26c6f8803f1240c15d83f17205563cb6046c6ed Mon Sep 17 00:00:00 2001
From: David Smith <smithdc@gmail.com>
Date: Sun, 1 Dec 2024 10:25:07 +0000
Subject: [PATCH] Refs #35783 -- Added NumDimensions geographic database
 function.

---
 django/contrib/gis/db/models/functions.py |  4 +++-
 docs/ref/contrib/gis/db-api.txt           |  1 +
 docs/ref/contrib/gis/geoquerysets.txt     | 23 +++++++++++++++++++++++
 tests/gis_tests/geoapp/test_functions.py  |  7 ++++++-
 4 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/django/contrib/gis/db/models/functions.py b/django/contrib/gis/db/models/functions.py
index 89e845a054..ceed7d5507 100644
--- a/django/contrib/gis/db/models/functions.py
+++ b/django/contrib/gis/db/models/functions.py
@@ -491,7 +491,9 @@ class MemSize(GeoFunc):
     arity = 1
 
 
-class NumDimensions(GeoFunc):
+@BaseSpatialField.register_lookup
+class NumDimensions(GeoFuncMixin, Transform):
+    lookup_name = "num_dimensions"
     output_field = IntegerField()
     arity = 1
 
diff --git a/docs/ref/contrib/gis/db-api.txt b/docs/ref/contrib/gis/db-api.txt
index d0700eec00..c5d13728d5 100644
--- a/docs/ref/contrib/gis/db-api.txt
+++ b/docs/ref/contrib/gis/db-api.txt
@@ -361,6 +361,7 @@ Lookup Type                        PostGIS    Oracle   MariaDB    MySQL [#]_   S
 :lookup:`intersects`               X          X        X          X            X          B
 :lookup:`isempty`                  X
 :lookup:`isvalid`                  X          X        X (≥ 11.7) 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
diff --git a/docs/ref/contrib/gis/geoquerysets.txt b/docs/ref/contrib/gis/geoquerysets.txt
index 19411b7304..e6c8b30f16 100644
--- a/docs/ref/contrib/gis/geoquerysets.txt
+++ b/docs/ref/contrib/gis/geoquerysets.txt
@@ -395,6 +395,29 @@ Oracle                               ``SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(p
 
     MariaDB 11.7+ support was added.
 
+
+.. fieldlookup:: num_dimensions
+
+``num_dimensions``
+------------------
+
+.. versionadded:: 5.2
+
+*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(poly)``
+===================  ==================
+
 .. fieldlookup:: overlaps
 
 ``overlaps``
diff --git a/tests/gis_tests/geoapp/test_functions.py b/tests/gis_tests/geoapp/test_functions.py
index 4cfc8e45a4..d11c3c7b4c 100644
--- a/tests/gis_tests/geoapp/test_functions.py
+++ b/tests/gis_tests/geoapp/test_functions.py
@@ -7,7 +7,7 @@ from django.contrib.gis.db.models import GeometryField, PolygonField, functions
 from django.contrib.gis.geos import GEOSGeometry, LineString, Point, Polygon, fromstr
 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
@@ -597,6 +597,11 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
         )
         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"))