shapely 1.3.3__cp34-none-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Shapely-1.3.3.data/data/shapely/_geos.pxi +52 -0
- Shapely-1.3.3.dist-info/DESCRIPTION.rst +505 -0
- Shapely-1.3.3.dist-info/METADATA +527 -0
- Shapely-1.3.3.dist-info/RECORD +95 -0
- Shapely-1.3.3.dist-info/WHEEL +5 -0
- Shapely-1.3.3.dist-info/metadata.json +1 -0
- Shapely-1.3.3.dist-info/top_level.txt +1 -0
- shapely/DLLs/geos.dll +0 -0
- shapely/DLLs/geos_c.dll +0 -0
- shapely/__init__.py +1 -0
- shapely/_geos.pxi +52 -0
- shapely/affinity.py +255 -0
- shapely/algorithms/__init__.py +0 -0
- shapely/algorithms/cga.py +16 -0
- shapely/coords.py +206 -0
- shapely/ctypes_declarations.py +455 -0
- shapely/examples/__init__.py +1 -0
- shapely/examples/dissolve.py +53 -0
- shapely/examples/geoms.py +51 -0
- shapely/examples/intersect.py +81 -0
- shapely/examples/world.py +21 -0
- shapely/ftools.py +78 -0
- shapely/geometry/__init__.py +20 -0
- shapely/geometry/base.py +797 -0
- shapely/geometry/collection.py +43 -0
- shapely/geometry/geo.py +75 -0
- shapely/geometry/linestring.py +298 -0
- shapely/geometry/multilinestring.py +156 -0
- shapely/geometry/multipoint.py +213 -0
- shapely/geometry/multipolygon.py +186 -0
- shapely/geometry/point.py +269 -0
- shapely/geometry/polygon.py +504 -0
- shapely/geometry/proxy.py +51 -0
- shapely/geos.py +732 -0
- shapely/impl.py +126 -0
- shapely/iterops.py +55 -0
- shapely/linref.py +26 -0
- shapely/ops.py +234 -0
- shapely/predicates.py +23 -0
- shapely/prepared.py +61 -0
- shapely/speedups/__init__.py +43 -0
- shapely/speedups/_speedups.c +5674 -0
- shapely/speedups/_speedups.pyd +0 -0
- shapely/tests/__init__.py +81 -0
- shapely/tests/binascii_hex.txt +47 -0
- shapely/tests/test_affinity.py +255 -0
- shapely/tests/test_box.py +24 -0
- shapely/tests/test_cga.py +37 -0
- shapely/tests/test_collection.py +29 -0
- shapely/tests/test_delegated.py +34 -0
- shapely/tests/test_dlls.py +20 -0
- shapely/tests/test_doctests.py +36 -0
- shapely/tests/test_emptiness.py +53 -0
- shapely/tests/test_equality.py +29 -0
- shapely/tests/test_geointerface.py +74 -0
- shapely/tests/test_geomseq.py +13 -0
- shapely/tests/test_getitem.py +125 -0
- shapely/tests/test_invalid_geometries.py +30 -0
- shapely/tests/test_iterops.py +47 -0
- shapely/tests/test_linear_referencing.py +72 -0
- shapely/tests/test_linemerge.py +52 -0
- shapely/tests/test_linestring.py +111 -0
- shapely/tests/test_locale.py +56 -0
- shapely/tests/test_mapping.py +13 -0
- shapely/tests/test_multilinestring.py +61 -0
- shapely/tests/test_multipoint.py +73 -0
- shapely/tests/test_multipolygon.py +72 -0
- shapely/tests/test_ndarrays.py +54 -0
- shapely/tests/test_operations.py +70 -0
- shapely/tests/test_operators.py +18 -0
- shapely/tests/test_persist.py +34 -0
- shapely/tests/test_pickle.py +22 -0
- shapely/tests/test_point.py +139 -0
- shapely/tests/test_polygon.py +186 -0
- shapely/tests/test_polygonize.py +49 -0
- shapely/tests/test_predicates.py +34 -0
- shapely/tests/test_prepared.py +31 -0
- shapely/tests/test_products_z.py +17 -0
- shapely/tests/test_singularity.py +19 -0
- shapely/tests/test_speedups.py +39 -0
- shapely/tests/test_styles.py +20 -0
- shapely/tests/test_transform.py +84 -0
- shapely/tests/test_union.py +72 -0
- shapely/tests/test_validation.py +12 -0
- shapely/tests/test_vectorized.py +107 -0
- shapely/tests/test_xy.py +17 -0
- shapely/tests/threading_test.py +40 -0
- shapely/topology.py +60 -0
- shapely/validation.py +8 -0
- shapely/vectorized/__init__.py +3 -0
- shapely/vectorized/_vectorized.c +20816 -0
- shapely/vectorized/_vectorized.pyd +0 -0
- shapely/vectorized/_vectorized.pyx +120 -0
- shapely/wkb.py +37 -0
- shapely/wkt.py +26 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Multi-part collections of geometries
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
from shapely.geometry.base import BaseMultipartGeometry
|
|
5
|
+
from shapely.geometry.base import HeterogeneousGeometrySequence
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GeometryCollection(BaseMultipartGeometry):
|
|
9
|
+
|
|
10
|
+
"""A heterogenous collection of geometries
|
|
11
|
+
|
|
12
|
+
Attributes
|
|
13
|
+
----------
|
|
14
|
+
geoms : sequence
|
|
15
|
+
A sequence of Shapely geometry instances
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
BaseMultipartGeometry.__init__(self)
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def __geo_interface__(self):
|
|
23
|
+
geometries = []
|
|
24
|
+
for geom in self.geoms:
|
|
25
|
+
geometries.append(geom.__geo_interface__)
|
|
26
|
+
return dict(type='GeometryCollection', geometries=geometries)
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def geoms(self):
|
|
30
|
+
if self.is_empty:
|
|
31
|
+
return []
|
|
32
|
+
return HeterogeneousGeometrySequence(self)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Test runner
|
|
36
|
+
def _test():
|
|
37
|
+
import doctest
|
|
38
|
+
doctest.testmod()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
_test()
|
|
43
|
+
|
shapely/geometry/geo.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Geometry factories based on the geo interface
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .point import Point, asPoint
|
|
6
|
+
from .linestring import LineString, asLineString
|
|
7
|
+
from .polygon import Polygon, asPolygon
|
|
8
|
+
from .multipoint import MultiPoint, asMultiPoint
|
|
9
|
+
from .multilinestring import MultiLineString, asMultiLineString
|
|
10
|
+
from .multipolygon import MultiPolygon, MultiPolygonAdapter
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def box(minx, miny, maxx, maxy, ccw=True):
|
|
14
|
+
"""Returns a rectangular polygon with configurable normal vector"""
|
|
15
|
+
coords = [(maxx, miny), (maxx, maxy), (minx, maxy), (minx, miny)]
|
|
16
|
+
if not ccw:
|
|
17
|
+
coords = coords[::-1]
|
|
18
|
+
return Polygon(coords)
|
|
19
|
+
|
|
20
|
+
def shape(context):
|
|
21
|
+
"""Returns a new, independent geometry with coordinates *copied* from the
|
|
22
|
+
context.
|
|
23
|
+
"""
|
|
24
|
+
if hasattr(context, "__geo_interface__"):
|
|
25
|
+
ob = context.__geo_interface__
|
|
26
|
+
else:
|
|
27
|
+
ob = context
|
|
28
|
+
geom_type = ob.get("type").lower()
|
|
29
|
+
if geom_type == "point":
|
|
30
|
+
return Point(ob["coordinates"])
|
|
31
|
+
elif geom_type == "linestring":
|
|
32
|
+
return LineString(ob["coordinates"])
|
|
33
|
+
elif geom_type == "polygon":
|
|
34
|
+
return Polygon(ob["coordinates"][0], ob["coordinates"][1:])
|
|
35
|
+
elif geom_type == "multipoint":
|
|
36
|
+
return MultiPoint(ob["coordinates"])
|
|
37
|
+
elif geom_type == "multilinestring":
|
|
38
|
+
return MultiLineString(ob["coordinates"])
|
|
39
|
+
elif geom_type == "multipolygon":
|
|
40
|
+
return MultiPolygon(ob["coordinates"], context_type='geojson')
|
|
41
|
+
else:
|
|
42
|
+
raise ValueError("Unknown geometry type: %s" % geom_type)
|
|
43
|
+
|
|
44
|
+
def asShape(context):
|
|
45
|
+
"""Adapts the context to a geometry interface. The coordinates remain
|
|
46
|
+
stored in the context.
|
|
47
|
+
"""
|
|
48
|
+
if hasattr(context, "__geo_interface__"):
|
|
49
|
+
ob = context.__geo_interface__
|
|
50
|
+
else:
|
|
51
|
+
ob = context
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
geom_type = ob.get("type").lower()
|
|
55
|
+
except AttributeError:
|
|
56
|
+
raise ValueError("Context does not provide geo interface")
|
|
57
|
+
|
|
58
|
+
if geom_type == "point":
|
|
59
|
+
return asPoint(ob["coordinates"])
|
|
60
|
+
elif geom_type == "linestring":
|
|
61
|
+
return asLineString(ob["coordinates"])
|
|
62
|
+
elif geom_type == "polygon":
|
|
63
|
+
return asPolygon(ob["coordinates"][0], ob["coordinates"][1:])
|
|
64
|
+
elif geom_type == "multipoint":
|
|
65
|
+
return asMultiPoint(ob["coordinates"])
|
|
66
|
+
elif geom_type == "multilinestring":
|
|
67
|
+
return asMultiLineString(ob["coordinates"])
|
|
68
|
+
elif geom_type == "multipolygon":
|
|
69
|
+
return MultiPolygonAdapter(ob["coordinates"], context_type='geojson')
|
|
70
|
+
else:
|
|
71
|
+
raise ValueError("Unknown geometry type: %s" % geom_type)
|
|
72
|
+
|
|
73
|
+
def mapping(ob):
|
|
74
|
+
"""Returns a GeoJSON-like mapping"""
|
|
75
|
+
return ob.__geo_interface__
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""Line strings and related utilities
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
if sys.version_info[0] < 3:
|
|
7
|
+
range = xrange
|
|
8
|
+
|
|
9
|
+
from ctypes import c_double, cast, POINTER
|
|
10
|
+
|
|
11
|
+
from shapely.coords import required
|
|
12
|
+
from shapely.geos import lgeos, TopologicalError
|
|
13
|
+
from shapely.geometry.base import BaseGeometry, geom_factory, JOIN_STYLE
|
|
14
|
+
from shapely.geometry.proxy import CachingGeometryProxy
|
|
15
|
+
from shapely.geometry.point import Point
|
|
16
|
+
|
|
17
|
+
__all__ = ['LineString', 'asLineString']
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LineString(BaseGeometry):
|
|
21
|
+
"""
|
|
22
|
+
A one-dimensional figure comprising one or more line segments
|
|
23
|
+
|
|
24
|
+
A LineString has non-zero length and zero area. It may approximate a curve
|
|
25
|
+
and need not be straight. Unlike a LinearRing, a LineString is not closed.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, coordinates=None):
|
|
29
|
+
"""
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
coordinates : sequence
|
|
33
|
+
A sequence of (x, y [,z]) numeric coordinate pairs or triples or
|
|
34
|
+
an object that provides the numpy array interface, including
|
|
35
|
+
another instance of LineString.
|
|
36
|
+
|
|
37
|
+
Example
|
|
38
|
+
-------
|
|
39
|
+
Create a line with two segments
|
|
40
|
+
|
|
41
|
+
>>> a = LineString([[0, 0], [1, 0], [1, 1]])
|
|
42
|
+
>>> a.length
|
|
43
|
+
2.0
|
|
44
|
+
"""
|
|
45
|
+
BaseGeometry.__init__(self)
|
|
46
|
+
if coordinates is not None:
|
|
47
|
+
self._set_coords(coordinates)
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def __geo_interface__(self):
|
|
51
|
+
return {
|
|
52
|
+
'type': 'LineString',
|
|
53
|
+
'coordinates': tuple(self.coords)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def svg(self, scale_factor=1.):
|
|
57
|
+
"""
|
|
58
|
+
SVG representation of the geometry. Scale factor is multiplied by
|
|
59
|
+
the size of the SVG symbol so it can be scaled consistently for a
|
|
60
|
+
consistent appearance based on the canvas size.
|
|
61
|
+
"""
|
|
62
|
+
pnt_format = " ".join(["{0},{1}".format(*c) for c in self.coords])
|
|
63
|
+
return """<polyline
|
|
64
|
+
fill="none"
|
|
65
|
+
stroke="{2}"
|
|
66
|
+
stroke-width={1}
|
|
67
|
+
points="{0}"
|
|
68
|
+
opacity=".8"
|
|
69
|
+
/>""".format(
|
|
70
|
+
pnt_format,
|
|
71
|
+
2.*scale_factor,
|
|
72
|
+
"#66cc99" if self.is_valid else "#ff3333")
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def ctypes(self):
|
|
76
|
+
if not self._ctypes_data:
|
|
77
|
+
self._ctypes_data = self.coords.ctypes
|
|
78
|
+
return self._ctypes_data
|
|
79
|
+
|
|
80
|
+
def array_interface(self):
|
|
81
|
+
"""Provide the Numpy array protocol."""
|
|
82
|
+
return self.coords.array_interface()
|
|
83
|
+
|
|
84
|
+
__array_interface__ = property(array_interface)
|
|
85
|
+
|
|
86
|
+
# Coordinate access
|
|
87
|
+
def _set_coords(self, coordinates):
|
|
88
|
+
self.empty()
|
|
89
|
+
self._geom, self._ndim = geos_linestring_from_py(coordinates)
|
|
90
|
+
|
|
91
|
+
coords = property(BaseGeometry._get_coords, _set_coords)
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def xy(self):
|
|
95
|
+
"""Separate arrays of X and Y coordinate values
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
|
|
99
|
+
>>> x, y = LineString(((0, 0), (1, 1))).xy
|
|
100
|
+
>>> list(x)
|
|
101
|
+
[0.0, 1.0]
|
|
102
|
+
>>> list(y)
|
|
103
|
+
[0.0, 1.0]
|
|
104
|
+
"""
|
|
105
|
+
return self.coords.xy
|
|
106
|
+
|
|
107
|
+
def parallel_offset(
|
|
108
|
+
self, distance, side,
|
|
109
|
+
resolution=16, join_style=JOIN_STYLE.round, mitre_limit=5.0):
|
|
110
|
+
|
|
111
|
+
"""Returns a LineString or MultiLineString geometry at a distance from
|
|
112
|
+
the object on its right or its left side.
|
|
113
|
+
|
|
114
|
+
Distance must be a positive float value. The side parameter may be
|
|
115
|
+
'left' or 'right'. The resolution of the buffer around each vertex of
|
|
116
|
+
the object increases by increasing the resolution keyword parameter or
|
|
117
|
+
third positional parameter.
|
|
118
|
+
|
|
119
|
+
The join style is for outside corners between line segments. Accepted
|
|
120
|
+
values are JOIN_STYLE.round (1), JOIN_STYLE.mitre (2), and
|
|
121
|
+
JOIN_STYLE.bevel (3).
|
|
122
|
+
|
|
123
|
+
The mitre ratio limit is used for very sharp corners. It is the ratio
|
|
124
|
+
of the distance from the corner to the end of the mitred offset corner.
|
|
125
|
+
When two line segments meet at a sharp angle, a miter join will extend
|
|
126
|
+
far beyond the original geometry. To prevent unreasonable geometry, the
|
|
127
|
+
mitre limit allows controlling the maximum length of the join corner.
|
|
128
|
+
Corners with a ratio which exceed the limit will be beveled."""
|
|
129
|
+
if mitre_limit == 0.0:
|
|
130
|
+
raise ValueError(
|
|
131
|
+
'Cannot compute offset from zero-length line segment')
|
|
132
|
+
try:
|
|
133
|
+
return geom_factory(self.impl['parallel_offset'](
|
|
134
|
+
self, distance, resolution, join_style, mitre_limit,
|
|
135
|
+
bool(side == 'left')))
|
|
136
|
+
except OSError:
|
|
137
|
+
raise TopologicalError()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class LineStringAdapter(CachingGeometryProxy, LineString):
|
|
141
|
+
|
|
142
|
+
def __init__(self, context):
|
|
143
|
+
self.context = context
|
|
144
|
+
self.factory = geos_linestring_from_py
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def _ndim(self):
|
|
148
|
+
try:
|
|
149
|
+
# From array protocol
|
|
150
|
+
array = self.context.__array_interface__
|
|
151
|
+
n = array['shape'][1]
|
|
152
|
+
assert n == 2 or n == 3
|
|
153
|
+
return n
|
|
154
|
+
except AttributeError:
|
|
155
|
+
# Fall back on list
|
|
156
|
+
return len(self.context[0])
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def __array_interface__(self):
|
|
160
|
+
"""Provide the Numpy array protocol."""
|
|
161
|
+
try:
|
|
162
|
+
return self.context.__array_interface__
|
|
163
|
+
except AttributeError:
|
|
164
|
+
return self.array_interface()
|
|
165
|
+
|
|
166
|
+
_get_coords = BaseGeometry._get_coords
|
|
167
|
+
|
|
168
|
+
def _set_coords(self, ob):
|
|
169
|
+
raise NotImplementedError(
|
|
170
|
+
"Adapters can not modify their coordinate sources")
|
|
171
|
+
|
|
172
|
+
coords = property(_get_coords)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def asLineString(context):
|
|
176
|
+
"""Adapt an object the LineString interface"""
|
|
177
|
+
return LineStringAdapter(context)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
|
|
181
|
+
# If numpy is present, we use numpy.require to ensure that we have a
|
|
182
|
+
# C-continguous array that owns its data. View data will be copied.
|
|
183
|
+
ob = required(ob)
|
|
184
|
+
try:
|
|
185
|
+
# From array protocol
|
|
186
|
+
array = ob.__array_interface__
|
|
187
|
+
assert len(array['shape']) == 2
|
|
188
|
+
m = array['shape'][0]
|
|
189
|
+
if m < 2:
|
|
190
|
+
raise ValueError(
|
|
191
|
+
"LineStrings must have at least 2 coordinate tuples")
|
|
192
|
+
try:
|
|
193
|
+
n = array['shape'][1]
|
|
194
|
+
except IndexError:
|
|
195
|
+
raise ValueError(
|
|
196
|
+
"Input %s is the wrong shape for a LineString" % str(ob))
|
|
197
|
+
assert n == 2 or n == 3
|
|
198
|
+
|
|
199
|
+
# Make pointer to the coordinate array
|
|
200
|
+
if isinstance(array['data'], tuple):
|
|
201
|
+
# numpy tuple (addr, read-only)
|
|
202
|
+
cp = cast(array['data'][0], POINTER(c_double))
|
|
203
|
+
else:
|
|
204
|
+
cp = array['data']
|
|
205
|
+
|
|
206
|
+
# Create a coordinate sequence
|
|
207
|
+
if update_geom is not None:
|
|
208
|
+
cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
|
|
209
|
+
if n != update_ndim:
|
|
210
|
+
raise ValueError(
|
|
211
|
+
"Wrong coordinate dimensions; this geometry has "
|
|
212
|
+
"dimensions: %d" % update_ndim)
|
|
213
|
+
else:
|
|
214
|
+
cs = lgeos.GEOSCoordSeq_create(m, n)
|
|
215
|
+
|
|
216
|
+
# add to coordinate sequence
|
|
217
|
+
for i in range(m):
|
|
218
|
+
dx = c_double(cp[n*i])
|
|
219
|
+
dy = c_double(cp[n*i+1])
|
|
220
|
+
dz = None
|
|
221
|
+
if n == 3:
|
|
222
|
+
try:
|
|
223
|
+
dz = c_double(cp[n*i+2])
|
|
224
|
+
except IndexError:
|
|
225
|
+
raise ValueError("Inconsistent coordinate dimensionality")
|
|
226
|
+
|
|
227
|
+
# Because of a bug in the GEOS C API,
|
|
228
|
+
# always set X before Y
|
|
229
|
+
lgeos.GEOSCoordSeq_setX(cs, i, dx)
|
|
230
|
+
lgeos.GEOSCoordSeq_setY(cs, i, dy)
|
|
231
|
+
if n == 3:
|
|
232
|
+
lgeos.GEOSCoordSeq_setZ(cs, i, dz)
|
|
233
|
+
|
|
234
|
+
except AttributeError:
|
|
235
|
+
# Fall back on list
|
|
236
|
+
try:
|
|
237
|
+
m = len(ob)
|
|
238
|
+
except TypeError: # Iterators, e.g. Python 3 zip
|
|
239
|
+
ob = list(ob)
|
|
240
|
+
m = len(ob)
|
|
241
|
+
|
|
242
|
+
if m < 2:
|
|
243
|
+
raise ValueError(
|
|
244
|
+
"LineStrings must have at least 2 coordinate tuples")
|
|
245
|
+
|
|
246
|
+
def _coords(o):
|
|
247
|
+
if isinstance(o, Point):
|
|
248
|
+
return o.coords[0]
|
|
249
|
+
else:
|
|
250
|
+
return o
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
n = len(_coords(ob[0]))
|
|
254
|
+
except TypeError:
|
|
255
|
+
raise ValueError(
|
|
256
|
+
"Input %s is the wrong shape for a LineString" % str(ob))
|
|
257
|
+
assert n == 2 or n == 3
|
|
258
|
+
|
|
259
|
+
# Create a coordinate sequence
|
|
260
|
+
if update_geom is not None:
|
|
261
|
+
cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
|
|
262
|
+
if n != update_ndim:
|
|
263
|
+
raise ValueError(
|
|
264
|
+
"Wrong coordinate dimensions; this geometry has "
|
|
265
|
+
"dimensions: %d" % update_ndim)
|
|
266
|
+
else:
|
|
267
|
+
cs = lgeos.GEOSCoordSeq_create(m, n)
|
|
268
|
+
|
|
269
|
+
# add to coordinate sequence
|
|
270
|
+
for i in range(m):
|
|
271
|
+
coords = _coords(ob[i])
|
|
272
|
+
# Because of a bug in the GEOS C API,
|
|
273
|
+
# always set X before Y
|
|
274
|
+
lgeos.GEOSCoordSeq_setX(cs, i, coords[0])
|
|
275
|
+
lgeos.GEOSCoordSeq_setY(cs, i, coords[1])
|
|
276
|
+
if n == 3:
|
|
277
|
+
try:
|
|
278
|
+
lgeos.GEOSCoordSeq_setZ(cs, i, coords[2])
|
|
279
|
+
except IndexError:
|
|
280
|
+
raise ValueError("Inconsistent coordinate dimensionality")
|
|
281
|
+
|
|
282
|
+
if update_geom is not None:
|
|
283
|
+
return None
|
|
284
|
+
else:
|
|
285
|
+
return lgeos.GEOSGeom_createLineString(cs), n
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def update_linestring_from_py(geom, ob):
|
|
289
|
+
geos_linestring_from_py(ob, geom._geom, geom._ndim)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# Test runner
|
|
293
|
+
def _test():
|
|
294
|
+
import doctest
|
|
295
|
+
doctest.testmod()
|
|
296
|
+
|
|
297
|
+
if __name__ == "__main__":
|
|
298
|
+
_test()
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Collections of linestrings and related utilities
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
if sys.version_info[0] < 3:
|
|
7
|
+
range = xrange
|
|
8
|
+
|
|
9
|
+
from ctypes import c_double, c_void_p, cast, POINTER
|
|
10
|
+
|
|
11
|
+
from shapely.geos import lgeos
|
|
12
|
+
from shapely.geometry.base import BaseMultipartGeometry
|
|
13
|
+
from shapely.geometry.linestring import LineString, geos_linestring_from_py
|
|
14
|
+
from shapely.geometry.proxy import CachingGeometryProxy
|
|
15
|
+
|
|
16
|
+
__all__ = ['MultiLineString', 'asMultiLineString']
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MultiLineString(BaseMultipartGeometry):
|
|
20
|
+
"""
|
|
21
|
+
A collection of one or more line strings
|
|
22
|
+
|
|
23
|
+
A MultiLineString has non-zero length and zero area.
|
|
24
|
+
|
|
25
|
+
Attributes
|
|
26
|
+
----------
|
|
27
|
+
geoms : sequence
|
|
28
|
+
A sequence of LineStrings
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, lines=None):
|
|
32
|
+
"""
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
lines : sequence
|
|
36
|
+
A sequence of line-like coordinate sequences or objects that
|
|
37
|
+
provide the numpy array interface, including instances of
|
|
38
|
+
LineString.
|
|
39
|
+
|
|
40
|
+
Example
|
|
41
|
+
-------
|
|
42
|
+
Construct a collection containing one line string.
|
|
43
|
+
|
|
44
|
+
>>> lines = MultiLineString( [[[0.0, 0.0], [1.0, 2.0]]] )
|
|
45
|
+
"""
|
|
46
|
+
super(MultiLineString, self).__init__()
|
|
47
|
+
|
|
48
|
+
if not lines:
|
|
49
|
+
# allow creation of empty multilinestrings, to support unpickling
|
|
50
|
+
pass
|
|
51
|
+
else:
|
|
52
|
+
self._geom, self._ndim = geos_multilinestring_from_py(lines)
|
|
53
|
+
|
|
54
|
+
def shape_factory(self, *args):
|
|
55
|
+
return LineString(*args)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def __geo_interface__(self):
|
|
59
|
+
return {
|
|
60
|
+
'type': 'MultiLineString',
|
|
61
|
+
'coordinates': tuple(tuple(c for c in g.coords) for g in self.geoms)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
def svg(self, scale_factor=1.):
|
|
65
|
+
"""
|
|
66
|
+
SVG representation of the geometry. Scale factor is multiplied by
|
|
67
|
+
the size of the SVG symbol so it can be scaled consistently for a
|
|
68
|
+
consistent appearance based on the canvas size.
|
|
69
|
+
"""
|
|
70
|
+
parts = []
|
|
71
|
+
for part in self.geoms:
|
|
72
|
+
pnt_format = " ".join(["{0},{1}".format(*c) for c in part.coords])
|
|
73
|
+
parts.append("""<polyline
|
|
74
|
+
fill="none"
|
|
75
|
+
stroke="{2}"
|
|
76
|
+
stroke-width={1}
|
|
77
|
+
points="{0}"
|
|
78
|
+
opacity=".8"
|
|
79
|
+
/>""".format(
|
|
80
|
+
pnt_format,
|
|
81
|
+
2.*scale_factor,
|
|
82
|
+
"#66cc99" if self.is_valid else "#ff3333"))
|
|
83
|
+
return "\n".join(parts)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class MultiLineStringAdapter(CachingGeometryProxy, MultiLineString):
|
|
87
|
+
|
|
88
|
+
context = None
|
|
89
|
+
_other_owned = False
|
|
90
|
+
|
|
91
|
+
def __init__(self, context):
|
|
92
|
+
self.context = context
|
|
93
|
+
self.factory = geos_multilinestring_from_py
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def _ndim(self):
|
|
97
|
+
try:
|
|
98
|
+
# From array protocol
|
|
99
|
+
array = self.context[0].__array_interface__
|
|
100
|
+
n = array['shape'][1]
|
|
101
|
+
assert n == 2 or n == 3
|
|
102
|
+
return n
|
|
103
|
+
except AttributeError:
|
|
104
|
+
# Fall back on list
|
|
105
|
+
return len(self.context[0][0])
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def asMultiLineString(context):
|
|
109
|
+
"""Adapts a sequence of objects to the MultiLineString interface"""
|
|
110
|
+
return MultiLineStringAdapter(context)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def geos_multilinestring_from_py(ob):
|
|
114
|
+
# ob must be either a sequence or array of sequences or arrays
|
|
115
|
+
try:
|
|
116
|
+
# From array protocol
|
|
117
|
+
array = ob.__array_interface__
|
|
118
|
+
assert len(array['shape']) == 1
|
|
119
|
+
L = array['shape'][0]
|
|
120
|
+
assert L >= 1
|
|
121
|
+
|
|
122
|
+
# Array of pointers to sub-geometries
|
|
123
|
+
subs = (c_void_p * L)()
|
|
124
|
+
|
|
125
|
+
for l in range(L):
|
|
126
|
+
geom, ndims = geos_linestring_from_py(array['data'][l])
|
|
127
|
+
subs[i] = cast(geom, c_void_p)
|
|
128
|
+
N = lgeos.GEOSGeom_getDimensions(subs[0])
|
|
129
|
+
except (NotImplementedError, AttributeError):
|
|
130
|
+
obs = getattr(ob, 'geoms', ob)
|
|
131
|
+
L = len(obs)
|
|
132
|
+
exemplar = obs[0]
|
|
133
|
+
try:
|
|
134
|
+
N = len(exemplar[0])
|
|
135
|
+
except TypeError:
|
|
136
|
+
N = exemplar._ndim
|
|
137
|
+
assert L >= 1
|
|
138
|
+
assert N == 2 or N == 3
|
|
139
|
+
|
|
140
|
+
# Array of pointers to point geometries
|
|
141
|
+
subs = (c_void_p * L)()
|
|
142
|
+
|
|
143
|
+
# add to coordinate sequence
|
|
144
|
+
for l in range(L):
|
|
145
|
+
geom, ndims = geos_linestring_from_py(obs[l])
|
|
146
|
+
subs[l] = cast(geom, c_void_p)
|
|
147
|
+
|
|
148
|
+
return (lgeos.GEOSGeom_createCollection(5, subs, L), N)
|
|
149
|
+
|
|
150
|
+
# Test runner
|
|
151
|
+
def _test():
|
|
152
|
+
import doctest
|
|
153
|
+
doctest.testmod()
|
|
154
|
+
|
|
155
|
+
if __name__ == "__main__":
|
|
156
|
+
_test()
|