Performance & scale
Spatial indexes, parallelism, memory, and datasets too big to load at once.
There are only four reasons spatial code is slow, and the fixes are not interchangeable. Python-level iteration, a missing spatial index, I/O and parsing, and memory pressure each look like the others from the outside, so the first move is always a measurement rather than a guess.
One of the four is different in kind. Vectorising, parallelising and chunking all reduce the constant: the work still grows with the data, just less steeply. An index changes the complexity class — a spatial join goes from comparing every pair to comparing each feature with a handful of candidates — and that is the only fix that turns an impossible job into a possible one rather than a slow one into a faster one.
The pages below cover the four bottlenecks, how to tell which you have, and the techniques for each: indexes, vectorisation, parallelism, chunking and out-of-core formats.
🧭 Start with the concepts
The ideas the how-to guides below assume you already have.
- Spatial Indexes Explained: R-trees and Why Spatial Joins Are Fast
- Threads, Processes and the GIL: Parallelism in Python GIS Explained
- Spatial SQL or GeoPandas? Choosing Where the Work Happens
- PostGIS Spatial Indexes Explained: GiST and the Two-Phase Filter
- Why GeoPandas Is Slow: The Four Real Bottlenecks
- GeoParquet and Columnar Storage Explained: Why It Reads So Much Faster
Show every concept in this topic
- Nearest-Neighbour Joins Explained: Distance, Ties and Search Radius
- Lazy Loading Explained: Why xarray Reads Nothing Until You Ask
- Chunked Arrays and Zarr Explained
- Columnar or Row Storage: Why DuckDB Is Fast on Wide Tables
- How DuckDB Uses (and Does Not Use) a Spatial Index
- DuckDB's Memory Model: Streaming, Spilling and Limits
- HTTP Caching for Spatial Data: ETags, Max-Age and Invalidation
- Reruns and State Explained: Why Your Map App Redraws Everything
- What a Map App Can Afford to Send to the Browser
- Geohash Explained: Prefixes, Precision and the Edge Problem
- S2 Cells Explained: Squares on a Cube and When They Beat Hexagons
⚙️ Then do the task
Step-by-step walkthroughs with code you can copy and adapt.
- How to Speed Up Batch GIS Jobs with Parallel Processing in Python
- How to Cache Pipeline Steps So Only Changed Data Is Reprocessed
- How to Reduce GIS File Size in Python Without Wrecking the Data
- How to Process a Very Large GeoPackage in Chunks with Python
- How to Split a Large Layer into Tiles for Batch Processing
- How to Read a Large PostGIS Table into Python Without Running Out of Memory
Show every how-to guide in this topic
- How to Build a Small, Fast Python GIS Docker Image
- How to Replace a Row Loop with Vectorised GeoPandas Operations
- How to Use the Spatial Index Directly in GeoPandas (sindex)
- How to Profile a Slow Python GIS Script and Find the Real Bottleneck
- How to Build a Distance Matrix Between Two Layers in Python
- How to Measure Network Distance for Many Origin-Destination Pairs
- How to Choose Chunk and Tile Sizes That Actually Help
- How to Scale a GeoPandas Job with dask-geopandas
- How to Build an Offline Geocoder from Open Address Data
- How to Aggregate Millions of Points into a Grid with DuckDB
- How to Benchmark DuckDB Against GeoPandas Honestly
- How to Add Bounding Box and Attribute Filters to a Spatial API
- How to Paginate a Large Feature API in Python
- How to Set Cache Headers on a Spatial API and Tile Service
- How to Stream Large Query Results from a Spatial API
- How to Cache Spatial Data in a Map App
- How to Render a Million Points in a Map App with pydeck
- How to Open Hundreds of NetCDF Files as One Dataset
- How to Join Two Point Datasets on an H3 Index Instead of a Spatial Join
- How to Use H3 in DuckDB for Grid Aggregation at Scale
- How to Index Data by Quadkey and Web Map Tile
- How to Compact an H3 Cell Set to Store a Region Cheaply
- How to Choose Facility Locations with a p-Median Model in Python
- How to compute distance to the coast for many points
🔧 When it breaks
The errors this topic produces, with the cause explained rather than just the command.
- How to Speed Up GeoPandas: Tips for Large Datasets
- Fixing Memory Errors in GeoPandas When Working with Large Files
- Batch GIS Job Slows Down and Runs Out of Memory: How to Fix It
- Parallel GIS Batch Job Hangs or Crashes with multiprocessing: How to Fix It
- OSError: Too Many Open Files in a Batch GIS Job
- My PostGIS Spatial Query Is Slow: How to Fix It
Show every fix in this topic
- GIS Tests Run Out of Memory or Time Out in CI: How to Fix It
- GeoPandas .apply() Takes Hours: How to Fix It
- Reading a COG from a URL Is Slow or Downloads Everything
- dask-geopandas Uses More Memory or Time Than Plain GeoPandas
- My GeoJSON Is Too Big for the Browser
- Fixing a Map Export That Is Hundreds of Megabytes
- Fixing a DuckDB Spatial Join That Never Finishes
- Fixing DuckDB Out of Memory on a Large Spatial Query
- Fixing a Spatial API That Is Slow Under Load
- Fixing an API Worker That Runs Out of Memory
- Fixing a Map App That Takes Ten Seconds to Load
- Fixing an App Whose Memory Grows With Every User
- Fixing open_mfdataset That Is Slow, Hangs or Will Not Combine
- Fixing a Location-Allocation Model That Never Solves
- stop_times.txt is too big to load comfortably
Learning paths through this topic
A curated route rather than a list — the guides in the order that builds on itself.
- Spatial Statistics
- Networks & Routing
- Spatial SQL & PostGIS
- GIS Data Cleaning
- Batch Processing
- Automation & Pipelines
- Cloud-Native GIS
- Publishing Web Maps
- Geocoding & Addresses
- Cartography & Map Design
- DuckDB for Spatial Data
- Serving Spatial Data
- Maps, Apps & Dashboards
- Gridded & Climate Data
- H3 & Global Grids
- Location & Accessibility
- Coastal & Marine
- Public Transit & GTFS
Related topics
Subjects that keep coming up alongside this one, most overlap first.