1+ import warnings
2+
13from django .contrib .gis import geos
24from django .contrib .gis .db import models
35from django .contrib .gis .db .backends .base .operations import BaseSpatialOperations
6+ from django .contrib .gis .measure import Distance
7+ from django .db .backends .base .operations import BaseDatabaseOperations
48
59from .adapter import Adapter
10+ from .utils import SpatialOperator
11+
12+
13+ def _gis_within_operator (field , value , op = None , params = None ):
14+ print (f"Within value: { value } " )
15+ return {
16+ field : {
17+ "$geoWithin" : {
18+ "$geometry" : {
19+ "type" : value ["type" ],
20+ "coordinates" : value ["coordinates" ],
21+ }
22+ }
23+ }
24+ }
25+
26+
27+ def _gis_intersects_operator (field , value , op = None , params = None ):
28+ return {
29+ field : {
30+ "$geoIntersects" : {
31+ "$geometry" : {
32+ "type" : value ["type" ],
33+ "coordinates" : value ["coordinates" ],
34+ }
35+ }
36+ }
37+ }
38+
39+
40+ def _gis_disjoint_operator (field , value , op = None , params = None ):
41+ return {
42+ field : {
43+ "$not" : {
44+ "$geoIntersects" : {
45+ "$geometry" : {
46+ "type" : value ["type" ],
47+ "coordinates" : value ["coordinates" ],
48+ }
49+ }
50+ }
51+ }
52+ }
53+
654
55+ def _gis_contains_operator (field , value , op = None , params = None ):
56+ value_type = value ["type" ]
57+ if value_type != "Point" :
58+ warnings .warn (
59+ "MongoDB does not support strict contains on non-Point query geometries. Results will be for intersection."
60+ )
61+ return {
62+ field : {
63+ "$geoIntersects" : {
64+ "$geometry" : {
65+ "type" : value_type ,
66+ "coordinates" : value ["coordinates" ],
67+ }
68+ }
69+ }
70+ }
771
8- class GISOperations (BaseSpatialOperations ):
72+
73+ def _gis_distance_operator (field , value , op = None , params = None ):
74+ print (f"Distance: { params } " )
75+ if hasattr (params [0 ], "m" ):
76+ distance = params [0 ].m
77+ else :
78+ distance = params [0 ]
79+ if op == "distance_gt" or op == "distance_gte" :
80+ cmd = {
81+ field : {
82+ "$not" : {
83+ "$geoWithin" : {
84+ "$centerSphere" : [
85+ value ["coordinates" ],
86+ distance / 6378100 , # radius of earth in meters
87+ ],
88+ }
89+ }
90+ }
91+ }
92+ else :
93+ cmd = {
94+ field : {
95+ "$geoWithin" : {
96+ "$centerSphere" : [
97+ value ["coordinates" ],
98+ distance / 6378100 , # radius of earth in meters
99+ ],
100+ }
101+ }
102+ }
103+ print (f"Command: { cmd } " )
104+ return cmd
105+
106+
107+ class GISOperations (BaseSpatialOperations , BaseDatabaseOperations ):
9108 Adapter = Adapter
10109
11110 disallowed_aggregates = (
@@ -18,7 +117,16 @@ class GISOperations(BaseSpatialOperations):
18117
19118 @property
20119 def gis_operators (self ):
21- return {}
120+ return {
121+ "contains" : SpatialOperator ("contains" , _gis_contains_operator ),
122+ "intersects" : SpatialOperator ("intersects" , _gis_intersects_operator ),
123+ "disjoint" : SpatialOperator ("disjoint" , _gis_disjoint_operator ),
124+ "within" : SpatialOperator ("within" , _gis_within_operator ),
125+ "distance_gt" : SpatialOperator ("distance_gt" , _gis_distance_operator ),
126+ "distance_gte" : SpatialOperator ("distance_gte" , _gis_distance_operator ),
127+ "distance_lt" : SpatialOperator ("distance_lt" , _gis_distance_operator ),
128+ "distance_lte" : SpatialOperator ("distance_lte" , _gis_distance_operator ),
129+ }
22130
23131 unsupported_functions = {
24132 "Area" ,
@@ -33,7 +141,6 @@ def gis_operators(self):
33141 "Centroid" ,
34142 "ClosestPoint" ,
35143 "Difference" ,
36- "Distance" ,
37144 "Envelope" ,
38145 "ForcePolygonCW" ,
39146 "FromWKB" ,
@@ -95,3 +202,15 @@ def converter(value, expression, connection): # noqa: ARG001
95202 return geom_class (* value ["coordinates" ], srid = srid )
96203
97204 return converter
205+
206+ def get_distance (self , f , value , lookup_type ):
207+ value = value [0 ]
208+ if isinstance (value , Distance ):
209+ if f .geodetic (self .connection ):
210+ raise ValueError (
211+ "Only numeric values of degree units are allowed on geodetic distance queries."
212+ )
213+ dist_param = getattr (value , Distance .unit_attname (f .units_name (self .connection )))
214+ else :
215+ dist_param = value
216+ return [dist_param ]
0 commit comments