109

I want to use Shapely for my computational geometry project. I need to be able to visualize and display polygons, lines, and other geometric objects for this. I've tried to use Matplotlib for this but I am having trouble with it.

from shapely.geometry import Polygon
import matplotlib.pyplot as plt

polygon1 = Polygon([(0,5),
                    (1,1),
                    (3,0),
                    ])

plt.plot(polygon1)
plt.show()

I would like to be able to display this polygon in a plot. How would I change my code to do this?

3
  • 3
    1. Nicely written first question! 2. If you can get individual lists of the x and y coordinates for your polygon you can plot like: plt.plot(polygon_x,polygon_y). You'll also have to append the first elements to the end to get the final line drawn. I unfortunately know nothing about shapely but hopefully this helps! Commented Apr 4, 2019 at 18:20
  • 4
    It would be plt.plot(*polygon1.exterior.xy). More generally you can plot shapely objects through descates. Googling for "shapely matplotlib" should already give you relevant results. Commented Apr 4, 2019 at 19:40
  • 1
    geopandas.org may be worth investigating for this functionality. Commented Apr 5, 2019 at 5:32

10 Answers 10

146

Use:

import matplotlib.pyplot as plt

x,y = polygon1.exterior.xy
plt.plot(x,y)

Or, more succinctly:

plt.plot(*polygon1.exterior.xy)
Sign up to request clarification or add additional context in comments.

2 Comments

this doesn't work for objects with holes in it unfortunately
If you want the interior rings (aka "holes") you can check from the LinearRings in polygon1.interiors.
51

A little late but I find the most convenient way to do this is with Geopandas as suggested above but without writing to a file first.

from shapely.geometry import Polygon
import matplotlib.pyplot as plt
import geopandas as gpd

polygon1 = Polygon([(0,5),
                    (1,1),
                    (3,0),
                    ])

 p = gpd.GeoSeries(polygon1)
 p.plot()
 plt.show()

Polygon Plotted using Geopandas

Checkout the docs for Geopandas.GeoSeries

3 Comments

The wrapping solution also helps to plot on a map: import mplleaflet; p.plot(); mplleaflet.show()
@mins much like the accepted answer answers "how do I plot two numpy arrays for x and y", not "How do I plot Shapely polygons" ;)
Thanks, it works fine. If you want to plot multiple polygons on one plot, you can use ax = plt.axes(); poly1.plot(ax=ax); poly2.plot(ax=ax); plt.show().
26

Geometries can be Point, LineString, Polygon, and their collection versions MultiPoint, MultiLineString, MultiPolygon.

Point

Just pass the coordinates to pyplot:

points = (point1, point2, point3, point3D)
xs = [point.x for point in points]
ys = [point.y for point in points]

fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.scatter(xs, ys)

LineString

Just pass the x and y collections to pyplot. They are obtained using xy attribute. This attribute returns something like:

(array('d', [3.0, 2.0, 9.0]), array('d', [6.0, -1.0, 4.0]))

and can be used this way:

ax.plot(line.xy[0], line.xy[1])
ax.plot(*line.xy) # Equivalent

Polygon

For Polygon, the currently accepted answer indeed works only for degraded polygons, that is polygons without holes. Here is a version working for any polygon with usual keywords for colors and other attributes. It's not my design, it's just adapted from GeoPandas source

import numpy as np
from matplotlib.path import Path
from matplotlib.patches import PathPatch
from matplotlib.collections import PatchCollection


# Plots a Polygon to pyplot `ax`
def plot_polygon(ax, poly, **kwargs):
    path = Path.make_compound_path(
        Path(np.asarray(poly.exterior.coords)[:, :2]),
        *[Path(np.asarray(ring.coords)[:, :2]) for ring in poly.interiors])

    patch = PathPatch(path, **kwargs)
    collection = PatchCollection([patch], **kwargs)
    
    ax.add_collection(collection, autolim=True)
    ax.autoscale_view()
    return collection

It is used this way:

from shapely.geometry import Polygon
import matplotlib.pyplot as plt


# Input polygon with two holes
# (remember exterior point order is ccw, holes cw else
# holes may not appear as holes.)
polygon = Polygon(shell=((0,0),(10,0),(10,10),(0,10)),
                  holes=(((1,3),(5,3),(5,1),(1,1)),
                         ((9,9),(9,8),(8,8),(8,9))))

fig, ax = plt.subplots()
plot_polygon(ax, polygon, facecolor='lightblue', edgecolor='red')

enter image description here

Collections

For Multi- collections, just call the plot function on each element.

2 Comments

