mirror of
				https://github.com/django/django.git
				synced 2025-10-30 09:06:13 +00:00 
			
		
		
		
	Fixed #28738 -- Added the GeometryDistance function.
This commit is contained in:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						parent
						
							638d5ea375
						
					
				
				
					commit
					0193bf874f
				
			
							
								
								
									
										1
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								AUTHORS
									
									
									
									
									
								
							| @@ -282,6 +282,7 @@ answer newbie questions, and generally made Django that much better: | |||||||
|     Florian Apolloner <florian@apolloner.eu> |     Florian Apolloner <florian@apolloner.eu> | ||||||
|     Florian Moussous <florian.moussous@gmail.com> |     Florian Moussous <florian.moussous@gmail.com> | ||||||
|     Francisco Albarran Cristobal <pahko.xd@gmail.com> |     Francisco Albarran Cristobal <pahko.xd@gmail.com> | ||||||
|  |     Francisco Couzo <franciscouzo@gmail.com> | ||||||
|     François Freitag <mail@franek.fr> |     François Freitag <mail@franek.fr> | ||||||
|     Frank Tegtmeyer <fte@fte.to> |     Frank Tegtmeyer <fte@fte.to> | ||||||
|     Frank Wierzbicki |     Frank Wierzbicki | ||||||
|   | |||||||
| @@ -40,10 +40,10 @@ class BaseSpatialOperations: | |||||||
|     unsupported_functions = { |     unsupported_functions = { | ||||||
|         'Area', 'AsGeoJSON', 'AsGML', 'AsKML', 'AsSVG', 'Azimuth', |         'Area', 'AsGeoJSON', 'AsGML', 'AsKML', 'AsSVG', 'Azimuth', | ||||||
|         'BoundingCircle', 'Centroid', 'Difference', 'Distance', 'Envelope', |         'BoundingCircle', 'Centroid', 'Difference', 'Distance', 'Envelope', | ||||||
|         'GeoHash', 'Intersection', 'IsValid', 'Length', 'LineLocatePoint', |         'GeoHash', 'GeometryDistance', 'Intersection', 'IsValid', 'Length', | ||||||
|         'MakeValid', 'MemSize', 'NumGeometries', 'NumPoints', 'Perimeter', |         'LineLocatePoint', 'MakeValid', 'MemSize', 'NumGeometries', | ||||||
|         'PointOnSurface', 'Reverse', 'Scale', 'SnapToGrid', 'SymDifference', |         'NumPoints', 'Perimeter', 'PointOnSurface', 'Reverse', 'Scale', | ||||||
|         'Transform', 'Translate', 'Union', |         'SnapToGrid', 'SymDifference', 'Transform', 'Translate', 'Union', | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     # Constructors |     # Constructors | ||||||
|   | |||||||
| @@ -54,9 +54,9 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations): | |||||||
|     def unsupported_functions(self): |     def unsupported_functions(self): | ||||||
|         unsupported = { |         unsupported = { | ||||||
|             'AsGML', 'AsKML', 'AsSVG', 'Azimuth', 'BoundingCircle', |             'AsGML', 'AsKML', 'AsSVG', 'Azimuth', 'BoundingCircle', | ||||||
|             'ForcePolygonCW', 'LineLocatePoint', 'MakeValid', 'MemSize', |             'ForcePolygonCW', 'GeometryDistance', 'LineLocatePoint', | ||||||
|             'Perimeter', 'PointOnSurface', 'Reverse', 'Scale', 'SnapToGrid', |             'MakeValid', 'MemSize', 'Perimeter', 'PointOnSurface', 'Reverse', | ||||||
|             'Transform', 'Translate', |             'Scale', 'SnapToGrid', 'Transform', 'Translate', | ||||||
|         } |         } | ||||||
|         if self.connection.mysql_is_mariadb: |         if self.connection.mysql_is_mariadb: | ||||||
|             unsupported.update({'GeoHash', 'IsValid'}) |             unsupported.update({'GeoHash', 'IsValid'}) | ||||||
|   | |||||||
| @@ -107,8 +107,8 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations): | |||||||
|  |  | ||||||
|     unsupported_functions = { |     unsupported_functions = { | ||||||
|         'AsGeoJSON', 'AsKML', 'AsSVG', 'Azimuth', 'ForcePolygonCW', 'GeoHash', |         'AsGeoJSON', 'AsKML', 'AsSVG', 'Azimuth', 'ForcePolygonCW', 'GeoHash', | ||||||
|         'LineLocatePoint', 'MakeValid', 'MemSize', 'Scale', 'SnapToGrid', |         'GeometryDistance', 'LineLocatePoint', 'MakeValid', 'MemSize', | ||||||
|         'Translate', |         'Scale', 'SnapToGrid', 'Translate', | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     def geo_quote_name(self, name): |     def geo_quote_name(self, name): | ||||||
|   | |||||||
| @@ -79,7 +79,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations): | |||||||
|  |  | ||||||
|     @cached_property |     @cached_property | ||||||
|     def unsupported_functions(self): |     def unsupported_functions(self): | ||||||
|         unsupported = {'BoundingCircle', 'MemSize'} |         unsupported = {'BoundingCircle', 'GeometryDistance', 'MemSize'} | ||||||
|         if not self.lwgeom_version(): |         if not self.lwgeom_version(): | ||||||
|             unsupported |= {'Azimuth', 'GeoHash', 'IsValid', 'MakeValid'} |             unsupported |= {'Azimuth', 'GeoHash', 'IsValid', 'MakeValid'} | ||||||
|         return unsupported |         return unsupported | ||||||
|   | |||||||
| @@ -48,7 +48,7 @@ class GeoFuncMixin: | |||||||
|         return self.source_expressions[self.geom_param_pos[0]].field |         return self.source_expressions[self.geom_param_pos[0]].field | ||||||
|  |  | ||||||
|     def as_sql(self, compiler, connection, function=None, **extra_context): |     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) |             function = connection.ops.spatial_function_name(self.name) | ||||||
|         return super().as_sql(compiler, connection, function=function, **extra_context) |         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) |         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): | class Intersection(OracleToleranceMixin, GeomOutputGeoFunc): | ||||||
|     arity = 2 |     arity = 2 | ||||||
|     geom_param_pos = (0, 1) |     geom_param_pos = (0, 1) | ||||||
|   | |||||||
| @@ -20,17 +20,17 @@ get a ``NotImplementedError`` exception. | |||||||
|  |  | ||||||
| Function's summary: | Function's summary: | ||||||
|  |  | ||||||
| ==================  ========================   ======================  =======================  ==================  ===================== | =========================  ========================  ======================  =======================  ==================  ===================== | ||||||
| Measurement         Relationships              Operations              Editors                  Output format       Miscellaneous | Measurement                Relationships             Operations              Editors                  Output format       Miscellaneous | ||||||
| ==================  ========================   ======================  =======================  ==================  ===================== | =========================  ========================  ======================  =======================  ==================  ===================== | ||||||
| :class:`Area`       :class:`Azimuth`           :class:`Difference`     :class:`ForcePolygonCW`  :class:`AsGeoJSON`  :class:`IsValid` | :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:`Distance`          :class:`BoundingCircle`   :class:`Intersection`   :class:`MakeValid`       :class:`AsGML`      :class:`MemSize` | ||||||
| :class:`Length`     :class:`Centroid`          :class:`SymDifference`  :class:`Reverse`         :class:`AsKML`      :class:`NumGeometries` | :class:`GeometryDistance`  :class:`Centroid`         :class:`SymDifference`  :class:`Reverse`         :class:`AsKML`      :class:`NumGeometries` | ||||||
| :class:`Perimeter`  :class:`Envelope`          :class:`Union`          :class:`Scale`           :class:`AsSVG`      :class:`NumPoints` | :class:`Length`            :class:`Envelope`         :class:`Union`          :class:`Scale`           :class:`AsSVG`      :class:`NumPoints` | ||||||
| ..                  :class:`LineLocatePoint`                           :class:`SnapToGrid`      :class:`GeoHash` | :class:`Perimeter`         :class:`LineLocatePoint`                          :class:`SnapToGrid`      :class:`GeoHash` | ||||||
| ..                  :class:`PointOnSurface`                            :class:`Transform` | ..                         :class:`PointOnSurface`                           :class:`Transform` | ||||||
| ..                                                                     :class:`Translate` | ..                                                                           :class:`Translate` | ||||||
| ==================  ========================   ======================  =======================  ==================  ===================== | =========================  ========================  ======================  =======================  ==================  ===================== | ||||||
|  |  | ||||||
| ``Area`` | ``Area`` | ||||||
| ======== | ======== | ||||||
| @@ -308,6 +308,19 @@ result. | |||||||
|  |  | ||||||
| __ https://en.wikipedia.org/wiki/Geohash | __ 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`` | ``Intersection`` | ||||||
| ================ | ================ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -67,6 +67,9 @@ Minor features | |||||||
| * Allowed MySQL spatial lookup functions to operate on real geometries. | * Allowed MySQL spatial lookup functions to operate on real geometries. | ||||||
|   Previous support was limited to bounding boxes. |   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` | :mod:`django.contrib.messages` | ||||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -240,6 +240,21 @@ class GISFunctionsTests(FuncTestMixin, TestCase): | |||||||
|         self.assertEqual(ref_hash, h1.geohash[:len(ref_hash)]) |         self.assertEqual(ref_hash, h1.geohash[:len(ref_hash)]) | ||||||
|         self.assertEqual(ref_hash[:5], h2.geohash) |         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") |     @skipUnlessDBFeature("has_Intersection_function") | ||||||
|     def test_intersection(self): |     def test_intersection(self): | ||||||
|         geom = Point(5, 23, srid=4326) |         geom = Point(5, 23, srid=4326) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user