Fixed #28738 -- Added the GeometryDistance function.

This commit is contained in:
Francisco Couzo 2019-03-20 13:54:42 -03:00 committed by Tim Graham
parent 638d5ea375
commit 0193bf874f
9 changed files with 62 additions and 22 deletions

View File

@ -282,6 +282,7 @@ answer newbie questions, and generally made Django that much better:
Florian Apolloner <florian@apolloner.eu>
Florian Moussous <florian.moussous@gmail.com>
Francisco Albarran Cristobal <pahko.xd@gmail.com>
Francisco Couzo <franciscouzo@gmail.com>
François Freitag <mail@franek.fr>
Frank Tegtmeyer <fte@fte.to>
Frank Wierzbicki

View File

@ -40,10 +40,10 @@ class BaseSpatialOperations:
unsupported_functions = {
'Area', 'AsGeoJSON', 'AsGML', 'AsKML', 'AsSVG', 'Azimuth',
'BoundingCircle', 'Centroid', 'Difference', 'Distance', 'Envelope',
'GeoHash', 'Intersection', 'IsValid', 'Length', 'LineLocatePoint',
'MakeValid', 'MemSize', 'NumGeometries', 'NumPoints', 'Perimeter',
'PointOnSurface', 'Reverse', 'Scale', 'SnapToGrid', 'SymDifference',
'Transform', 'Translate', 'Union',
'GeoHash', 'GeometryDistance', 'Intersection', 'IsValid', 'Length',
'LineLocatePoint', 'MakeValid', 'MemSize', 'NumGeometries',
'NumPoints', 'Perimeter', 'PointOnSurface', 'Reverse', 'Scale',
'SnapToGrid', 'SymDifference', 'Transform', 'Translate', 'Union',
}
# Constructors

View File

@ -54,9 +54,9 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations):
def unsupported_functions(self):
unsupported = {
'AsGML', 'AsKML', 'AsSVG', 'Azimuth', 'BoundingCircle',
'ForcePolygonCW', 'LineLocatePoint', 'MakeValid', 'MemSize',
'Perimeter', 'PointOnSurface', 'Reverse', 'Scale', 'SnapToGrid',
'Transform', 'Translate',
'ForcePolygonCW', 'GeometryDistance', 'LineLocatePoint',
'MakeValid', 'MemSize', 'Perimeter', 'PointOnSurface', 'Reverse',
'Scale', 'SnapToGrid', 'Transform', 'Translate',
}
if self.connection.mysql_is_mariadb:
unsupported.update({'GeoHash', 'IsValid'})

View File

@ -107,8 +107,8 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
unsupported_functions = {
'AsGeoJSON', 'AsKML', 'AsSVG', 'Azimuth', 'ForcePolygonCW', 'GeoHash',
'LineLocatePoint', 'MakeValid', 'MemSize', 'Scale', 'SnapToGrid',
'Translate',
'GeometryDistance', 'LineLocatePoint', 'MakeValid', 'MemSize',
'Scale', 'SnapToGrid', 'Translate',
}
def geo_quote_name(self, name):

View File

@ -79,7 +79,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
@cached_property
def unsupported_functions(self):
unsupported = {'BoundingCircle', 'MemSize'}
unsupported = {'BoundingCircle', 'GeometryDistance', 'MemSize'}
if not self.lwgeom_version():
unsupported |= {'Azimuth', 'GeoHash', 'IsValid', 'MakeValid'}
return unsupported

View File

@ -48,7 +48,7 @@ class GeoFuncMixin:
return self.source_expressions[self.geom_param_pos[0]].field
def as_sql(self, compiler, connection, function=None, **extra_context):
if not self.function and not function:
if self.function is None and function is None:
function = connection.ops.spatial_function_name(self.name)
return super().as_sql(compiler, connection, function=function, **extra_context)
@ -299,6 +299,14 @@ class GeoHash(GeoFunc):
return clone.as_sql(compiler, connection, **extra_context)
class GeometryDistance(GeoFunc):
output_field = FloatField()
arity = 2
function = ''
arg_joiner = ' <-> '
geom_param_pos = (0, 1)
class Intersection(OracleToleranceMixin, GeomOutputGeoFunc):
arity = 2
geom_param_pos = (0, 1)

View File

@ -20,17 +20,17 @@ get a ``NotImplementedError`` exception.
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:`Length` :class:`Centroid` :class:`SymDifference` :class:`Reverse` :class:`AsKML` :class:`NumGeometries`
:class:`Perimeter` :class:`Envelope` :class:`Union` :class:`Scale` :class:`AsSVG` :class:`NumPoints`
.. :class:`LineLocatePoint` :class:`SnapToGrid` :class:`GeoHash`
.. :class:`PointOnSurface` :class:`Transform`
.. :class:`Translate`
================== ======================== ====================== ======================= ================== =====================
========================= ======================== ====================== ======================= ================== =====================
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:`GeoHash`
.. :class:`PointOnSurface` :class:`Transform`
.. :class:`Translate`
========================= ======================== ====================== ======================= ================== =====================
``Area``
========
@ -308,6 +308,19 @@ result.
__ https://en.wikipedia.org/wiki/Geohash
``GeometryDistance``
====================
.. class:: GeometryDistance(expr1, expr2, **extra)
.. versionadded:: 3.0
*Availability*: `PostGIS <https://postgis.net/docs/geometry_distance_knn.html>`__
Accepts two geographic fields or expressions and returns the distance between
them. When used in an :meth:`~django.db.models.query.QuerySet.order_by` clause,
it provides index-assisted nearest-neighbor result sets.
``Intersection``
================

View File

@ -67,6 +67,9 @@ Minor features
* Allowed MySQL spatial lookup functions to operate on real geometries.
Previous support was limited to bounding boxes.
* Added the :class:`~django.contrib.gis.db.models.functions.GeometryDistance`
function, supported on PostGIS.
:mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -240,6 +240,21 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
self.assertEqual(ref_hash, h1.geohash[:len(ref_hash)])
self.assertEqual(ref_hash[:5], h2.geohash)
@skipUnlessDBFeature('has_GeometryDistance_function')
def test_geometry_distance(self):
point = Point(-90, 40, srid=4326)
qs = City.objects.annotate(distance=functions.GeometryDistance('point', point)).order_by('distance')
self.assertEqual([city.distance for city in qs], [
2.99091995527296,
5.33507274054713,
9.33852187483721,
9.91769193646233,
11.556465744884,
14.713098433352,
34.3635252198568,
276.987855073372,
])
@skipUnlessDBFeature("has_Intersection_function")
def test_intersection(self):
geom = Point(5, 23, srid=4326)