Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions django/contrib/gis/db/backends/base/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def select_extent(self):
"LineLocatePoint",
"MakeValid",
"MemSize",
"NumDimensions",
"NumGeometries",
"NumPoints",
"Perimeter",
Expand Down
1 change: 1 addition & 0 deletions django/contrib/gis/db/backends/mysql/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def unsupported_functions(self):
"LineLocatePoint",
"MakeValid",
"MemSize",
"NumDimensions",
"Perimeter",
"PointOnSurface",
"Reverse",
Expand Down
1 change: 1 addition & 0 deletions django/contrib/gis/db/backends/oracle/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def unsupported_functions(self):
"LineLocatePoint",
"MakeValid",
"MemSize",
"NumDimensions",
"Rotate",
"Scale",
"SnapToGrid",
Expand Down
1 change: 1 addition & 0 deletions django/contrib/gis/db/backends/postgis/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ def function_names(self):
"BoundingCircle": "ST_MinimumBoundingCircle",
"FromWKB": "ST_GeomFromWKB",
"FromWKT": "ST_GeomFromText",
"NumDimensions": "ST_NDims",
"NumPoints": "ST_NPoints",
"GeometryType": "GeometryType",
}
Expand Down
1 change: 1 addition & 0 deletions django/contrib/gis/db/backends/spatialite/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 7 additions & 0 deletions django/contrib/gis/db/models/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 0 additions & 4 deletions django/contrib/gis/gdal/libgdal.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@
"gdal305",
"gdal304",
"gdal303",
"gdal302",
"gdal301",
]
elif os.name == "posix":
# *NIX library names.
Expand All @@ -50,8 +48,6 @@
"gdal3.5.0",
"gdal3.4.0",
"gdal3.3.0",
"gdal3.2.0",
"gdal3.1.0",
]
else:
raise ImproperlyConfigured('GDAL is unsupported on OS "%s".' % os.name)
Expand Down
24 changes: 7 additions & 17 deletions django/contrib/gis/gdal/prototypes/geom.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from ctypes import POINTER, c_char_p, c_double, c_int, c_void_p

from django.contrib.gis.gdal.envelope import OGREnvelope
from django.contrib.gis.gdal.libgdal import GDAL_VERSION, lgdal
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.prototypes.errcheck import check_envelope
from django.contrib.gis.gdal.prototypes.generation import (
bool_output,
Expand Down Expand Up @@ -54,18 +54,11 @@ def topology_func(f):
getm = pnt_func(lgdal.OGR_G_GetM)

# Geometry creation routines.
if GDAL_VERSION >= (3, 3):
from_wkb = geom_output(
lgdal.OGR_G_CreateFromWkbEx,
[c_char_p, c_void_p, POINTER(c_void_p), c_int],
offset=-2,
)
else:
from_wkb = geom_output(
lgdal.OGR_G_CreateFromWkb,
[c_char_p, c_void_p, POINTER(c_void_p), c_int],
offset=-2,
)
from_wkb = geom_output(
lgdal.OGR_G_CreateFromWkbEx,
[c_char_p, c_void_p, POINTER(c_void_p), c_int],
offset=-2,
)
from_wkt = geom_output(
lgdal.OGR_G_CreateFromWkt,
[POINTER(c_char_p), c_void_p, POINTER(c_void_p)],
Expand Down Expand Up @@ -114,10 +107,7 @@ def topology_func(f):
to_gml = string_output(
lgdal.OGR_G_ExportToGML, [c_void_p], str_result=True, decoding="ascii"
)
if GDAL_VERSION >= (3, 3):
get_wkbsize = int_output(lgdal.OGR_G_WkbSizeEx, [c_void_p])
else:
get_wkbsize = int_output(lgdal.OGR_G_WkbSize, [c_void_p])
get_wkbsize = int_output(lgdal.OGR_G_WkbSizeEx, [c_void_p])

# Geometry spatial-reference related routines.
assign_srs = void_output(
Expand Down
2 changes: 2 additions & 0 deletions docs/ref/contrib/gis/db-api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions docs/ref/contrib/gis/functions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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``
-----------------

Expand Down
22 changes: 22 additions & 0 deletions docs/ref/contrib/gis/geoquerysets.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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``
Expand Down
10 changes: 4 additions & 6 deletions docs/ref/contrib/gis/install/geolibs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ Geospatial libraries
GeoDjango uses and/or provides interfaces for the following open source
geospatial libraries:

============================== ==================================== ================================ =============================================================
============================== ==================================== ================================ ===================================================
Program Description Required Supported Versions
============================== ==================================== ================================ =============================================================
============================== ==================================== ================================ ===================================================
:ref:`GEOS <geos-overview>` Geometry Engine Open Source Yes 3.14, 3.13, 3.12, 3.11, 3.10, 3.9
`PROJ`_ Cartographic Projections library Yes (PostgreSQL and SQLite only) 9.x, 8.x, 7.x, 6.x
:ref:`GDAL <gdal-overview>` Geospatial Data Abstraction Library Yes 3.12, 3.11, 3.10, 3.9, 3.8, 3.7, 3.6, 3.5, 3.4, 3.3, 3.2, 3.1
:ref:`GDAL <gdal-overview>` Geospatial Data Abstraction Library Yes 3.12, 3.11, 3.10, 3.9, 3.8, 3.7, 3.6, 3.5, 3.4, 3.3
:ref:`GeoIP <geoip2-overview>` IP-based geolocation library No 2
`PostGIS`__ Spatial extensions for PostgreSQL Yes (PostgreSQL only) 3.6, 3.5, 3.4, 3.3, 3.2
`SpatiaLite`__ Spatial extensions for SQLite Yes (SQLite only) 5.1, 5.0, 4.3
============================== ==================================== ================================ =============================================================
============================== ==================================== ================================ ===================================================

Note that older or more recent versions of these libraries *may* also work
totally fine with GeoDjango. Your mileage may vary.
Expand All @@ -32,8 +32,6 @@ totally fine with GeoDjango. Your mileage may vary.
GEOS 3.12.0 2023-06-27
GEOS 3.13.0 2024-09-06
GEOS 3.14.0 2025-08-21
GDAL 3.1.0 2020-05-07
GDAL 3.2.0 2020-11-02
GDAL 3.3.0 2021-05-03
GDAL 3.4.0 2021-11-04
GDAL 3.5.0 2022-05-13
Expand Down
7 changes: 7 additions & 0 deletions docs/releases/6.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -340,6 +345,8 @@ backends.

* Support for GEOS 3.8 is removed.

* Support for GDAL 3.1 and 3.2 is removed.

:mod:`django.contrib.postgres`
------------------------------

Expand Down
3 changes: 3 additions & 0 deletions tests/delete/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ class RelatedDbOptionGrandParent(models.Model):
class RelatedDbOptionParent(models.Model):
p = models.ForeignKey(RelatedDbOptionGrandParent, models.DB_CASCADE, null=True)

class Meta:
required_db_features = {"supports_on_delete_db_cascade"}


class CascadeDbModel(models.Model):
name = models.CharField(max_length=30)
Expand Down
25 changes: 24 additions & 1 deletion tests/gis_tests/geoapp/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
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
Expand All @@ -28,6 +28,7 @@
Feature,
ManyPointModel,
State,
ThreeDimensionalFeature,
Track,
)

Expand Down Expand Up @@ -608,6 +609,28 @@ def test_num_geom(self):
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)]
Expand Down