|
0
|
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 |