Skip to content

Commit e20e189

Browse files
smithdc1jacobtylerwalls
authored andcommitted
Refs #33783 -- Added IsEmpty GIS database function and __isempty lookup on SpatiaLite.
1 parent 6fe9663 commit e20e189

7 files changed

Lines changed: 36 additions & 7 deletions

File tree

django/contrib/gis/db/backends/spatialite/operations.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
7373
"ForcePolygonCW": "ST_ForceLHR",
7474
"FromWKB": "ST_GeomFromWKB",
7575
"FromWKT": "ST_GeomFromText",
76+
"IsEmpty": "ST_IsEmpty",
7677
"Length": "ST_Length",
7778
"LineLocatePoint": "ST_Line_Locate_Point",
7879
"NumPoints": "ST_NPoints",
@@ -84,7 +85,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
8485

8586
@cached_property
8687
def unsupported_functions(self):
87-
unsupported = {"GeometryDistance", "IsEmpty", "MemSize", "Rotate"}
88+
unsupported = {"GeometryDistance", "MemSize", "Rotate"}
8889
if not self.geom_lib_version():
8990
unsupported |= {"Azimuth", "GeoHash", "MakeValid"}
9091
if self.spatial_version < (5, 1):

django/contrib/gis/db/models/functions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,10 @@ class IsEmpty(GeoFuncMixin, Transform):
451451
lookup_name = "isempty"
452452
output_field = BooleanField()
453453

454+
def as_sqlite(self, compiler, connection, **extra_context):
455+
sql, params = super().as_sql(compiler, connection, **extra_context)
456+
return "NULLIF(%s, -1)" % sql, params
457+
454458

455459
@BaseSpatialField.register_lookup
456460
class IsValid(OracleToleranceMixin, GeoFuncMixin, Transform):

docs/ref/contrib/gis/db-api.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ Lookup Type PostGIS Oracle MariaDB MySQL [#]_
363363
:lookup:`exact <same_as>` X X X X X B
364364
:lookup:`geom_type` X X (≥ 23c) X X X
365365
:lookup:`intersects` X X X X X B
366-
:lookup:`isempty` X
366+
:lookup:`isempty` X X
367367
:lookup:`isvalid` X X X (≥ 12.0.1) X X
368368
:lookup:`overlaps` X X X X X B
369369
:lookup:`relate` X X X X C
@@ -414,7 +414,7 @@ Function PostGIS Oracle MariaDB MySQL
414414
:class:`GeometryDistance` X
415415
:class:`GeometryType` X X (≥ 23c) X X X
416416
:class:`Intersection` X X X X X
417-
:class:`IsEmpty` X
417+
:class:`IsEmpty` X X
418418
:class:`IsValid` X X X (≥ 12.0.1) X X
419419
:class:`Length` X X X X X
420420
:class:`LineLocatePoint` X X

docs/ref/contrib/gis/functions.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,11 +621,16 @@ Miscellaneous
621621

622622
.. class:: IsEmpty(expr)
623623

624-
*Availability*: `PostGIS <https://postgis.net/docs/ST_IsEmpty.html>`__
624+
*Availability*: `PostGIS <https://postgis.net/docs/ST_IsEmpty.html>`__,
625+
SpatiaLite
625626

626627
Accepts a geographic field or expression and tests if the value is an empty
627628
geometry. Returns ``True`` if its value is empty and ``False`` otherwise.
628629

630+
.. versionchanged:: 6.1
631+
632+
SpatiaLite support was added.
633+
629634
``IsValid``
630635
-----------
631636

docs/ref/contrib/gis/geoquerysets.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,14 +361,19 @@ SpatiaLite ``Intersects(poly, geom)``
361361
``isempty``
362362
-----------
363363

364-
*Availability*: `PostGIS <https://postgis.net/docs/ST_IsEmpty.html>`__
364+
*Availability*: `PostGIS <https://postgis.net/docs/ST_IsEmpty.html>`__,
365+
SpatiaLite
365366

366367
Tests if the geometry is empty.
367368

368369
Example::
369370

370371
Zipcode.objects.filter(poly__isempty=True)
371372

373+
.. versionchanged:: 6.1
374+
375+
SpatiaLite support was added.
376+
372377
.. fieldlookup:: isvalid
373378

374379
``isvalid``

docs/releases/6.1.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ Minor features
5353
:mod:`django.contrib.gis`
5454
~~~~~~~~~~~~~~~~~~~~~~~~~
5555

56-
* ...
56+
* The :lookup:`isempty` lookup and
57+
:class:`IsEmpty() <django.contrib.gis.db.models.functions.IsEmpty>`
58+
database function are now supported on SpatiaLite.
5759

5860
:mod:`django.contrib.messages`
5961
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

tests/gis_tests/geoapp/test_functions.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ def test_intersection(self):
431431
self.assertIs(c.inter.empty, True)
432432

433433
@skipUnlessDBFeature("supports_empty_geometries", "has_IsEmpty_function")
434-
def test_isempty(self):
434+
def test_isempty_geometry_empty(self):
435435
empty = City.objects.create(name="Nowhere", point=Point(srid=4326))
436436
City.objects.create(name="Somewhere", point=Point(6.825, 47.1, srid=4326))
437437
self.assertSequenceEqual(
@@ -442,6 +442,18 @@ def test_isempty(self):
442442
)
443443
self.assertSequenceEqual(City.objects.filter(point__isempty=True), [empty])
444444

445+
@skipUnlessDBFeature("has_IsEmpty_function")
446+
def test_isempty_geometry_null(self):
447+
nowhere = State.objects.create(name="Nowhere", poly=None)
448+
qs = State.objects.annotate(isempty=functions.IsEmpty("poly"))
449+
self.assertSequenceEqual(qs.filter(isempty=None), [nowhere])
450+
self.assertSequenceEqual(
451+
qs.filter(isempty=False).order_by("name").values_list("name", flat=True),
452+
["Colorado", "Kansas"],
453+
)
454+
self.assertSequenceEqual(qs.filter(isempty=True), [])
455+
self.assertSequenceEqual(State.objects.filter(poly__isempty=True), [])
456+
445457
@skipUnlessDBFeature("has_IsValid_function")
446458
def test_isvalid(self):
447459
valid_geom = fromstr("POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))")

0 commit comments

Comments
 (0)