django/contrib/gis/geos/linestring.py
author Justin Bronn <jbronn@geodjango.org>
Thu Mar 19 13:08:50 2009 -0500 (3 years ago)
branchtrunk
changeset 362 87b34dce202d
parent 360 fd85d0fb4fb5
child 435 d75ff359e306
permissions -rw-r--r--
Added `merged` property to LineString & MultiLineString, which returns the output of the GEOS line merging operation.
     1 from django.contrib.gis.geos.base import numpy
     2 from django.contrib.gis.geos.coordseq import GEOSCoordSeq
     3 from django.contrib.gis.geos.error import GEOSException
     4 from django.contrib.gis.geos.geometry import GEOSGeometry
     5 from django.contrib.gis.geos.point import Point
     6 from django.contrib.gis.geos import prototypes as capi
     7 
     8 class LineString(GEOSGeometry):
     9     _init_func = capi.create_linestring
    10     _minlength = 2
    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 __iter__(self):
    69         "Allows iteration over this LineString."
    70         for i in xrange(len(self)):
    71             yield self[i]
    72 
    73     def __len__(self):
    74         "Returns the number of points in this LineString."
    75         return len(self._cs)
    76 
    77     def _getitem_external(self, index):
    78         self._checkindex(index)
    79         return self._cs[index]
    80     _getitem_internal = _getitem_external
    81 
    82     def _set_collection(self, length, items):
    83         ndim = self._cs.dims #
    84         hasz = self._cs.hasz # I don't understand why these are different
    85 
    86         # create a new coordinate sequence and populate accordingly
    87         cs = GEOSCoordSeq(capi.create_cs(length, ndim), z=hasz)
    88         for i, c in enumerate(items):
    89             cs[i] = c
    90 
    91         ptr = self._init_func(cs.ptr)
    92         if ptr:
    93             capi.destroy_geom(self.ptr)
    94             self.ptr = ptr
    95             self._post_init(self.srid)
    96         else:
    97             # can this happen?
    98             raise GEOSException('Geometry resulting from slice deletion was invalid.')
    99 
   100     def _set_single(self, index, value):
   101         self._checkindex(index)
   102         self._cs[index] = value
   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 merged(self):
   130         "Returns the line merge of this LineString."
   131         return self._topology(capi.geos_linemerge(self.ptr))   
   132 
   133     @property
   134     def x(self):
   135         "Returns a list or numpy array of the X variable."
   136         return self._listarr(self._cs.getX)
   137 
   138     @property
   139     def y(self):
   140         "Returns a list or numpy array of the Y variable."
   141         return self._listarr(self._cs.getY)
   142 
   143     @property
   144     def z(self):
   145         "Returns a list or numpy array of the Z variable."
   146         if not self.hasz: return None
   147         else: return self._listarr(self._cs.getZ)
   148 
   149 # LinearRings are LineStrings used within Polygons.
   150 class LinearRing(LineString):
   151     _minLength = 4
   152     _init_func = capi.create_linearring