Django

Code

Show
Ignore:
Timestamp:
06/15/08 14:48:57 (7 months ago)
Author:
jbronn
Message:

gis: Refactor of the GeoQuerySet; new features include:

(1) Creation of internal API that eases generation of GeoQuerySet methods.
(2) GeoQuerySet.distance now returns Distance objects instead of floats.
(3) Added the new GeoQuerySet methods: area, centroid, difference, envelope, intersection, length, make_line, mem_size, num_geom, num_points, perimeter, point_on_surface, scale, svg, sym_difference, translate, union.
(4) The model_att keyword may be used to customize the attribute that GeoQuerySet methods attach output to.
(5) Geographic distance lookups and GeoQuerySet.distance calls now use ST_distance_sphere by default (performance benefits far outweigh small loss in accuracy); ST_distance_spheroid may still be used by specifying an option.
(6) GeoQuerySet methods may now operate accross ForeignKey? relations specified via the field_name keyword (but this does not work on Oracle).
(7) Area now has the same units of measure as Distance.

Backward Incompatibilites:

  • The aggregate union method is now known as unionagg.
  • The field_name keyword used for GeoQuerySet methods may no longer be specified via positional arguments.
  • Distance objects returned instead of floats from GeoQuerySet.distance.
  • ST_Distance_sphere used by default for geographic distance calculations.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/gis/django/contrib/gis/measure.py

    r7047 r7641  
    3131and conversions. 
    3232 
    33 Author: Robert Coup 
     33Author: Robert Coup, Justin Bronn 
    3434 
    3535Inspired by GeoPy (http://exogen.case.edu/projects/geopy/) 
    3636and Geoff Biggs' PhD work on dimensioned units for robotics. 
    3737""" 
     38__all__ = ['A', 'Area', 'D', 'Distance'] 
    3839from decimal import Decimal 
    3940 
    40 class Distance(object): 
     41class MeasureBase(object): 
     42    def default_units(self, kwargs): 
     43        """ 
     44        Return the unit value and the the default units specified 
     45        from the given keyword arguments dictionary. 
     46        """ 
     47        val = 0.0 
     48        for unit, value in kwargs.iteritems(): 
     49            if unit in self.UNITS: 
     50                val += self.UNITS[unit] * value 
     51                default_unit = unit 
     52            elif unit in self.ALIAS: 
     53                u = self.ALIAS[unit] 
     54                val += self.UNITS[u] * value 
     55                default_unit = u 
     56            else: 
     57                lower = unit.lower() 
     58                if lower in self.UNITS: 
     59                    val += self.UNITS[lower] * value 
     60                    default_unit = lower 
     61                elif lower in self.LALIAS: 
     62                    u = self.LALIAS[lower] 
     63                    val += self.UNITS[u] * value 
     64                    default_unit = u 
     65                else: 
     66                    raise AttributeError('Unknown unit type: %s' % unit) 
     67        return val, default_unit 
     68 
     69    @classmethod 
     70    def unit_attname(cls, unit_str): 
     71        """ 
     72        Retrieves the unit attribute name for the given unit string.   
     73        For example, if the given unit string is 'metre', 'm' would be returned. 
     74        An exception is raised if an attribute cannot be found. 
     75        """ 
     76        lower = unit_str.lower() 
     77        if unit_str in cls.UNITS: 
     78            return unit_str 
     79        elif lower in cls.UNITS: 
     80            return lower 
     81        elif lower in cls.LALIAS: 
     82            return cls.LALIAS[lower] 
     83        else: 
     84            raise Exception('Could not find a unit keyword associated with "%s"' % unit_str) 
     85 
     86class Distance(MeasureBase): 
    4187    UNITS = { 
    4288        'chain' : 20.1168, 
     
    54100        'ft': 0.3048, 
    55101        'german_m' : 1.0000135965, 
    56         'grad' : 0.0157079632679, 
    57102        'gold_coast_ft' : 0.304799710181508, 
    58103        'indian_yd' : 0.914398530744, 
     
    93138        'British chain (Sears 1922 truncated)' : 'british_chain_sears_truncated', 
    94139        'British foot (Sears 1922)' : 'british_ft', 
     140        'British foot' : 'british_ft', 
    95141        'British yard (Sears 1922)' : 'british_yd', 
     142        'British yard' : 'british_yd', 
    96143        "Clarke's Foot" : 'clarke_ft', 
    97         "Clarke's foot" : 'clarke_ft', 
    98144        "Clarke's link" : 'clarke_link', 
    99145        'Chain (Benoit)' : 'chain_benoit', 
     
    112158        'Yard (Sears)' : 'sears_yd' 
    113159        } 
    114     REV_ALIAS = dict((value, key) for key, value in ALIAS.items()
     160    LALIAS = dict([(k.lower(), v) for k, v in ALIAS.items()]
    115161 
    116162    def __init__(self, default_unit=None, **kwargs): 
    117163        # The base unit is in meters. 
    118         self.m = 0.0 
    119         self._default_unit = 'm' 
    120          
    121         for unit,value in kwargs.items(): 
    122             if unit in self.UNITS: 
    123                 self.m += self.UNITS[unit] * value 
    124                 self._default_unit = unit 
    125             elif unit in self.ALIAS: 
    126                 u = self.ALIAS[unit] 
    127                 self.m += self.UNITS[u] * value 
    128                 self._default_unit = u 
    129             else: 
    130                 lower = unit.lower() 
    131                 if lower in self.UNITS: 
    132                     self.m += self.UNITS[lower] * value 
    133                     self._default_unit = lower 
    134                 elif lower in self.ALIAS: 
    135                     u = self.ALIAS[lower] 
    136                     self.m += self.UNITS[u] * value 
    137                     self._default_unit = u 
    138                 else: 
    139                     raise AttributeError('Unknown unit type: %s' % unit) 
    140  
     164        self.m, self._default_unit = self.default_units(kwargs) 
    141165        if default_unit and isinstance(default_unit, str): 
    142166            self._default_unit = default_unit 
     
    217241        return bool(self.m) 
    218242 
    219     @classmethod 
    220     def unit_attname(cls, unit_str): 
    221         """ 
    222         Retrieves the unit attribute name for the given unit string.   
    223         For example, if the given unit string is 'metre', 'm' would be returned.   
    224         An exception is raised if an attribute cannot be found. 
    225         """ 
    226         lower = unit_str.lower() 
    227  
    228         if unit_str in cls.UNITS: 
    229             return unit_str 
    230         elif lower in cls.UNITS: 
    231             return lower 
    232         elif unit_str in cls.ALIAS: 
    233             return cls.ALIAS[unit_str] 
    234         elif lower in cls.ALIAS: 
    235             return cls.ALIAS[lower] 
    236         else: 
    237             raise Exception('Could not find a unit keyword associated with "%s"' % unit_str) 
    238  
    239 class Area(object): 
    240     # TODO: Add units from above. 
    241     UNITS = { 
    242         'sq_m': 1.0, 
    243         'sq_km': 1000000.0, 
    244         'sq_mi': 2589988.110336, 
    245         'sq_ft': 0.09290304, 
    246         'sq_yd': 0.83612736, 
    247         'sq_nm': 3429904.0, 
    248     } 
     243class Area(MeasureBase): 
     244    # Getting the square units values and the alias dictionary. 
     245    UNITS = dict([('sq_%s' % k, v ** 2) for k, v in Distance.UNITS.items()]) 
     246    ALIAS = dict([(k, 'sq_%s' % v) for k, v in Distance.ALIAS.items()]) 
     247    LALIAS = dict([(k.lower(), v) for k, v in ALIAS.items()]) 
    249248 
    250249    def __init__(self, default_unit=None, **kwargs): 
    251         self.sq_m = 0.0 
    252         self._default_unit = 'sq_m' 
    253          
    254         for unit,value in kwargs.items(): 
    255             if unit in self.UNITS: 
    256                 self.sq_m += self.UNITS[unit] * value 
    257                 self._default_unit = unit 
    258             else: 
    259                 raise AttributeError('Unknown unit type: ' + unit) 
    260  
    261         if default_unit: 
     250        self.sq_m, self._default_unit = self.default_units(kwargs) 
     251        if default_unit and isinstance(default_unit, str): 
    262252            self._default_unit = default_unit 
    263253     
     
    334324    def __nonzero__(self): 
    335325        return bool(self.sq_m) 
    336  
    337326         
    338327# Shortcuts