Planetary-scale answers, unlocked.
A Hands-On Guide for Working with Large-Scale Spatial Data. Learn more.
Authors
Teams struggle to scale geospatial analytics for five reasons. Location data is large. It comes in two forms: vector data, shapes such as buildings and roads, and raster data, grids of pixels such as satellite images. It is also recorded in many coordinate systems, the different ways of turning a place on Earth into numbers.
Spatial joins are the fourth reason. A spatial join matches rows by location, such as each building to the county it sits in. A filter or a sum splits easily across many machines. A spatial join does not. The fifth reason is upkeep: each new data source adds pipeline steps to convert it, move it into a shared coordinate system, and reconcile it with the rest.
The examples below measure each reason on open data in Wherobots: buildings, roads and satellite imagery for Washington State and the lower 48 US states.
The causes compound. A team running a national flood study joins every building polygon to elevation rasters in a different CRS. The team repeats the work when either source updates.
Geospatial big data is location data that is too large, too varied or too fast-changing for one computer and the usual geographic information system (GIS) tools. GIS tools are the software analysts use to work with maps. Common examples are building footprints (outlines) for a country, years of satellite scenes, and Global Positioning System (GPS) tracks from vehicle fleets.
Reviews of the field appeared in the mid-2010s:
The geospatial data guide covers the data types themselves. The geospatial analysis guide covers the operations.
Open map data alone reaches billions of rows. Query 1 below reads the table metadata of four Overture Maps tables in Wherobots. Table metadata is the record of each table's files, with their row counts and sizes.
Query 1 returned these sizes:
A tool that runs on one machine loads the data it works on into memory. Apache Sedona is an open-source engine for spatial data. Its benchmark, SpatialBench, sets its data size with a scale factor (SF), and a larger SF means more data. The SpatialBench single-node results show where the memory limit falls.
GeoPandas is a Python library for map data. On one machine with 8 CPUs and 32 GB of memory, "GeoPandas completes SF 1 but not SF 10." DuckDB, another single-machine database, "handles some join queries well but encounters scaling issues in certain cases."
Volume also sets how much a query reads. The Overture tables in Wherobots store data as many files. A bounding box is the smallest rectangle around a shape.
For each file, the table metadata records the range of bounding-box values inside it. A query for one area can therefore skip files whose boxes miss that area. The geospatial database guide reports a test of that skipping on the same buildings table.
Analysts use vector and raster data for different questions. Vector shapes give precise boundaries, such as a land parcel or a road. Raster grids give a value for each cell, or pixel, such as elevation or one band (light range) of a satellite image. The raster vs vector guide compares them in detail.
Each data model has its own file format for cloud storage:
A tool that reads only one data model needs the other exported first. Imagery gets converted to polygons, or polygons get drawn onto a pixel grid. Only then can the two be compared.
The Copernicus 30 m elevation table in Wherobots shows how a raster table can stay small. The table is a grid of ground heights with 30-meter pixels. It holds 3,961,635 rows in 0.1 GB.
Query 5 below sampled 1,000 rows, and all 1,000 were out-of-database (out-db) rasters. An out-db raster row stores a reference to an image file, plus its georeferencing: the numbers that tie each pixel to a place on Earth. The RS_FromPath documentation states that pixel data is not loaded until a function such as RS_Value reads it. The out-of-database architecture post describes the design.
RS_Value
Each data producer picks a coordinate system that suits its own data. The UTM system splits Earth into narrow north-south zones. Each zone is 6° of longitude wide.
The European Space Agency (ESA) Sentinel-2 product guide describes the Level-2A tiles as 100 km by 100 km images in UTM. With the overlap between neighbouring tiles, each covers about 110 km by 110 km. Level-2A images are corrected for the atmosphere. The guide says UTM "divides Earth into 60 longitudinal zones."
An analysis of a whole country therefore covers many zones. Query 2 counts them for Sentinel-2 scenes from July 2025 over the lower 48 states. The scenes fall in 995 tiles, fixed squares of the Sentinel-2 grid, across 10 UTM zones.
Each zone has a code from the European Petroleum Survey Group (EPSG), the standard numbers for coordinate systems. These EPSG codes run from 32610 to 32619. 43 of the 48 states touch tiles from two or three zones. 17 span three zones, 26 span two, and 5 fit in one.
The other layers in this article use different systems again. The Sentinel-2 footprints and the Overture boundaries are stored in longitude and latitude. The Copernicus elevation tiles are 256 by 256 pixels in EPSG:4326, also longitude and latitude.
Measuring areas or distances across these sources means choosing one projected CRS and reprojecting the others to it. A projected CRS lays the curved Earth flat, with distances in units such as meters. The coordinate reference system guide covers how to pick one. The map projection guide covers what each projection distorts.
A filter or a sum splits cleanly. Each row goes to a worker, one of the machines in a cluster. The partial results then add up.
A spatial join pairs rows from two tables by location, such as each building with the county that contains it. There is no shared ID column to group rows by. So the simplest plan tests every building against every county.
Spatial engines avoid that cost with a broadcast index join or a partitioned join. The spatial join guide describes both:
On one machine, the usual workaround is tiling by hand: cutting the map into pieces and running each piece in turn. The partitioning tax article has measurements of what that costs: loops over every tile, errors at tile edges, and reruns.
Query 3 measures how the input to the same building query grows with its area. An extent is the rectangle around a study area. Overture's building files record their bounding boxes, so the count of files that can match depends on the extent:
Query 4 runs the join for Washington. It returned 3,824,072 building-county pairs across all 39 counties. Its plan for the lower 48 states is the same. The plan is a BroadcastIndexJoin with an R-tree on the county polygons. The box filter is pushed into the table scan, so files are skipped as the table is read.
BroadcastIndexJoin
Every source in this article comes in its own format:
Before one analysis, a team takes these steps:
Each refresh of a source repeats the chain. A team with a pipeline built on one machine also has to add tiling once the data outgrows that machine. Tiling brings the edge errors and reruns described in the partitioning tax article.
Two Wherobots posts describe this pattern. One covers why traditional spatial systems break down on mobility data, the location records of moving vehicles and people. The other covers a pipeline design that pairs PostGIS with Wherobots. PostGIS adds spatial functions to the PostgreSQL database.
Expected result. If the five causes are real, each should show up as a number in open data. The expected numbers are billions of rows, out-db rasters, several UTM zones per state, and a join whose input grows with its area.
Method. Six queries in Structured Query Language (SQL) ran in a Wherobots SQL session on October 13, 2026. WherobotsDB, the engine in Wherobots, comes from the original creators of Apache Sedona.
The queries read these tables:
All are tables in the Havasu catalog. Havasu tables use the Apache Iceberg table format, with column types for geometry and rasters.
An Iceberg table keeps a .files metadata table. It has one row per data file, with the file's row count and size. Reading it touches no data files.
.files
SELECT 'buildings_building' AS tbl, COUNT(*) AS files, SUM(record_count) AS row_count, ROUND(SUM(file_size_in_bytes)/1e9, 1) AS gb FROM wherobots_open_data.overture_maps_foundation.buildings_building.files UNION ALL SELECT 'places_place', COUNT(*), SUM(record_count), ROUND(SUM(file_size_in_bytes)/1e9, 1) FROM wherobots_open_data.overture_maps_foundation.places_place.files UNION ALL SELECT 'transportation_segment', COUNT(*), SUM(record_count), ROUND(SUM(file_size_in_bytes)/1e9, 1) FROM wherobots_open_data.overture_maps_foundation.transportation_segment.files UNION ALL SELECT 'divisions_division_area', COUNT(*), SUM(record_count), ROUND(SUM(file_size_in_bytes)/1e9, 1) FROM wherobots_open_data.overture_maps_foundation.divisions_division_area.files UNION ALL SELECT 'copernicus_dem_glo_30m', COUNT(*), SUM(record_count), ROUND(SUM(file_size_in_bytes)/1e9, 1) FROM wherobots_open_data.copernicus_dem.glo_30m.files
The Sentinel-2 catalog stores each scene's EPSG code in proj:epsg. Query 2a joins July 2025 scenes to the state polygons, copied to every worker. It returns one row per state and a total.
proj:epsg
WITH states AS ( SELECT region, geometry FROM wherobots_open_data.overture_maps_foundation.divisions_division_area WHERE country = 'US' AND subtype = 'region' AND is_land = true AND region NOT IN ('US-AK','US-HI','US-PR','US-GU','US-VI','US-AS','US-MP','US-UM') ), scenes AS ( SELECT mgrs_tile, `proj:epsg` AS epsg, geometry FROM wherobots_open_data.sentinel2.l2a_source_items WHERE year = 2025 AND month = 7 AND bbox.xmax >= -125 AND bbox.xmin <= -66.9 AND bbox.ymax >= 24.5 AND bbox.ymin <= 49.4 ), pairs AS ( SELECT /*+ BROADCAST(s) */ DISTINCT s.region, c.mgrs_tile, c.epsg FROM scenes c JOIN states s ON ST_Intersects(s.geometry, c.geometry) ) SELECT region, COUNT(DISTINCT epsg) AS utm_zones, COUNT(DISTINCT mgrs_tile) AS tiles, MIN(epsg) AS min_epsg, MAX(epsg) AS max_epsg FROM pairs GROUP BY region UNION ALL SELECT 'ALL', COUNT(DISTINCT epsg), COUNT(DISTINCT mgrs_tile), MIN(epsg), MAX(epsg) FROM pairs ORDER BY region
The filter on bbox keeps every scene whose box overlaps the lower 48 box from query 3, rounded outward. The join with the state polygons sets which tiles count. The total row reads 995 tiles in 10 UTM zones, EPSG:32610 to EPSG:32619.
bbox
California touched three zones across 74 tiles, and Texas three zones across 106 tiles. The states query includes the District of Columbia, which touched one zone.
Query 2b leaves out the District of Columbia and groups the 48 states by zone count. It also counts the county polygons used in query 4's plan and the July 2025 scenes in the box.
WITH states AS ( SELECT region, geometry FROM wherobots_open_data.overture_maps_foundation.divisions_division_area WHERE country = 'US' AND subtype = 'region' AND is_land = true AND region NOT IN ('US-AK','US-HI','US-PR','US-GU','US-VI','US-AS','US-MP','US-UM','US-DC') ), scenes AS ( SELECT mgrs_tile, `proj:epsg` AS epsg, geometry FROM wherobots_open_data.sentinel2.l2a_source_items WHERE year = 2025 AND month = 7 AND bbox.xmax >= -125 AND bbox.xmin <= -66.9 AND bbox.ymax >= 24.5 AND bbox.ymin <= 49.4 ), per_state AS ( SELECT /*+ BROADCAST(s) */ s.region, COUNT(DISTINCT c.epsg) AS zones FROM scenes c JOIN states s ON ST_Intersects(s.geometry, c.geometry) GROUP BY s.region ) SELECT 'states_with_' || zones || '_zones' AS k, COUNT(*) AS v FROM per_state GROUP BY zones UNION ALL SELECT 'lower48_counties', COUNT(*) FROM wherobots_open_data.overture_maps_foundation.divisions_division_area WHERE country='US' AND subtype='county' AND is_land = true AND region NOT IN ('US-AK','US-HI','US-PR','US-GU','US-VI','US-AS','US-MP','US-UM') UNION ALL SELECT 'scenes_july_2025', COUNT(*) FROM scenes
Each extent's box comes from the Overture boundary polygons. The file bounds come from the Iceberg metadata for the bbox column.
WITH ext AS ( SELECT 'King County' AS extent, ST_XMin(geometry) xmin, ST_XMax(geometry) xmax, ST_YMin(geometry) ymin, ST_YMax(geometry) ymax FROM wherobots_open_data.overture_maps_foundation.divisions_division_area WHERE country='US' AND region='US-WA' AND subtype='county' AND is_land = true AND names.primary = 'King County' UNION ALL SELECT 'Washington', ST_XMin(geometry), ST_XMax(geometry), ST_YMin(geometry), ST_YMax(geometry) FROM wherobots_open_data.overture_maps_foundation.divisions_division_area WHERE country='US' AND region='US-WA' AND subtype='region' AND is_land = true UNION ALL SELECT 'Lower 48 states', MIN(ST_XMin(geometry)), MAX(ST_XMax(geometry)), MIN(ST_YMin(geometry)), MAX(ST_YMax(geometry)) FROM wherobots_open_data.overture_maps_foundation.divisions_division_area WHERE country='US' AND subtype='region' AND is_land = true AND region NOT IN ('US-AK','US-HI','US-PR','US-GU','US-VI','US-AS','US-MP','US-UM') ), f AS ( SELECT record_count, file_size_in_bytes, readable_metrics.`bbox.xmin`.lower_bound AS fxmin, readable_metrics.`bbox.xmax`.upper_bound AS fxmax, readable_metrics.`bbox.ymin`.lower_bound AS fymin, readable_metrics.`bbox.ymax`.upper_bound AS fymax FROM wherobots_open_data.overture_maps_foundation.buildings_building.files ) SELECT /*+ BROADCAST(e) */ e.extent, ROUND(e.xmin,3) xmin, ROUND(e.xmax,3) xmax, ROUND(e.ymin,3) ymin, ROUND(e.ymax,3) ymax, COUNT(*) AS files, SUM(f.record_count) AS rows_in_files, ROUND(SUM(f.file_size_in_bytes)/1e9, 2) AS gb FROM f JOIN ext e ON f.fxmax >= e.xmin AND f.fxmin <= e.xmax AND f.fymax >= e.ymin AND f.fymin <= e.ymax GROUP BY e.extent, e.xmin, e.xmax, e.ymin, e.ymax ORDER BY files
The whole table, from query 1, is 5,141 files with 2,533,842,612 rows in 253.2 GB.
The filter keeps every building whose box overlaps the Washington extent that query 3 returned. The county polygons are the small side of the join. The BROADCAST hint is an instruction in the SQL. With it, the county polygons are copied to every worker.
BROADCAST
WITH counties AS ( SELECT names.primary AS county, geometry FROM wherobots_open_data.overture_maps_foundation.divisions_division_area WHERE country='US' AND region='US-WA' AND subtype='county' AND is_land = true ), b AS ( SELECT geometry FROM wherobots_open_data.overture_maps_foundation.buildings_building WHERE bbox.xmax >= -124.754 AND bbox.xmin <= -116.916 AND bbox.ymax >= 45.544 AND bbox.ymin <= 49.002 ) SELECT /*+ BROADCAST(c) */ COUNT(DISTINCT c.county) AS counties, COUNT(*) AS building_county_pairs FROM b JOIN counties c ON ST_Intersects(c.geometry, b.geometry)
The join returned 3,824,072 pairs across 39 counties. A building that crosses a county line counts once per county.
The national version uses all 3,108 county polygons in the lower 48 states and the lower 48 box from query 3. It was planned with EXPLAIN FORMATTED, which prints the plan without running the query. It was not run, to keep the shared session free.
EXPLAIN FORMATTED
EXPLAIN FORMATTED WITH counties AS ( SELECT region, names.primary AS county, geometry FROM wherobots_open_data.overture_maps_foundation.divisions_division_area WHERE country='US' AND subtype='county' AND is_land = true AND region NOT IN ('US-AK','US-HI','US-PR','US-GU','US-VI','US-AS','US-MP','US-UM') ), b AS ( SELECT geometry FROM wherobots_open_data.overture_maps_foundation.buildings_building WHERE bbox.xmax >= -124.754 AND bbox.xmin <= -66.95 AND bbox.ymax >= 24.523 AND bbox.ymin <= 49.384 ) SELECT /*+ BROADCAST(c) */ c.region, c.county, COUNT(*) AS buildings FROM b JOIN counties c ON ST_Intersects(c.geometry, b.geometry) GROUP BY c.region, c.county
EXPLAIN printed a BroadcastIndexJoin over a SpatialIndex of type RTREE on the county polygons. The Iceberg scan of the buildings table carried the four bbox filters. That is the Washington plan with a larger input.
EXPLAIN
SpatialIndex
RTREE
RS_BandPath returns the file path of an out-db raster. It returns NULL, no value, for a raster stored in the row.
RS_BandPath
SELECT COUNT(*) AS sampled, SUM(CASE WHEN RS_BandPath(rast) IS NOT NULL THEN 1 ELSE 0 END) AS out_db, FIRST(RS_Width(rast)) AS width_px, FIRST(RS_Height(rast)) AS height_px, FIRST(RS_SRID(rast)) AS srid FROM (SELECT rast FROM wherobots_open_data.copernicus_dem.glo_30m LIMIT 1000)
All 1,000 sampled rows were out-db rasters, each 256 by 256 pixels in EPSG:4326.
Each cause showed up as a number:
Every test above ran as plain SQL in WherobotsDB, a cloud spatial engine for vector and raster data, with no tiling step.
General-purpose cluster computing came first. A cluster is a group of networked machines that share one job. The Association for Computing Machinery (ACM) published these papers in its journal, Communications of the ACM:
Spatial systems followed:
GeoSpark became Apache Sedona.
Varun Pandey and colleagues compared these systems in How good are modern spatial analytics systems? (Proceedings of the VLDB Endowment, 2018). On SpatialBench at scale factor 1000, the current WherobotsDB runs spatial queries and joins up to 2.5 times faster than the previous generation. The mean speedup is 1.9 times. The WherobotsDB performance post reports these results.
What changes at scale is where the work happens. Data moves from one file to many files in object storage, the cloud storage that holds files. Table metadata records each file's range of bounding-box values, so a query can skip files outside its area.
Rasters stay in their image files until a function reads pixels. Joins run as partitioned or broadcast plans. The analyst's SQL stays the same from a county to a country.
Wherobots is built to remove these five burdens, from one county to the whole planet. Taylor Geospatial worked with Wherobots to produce 8.2 billion field boundaries worldwide for the Fields of the World program. Developers building applications for the physical world are better off on one engine that handles all of their workloads from the first day. Who knows what question you will need to answer tomorrow?
Five causes add up.
Data volume: Overture Maps lists 2.53 billion buildings in 253.2 GB.
Mixed formats: the data mixes vector shapes and raster pixels, stored in different file formats.
Many coordinate systems: one study area spans several. Sentinel-2 imagery of the lower 48 US states sits in 10 Universal Transverse Mercator (UTM) zones. Each zone is a map strip 6° of longitude wide.
Spatial joins: they match rows by location, so both sides of each pair have to be on the same machine.
Pipeline upkeep: each new data source adds pipeline steps to convert it, reproject it and reconcile it with the rest.
In the Wherobots test, building data for one county sat in 7 files. For the lower 48 states, it sat in 444 files.
Geospatial big data is location data that is too large, too varied or too fast-changing for one computer and the usual GIS tools. Examples are building footprints for a whole country, years of satellite imagery, and GPS tracks from vehicle fleets. Handling it takes storage that splits the data into files by location. It also takes compute that runs one query across many machines.
A spatial join pairs rows by where their shapes sit. There is no shared key to group them by. Without an index, every row on one side is tested against every row on the other.
On a cluster, rows that could match have to land on the same worker machine. Engines solve this with spatial partitioning and indexes, or by copying the smaller table to every worker. The spatial join guide covers both plans.
The lower 48 states span 10 UTM zones, numbers 10 to 19 (EPSG:32610 to EPSG:32619). A Wherobots query covered Sentinel-2 scenes from July 2025. In it, 43 of the 48 states touched tiles from two or three zones. Only 5 states fell inside one zone.
GeoPandas runs on one machine and holds its data in memory. Apache Sedona’s single-node SpatialBench test used a machine with 8 CPUs and 32 GB of memory. On it, GeoPandas completed scale factor 1 but not scale factor 10. The scale factor sets the size of the test data. For larger data, run the same steps in a distributed engine such as Apache Sedona or WherobotsDB.
An out-of-database (out-db) raster is a table row that stores a reference to an image file and its georeferencing. The pixels stay in the file. They are read only when a function uses them. In Wherobots, the Copernicus 30 m elevation table holds 3,961,635 such rows in 0.1 GB. The table is small because each row points to a Cloud Optimized GeoTIFF.
Use one when a table no longer fits in one machine’s memory. The same holds when two large tables must be joined by location. It also holds when one analysis has to run on a state or a country. In the Wherobots test, building data for King County sat in 7 files. For the lower 48 states, it sat in 444 files holding 185.5 million rows.
No. One SQL query runs on the full extent. WherobotsDB reads only the files whose stored bounding boxes overlap the query area. It splits the work by location. It builds an index on the smaller side of a join. A larger area takes a larger runtime and the same SQL.
Most Dangerous Highway in the US: 25 Deadliest Stretches
The most dangerous highway in the US is a 16.5-mile stretch of U.S. 90 in New Orleans. See the 25 deadliest stretches and the deadliest road in each state.
What is Map Matching? GPS Traces, Methods and Examples
Map matching places each GPS record on the road a vehicle drove. How it works, nearest-road snapping vs hidden Markov models, and a test on Ann Arbor drives.
What is an Isochrone? Isochrone Maps and Travel Time
An isochrone is the area reachable from a place within a set travel time. How isochrones are computed, isochrone vs radius, and a King County test.
share this article
Awesome that you’d like to share our articles. Where would you like to share it to: