django/contrib/gis/geos/linestring.py
author Justin Bronn <jbronn@geodjango.org>
Sat Jan 31 15:18:50 2009 -0600 (3 years ago)
branchtrunk
changeset 138 466bece04a15
parent 124 2c62df811d06
child 139 1574381fb26c
permissions -rw-r--r--
Merged in patch from Aryeh Leib Taurog for #9877, adapting as necessary.
jbronn@1
     1
from django.contrib.gis.geos.base import numpy
jbronn@1
     2
from django.contrib.gis.geos.coordseq import GEOSCoordSeq
jbronn@1
     3
from django.contrib.gis.geos.geometry import GEOSGeometry
jbronn@1
     4
from django.contrib.gis.geos.point import Point
jbronn@124
     5
from django.contrib.gis.geos import prototypes as capi
jbronn@1
     6
jbronn@1
     7
class LineString(GEOSGeometry):
jbronn@138
     8
    _init_func = capi.create_linestring
jbronn@138
     9
    _canSetSingle = True
jbronn@138
    10
    _assignExtendedSlice = GEOSGeometry._assignExtendedSlice_no_rebuild
jbronn@1
    11
jbronn@1
    12
    #### Python 'magic' routines ####
jbronn@1
    13
    def __init__(self, *args, **kwargs):
jbronn@1
    14
        """
jbronn@1
    15
        Initializes on the given sequence -- may take lists, tuples, NumPy arrays
jbronn@1
    16
        of X,Y pairs, or Point objects.  If Point objects are used, ownership is
jbronn@1
    17
        _not_ transferred to the LineString object.
jbronn@1
    18
jbronn@1
    19
        Examples:
jbronn@1
    20
         ls = LineString((1, 1), (2, 2))
jbronn@1
    21
         ls = LineString([(1, 1), (2, 2)])
jbronn@1
    22
         ls = LineString(array([(1, 1), (2, 2)]))
jbronn@1
    23
         ls = LineString(Point(1, 1), Point(2, 2))
jbronn@1
    24
        """
jbronn@1
    25
        # If only one argument provided, set the coords array appropriately
jbronn@1
    26
        if len(args) == 1: coords = args[0]
jbronn@1
    27
        else: coords = args
jbronn@1
    28
jbronn@1
    29
        if isinstance(coords, (tuple, list)):
jbronn@1
    30
            # Getting the number of coords and the number of dimensions -- which
jbronn@1
    31
            #  must stay the same, e.g., no LineString((1, 2), (1, 2, 3)).
jbronn@1
    32
            ncoords = len(coords)
jbronn@1
    33
            if coords: ndim = len(coords[0])
jbronn@1
    34
            else: raise TypeError('Cannot initialize on empty sequence.')
jbronn@1
    35
            self._checkdim(ndim)
jbronn@1
    36
            # Incrementing through each of the coordinates and verifying
jbronn@1
    37
            for i in xrange(1, ncoords):
jbronn@1
    38
                if not isinstance(coords[i], (tuple, list, Point)):
jbronn@1
    39
                    raise TypeError('each coordinate should be a sequence (list or tuple)')
jbronn@1
    40
                if len(coords[i]) != ndim: raise TypeError('Dimension mismatch.')
jbronn@1
    41
            numpy_coords = False
jbronn@1
    42
        elif numpy and isinstance(coords, numpy.ndarray):
jbronn@1
    43
            shape = coords.shape # Using numpy's shape.
jbronn@1
    44
            if len(shape) != 2: raise TypeError('Too many dimensions.')
jbronn@1
    45
            self._checkdim(shape[1])
jbronn@1
    46
            ncoords = shape[0]
jbronn@1
    47
            ndim = shape[1]
jbronn@1
    48
            numpy_coords = True
jbronn@1
    49
        else:
jbronn@1
    50
            raise TypeError('Invalid initialization input for LineStrings.')
jbronn@1
    51
jbronn@1
    52
        # Creating a coordinate sequence object because it is easier to 
jbronn@1
    53
        #  set the points using GEOSCoordSeq.__setitem__().
jbronn@124
    54
        cs = GEOSCoordSeq(capi.create_cs(ncoords, ndim), z=bool(ndim==3))
jbronn@124
    55
jbronn@1
    56
        for i in xrange(ncoords):
jbronn@1
    57
            if numpy_coords: cs[i] = coords[i,:]
jbronn@1
    58
            elif isinstance(coords[i], Point): cs[i] = coords[i].tuple
jbronn@1
    59
            else: cs[i] = coords[i]        
jbronn@1
    60
jbronn@1
    61
        # If SRID was passed in with the keyword arguments
