Library / SDK
shapely/shapely avatar
shapely/shapely

Shapely: planar geometry in Python, backed by GEOS

Manipulation and analysis of geometric objects

4,516 stars636 forksPythonBSD-3-Clause

At a glance

What is it?
Shapely wraps the GEOS library to give Python scalar geometry objects and NumPy ufuncs for arrays of them. It is a geometry engine, not a file format reader or a coordinate system handler.
Who is it for?
Adopt Shapely if you already have coordinates in memory and need predicates, buffers or set operations on planar shapes, and you accept that projections and file parsing belong to other libraries. Do not adopt it expecting a GIS data layer: it does not read or write data files.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Shapely actually solves, and who ends up using it

The README describes Shapely as a BSD-licensed Python package for manipulation and analysis of planar geometric objects. That sentence is doing more work than it looks like. The package does not know about latitude and longitude, does not reproject anything, and does not open shapefiles. It gives you objects such as Point and Polygon, and operations on them, in the Cartesian plane.

The audience follows from that. If you have a pipeline that already holds coordinates as numbers, and you need to ask whether one shape contains another, compute an intersection, or buffer a point into an approximate circle, Shapely is the layer that answers those questions. The README's canonical example does exactly this: a Point at the origin buffered by 10.0 produces a polygon whose area comes out as 313.6548490545941, slightly under the 314.159 you would get from an exact circle, because the buffer is a polygon approximation.

What Shapely is not is stated plainly in the README: it is not primarily focused on data serialization formats or coordinate systems, but can be readily integrated with packages that are. That is the boundary to hold on to. Teams that treat Shapely as a GIS stack in a single import will be disappointed; teams that treat it as the geometry kernel under a stack they already have will not.

Two interfaces over one GEOS core

Shapely wraps GEOS geometries and operations. GEOS is the engine of PostGIS and a port of JTS, so the topology semantics you get in Shapely are the same family of semantics you get in those systems. The wrapper exposes two calling styles.

The scalar Geometry interface is object-oriented. You construct a geometry, then call methods or properties on it, as in the buffered-point example. This reads well for one-off shapes and for code that mirrors how a person thinks about a single polygon.

The vectorized interface is a set of NumPy ufuncs that operate element by element over arrays of geometries and support broadcasting. The README's second example builds an array of three points and a box, then calls shapely.contains(polygon, geoms), which returns array([False, True, False]). The underlying loops run in C, which is the point: a Python-level for loop over thousands of geometries pays interpreter overhead on every iteration, and the ufunc form avoids that.

Both interfaces sit on the same GEOS calls. Choosing between them is a performance and style decision, not a correctness one, and the two can be mixed in a single program.

Installing Shapely and running a first containment check

The README recommends installing from a built distribution rather than building from source. Two package managers are named, and both are one line.

bash
pip install shapely
# or using conda
conda install shapely --channel conda-forge

After that, the scalar interface is available from the top-level package. This is the README's own example, and it should print a POLYGON with many vertices and then an area just under 314.16.

python
from shapely import Point
patch = Point(0.0, 0.0).buffer(10.0)
print(patch)
print(patch.area)

The vectorized form needs NumPy alongside Shapely. Passing an array of points against a single polygon broadcasts the polygon across the array, and the result is a boolean array, one entry per input point.

python
import shapely
import numpy as np
from shapely import Point

geoms = np.array([Point(0, 0), Point(1, 1), Point(2, 2)])
polygon = shapely.box(0, 0, 2, 2)
print(shapely.contains(polygon, geoms))

The output is array([False, True, False]). The corner points fall on the boundary of the box and are not counted as contained, which is worth noticing before you build a filter on top of this predicate.

Serialization is borrowed, not native

Shapely does not read or write data files. It can serialize and deserialize using well known formats, and the shapely.wkt and shapely.wkb modules provide dumpers and loaders inspired by Python's pickle module. The README shows a round trip through WKT that is not byte-identical: loads('POINT (0 0)') fed through dumps comes back as 'POINT (0.0000000000000000 0.0000000000000000)'. If you are comparing serialized strings for equality, that formatting difference will bite you.

For GeoJSON-like structures, the geometry module provides mapping and shape. A dict with type Point and coordinates [0.0, 0.0] becomes a POINT (0 0), and mapping converts it back to a dict. This is the integration seam with the wider Python GIS ecosystem: Shapely handles the geometry, and other packages handle the file formats and the coordinate reference systems.

That division is deliberate, and it is also the most common source of confusion for newcomers. A user who loads a GeoJSON file with a third-party reader and passes the coordinates to Shapely is using the library as intended. A user who expects Shapely to open the file is not.

Threading, the GIL, and the shared-array hazard

Shapely functions generally support multithreading by releasing the Global Interpreter Lock during execution. In normal Python, the GIL prevents multiple threads from computing at the same time; Shapely releases that constraint so the heavy GEOS work can proceed concurrently from a single Python process. For CPU-bound geometry work, that is a real difference from pure-Python libraries that cannot do the same.

The README attaches a warning to this, and it is the sharpest limitation in the whole document. When sharing a NumPy array of geometries between threads, extreme care must be taken to avoid thread safety issues when mutating arrays. The reason given is concrete: when overwriting a geometry in an array, the old geometry is deallocated, and if another thread is operating on that geometry at the same moment, interpreter crashes occur. This is not a graceful exception; it is a crash.

