Django

Code

Changeset 7138

Show
Ignore:
Timestamp:
02/20/08 15:15:43 (5 months ago)
Author:
jbronn
Message:

gis: Fixed bug in GeoQuerySet.distance caused by SQL substitution that shouldn't be done there (thanks robotika); an exception is no longer raised when trying to import the spatial metadata models on backends that do not support them (e.g., MySQL).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/gis/django/contrib/gis/db/models/fields/__init__.py

    r7104 r7138  
    1313try: 
    1414    from django.contrib.gis.models import SpatialRefSys 
    15 except NotImplementedError: 
     15except ImportError: 
    1616    SpatialRefSys = None 
    1717 
     
    2222    # The OpenGIS Geometry name. 
    2323    _geom = 'GEOMETRY' 
     24 
     25    # Geodetic units. 
     26    geodetic_units = ('Decimal Degree', 'degree') 
    2427 
    2528    def __init__(self, srid=4326, spatial_index=True, dim=2, **kwargs): 
     
    8386        then 1000 would be returned. 
    8487        """ 
     88         
    8589        if isinstance(dist, Distance): 
    86             if self._unit_name in ('Decimal Degree', 'degree')
     90            if self._unit_name in self.geodetic_units
    8791                # Spherical distance calculation parameter should be in meters. 
    8892                dist_param = dist.m 
     
    9498 
    9599        # Sphereical distance query; returning meters. 
    96         if SpatialBackend.name == 'postgis' and self._unit_name == 'degree'
     100        if SpatialBackend.name == 'postgis' and self._unit_name in self.geodetic_units
    97101            return [gqn(self._spheroid), dist_param] 
    98102        else: 
  • django/branches/gis/django/contrib/gis/db/models/query.py

    r7104 r7138  
    55from django.db.models.fields import FieldDoesNotExist 
    66from django.utils.datastructures import SortedDict 
    7 from django.contrib.gis.db.models.fields import GeometryField 
     7from django.contrib.gis.db.models.fields import GeometryField, PointField 
    88# parse_lookup depends on the spatial database backend. 
    99from django.contrib.gis.db.backend import gqn, parse_lookup, SpatialBackend 
    10 from django.contrib.gis.geos import GEOSGeometry 
     10from django.contrib.gis.geos import GEOSGeometry, Point 
    1111 
    1212# Shortcut booleans for determining the backend. 
     
    280280        where, params = geo_field.get_db_prep_lookup('distance_lte', (geom, 0)) 
    281281        if oracle: 
    282             # The `tolerance` keyword may be used for Oracle. 
     282            # The `tolerance` keyword may be used for Oracle; the tolerance is  
     283            # in meters -- a default of 5 centimeters is used. 
    283284            tolerance = kwargs.get('tolerance', 0.05) 
    284  
    285             # More legwork here because the OracleSpatialAdaptor doesn't do 
    286             # quoting of the WKT. 
    287             tmp_params = [gqn(str(params[0]))] 
    288             tmp_params.extend(params[1:]) 
    289             dsql = where[0] % tuple(tmp_params) 
    290             dist_select = {'distance' : '%s(%s, %s, %s)' % (DISTANCE, geo_col, dsql, tolerance)} 
    291         else: 
    292             dsql = where[0] % tuple(params) 
     285            dist_select = {'distance' : '%s(%s, %s, %s)' % (DISTANCE, geo_col, where[0], tolerance)} 
     286        else: 
    293287            if len(where) == 3: 
     288                # Spherical distance calculation was requested (b/c spheroid  
     289                # parameter was attached) However, the PostGIS ST_distance_spheroid()  
     290                # procedure may only do queries from point columns to point geometries 
     291                # some error checking is required. 
     292                if not isinstance(geo_field, PointField):  
     293                    raise TypeError('Spherical distance calculation only supported on PointFields.') 
     294                if not isinstance(GEOSGeometry(params[0].wkb), Point): 
     295                    raise TypeError('Spherical distance calculation only supported with Point Geometry parameters') 
     296 
    294297                # Call to distance_spheroid() requires the spheroid as well. 
    295                 dist_sql = '%s(%s, %s, %s)' % (SpatialBackend.distance_spheroid, geo_col, dsql, where[1]) 
    296             else: 
    297                 dist_sql = '%s(%s, %s)' % (DISTANCE, geo_col, dsql
     298                dist_sql = '%s(%s, %s, %s)' % (SpatialBackend.distance_spheroid, geo_col, where[0], where[1]) 
     299            else: 
     300                dist_sql = '%s(%s, %s)' % (DISTANCE, geo_col, where[0]
    298301            dist_select = {'distance' : dist_sql} 
    299         return self.extra(select=dist_select
     302        return self.extra(select=dist_select, params=params
    300303 
    301304    def extent(self, field_name=None): 
  • django/branches/gis/django/contrib/gis/models.py

    r7104 r7138  
    215215    from django.contrib.gis.db.backend.oracle.models import GeometryColumns, SpatialRefSys 
    216216else: 
    217     raise NotImplementedError('No SpatialRefSys or GeometryColumns models for backend: %s' % settings.DATABASE_ENGINE) 
     217    pass 
  • django/branches/gis/django/contrib/gis/tests/distapp/data.py

    r7104 r7138  
    99             ('Hobart', 147.33, -42.8827), 
    1010             ('Adelaide', 138.6, -34.9258), 
     11             ('Hillsdale', 151.231341, -33.952685), 
    1112             ) 
    1213 
  • django/branches/gis/django/contrib/gis/tests/distapp/tests.py

    r7104 r7138  
    33 
    44from django.contrib.gis.gdal import DataSource 
    5 from django.contrib.gis.geos import GEOSGeometry, Point 
     5from django.contrib.gis.geos import GEOSGeometry, Point, LineString 
    66from django.contrib.gis.measure import D # alias for Distance 
    77from django.contrib.gis.db.models import GeoQ 
     
    3636 
    3737        self.assertEqual(10, SouthTexasCity.objects.count()) 
    38         self.assertEqual(10, AustraliaCity.objects.count()) 
     38        self.assertEqual(11, AustraliaCity.objects.count()) 
    3939 
    4040    def test02_dwithin(self): 
     
    5757                     70870.188967, 165337.758878, 102128.654360,  
    5858                     139196.085105] 
     59 
     60        # Testing when the field name is explicitly set. 
    5961        dist1 = SouthTexasCity.objects.distance('point', lagrange) 
    60         dist2 = SouthTexasCity.objects.distance(lagrange) 
     62        dist2 = SouthTexasCity.objects.distance(lagrange)  # Using GEOSGeometry parameter 
     63        dist3 = SouthTexasCity.objects.distance(lagrange.ewkt) # Using EWKT string parameter. 
    6164 
    6265        # Original query done on PostGIS, have to adjust AlmostEqual tolerance 
    6366        # for Oracle. 
    64         if oracle: tol = 3 
     67        if oracle: tol = 2 
    6568        else: tol = 5 
    6669 
    67         for qs in [dist1, dist2]: 
     70        # Ensuring expected distances are returned for each distance queryset. 
     71        for qs in [dist1, dist2, dist3]: 
    6872            for i, c in enumerate(qs): 
    6973                self.assertAlmostEqual(distances[i], c.distance, tol) 
     74 
     75        # Now testing geodetic distance aggregation. 
     76        hillsdale = AustraliaCity.objects.get(name='Hillsdale') 
     77        if not oracle: 
     78            # PostGIS is limited to disance queries only to/from point geometries, 
     79            # ensuring a TypeError is raised if something else is put in. 
     80            self.assertRaises(TypeError, AustraliaCity.objects.distance, 'LINESTRING(0 0, 1 1)') 
     81            self.assertRaises(TypeError, AustraliaCity.objects.distance, LineString((0, 0), (1, 1))) 
     82 
     83        # Got these distances using the raw SQL statement: 
     84        #  SELECT ST_distance_spheroid(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326), 'SPHEROID["WGS 84",6378137.0,298.257223563]') FROM distapp_australiacity WHERE (NOT (id = 11)); 
     85        geodetic_distances = [60504.0628825298, 77023.948962654, 49154.8867507115, 90847.435881812, 217402.811862568, 709599.234619957, 640011.483583758, 7772.00667666425, 1047861.7859506, 1165126.55237647] 
     86 
     87        # Ensuring the expected distances are returned. 
     88        qs = AustraliaCity.objects.exclude(id=hillsdale.id).distance(hillsdale.point) 
     89        for i, c in enumerate(qs): 
     90            self.assertAlmostEqual(geodetic_distances[i], c.distance, tol) 
    7091 
    7192    def test04_distance_lookups(self):