mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #33783 -- Added IsEmpty GIS database function and __isempty lookup on PostGIS.
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						parent
						
							6774e9359c
						
					
				
				
					commit
					2a14b8df39
				
			| @@ -48,6 +48,7 @@ class BaseSpatialOperations: | ||||
|         "GeoHash", | ||||
|         "GeometryDistance", | ||||
|         "Intersection", | ||||
|         "IsEmpty", | ||||
|         "IsValid", | ||||
|         "Length", | ||||
|         "LineLocatePoint", | ||||
|   | ||||
| @@ -72,6 +72,7 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations): | ||||
|             "BoundingCircle", | ||||
|             "ForcePolygonCW", | ||||
|             "GeometryDistance", | ||||
|             "IsEmpty", | ||||
|             "LineLocatePoint", | ||||
|             "MakeValid", | ||||
|             "MemSize", | ||||
|   | ||||
| @@ -122,6 +122,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations): | ||||
|         "ForcePolygonCW", | ||||
|         "GeoHash", | ||||
|         "GeometryDistance", | ||||
|         "IsEmpty", | ||||
|         "LineLocatePoint", | ||||
|         "MakeValid", | ||||
|         "MemSize", | ||||
|   | ||||
| @@ -78,7 +78,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations): | ||||
|  | ||||
|     @cached_property | ||||
|     def unsupported_functions(self): | ||||
|         unsupported = {"BoundingCircle", "GeometryDistance", "MemSize"} | ||||
|         unsupported = {"BoundingCircle", "GeometryDistance", "IsEmpty", "MemSize"} | ||||
|         if not self.geom_lib_version(): | ||||
|             unsupported |= {"Azimuth", "GeoHash", "MakeValid"} | ||||
|         return unsupported | ||||
|   | ||||
| @@ -381,6 +381,12 @@ class Intersection(OracleToleranceMixin, GeomOutputGeoFunc): | ||||
|     geom_param_pos = (0, 1) | ||||
|  | ||||
|  | ||||
| @BaseSpatialField.register_lookup | ||||
| class IsEmpty(GeoFuncMixin, Transform): | ||||
|     lookup_name = "isempty" | ||||
|     output_field = BooleanField() | ||||
|  | ||||
|  | ||||
| @BaseSpatialField.register_lookup | ||||
| class IsValid(OracleToleranceMixin, GeoFuncMixin, Transform): | ||||
|     lookup_name = "isvalid" | ||||
|   | ||||
| @@ -316,6 +316,7 @@ Lookup Type                        PostGIS    Oracle   MariaDB   MySQL [#]_   Sp | ||||
| :lookup:`equals`                   X          X        X         X            X          C | ||||
| :lookup:`exact <same_as>`          X          X        X         X            X          B | ||||
| :lookup:`intersects`               X          X        X         X            X          B | ||||
| :lookup:`isempty`                  X | ||||
| :lookup:`isvalid`                  X          X                  X            X | ||||
| :lookup:`overlaps`                 X          X        X         X            X          B | ||||
| :lookup:`relate`                   X          X        X                      X          C | ||||
| @@ -361,6 +362,7 @@ Function                              PostGIS  Oracle         MariaDB      MySQL | ||||
| :class:`ForcePolygonCW`               X                                                X | ||||
| :class:`GeoHash`                      X                                    X           X (LWGEOM/RTTOPO) | ||||
| :class:`Intersection`                 X        X              X            X           X | ||||
| :class:`IsEmpty`                      X | ||||
| :class:`IsValid`                      X        X                           X           X | ||||
| :class:`Length`                       X        X              X            X           X | ||||
| :class:`LineLocatePoint`              X                                                X | ||||
|   | ||||
| @@ -23,11 +23,11 @@ Function's summary: | ||||
| =========================  ========================  ======================  =======================  ==================  ===================== | ||||
| Measurement                Relationships             Operations              Editors                  Output format       Miscellaneous | ||||
| =========================  ========================  ======================  =======================  ==================  ===================== | ||||
| :class:`Area`              :class:`Azimuth`          :class:`Difference`     :class:`ForcePolygonCW`  :class:`AsGeoJSON`  :class:`IsValid` | ||||
| :class:`Distance`          :class:`BoundingCircle`   :class:`Intersection`   :class:`MakeValid`       :class:`AsGML`      :class:`MemSize` | ||||
| :class:`GeometryDistance`  :class:`Centroid`         :class:`SymDifference`  :class:`Reverse`         :class:`AsKML`      :class:`NumGeometries` | ||||
| :class:`Length`            :class:`Envelope`         :class:`Union`          :class:`Scale`           :class:`AsSVG`      :class:`NumPoints` | ||||
| :class:`Perimeter`         :class:`LineLocatePoint`                          :class:`SnapToGrid`      :class:`AsWKB` | ||||
| :class:`Area`              :class:`Azimuth`          :class:`Difference`     :class:`ForcePolygonCW`  :class:`AsGeoJSON`  :class:`IsEmpty` | ||||
| :class:`Distance`          :class:`BoundingCircle`   :class:`Intersection`   :class:`MakeValid`       :class:`AsGML`      :class:`IsValid` | ||||
| :class:`GeometryDistance`  :class:`Centroid`         :class:`SymDifference`  :class:`Reverse`         :class:`AsKML`      :class:`MemSize` | ||||
| :class:`Length`            :class:`Envelope`         :class:`Union`          :class:`Scale`           :class:`AsSVG`      :class:`NumGeometries` | ||||
| :class:`Perimeter`         :class:`LineLocatePoint`                          :class:`SnapToGrid`      :class:`AsWKB`      :class:`NumPoints` | ||||
| ..                         :class:`PointOnSurface`                           :class:`Transform`       :class:`AsWKT` | ||||
| ..                                                                           :class:`Translate`       :class:`GeoHash` | ||||
| =========================  ========================  ======================  =======================  ==================  ===================== | ||||
| @@ -368,6 +368,18 @@ it provides index-assisted nearest-neighbor result sets. | ||||
| Accepts two geographic fields or expressions and returns the geometric | ||||
| intersection between them. | ||||
|  | ||||
| ``IsEmpty`` | ||||
| =========== | ||||
|  | ||||
| .. versionadded:: 4.2 | ||||
|  | ||||
| .. class:: IsEmpty(expr) | ||||
|  | ||||
| *Availability*: `PostGIS <https://postgis.net/docs/ST_IsEmpty.html>`__ | ||||
|  | ||||
| Accepts a geographic field or expression and tests if the value is an empty | ||||
| geometry. Returns ``True`` if its value is empty and ``False`` otherwise. | ||||
|  | ||||
| ``IsValid`` | ||||
| =========== | ||||
|  | ||||
|   | ||||
| @@ -346,6 +346,21 @@ MySQL       ``ST_Intersects(poly, geom)`` | ||||
| SpatiaLite  ``Intersects(poly, geom)`` | ||||
| ==========  ================================================= | ||||
|  | ||||
| .. fieldlookup:: isempty | ||||
|  | ||||
| ``isempty`` | ||||
| ----------- | ||||
|  | ||||
| .. versionadded:: 4.2 | ||||
|  | ||||
| *Availability*: `PostGIS <https://postgis.net/docs/ST_IsEmpty.html>`__ | ||||
|  | ||||
| Tests if the geometry is empty. | ||||
|  | ||||
| Example:: | ||||
|  | ||||
|     Zipcode.objects.filter(poly__isempty=True) | ||||
|  | ||||
| .. fieldlookup:: isvalid | ||||
|  | ||||
| ``isvalid`` | ||||
|   | ||||
| @@ -155,6 +155,10 @@ Minor features | ||||
| * :class:`~django.contrib.gis.forms.widgets.OpenLayersWidget` is now based on | ||||
|   OpenLayers 7.2.2 (previously 4.6.5). | ||||
|  | ||||
| * The new :lookup:`isempty` lookup and | ||||
|   :class:`IsEmpty() <django.contrib.gis.db.models.functions.IsEmpty>` | ||||
|   expression allow filtering empty geometries on PostGIS. | ||||
|  | ||||
| :mod:`django.contrib.messages` | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
|   | ||||
| @@ -371,6 +371,18 @@ class GISFunctionsTests(FuncTestMixin, TestCase): | ||||
|             else: | ||||
|                 self.assertIs(c.inter.empty, True) | ||||
|  | ||||
|     @skipUnlessDBFeature("supports_empty_geometries", "has_IsEmpty_function") | ||||
|     def test_isempty(self): | ||||
|         empty = City.objects.create(name="Nowhere", point=Point(srid=4326)) | ||||
|         City.objects.create(name="Somewhere", point=Point(6.825, 47.1, srid=4326)) | ||||
|         self.assertSequenceEqual( | ||||
|             City.objects.annotate(isempty=functions.IsEmpty("point")).filter( | ||||
|                 isempty=True | ||||
|             ), | ||||
|             [empty], | ||||
|         ) | ||||
|         self.assertSequenceEqual(City.objects.filter(point__isempty=True), [empty]) | ||||
|  | ||||
|     @skipUnlessDBFeature("has_IsValid_function") | ||||
|     def test_isvalid(self): | ||||
|         valid_geom = fromstr("POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user