|
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 _get_single_external(self, index): |
|
78 return self._cs[index] |
|
79 |
|
80 _get_single_internal = _get_single_external |
|
81 |
|
82 def _set_list(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 |