88

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?

Georgy
  • 12,464
  • 7
  • 65
  • 73
ebeb9
  • 899
  • 2
  • 9
  • 16
  • 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! – Hoog Apr 04 '19 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. – ImportanceOfBeingErnest Apr 04 '19 at 19:40
  • 1
    http://geopandas.org/ may be worth investigating for this functionality. – daryl Apr 05 '19 at 05:32

9 Answers9

127

Use:

import matplotlib.pyplot as plt

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

Or, more succinctly:

plt.plot(*polygon1.exterior.xy)
Nitay
  • 4,193
  • 6
  • 33
  • 42
ap21
  • 2,372
  • 3
  • 16
  • 32
43

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

Micah Johnson
  • 646
  • 5
  • 8
  • The wrapping solution also helps to plot on a map: import mplleaflet; p.plot(); mplleaflet.show() – Ture Friese Nov 11 '20 at 11:54
  • This answers "how do I plot Geopandas GeoSeries", not "How do I plot Shapely polygons" – mins Dec 30 '21 at 12:37
  • 2
    @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" ;) – mcsoini May 28 '22 at 06:35
18

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.

mins
  • 6,478
  • 12
  • 56
  • 75
8

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()

Mitchell Tracy
  • 1,395
  • 1
  • 15
  • 16
  • 1
    Any way to plot this without writing to file first? – stefanbschneider Apr 29 '20 at 09:22
  • 1
    You may be able to get away with wrapping the string in a [StringIO](https://docs.python.org/3/library/io.html) as shown in [this question](https://stackoverflow.com/questions/141449/how-do-i-wrap-a-string-in-a-file-in-python), and passing the result to `gpd.read_file` though I have not tested this. – Mitchell Tracy Apr 30 '20 at 13:23
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)

Ideogram
  • 1,265
  • 12
  • 21
3

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

Mike T
  • 41,085
  • 18
  • 152
  • 203
2

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.

  • Thank you. I am building a project that visualizes the Ham Sandwich theorem for my computational geometry course. – ebeb9 Apr 13 '19 at 16:49
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
nightwuffle
  • 311
  • 3
  • 10
  • 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. – Ideogram Nov 01 '21 at 16:16
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

adamconkey
  • 4,104
  • 5
  • 32
  • 61