Shapely#
Shapely is a Python package for manipulating and analysing two-dimensional geometric shapes.
Since version 2.0 it has included the function from_ragged_array
which is ideally suited to
create Shapely geometries from contours created by ContourPy.
Contour lines to Shapely#
Contour lines with a line type of LineType.ChunkCombinedOffset
are easily converted to Shapely
geometries using shapely.from_ragged_array
.
Here is the same example from the Line type section of the User Guide:
>>> from contourpy import contour_generator
>>> z = [[1.4, 1.2, 0.9, 0], [0.6, 3, 0.4, 0.7], [0.2, 0.2, 0.5, 3]]
>>> cont_gen = contour_generator(z=z, line_type="ChunkCombinedOffset")
>>> lines = cont_gen.lines(0.5)
>>> lines
([array([[0.58, 1.], [1., 0.44], [1.38, 1.], [1., 1.36], [0.58, 1.], [2.6, 2.], [3., 1.57]])],
[array([0, 5, 7], dtype=uint32)])
This has a single chunk containing one array of points and one array of offsets. To create
Shapely LineString
geometries for this single chunk:
>>> from shapely import GeometryType, from_ragged_array, unary_union
>>> points, offsets = lines[0][0], lines[1][0]
>>> linestrings = from_ragged_array(GeometryType.LINESTRING, points, (offsets,))
>>> linestrings
[<LINESTRING (2.444 0, 2 0.8, 1.962 1, 1 1.893, 0 1.25)>
<LINESTRING (2 2, 2.333 1, 3 0.714)>]
where linestrings
is a NumPy array of two Shapely LineStrings. To create a Shapely
MultiLineString instead you can either use shapely.unary_union
on the LineStrings:
>>> multilinestring = unary_union(linestrings)
>>> multilinestring
MULTILINESTRING ((2.444 0, 2 0.8, 1.962 1, 1 1.893, 0 1.25), (2 2, 2.333 1, 3 0.714))
or create them directly from the ContourPy lines
using:
>>> multilinestrings = from_ragged_array(GeometryType.MULTILINESTRING,
... points, (offsets, [0, len(offsets)-1]))
>>> multilinestrings
[<MULTILINESTRING ((2.444 0, 2 0.8, 1.962 1, 1 1.893, 0 1.25), (2 2, 2.333 1,...>]
The shapely.unary_union
approach returns a single MultiLineString whereas this approach returns
a NumPy array containing the single MultiLineString.
Note
If your contour lines have a different line type then you can convert them using
convert_lines()
. If you have more than one chunk you can combine them using
dechunk_lines()
or iterate over the chunks, convert one chunk at a time and
then combine the geometries.
Filled contours to Shapely#
Filled contours with a fill type of FillType.ChunkCombinedOffsetOffset
are easily converted to
Shapely geometries using shapely.from_ragged_array
.
Here is the same example from the Fill type section of the User Guide:
>>> from contourpy import contour_generator
>>> z = [[1.4, 1.2, 0.9, 0], [0.6, 3, 0.4, 0.7], [0.2, 0.2, 0.5, 3]]
>>> cont_gen = contour_generator(z=z, fill_type="ChunkCombinedOffsetOffset")
>>> filled = cont_gen.filled(1, 2)
>>> filled
([array([[0., 0.], [1., 0.], [1.67, 0.], [1.77, 1.], [1., 1.71], [0.17, 1.], [0., 0.5],
[0., 0.], [1., 0.44], [0.58, 1.], [1., 1.36], [1.38, 1.], [1., 0.44], [2.2 , 2.],
[3., 1.13], [3., 1.57], [2.6, 2.], [2.2, 2.]])],
[array([0, 8, 13, 18], dtype=uint32)],
[array([0, 2, 3], dtype=uint32)])
This has a single chunk containing one array of points and two arrays of offsets which are the
boundary offsets and the polygon (outer boundary) offsets. To create Shapely Polygon
geometries
for this single chunk:
>>> from shapely import GeometryType, from_ragged_array, unary_union
>>> points, offsets, outer_offsets = filled[0][0], filled[1][0], filled[2][0]
>>> polygons = from_ragged_array(GeometryType.POLYGON, points, (offsets, outer_offsets))
[<POLYGON ((0 0, 1 0, 1.667 0, 1.769 1, 1 1.714, 0.167 1, 0 0.5, 0 0), (1 0.4...>
<POLYGON ((2.2 2, 3 1.13, 3 1.565, 2.6 2, 2.2 2))>]
where polygons
is a NumPy array of two Shapely Polygons. To create a Shapely
MultiPolygon instead you can either use shapely.unary_union
on the Polygons:
>>> multipolygon = unary_union(polygons)
>>> multipolygon
<MULTIPOLYGON (((0 0, 1 0, 1.667 0, 1.769 1, 1 1.714, 0.167 1, 0 0.5, 0 0), ...>
or create them directly from the ContourPy filled
using:
>>> multipolygons = from_ragged_array(GeometryType.MULTIPOLYGON,
... points,
... (offsets, outer_offsets, [0, len(outer_offsets)-1]))
>>> multipolygons
[<MULTIPOLYGON (((0 0, 1 0, 1.667 0, 1.769 1, 1 1.714, 0.167 1, 0 0.5, 0 0), ...>]
The shapely.unary_union
approach returns a single MultiPolygon whereas this approach returns
a NumPy array containing the single MultiPolygon.
Note
If your filled contours have a different line type then you can convert them using
convert_filled()
. If you have more than one chunk you can combine them using
dechunk_filled()
or iterate over the chunks, convert one chunk at a time and
then combine the geometries.
Example use of Shapely geometries#
As an example of what can be done with Shapely geometries, consider the single multipolygon
created above. You can calculate the area
>>> multipolygon.area
2.143832
the bounding box
>>> multipolygon.bounds
bounds (0.0, 0.0, 3.0, 2.0)
and whether it contains particular points or not
>>> from shapely import Point
>>> multipolygon.contains(Point(2, 1))
False
>>> multipolygon.contains(Point(1.5, 1))
True
Note
You can use the polygons
instead of the multipolygon
here but first you will need to
convert the array to a shapely.GeometryCollection
first using:
>>> from shapely import GeometryCollection
>>> polygons = GeometryCollection(list(polygons))