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