If you’re still reaching for Shapefile or GeoJSON as your default format for large static datasets, FlatGeobuf is worth understanding. It’s been in GDAL since version 3.1 (2020) and QGIS since 3.16 — but most teams still haven’t adopted it where it provides the clearest advantage: large datasets served from cloud storage.
Here’s what it does, how its internals work, and where it outperforms the alternatives.
What FlatGeobuf Is
FlatGeobuf is a binary encoding for vector geographic data built on FlatBuffers, a cross-platform serialization library developed at Google. A FlatGeobuf file has four sections in fixed order:
- Magic bytes —
0x6667620366676201(the fourth byte indicates the major format version) - Header — variable-size FlatBuffer containing the bounding box, geometry type, CRS (as WKT or PROJJSON), attribute schema, and feature count
- Spatial index (optional) — a packed Hilbert R-tree, built at write time
- Feature data — variable-size FlatBuffer records, one per feature
The header and spatial index appear before the feature data. This ordering is what enables efficient remote access.
The Spatial Index and HTTP Range Requests
This is the design decision that distinguishes FlatGeobuf from GeoPackage, GeoJSON, and Shapefile.
A packed Hilbert R-tree organizes features by their position on a Hilbert space-filling curve, which clusters spatially adjacent features into nearby byte ranges in the file. When a client queries a bounding box:
- The client fetches the header (small, first bytes) — learns the feature count, index size, and byte offset where feature data begins
- The client fetches the index byte range — the R-tree
- The R-tree returns the byte offsets of features that intersect the query bounding box
- The client fetches only those feature byte ranges
All three steps use HTTP range requests — the same mechanism Cloud-Optimized GeoTIFF uses for raster data. A 500MB dataset hosted on S3 can be queried for features in a specific municipality with three HTTP requests, transferring a fraction of the file.
GeoPackage uses SQLite, which requires random read/write access to a local file. Querying a GeoPackage hosted on S3 means downloading the entire file first. This is not a workaround issue — it’s a fundamental constraint of the SQLite format.
Performance
The FlatGeobuf project publishes benchmarks against Shapefile using Danish road data (906,602 LineString features):
| Metric | FlatGeobuf vs Shapefile |
|---|---|
| Read full dataset | 0.46× (2.2× faster) |
| Read with spatial filter | 0.71× |
| Write full dataset | 0.39× |
| Write with spatial index | 0.65× |
| File size | 0.77× (23% smaller) |
A second test on 2.5M polygon features showed read-full-dataset performance at 0.12× relative to Shapefile — roughly 8× faster for large polygon counts.
These are local disk numbers. The HTTP range-request advantage for cloud-hosted spatial queries adds on top of this.
What FlatGeobuf Supports That Shapefile Doesn’t
Attribute field names without length limits. Shapefile truncates field names to 10 characters. The silent truncation that breaks GeoJSON-to-Shapefile pipelines doesn’t exist in FlatGeobuf — field names are UTF-8 strings with no restriction.
15 attribute types. FlatGeobuf supports Byte, UByte, Bool, Short, UShort, Int, UInt, Long, ULong, Float, Double, String, Json, DateTime, and Binary. Shapefile supports five. GeoJSON has no native DateTime or binary types.
No 2GB per-file size limit. Shapefile’s .shp and .dbf components each cap at 2GB — a real constraint for national-scale datasets. FlatGeobuf has no size limit.
All OGC Simple Features geometry types. Including CircularString, CompoundCurve, CurvePolygon, PolyhedralSurface, TIN, and Triangle. Shapefile handles only 2D points, polylines, and polygons.
Self-describing CRS. The CRS is stored in the file header as ESRI WKT or PROJJSON. No companion .prj file required.
Ecosystem Support
| Tool | Version | Notes |
|---|---|---|
| GDAL | 3.1+ | Read/write; full ogr2ogr support |
| QGIS | 3.16+ | Read/write; can stream from URLs directly |
| Fiona | 1.8.18+ | Python read/write |
| PostGIS | 3.2.0+ | ST_GeomFromFlatGeobuf() / ST_AsFlatGeobuf() |
| GeoServer | 2.17+ | WFS output format |
| MapServer | 7.6+ | Read support |
Converting with ogr2ogr is a single command:
# Shapefile → FlatGeobuf with spatial index
ogr2ogr -f FlatGeobuf output.fgb input.shp
# GeoJSON → FlatGeobuf
ogr2ogr -f FlatGeobuf output.fgb input.geojson
# GeoPackage → FlatGeobuf, explicit spatial index
ogr2ogr -f FlatGeobuf -lco SPATIAL_INDEX=YES output.fgb input.gpkg layer_name
Streaming from a URL in QGIS requires nothing special — paste the HTTPS URL of the .fgb file as the data source. QGIS fetches the header and index, then downloads only the features in the current map extent.
The Write Limitation
FlatGeobuf does not support appending. The spatial index is built at write time by sorting features along the Hilbert curve and packing the R-tree. Appending a feature would invalidate the sort order and corrupt the index.
To add records, you rewrite the entire file. For data pipelines that produce static snapshots — a nightly or weekly export from a PostGIS database — this is fine. For datasets that need frequent interactive edits, it’s the wrong choice. Use GeoPackage for that.
When to Use FlatGeobuf vs the Alternatives
FlatGeobuf is the right choice when:
- Dataset is large and mostly static (100MB+)
- You’re serving from cloud storage and clients need spatial subsets
- Read performance on full scans and spatial filters matters
- You need rich attribute types or long field names
- A single-file format is preferred (no companion .shx/.dbf/.prj)
GeoPackage is the right choice when:
- Data needs frequent editing
- You want SQL attribute queries in a portable format
- Dataset stays on a local machine or shared drive
- You need raster and vector in one file
GeoJSON is the right choice when:
- The consumer is a web API, Leaflet map, or Mapbox GL layer
- Human readability matters for debugging
- Dataset is small (under 10MB — GeoJSON is an interchange format, not a working format at scale)
GeoParquet is the right choice when:
- Workloads are analytic and columnar (summing attributes across millions of features)
- You’re integrating with BigQuery, Snowflake, DuckDB, or Arrow-based tools
- Geometry access is secondary to attribute access
For a full comparison of GeoParquet, GeoPackage, and Shapefile for different team scenarios, see GeoParquet vs GeoPackage vs Shapefile — A Decision Tree for the 2026 GIS Stack.
One Limitation Worth Knowing: No Overviews
FlatGeobuf has no concept of multi-resolution overviews. When you zoom out to country scale in QGIS, the file serves every feature that intersects the current bounding box — there’s no pre-simplified lower-resolution layer for small-scale display. For desktop analysis this is usually acceptable. For web map tile rendering at small scales, it becomes slow.
The standard solution: use FlatGeobuf as the authoritative source format and generate vector tiles (PMTiles or MBTiles) from it for multi-scale web rendering. See converting vector tile formats with QGIS and GDAL for that workflow.
FlatGeobuf in a Format Conversion Pipeline
FlatGeobuf works well as an intermediate in pipelines that start from one format and need to land in another:
- Receive GeoJSON from an API → write to FlatGeobuf with spatial index → serve from S3 for downstream spatial queries
- Receive Shapefile from a vendor → convert to FlatGeobuf for analysis → deliver as GeoJSON to a web client
When the destination is Shapefile — because a legacy tool requires it — that conversion is one ogr2ogr command:
ogr2ogr -f "ESRI Shapefile" output.shp input.fgb
The main concern going FlatGeobuf → Shapefile is field name truncation. FlatGeobuf supports administrative_boundary_level as a field name; Shapefile silently truncates it to administrat. Write an explicit field rename mapping for any field names over 10 characters before the conversion step.
For GeoJSON-to-Shapefile conversions where field truncation and geometry validation are concerns, GeoConvert handles the CRS preservation, RFC 7946 compliance, and geometry repair steps automatically.