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.
jbronn@1
     1
from django.contrib.gis.geos.base import numpy
jbronn@1
     2
from django.contrib.gis.geos.coordseq import GEOSCoordSeq
jbronn@139
     3
from django.contrib.gis.geos.error import GEOSException
jbronn@1
     4
from django.contrib.gis.geos.geometry import GEOSGeometry
jbronn@1
     5
from django.contrib.gis.geos.point import Point
jbronn@124
     6
from django.contrib.gis.geos import prototypes as capi
jbronn@1
     7
jbronn@1
     8
class LineString(GEOSGeometry):
jbronn@138
     9
    _init_func = capi.create_linestring
jbronn@360
    10
    _minlength = 2
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@352
    52
        # Creating a coordinate sequence object because it is easier to
jbronn@353
    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@352
    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@352
    63
jbronn@352
    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@353
    68
    def __iter__(self):
jbronn@353
    69
        "Allows iteration over this LineString."
jbronn@353
    70
        for i in xrange(len(self)):
jbronn@353
    71
            yield self[i]
jbronn@353
    72
jbronn@353
    73
    def __len__(self):
jbronn@353
    74
        "Returns the number of points in this LineString."
jbronn@353
    75
        return len(self._cs)
jbronn@353
    76
jbronn@358
    77
    def _getitem_external(self, index):
jbronn@138
    78
        self._checkindex(index)
jbronn@1
    79
        return self._cs[index]
jbronn@358
    80
    _getitem_internal = _getitem_external
jbronn@1
    81
jbronn@359
    82
    def _set_collection(self, length, items):
jbronn@138
    83
        ndim = self._cs.dims #
jbronn@138
    84
        hasz = self._cs.hasz # I don't understand why these are different
jbronn@138
    85
jbronn@138
    86
        # create a new coordinate sequence and populate accordingly
jbronn@138
    87
        cs = GEOSCoordSeq(capi.create_cs(length, ndim), z=hasz)
jbronn@138
    88
        for i, c in enumerate(items):
jbronn@138
    89
            cs[i] = c
jbronn@138
    90
jbronn@138
    91
        ptr = self._init_func(cs.ptr)
jbronn@138
    92
        if ptr:
jbronn@138
    93
            capi.destroy_geom(self.ptr)
jbronn@139
    94
            self.ptr = ptr
jbronn@138
    95
            self._post_init(self.srid)
jbronn@138
    96
        else:
jbronn@138
    97
            # can this happen?
jbronn@138
    98
            raise GEOSException('Geometry resulting from slice deletion was invalid.')
jbronn@352
    99
jbronn@359
   100
    def _set_single(self, index, value):
jbronn@138
   101
        self._checkindex(index)
jbronn@1
   102
        self._cs[index] = value
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@362
   129
    def merged(self):
jbronn@362
   130
        "Returns the line merge of this LineString."
jbronn@362
   131
        return self._topology(capi.geos_linemerge(self.ptr))   
jbronn@362
   132
jbronn@362
   133
    @property
jbronn@1
   134
    def x(self):
jbronn@1
   135
        "Returns a list or numpy array of the X variable."
jbronn@1
   136
        return self._listarr(self._cs.getX)
jbronn@352
   137
jbronn@1
   138
    @property
jbronn@1
   139
    def y(self):
jbronn@1
   140
        "Returns a list or numpy array of the Y variable."
jbronn@1
   141
        return self._listarr(self._cs.getY)
jbronn@1
   142
jbronn@1
   143
    @property
jbronn@1
   144
    def z(self):
jbronn@1
   145
        "Returns a list or numpy array of the Z variable."
jbronn@1
   146
        if not self.hasz: return None
jbronn@1
   147
        else: return self._listarr(self._cs.getZ)
jbronn@1
   148
jbronn@1
   149
# LinearRings are LineStrings used within Polygons.
jbronn@1
   150
class LinearRing(LineString):
jbronn@352
   151
    _minLength = 4
jbronn@138
   152
    _init_func = capi.create_linearring