django/contrib/gis/geos/base.py
author Justin Bronn <jbronn@geodjango.org>
Sat Jan 31 15:18:50 2009 -0600 (3 years ago)
branchtrunk
changeset 138 466bece04a15
parent 123 9ec2cb291fb8
child 139 1574381fb26c
permissions -rw-r--r--
Merged in patch from Aryeh Leib Taurog for #9877, adapting as necessary.
     1 from ctypes import c_void_p
     2 from types import NoneType
     3 from django.contrib.gis.geos.error import GEOSException, GEOSIndexError
     4 
     5 # Trying to import GDAL libraries, if available.  Have to place in
     6 # try/except since this package may be used outside GeoDjango.
     7 try:
     8     from django.contrib.gis import gdal
     9 except ImportError:
    10     # A 'dummy' gdal module.
    11     class GDALInfo(object):
    12         HAS_GDAL = False
    13         GEOJSON = False
    14     gdal = GDALInfo()
    15 
    16 # NumPy supported?
    17 try:
    18     import numpy
    19 except ImportError:
    20     numpy = False
    21 
    22 class GEOSBase(object):
    23     """
    24     Base object for GEOS objects that has a pointer access property
    25     that controls access to the underlying C pointer.
    26     """
    27     # Initially the pointer is NULL.
    28     _ptr = None
    29 
    30     # Default allowed pointer type.
    31     ptr_type = c_void_p
    32 
    33     # Pointer access property.
    34     def _get_ptr(self):
    35         # Raise an exception if the pointer isn't valid don't
    36         # want to be passing NULL pointers to routines --
    37         # that's very bad.
    38         if self._ptr: return self._ptr
    39         else: raise GEOSException('NULL GEOS %s pointer encountered.' % self.__class__.__name__)
    40 
    41     def _set_ptr(self, ptr):
    42         # Only allow the pointer to be set with pointers of the
    43         # compatible type or None (NULL).
    44         if isinstance(ptr, int):
    45             self._ptr = self.ptr_type(ptr)
    46         elif isinstance(ptr, (self.ptr_type, NoneType)):
    47             self._ptr = ptr
    48         else:
    49             raise TypeError('Incompatible pointer type')
    50 
    51     # Property for controlling access to the GEOS object pointers.  Using
    52     # this raises an exception when the pointer is NULL, thus preventing
    53     # the C library from attempting to access an invalid memory location.
    54     ptr = property(_get_ptr, _set_ptr)
    55 
    56 class ListMixin(object):
    57 
    58     _canSetSingle = False
    59     
    60     def _setSingle_rebuild(self, index, value):
    61         self._checkindex(index)
    62         self._setSlice(slice(index, index + 1, 1), [value])        
    63     _setSingle = _setSingle_rebuild
    64 
    65     def _checkindex(self, index):
    66         if index < 0 or index >= len(self):
    67             raise GEOSIndexError('invalid index: %s' % str(index))
    68 
    69     def _checkAllowedTypes(self, items):
    70         # Ensuring that only the permitted geometries are allowed in this collection
    71         if hasattr(self, '_allowed'):
    72             if False in [isinstance(geom, self._allowed) for geom in items]:
    73                 raise TypeError('Invalid Geometry type encountered in the arguments.')
    74 
    75     def __getitem__(self, index):
    76         "Gets the coordinates of the point(s) at the specified index/slice."
    77         if isinstance(index, slice):
    78             return [self._getItemExternal(i) for i in xrange(*index.indices(len(self)))]
    79         else:
    80             if index < 0:
    81                 index += len(self)
    82             return self._getItemExternal(index)
    83 
    84     def __delitem__(self, index):
    85         "Delete the point(s) at the specified index/slice."
    86         if not isinstance(index, (int, long, slice)):
    87             raise TypeError("%s is not a legal index" % index)
    88 
    89         # calculate new length and dimensions
    90         origLen     = len(self)
    91         if isinstance(index, (int, long)):
    92             if index < 0: index += origLen
    93             if not 0 <= index < origLen:
    94                 raise GEOSIndexError('invalid index: %d' % index)
    95             indexRange  = [index]
    96         else:
    97             indexRange  = range(*index.indices(origLen))
    98 
    99         newLen      = origLen - len(indexRange)
   100         newItems    = ( self._getItemInternal(i)
   101                         for i in xrange(origLen)
   102                         if i not in indexRange )
   103 
   104         self._setCollection(newLen, newItems)
   105 
   106     def __setitem__(self, index, geom):
   107         "Sets the Geometry at the specified index."
   108         if isinstance(index, slice):
   109             self._setSlice(index, geom)
   110         else:
   111             if index < 0: index += len(self)
   112             self._setSingle(index, geom)
   113 
   114     def _setSlice(self, index, values):
   115         "Assign values to a slice of the object"
   116         try:
   117             iter(values)
   118         except TypeError:
   119             raise TypeError('can only assign an iterable to a slice')
   120 
   121         self._checkAllowedTypes(values)
   122 
   123         origLen     = len(self)
   124         valueList   = list(values)
   125         start, stop, step = index.indices(origLen)
   126         stop = max(0, stop) # stop will be -1 if out-of-bounds
   127                             # negative index is given
   128 
   129         # CAREFUL: index.step and step are not the same!
   130         # step will never be None
   131         #
   132         if index.step is None:
   133             self._assignSimpleSlice(start, stop, valueList)
   134         else:
   135             self._assignExtendedSlice(start, stop, step, valueList)
   136 
   137     def _assignExtendedSlice(self, start, stop, step, valueList):
   138         'Assign an extended slice by rebuilding entire list'
   139         indexList   = range(start, stop, step)
   140         # extended slice, only allow assigning slice of same size
   141         if len(valueList) != len(indexList):
   142             raise ValueError('attempt to assign sequence of size %d '
   143                              'to extended slice of size %d'
   144                              % (len(valueList), len(indexList)))
   145 
   146         # we're not changing the length of the sequence
   147         newLen  = len(self)
   148         newVals = dict(zip(indexList, valueList))
   149         def newItems():
   150             for i in xrange(newLen):
   151                 if i in newVals:
   152                     yield newVals[i]
   153                 else:
   154                     yield self._getItemInternal(i)
   155 
   156         self._setCollection(newLen, newItems())
   157 
   158     def _assignExtendedSlice_no_rebuild(self, start, stop, step, valueList):
   159         'Assign an extended slice by re-assigning individual items'
   160         indexList   = range(start, stop, step)
   161         # extended slice, only allow assigning slice of same size
   162         if len(valueList) != len(indexList):
   163             raise ValueError('attempt to assign sequence of size %d '
   164                              'to extended slice of size %d'
   165                              % (len(valueList), len(indexList)))
   166 
   167         for i, val in zip(indexList, valueList):
   168             self._setSingle(i, val)
   169 
   170     def _assignSimpleSlice(self, start, stop, valueList):
   171         'Assign a simple slice; Can assign slice of any length'
   172         origLen = len(self)
   173         newLen  = origLen - stop + start + len(valueList)
   174         def newItems():
   175             for i in xrange(origLen + 1):
   176                 if i == start:
   177                     for val in valueList:
   178                         yield val
   179 
   180                 if i < origLen:
   181                     if i < start or i >= stop:
   182                         yield self._getItemInternal(i)
   183 
   184         self._setCollection(newLen, newItems())
   185 
   186     def append(self, val):
   187         "Standard list append method"
   188         self[len(self):] = [val]
   189 
   190     def extend(self, vals):
   191         "Standard list extend method"
   192         self[len(self):] = vals
   193 
   194     def insert(self, index, val):
   195         "Standard list insert method"
   196         if not isinstance(index, (int, long)):
   197             raise TypeError("%s is not a legal index" % index)
   198         self[index:index] = [val]
   199 
   200     def pop(self, index=-1):
   201         "Standard list pop method"
   202         result = self[index]
   203         del self[index]
   204         return result
   205 
   206     def index(self, val):
   207         "Standard list index method"
   208         for i in xrange(0, len(self)):
   209             if self[i] == val: return i
   210         raise ValueError('%s not in geometry' % str(val))
   211 
   212     def remove(self, val):
   213         "Standard list remove method"
   214         del self[self.index(val)]
   215 
   216     def count(self):
   217         "Standard list count method"
   218         return len(self)