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