Browse Source

Fixed #34266 -- Added ClosestPoint GIS database functions.

Niccolò Mineo 2 years ago
parent
commit
79c298c9ce

+ 1 - 0
django/contrib/gis/db/backends/base/operations.py

@@ -42,6 +42,7 @@ class BaseSpatialOperations:
         "Azimuth",
         "BoundingCircle",
         "Centroid",
+        "ClosestPoint",
         "Difference",
         "Distance",
         "Envelope",

+ 1 - 0
django/contrib/gis/db/backends/mysql/operations.py

@@ -75,6 +75,7 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations):
             "AsSVG",
             "Azimuth",
             "BoundingCircle",
+            "ClosestPoint",
             "ForcePolygonCW",
             "GeometryDistance",
             "IsEmpty",

+ 1 - 0
django/contrib/gis/db/backends/oracle/operations.py

@@ -121,6 +121,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
         "AsKML",
         "AsSVG",
         "Azimuth",
+        "ClosestPoint",
         "ForcePolygonCW",
         "GeoHash",
         "GeometryDistance",

+ 5 - 0
django/contrib/gis/db/models/functions.py

@@ -280,6 +280,11 @@ class Centroid(OracleToleranceMixin, GeomOutputGeoFunc):
     arity = 1
 
 
+class ClosestPoint(GeomOutputGeoFunc):
+    arity = 2
+    geom_param_pos = (0, 1)
+
+
 class Difference(OracleToleranceMixin, GeomOutputGeoFunc):
     arity = 2
     geom_param_pos = (0, 1)

+ 1 - 0
docs/ref/contrib/gis/db-api.txt

@@ -356,6 +356,7 @@ Function                              PostGIS  Oracle         MariaDB      MySQL
 :class:`Azimuth`                      X                                                X (LWGEOM/RTTOPO)
 :class:`BoundingCircle`               X        X
 :class:`Centroid`                     X        X              X            X           X
+:class:`ClosestPoint`                 X                                                X
 :class:`Difference`                   X        X              X            X           X
 :class:`Distance`                     X        X              X            X           X
 :class:`Envelope`                     X        X              X            X           X

+ 17 - 4
docs/ref/contrib/gis/functions.txt

@@ -26,10 +26,10 @@ Measurement                Relationships             Operations              Edi
 :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:`FromWKB`    :class:`AsWKB`      :class:`NumPoints`
-                           :class:`PointOnSurface`                           :class:`Transform`       :class:`FromWKT`    :class:`AsWKT`
-                                                                             :class:`Translate`                           :class:`GeoHash`
+:class:`Length`            :class:`ClosestPoint`     :class:`Union`          :class:`Scale`                               :class:`AsSVG`      :class:`NumGeometries`
+:class:`Perimeter`         :class:`Envelope`                                 :class:`SnapToGrid`      :class:`FromWKB`    :class:`AsWKB`      :class:`NumPoints`
+                           :class:`LineLocatePoint`                          :class:`Transform`       :class:`FromWKT`    :class:`AsWKT`
+                           :class:`PointOnSurface`                           :class:`Translate`                           :class:`GeoHash`
 =========================  ========================  ======================  =======================  ==================  ==================  ======================
 
 ``Area``
@@ -237,6 +237,19 @@ The ``num_seg`` parameter is used only on PostGIS.
 Accepts a single geographic field or expression and returns the ``centroid``
 value of the geometry.
 
+``ClosestPoint``
+================
+
+.. versionadded:: 5.0
+
+.. class:: ClosestPoint(expr1, expr2, **extra)
+
+*Availability*: `PostGIS <https://postgis.net/docs/ST_ClosestPoint.html>`__,
+SpatiaLite
+
+Accepts two geographic fields or expressions and returns the 2-dimensional
+point on geometry A that is closest to geometry B.
+
 ``Difference``
 ==============
 

+ 4 - 1
docs/releases/5.0.txt

@@ -64,7 +64,10 @@ Minor features
 :mod:`django.contrib.gis`
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
-* ...
+* The new
+  :class:`ClosestPoint() <django.contrib.gis.db.models.functions.ClosestPoint>`
+  function returns a 2-dimensional point on the geometry that is closest to
+  another geometry.
 
 :mod:`django.contrib.messages`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 12 - 0
tests/gis_tests/geoapp/test_functions.py

@@ -456,6 +456,18 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
         ):
             qs.get(area__lt=500000)
 
+    @skipUnlessDBFeature("has_ClosestPoint_function")
+    def test_closest_point(self):
+        qs = Country.objects.annotate(
+            closest_point=functions.ClosestPoint("mpoly", functions.Centroid("mpoly"))
+        )
+        for country in qs:
+            self.assertIsInstance(country.closest_point, Point)
+            self.assertEqual(
+                country.mpoly.intersection(country.closest_point),
+                country.closest_point,
+            )
+
     @skipUnlessDBFeature("has_LineLocatePoint_function")
     def test_line_locate_point(self):
         pos_expr = functions.LineLocatePoint(