jbronn@1
    62
        srid = kwargs.get('srid', None)
jbronn@1
    63
       
jbronn@1
    64
        # Calling the base geometry initialization with the returned pointer 
jbronn@1
    65
        #  from the function.
jbronn@138
    66
        super(LineString, self).__init__(self._init_func(cs.ptr), srid=srid)
jbronn@1
    67
jbronn@138
    68
    def _getItemExternal(self, index):
jbronn@138
    69
        self._checkindex(index)
jbronn@1
    70
        return self._cs[index]
jbronn@138
    71
    _getItemInternal = _getItemExternal
jbronn@1
    72
jbronn@138
    73
    def _setCollection(self, length, items):
jbronn@138
    74
        ndim = self._cs.dims #
jbronn@138
    75
        hasz = self._cs.hasz # I don't understand why these are different
jbronn@138
    76
jbronn@138
    77
        # create a new coordinate sequence and populate accordingly
jbronn@138
    78
        cs = GEOSCoordSeq(capi.create_cs(length, ndim), z=hasz)
jbronn@138
    79
        for i, c in enumerate(items):
jbronn@138
    80
            cs[i] = c
jbronn@138
    81
jbronn@138
    82
        ptr = self._init_func(cs.ptr)
jbronn@138
    83
        if ptr:
jbronn@138
    84
            capi.destroy_geom(self.ptr)
jbronn@138
    85
            self._ptr = ptr
jbronn@138
    86
            self._post_init(self.srid)
jbronn@138
    87
        else:
jbronn@138
    88
            # can this happen?
jbronn@138
    89
            raise GEOSException('Geometry resulting from slice deletion was invalid.')
jbronn@138
    90
        
jbronn@138
    91
    def _setSingle(self, index, value):
jbronn@138
    92
        self._checkindex(index)
jbronn@1
    93
        self._cs[index] = value
jbronn@1
    94
jbronn@1
    95
    def __iter__(self):
jbronn@1
    96
        "Allows iteration over this LineString."
jbronn@1
    97
        for i in xrange(len(self)):
jbronn@1
    98
            yield self[i]
jbronn@1
    99
jbronn@1
   100
    def __len__(self):
jbronn@1
   101
        "Returns the number of points in this LineString."
jbronn@1
   102
        return len(self._cs)
jbronn@1
   103
jbronn@1
   104
    def _checkdim(self, dim):
jbronn@1
   105
        if dim not in (2, 3): raise TypeError('Dimension mismatch.')
jbronn@1
   106
jbronn@1
   107
    #### Sequence Properties ####
jbronn@1
   108
    @property
jbronn@1
   109
    def tuple(self):
jbronn@1
   110
        "Returns a tuple version of the geometry from the coordinate sequence."
jbronn@1
   111
        return self._cs.tuple
jbronn@1
   112
    coords = tuple
jbronn@1
   113
jbronn@1
   114
    def _listarr(self, func):
jbronn@1
   115
        """
jbronn@1
   116
        Internal routine that returns a sequence (list) corresponding with
jbronn@1
   117
        the given function.  Will return a numpy array if possible.
jbronn@1
   118
        """
jbronn@1
   119
        lst = [func(i) for i in xrange(len(self))]
jbronn@1
   120
        if numpy: return numpy.array(lst) # ARRRR!
jbronn@1
   121
        else: return lst
jbronn@1
   122
jbronn@1
   123
    @property
jbronn@1
   124
    def array(self):
jbronn@1
   125
        "Returns a numpy array for the LineString."
jbronn@1
   126
        return self._listarr(self._cs.__getitem__)
jbronn@1
   127
jbronn@1
   128
    @property
jbronn@1
   129
    def x(self):
jbronn@1
   130
        "Returns a list or numpy array of the X variable."
jbronn@1
   131
        return self._listarr(self._cs.getX)
jbronn@1
   132
    
jbronn@1
   133
    @property
jbronn@1
   134
    def y(self):
jbronn@1
   135
        "Returns a list or numpy array of the Y variable."
jbronn@1
   136
        return self._listarr(self._cs.getY)
jbronn@1
   137
jbronn@1
   138
    @property
jbronn@1
   139
    def z(self):
jbronn@1
   140
        "Returns a list or numpy array of the Z variable."
jbronn@1
   141
        if not self.hasz: return None
jbronn@1
   142
        else: return self._listarr(self._cs.getZ)
jbronn@1
   143
jbronn@1
   144
# LinearRings are LineStrings used within Polygons.
jbronn@1
   145
class LinearRing(LineString):
jbronn@138
   146
    _init_func = capi.create_linearring