Ticket #6715: distance_sphere.diff
File distance_sphere.diff, 10.0 KB (added by , 17 years ago) |
---|
-
django/contrib/gis/tests/distapp/models.py
10 10 class AustraliaCity(models.Model): 11 11 "City model for Australia, using WGS84." 12 12 name = models.CharField(max_length=30) 13 point = models.PointField( )13 point = models.PointField(distance_spheroid=True) 14 14 objects = models.GeoManager() 15 15 def __unicode__(self): return self.name 16 16 -
django/contrib/gis/db/models/fields/__init__.py
25 25 # Geodetic units. 26 26 geodetic_units = ('Decimal Degree', 'degree') 27 27 28 def __init__(self, srid=4326, spatial_index=True, dim=2, **kwargs): 28 def __init__(self, srid=4326, spatial_index=True, dim=2, 29 distance_spheroid=False, **kwargs): 29 30 """ 30 31 The initialization function for geometry fields. Takes the following 31 32 as keyword arguments: … … 41 42 42 43 dim: 43 44 The number of dimensions for this geometry. Defaults to 2. 45 46 distance_spheroid: 47 When geodetic distance calculations are performed on the geometries 48 for this field, the `ST_distance_spheroid` function will be used instead 49 of `ST_distance_sphere`. In other words, geodetic distance operations 50 will be more accurate at the expense of speed. Only affects PostGIS 51 spatial backends. 44 52 """ 45 53 46 54 # Setting the index flag with the value of the `spatial_index` keyword. … … 72 80 # (both required for distance queries). 73 81 self._unit, self._unit_name = SpatialRefSys.get_units(srs_wkt) 74 82 self._spheroid = SpatialRefSys.get_spheroid(srs_wkt) 83 self._distance_spheroid = distance_spheroid 84 else: 85 self._unit, self._unit_name, self._sphere_distance = None, None, None 75 86 76 87 # Setting the dimension of the geometry field. 77 88 self._dim = dim … … 79 90 super(GeometryField, self).__init__(**kwargs) # Calling the parent initializtion function 80 91 81 92 ### Routines specific to GeometryField ### 93 @property 94 def geodetic(self): 95 """ 96 Property that tells if the coordinate system for this GeometryField 97 is geographic in nature (e.g., uses lat/lon instead of projected 98 units). 99 """ 100 return self._unit_name in self.geodetic_units 101 82 102 def get_distance(self, dist): 83 103 """ 84 104 Returns a distance number in units of the field. For example, if … … 87 107 """ 88 108 89 109 if isinstance(dist, Distance): 90 if self. _unit_name in self.geodetic_units:110 if self.geodetic: 91 111 # Spherical distance calculation parameter should be in meters. 92 112 dist_param = dist.m 93 113 else: … … 97 117 dist_param = dist 98 118 99 119 # Sphereical distance query; returning meters. 100 if SpatialBackend.name == 'postgis' and self._unit_name in self.geodetic_units:120 if (SpatialBackend.name == 'postgis' and self.geodetic and self._distance_spheroid): 101 121 return [gqn(self._spheroid), dist_param] 102 122 else: 103 123 return [dist_param] -
django/contrib/gis/db/models/query.py
284 284 tolerance = kwargs.get('tolerance', 0.05) 285 285 dist_select = {'distance' : '%s(%s, %s, %s)' % (DISTANCE, geo_col, where[0], tolerance)} 286 286 else: 287 if len(where) == 3:287 if geo_field.geodetic: 288 288 # Spherical distance calculation was requested (b/c spheroid 289 289 # parameter was attached) However, the PostGIS ST_distance_spheroid() 290 290 # procedure may only do queries from point columns to point geometries … … 295 295 raise TypeError('Spherical distance calculation only supported with Point Geometry parameters') 296 296 297 297 # Call to distance_spheroid() requires the spheroid as well. 298 dist_sql = '%s(%s, %s, %s)' % (SpatialBackend.distance_spheroid, geo_col, where[0], where[1]) 298 if len(where) == 3: 299 dist_sql = '%s(%s, %s, %s)' % (SpatialBackend.distance_spheroid, geo_col, where[0], where[1]) 300 else: 301 dist_sql = '%s(%s, %s)' % (SpatialBackend.distance_sphere, geo_col, where[0]) 299 302 else: 300 303 dist_sql = '%s(%s, %s)' % (DISTANCE, geo_col, where[0]) 301 304 dist_select = {'distance' : dist_sql} -
django/contrib/gis/db/backend/__init__.py
22 22 from django.contrib.gis.db.backend.util import gqn 23 23 24 24 # These routines (needed by GeoManager), default to False. 25 ASGML, ASKML, DISTANCE, DISTANCE_SPHER OID, EXTENT, TRANSFORM, UNION, VERSION = tuple(False for i in range(8))25 ASGML, ASKML, DISTANCE, DISTANCE_SPHERE, DISTANCE_SPHEROID, EXTENT, TRANSFORM, UNION, VERSION = tuple(False for i in range(9)) 26 26 27 27 # Lookup types in which the rest of the parameters are not 28 28 # needed to be substitute in the WHERE SQL (e.g., the 'relate' … … 39 39 from django.contrib.gis.db.backend.postgis.creation import create_spatial_db 40 40 from django.contrib.gis.db.backend.postgis.query import \ 41 41 get_geo_where_clause, POSTGIS_TERMS as GIS_TERMS, \ 42 ASGML, ASKML, DISTANCE, DISTANCE_SPHEROID, DISTANCE_FUNCTIONS, \43 EXTENT, GEOM_SELECT, TRANSFORM, UNION, \42 DISTANCE, DISTANCE_SPHERE, DISTANCE_SPHEROID, DISTANCE_FUNCTIONS, \ 43 ASGML, ASKML, EXTENT, GEOM_SELECT, TRANSFORM, UNION, \ 44 44 MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2 45 45 # PostGIS version info is needed to determine calling order of some 46 46 # stored procedures (e.g., AsGML()). … … 76 76 as_kml = ASKML 77 77 as_gml = ASGML 78 78 distance = DISTANCE 79 distance_sphere = DISTANCE_SPHERE 79 80 distance_spheroid = DISTANCE_SPHEROID 80 81 extent = EXTENT 81 82 name = SPATIAL_BACKEND -
django/contrib/gis/db/backend/postgis/query.py
40 40 ASKML = get_func('AsKML') 41 41 ASGML = get_func('AsGML') 42 42 DISTANCE = get_func('Distance') 43 DISTANCE_SPHERE = get_func('distance_sphere') 43 44 DISTANCE_SPHEROID = get_func('distance_spheroid') 44 45 EXTENT = get_func('extent') 45 46 GEOM_FROM_TEXT = get_func('GeomFromText') … … 82 83 83 84 class PostGISSphereDistance(PostGISFunction): 84 85 "For PostGIS spherical distance operations." 85 dist_func = 'distance_spher oid'86 dist_func = 'distance_sphere' 86 87 def __init__(self, operator): 87 88 # An extra parameter in `end_subst` is needed for the spheroid string. 88 super(PostGISSphereDistance, self).__init__(self.dist_func, 89 beg_subst='%s(%s, %%s, %%s', 90 end_subst=') %s %s', 89 super(PostGISSphereDistance, self).__init__(self.dist_func, end_subst=') %s %s', 91 90 operator=operator, result='%%s') 92 91 92 class PostGISSpheroidDistance(PostGISFunction): 93 "For PostGIS spherical distance operations." 94 dist_func = 'distance_spheroid' 95 def __init__(self, operator): 96 # An extra parameter in `end_subst` is needed for the spheroid string. 97 super(PostGISSpheroidDistance, self).__init__(self.dist_func, 98 beg_subst='%s(%s, %%s, %%s', 99 end_subst=') %s %s', 100 operator=operator, result='%%s') 101 93 102 class PostGISRelate(PostGISFunctionParam): 94 103 "For PostGIS Relate(<geom>, <pattern>) calls." 95 104 pattern_regex = re.compile(r'^[012TF\*]{9}$') … … 162 171 163 172 # Valid distance types and substitutions 164 173 dtypes = (Decimal, Distance, float, int, long) 165 def get_dist_ops(op erator):174 def get_dist_ops(op): 166 175 "Returns operations for both regular and spherical distances." 167 return (PostGISDistance(op erator), PostGISSphereDistance(operator))176 return (PostGISDistance(op), PostGISSphereDistance(op), PostGISSpheroidDistance(op)) 168 177 DISTANCE_FUNCTIONS = { 169 178 'distance_gt' : (get_dist_ops('>'), dtypes), 170 179 'distance_gte' : (get_dist_ops('>='), dtypes), … … 228 237 if lookup_type == 'relate': 229 238 op = op(value[1]) 230 239 elif lookup_type in DISTANCE_FUNCTIONS and lookup_type != 'dwithin': 231 if field. _unit_name == 'degree':240 if field.geodetic: 232 241 # Geodetic distances are only availble from Points to PointFields. 233 242 if field._geom != 'POINT': 234 243 raise TypeError('PostGIS spherical operations are only valid on PointFields.') 235 244 if value[0].geom_typeid != 0: 236 245 raise TypeError('PostGIS geometry distance parameter is required to be of type Point.') 237 op = op[1] 246 247 # If the GeometryField `distance_spheroid` keyword is set, then 248 # `ST_distance_spheroid` is used instead. 249 if field._distance_spheroid: op = op[2] 250 else: op = op[1] 238 251 else: 239 252 op = op[0] 240 253 else: