Shapely Basics: Working with Geometry Objects in Python
Problem statement
A common GIS task is creating geometry directly in Python before doing any larger workflow. You may need to build a point from GPS coordinates, define a route as a line, create a parcel boundary as a polygon, or test whether a site falls inside a study area.
This is where Shapely becomes useful. Shapely gives you geometry objects you can create, inspect, compare, and transform in memory.
This page covers:
- creating common Shapely geometry objects
- reading basic geometry properties
- checking spatial relationships
- running simple geometry operations
- converting geometry to readable text
This page does not cover:
- reading shapefiles or GeoJSON with GeoPandas
- CRS management in detail
- advanced topology rules or performance tuning
Quick answer
Shapely lets you work with geometry objects such as Point, LineString, and Polygon in pure Python.
Basic workflow:
- import Shapely geometry classes
- create objects from coordinates
- inspect properties like
area,length,bounds, andcentroid - test relationships like
intersects()andcontains() - apply simple operations like
buffer()
from shapely.geometry import Point, LineString, Polygon
point = Point(12.5, 41.9)
line = LineString([(0, 0), (2, 2), (4, 1)])
polygon = Polygon([(0, 0), (4, 0), (4, 3), (0, 3), (0, 0)])
print(point.geom_type)
print(line.length)
print(polygon.area)
print(polygon.contains(point))
Step-by-step solution
Import Shapely geometry classes
For basic geometry work, import the main geometry types from shapely.geometry.
from shapely.geometry import (
Point,
LineString,
Polygon,
MultiPoint,
MultiLineString,
MultiPolygon,
)
The most common classes are:
Pointfor a single locationLineStringfor a connected pathPolygonfor an area- multipart types such as
MultiPointandMultiPolygonwhen one feature has multiple parts
Create a Point geometry
A Point is built from x and y coordinates.
from shapely.geometry import Point
site = Point(500000, 4649776)
print(site)
print(site.geom_type)
Typical GIS uses:
- site locations
- GPS observations
- address coordinates
- sampling points
This is the standard pattern when you need to create geometry from raw coordinates in Python.
Create a LineString geometry
A LineString is an ordered sequence of coordinate pairs.
from shapely.geometry import LineString
road_segment = LineString([
(500000, 4649700),
(500100, 4649750),
(500250, 4649800)
])
print(road_segment)
print(road_segment.geom_type)
Typical uses:
- roads
- rivers
- walking paths
- route segments
Order matters because the line follows the coordinates in the sequence you provide.
Create a Polygon geometry
A Polygon represents an area. It is created from a ring of coordinates.
from shapely.geometry import Polygon
study_area = Polygon([
(500000, 4649700),
(500300, 4649700),
(500300, 4650000),
(500000, 4650000),
(500000, 4649700)
])
print(study_area)
print(study_area.geom_type)
Typical uses:
- land parcels
- administrative boundaries
- service zones
- site extents
Shapely can close the ring for you, but repeating the first coordinate at the end makes the geometry definition clearer.
Inspect geometry properties
Once you create a geometry, you can inspect its key properties.
print(study_area.geom_type) # Polygon
print(study_area.bounds) # (minx, miny, maxx, maxy)
print(study_area.centroid) # center point
print(study_area.area) # area for polygons
print(road_segment.length) # line length
print(site.x, site.y) # point coordinates
Useful properties in GIS work:
geom_type: confirms the geometry typebounds: helpful for extent checks and filteringcentroid: useful for labels or quick reference pointsarea: used for polygon measurementlength: used for line length and polygon perimeter- point
xandy: direct coordinate access
Check spatial relationships between geometries
Shapely supports common spatial relationship checks.
print(study_area.contains(site))
print(site.within(study_area))
print(road_segment.intersects(study_area))
print(road_segment.touches(study_area))
These methods are useful for:
- point-in-polygon tests
- overlap checks
- boundary contact checks
- filtering features by location
In a real workflow, this is often the step where you decide which features should be kept, clipped, flagged, or passed into a GeoPandas analysis.
Run simple geometry operations
Shapely geometry operations return new geometry objects.
buffered_site = site.buffer(100)
print(buffered_site.geom_type)
print(buffered_site.area)
intersection_geom = road_segment.intersection(study_area)
print(intersection_geom.geom_type)
distance_to_boundary = site.distance(study_area.boundary)
print(distance_to_boundary)
combined = study_area.union(buffered_site)
print(combined.geom_type)
Common operations:
buffer(distance)creates an area around a geometryintersection(other)returns the shared partunion(other)merges geometriesdistance(other)calculates distance between geometries
These operations are often used to build study buffers, trim features to an analysis area, combine overlapping zones, or measure proximity.
Convert geometry to readable formats
Readable output helps with debugging and exporting.
print(site.wkt)
print(study_area.wkt)
print(site.__geo_interface__)
Useful formats:
wkt: Well-Known Text such asPOINT (500000 4649776)__geo_interface__: a GeoJSON-like mapping structure
WKT is useful for logs, debugging, database inserts, and quick inspection.
Code examples
Example 1: Create basic geometry objects
from shapely.geometry import Point, LineString, Polygon
point = Point(10, 20)
line = LineString([(0, 0), (2, 1), (4, 3)])
polygon = Polygon([(0, 0), (4, 0), (4, 4), (0, 4), (0, 0)])
print(point)
print(line)
print(polygon)
Example 2: Read common geometry properties
from shapely.geometry import Point, LineString, Polygon
point = Point(10, 20)
line = LineString([(0, 0), (3, 4)])
polygon = Polygon([(0, 0), (6, 0), (6, 4), (0, 4), (0, 0)])
print(point.geom_type)
print(point.bounds)
print(point.centroid)
print(line.geom_type)
print(line.length)
print(line.bounds)
print(polygon.geom_type)
print(polygon.area)
print(polygon.length)
print(polygon.centroid)
Example 3: Test spatial relationships
from shapely.geometry import Point, Polygon, LineString
point = Point(2, 2)
polygon = Polygon([(0, 0), (5, 0), (5, 5), (0, 5), (0, 0)])
line = LineString([(4, 4), (8, 8)])
print(polygon.contains(point)) # True
print(point.within(polygon)) # True
print(line.intersects(polygon)) # True
print(line.touches(polygon)) # False
Example 4: Apply a simple buffer
from shapely.geometry import Point
well = Point(100, 200)
buffer_zone = well.buffer(50)
print(buffer_zone.geom_type)
print(buffer_zone.area)
print(buffer_zone.bounds)
Example 5: Export geometry to WKT
from shapely.geometry import Polygon
parcel = Polygon([
(1000, 1000),
(1100, 1000),
(1100, 1050),
(1000, 1050),
(1000, 1000)
])
print(parcel.wkt)
Explanation
Shapely works with geometry objects in memory. It does not read shapefiles by itself and it does not manage attribute tables like GeoPandas does. Its job is geometry creation and geometry logic.
It helps to separate three things:
- coordinate lists: raw tuples such as
[(0, 0), (1, 1)] - geometry objects: Shapely objects such as
LineString(...) - geometry methods and properties: tools like
.area,.buffer(), and.intersects()
In practice, many Python GIS workflows start here. You create or receive geometry objects, test them, measure them, or transform them, then later place them into a GeoDataFrame for file export, filtering, clipping, joins, or larger analysis.
Edge cases or notes
Geometry objects do not store CRS by themselves
A Shapely geometry is just coordinates. It does not know whether those values are in EPSG:4326, UTM, or a local projected CRS. If you need CRS-aware workflows, use GeoPandas or manage CRS separately.
Polygon rings should be valid
Self-intersecting polygons or badly constructed rings can produce invalid geometry. That can break overlay or area calculations. You can do a basic check with geometry.is_valid.
print(study_area.is_valid)
If you run into problems, see How to Fix Invalid Geometry Errors in Shapely.
Area and length depend on coordinate units
If your coordinates are in longitude and latitude, area and length are not in meters. For meaningful measurements, use an appropriate projected CRS before calculating. For background, see Coordinate Reference Systems (CRS) Explained for Python GIS.
Operations return new geometries
Methods such as buffer() and intersection() do not change the original object in place.
new_geom = site.buffer(100)
Some results may be multipart geometries
An intersection or union can return MultiPolygon, MultiLineString, or an empty geometry depending on the inputs.
Internal links
For broader context, read Python for GIS: What It Is and When to Use It.
Related task pages:
- Create Points, Lines, and Polygons with Shapely in Python
- Check if a Point Is Inside a Polygon in Python with Shapely
Related background pages:
- GeoPandas Basics: Working with Spatial Data in Python
- Coordinate Reference Systems (CRS) Explained for Python GIS
Troubleshooting:
FAQ
What is Shapely used for in Python GIS?
Shapely is used to create and manipulate geometry objects in Python. Common tasks include building points, lines, and polygons, checking intersections, measuring area or distance, and running operations such as buffer or union.
What geometry types can I create with Shapely?
You can create Point, LineString, Polygon, MultiPoint, MultiLineString, and MultiPolygon, along with other geometry types such as geometry collections.
Does Shapely handle coordinate reference systems?
No. Shapely geometry objects do not store CRS information by themselves. If you need CRS management, use GeoPandas or another GIS structure that tracks CRS separately.
What is the difference between Shapely and GeoPandas?
Shapely handles geometry objects and geometry operations. GeoPandas builds on Shapely and adds tabular data handling, file I/O, CRS support, and dataframe-style spatial workflows.
Why is my polygon area incorrect in Shapely?
The most common reason is using geographic coordinates instead of a projected CRS. If your polygon coordinates are in degrees, the area result is not a real square-meter value. Invalid polygon geometry can also cause incorrect results.