Readers should pay attention to the fact that the shell and holes have a different winding order in the example shown. In my testing if the shell has the same winding order as the holes then the holes will be filled in. I'm not sure if this is a property of shapely or matplotlib. I suspect the latter.
@Phil: It's a property of shapely linear ring, the orientation indicate whether the space is positive or negative. Polygon holes must be negative: A polygon is a two-dimensional feature and has a non-zero area. It may have one or more negative-space "holes" which are also bounded by linear rings (from source)
24

If you are using Shapely 2.0+, use the shapely.plotting module:

import shapely.plotting
from shapely.geometry import Polygon

polygon1 = Polygon([(0, 5), (1, 1), (3, 0)])

shapely.plotting.plot_polygon(polygon1)

enter image description here

4 Comments

How would I save the plot to a file then?
fig, ax = plt.subplots() shapely.plotting.plot_polygon(polygon1, ax=ax) plt.savefig(f"{workspace}/test.png")
This is by far the easiest
Note that only polygons, lines and points can be drawn. Also note that this requires MatPlotLib.
7

If your data is in a .shp file, I would recommend geopandas:

import geopandas as gpd
import matplotlib.pyplot as plt

shapefile = gpd.read_file("path/to/shapes.shp")
shapefile.plot()
plt.show()

2 Comments

Any way to plot this without writing to file first?
You may be able to get away with wrapping the string in a StringIO as shown in this question, and passing the result to gpd.read_file though I have not tested this.
4

You can also 'follow along' with the source code in the Shapely User Manual: (click on 'Source code).

The 'source code' provided here is not the actual Shapely source code, but the code used in the User Manual to create the examples. Using this 'example code' from the Shapely User Manual allows you to quickly create images in the same friendly style.

Screenshot from https://shapely.readthedocs.io/en/latest/manual.html#linestrings november 2021

You'll need the 'figures' module, which is just one short, quite simple, python file from: https://github.com/Toblerity/Shapely/blob/main/docs/code/figures.py. (Taken from per https://gis.stackexchange.com/questions/362492/shapely-examples-use-figures-what-is-this-library)

Comments

3

It might be an overkill, but as an alternative to other good comments I would add an option of installing QGIS - a free software for working with geometries. All you need to do is to save your geometries as a shape file (.shp), geoJSON or any other format and open it with QGIS. If you're planning a big project it maybe more convenient at the end than using matplotlib.

1 Comment

Thank you. I am building a project that visualizes the Ham Sandwich theorem for my computational geometry course.
2

I was tired of Matplotlib's janky API for creating these plot images, so I made my own library. The Python module is called WKTPlot, and uses Bokeh to make interactive plots of your data. I have examples on how to plot WKT string data as well as data from Shapefiles.

It supports all most shapely geometric types:

  • Point
  • MultiPoint
  • LineString
  • MultiLineString
  • LinearRing
  • Polygon
  • MultiPolygon
  • GeometryCollection

1 Comment

It might be good to mention that one can pass the "Polgyon" object from the original question directly to this module. The examples on GitHub seem to suggest that you can only use (WKT) strings, but the add_shape method of the module also accepts many Shapely geometries.
1

Here is a solution using matplotlib patches that also accounts for holes:

import numpy as np
import shapely.geometry as sg
import matplotlib.pyplot as plt
import matplotlib.patches as patches


def add_polygon_patch(coords, ax, fc='blue'):
    patch = patches.Polygon(np.array(coords.xy).T, fc=fc)
    ax.add_patch(patch)


border = [(-10, -10), (-10, 10), (10, 10), (10, -10)]  # Large square
holes = [
    [(-6, -2), (-6, 2), (-2, 2), (-2, -2)],  # Square hole
    [(2, -2), (4, 2), (6, -2)]               # Triangle hole
]
region = sg.Polygon(shell=border, holes=holes)

fig, ax = plt.subplots(1, 1)

add_polygon_patch(region.exterior, ax)
for interior in region.interiors:
    add_polygon_patch(interior, ax, 'white')
        
ax.axis('equal')
plt.show()

Polygon region with polygon holes from a

Comments

0

It's possible to export geometries to SVG using the

shapely.geometry.base.BaseGeometry._repr_svg_

method. I would not use it for production-code, because it uses a 'protected member' of shapely.geometry.base class, but it works nicely for debugging.

Example:

drawing = MultiLineString([[[0, 0], [1, 2]], [[4, 4], [5, 6]]])
with open("geometry-dump.svg", 'w') as fh:
    fh.write(drawing._repr_svg_())

Result: SVG displayed in Firefox showing a MultiLineString

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.