The practical reading is that concurrent reads of a stable array are the safe pattern, and concurrent mutation is not. The README notes the hazard applies to any NumPy array, but is worse for arrays of Shapely geometries. There is no documented locking helper in the README to make mutation safe, so the responsibility sits with the caller.

Where Shapely is the wrong tool

Three cases stand out from what the documentation states rather than from anything measured here.

First, anything involving coordinate systems. Shapely works in the Cartesian plane and the README explicitly says it is not primarily focused on coordinate systems. If your inputs are in different projections, transforming them is somebody else's job before the geometry reaches Shapely.

Second, file I/O. The README says Shapely does not read or write data files. A workflow whose first step is opening a shapefile or a GeoPackage needs another library in front.

Third, exact arithmetic. The buffer example returns 313.6548490545941 rather than the analytic circle area, because the result is a polygon. Any workflow that depends on exact areas or exact boundary membership should treat Shapely's answers as approximations, and the containment example above shows boundary points falling on the False side of the predicate. GEOS predicates have their own semantics here, and the README does not attempt to enumerate them.

Shapely against GeoPandas, and what the split buys you

GeoPandas is the natural comparison, and the difference is architectural rather than a matter of which is better. GeoPandas builds on top of Shapely (and on pandas) to provide a tabular, file-aware layer: you work with a GeoDataFrame, and reading and writing formats is part of the package's job. Shapely sits underneath, holding individual geometries and arrays of them with no notion of a table or a file.

That split has consequences. Using Shapely directly means you own the loop over your data and the format handling, and in exchange you avoid pulling a dataframe stack into a program that only needs to test containment on a few thousand shapes. Using GeoPandas means the file reading, the column alignment and the CRS bookkeeping come with the package, at the cost of a larger dependency surface.

The README's own framing supports this: Shapely is designed to be integrated with packages that handle serialization and coordinate systems, not to replace them. The choice is about which layer your problem actually lives in.

Requirements, release cadence and licence

Shapely 2.2 requires Python >=3.11, GEOS >=3.10, and NumPy >=1.26. Those floors are the upgrade cost: an environment pinned to an older Python or an older system GEOS cannot take the 2.2 line without work. The pyproject.toml classifiers list Python 3.11 through 3.15 and mark the project as Production/Stable, and the build backend is mesonpy with a build-time requirement on Cython, meson-python>=0.15.0 and numpy>=1.26,<3. Building from source therefore needs a compiler toolchain and a compatible GEOS, which is why the README recommends the built distributions first.

The repository shows 2.2.0rc1 dated 2026-09-21, with 2.1.2 on 2025-09-24 and 2.1.1 on 2025-05-19 before it. The last push to the default branch was on 2026-09-22.

On licensing: Shapely is BSD 3-Clause, and the README states that GEOS is available under the GNU Lesser General Public License (LGPL) 2.1. The two are different licences attached to two different pieces of software, and how the LGPL applies to your distribution is a question for your own counsel, not for this article. What can be said from the README is simply that the distinction exists and that Shapely's own licence is permissive.

Editorial conclusion

Adopt Shapely if you already have coordinates in memory and need predicates, buffers or set operations on planar shapes, and you accept that projections and file parsing belong to other libraries. Do not adopt it expecting a GIS data layer: it does not read or write data files. Before committing, confirm that your environment can supply GEOS 3.10 or newer and NumPy 1.26 or newer, and decide whether the scalar Geometry interface or the vectorized ufuncs fit your workload, because the two have different calling conventions and different failure modes around shared arrays.

Frequently asked questions

How do I install Shapely in Python?

The README recommends installing from a built distribution, either with pip install shapely or with conda install shapely --channel conda-forge. Shapely 2.2 requires Python >=3.11, GEOS >=3.10 and NumPy >=1.26, so an environment below those floors needs updating first.

How do I use Shapely on a polygon?

Construct a geometry such as shapely.box(0, 0, 2, 2) or a Point, then call predicates and operations on it. The README's examples show Point(0.0, 0.0).buffer(10.0) producing a polygon with an area property, and shapely.contains(polygon, geoms) testing an array of points against a box.

What is Shapely in Python?

It is a BSD-licensed Python package for manipulation and analysis of planar geometric objects, wrapping the GEOS library. It offers a scalar Geometry interface and NumPy ufuncs over arrays of geometries, and it does not handle data files or coordinate systems itself.

Can I install Shapely with pip?

Yes. The README lists pip install shapely as the first recommended install route, alongside conda with the conda-forge channel. The built distributions avoid the need for the Cython, meson-python and compiler toolchain that a source build requires.

Can I install Shapely in Anaconda?

The README gives conda install shapely --channel conda-forge as the conda route, and the project also carries an Anaconda badge pointing at the conda-forge package. The same Python, GEOS and NumPy version floors apply.

Official sources

  1. License: BSD-3-Clause
  2. Project website
  3. README
  4. Releases
  5. shapely/shapely on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/shapely-shapely.svg)](https://hysenlabs.com/projects/shapely-shapely)