FlatGeobuf
A FlatGeobuf (.fgb) file holds features (points, lines, polygons) with their
properties in a compact binary form, often with a spatial index. maplibre-yaml
reads it into an ordinary GeoJSON source, so everything that works with a
.geojson file works with it: popups, classify:, clustering, fitTo. Use it
when GeoJSON gets slow; for large static data, PMTiles is
faster still. It is one of the large-data formats.
Quick start
Section titled “Quick start”Keep type: geojson and point url: at the file:
type: mapid: districtsconfig: center: [10.2, 42.15] zoom: 10 mapStyle: "https://tiles.openfreemap.org/styles/positron" fitTo: { source: districts, padding: 24 }sources: districts: type: geojson url: /gallery-assets/cloud-native/districts.fgblayers: - id: districts type: fill source: districts paint: fill-color: "#e9c46a" fill-opacity: 0.6 interactive: click: popup: - h3: [{ property: name }]The file is on this documentation site; to run the document on yours,
download districts.fgb
into gallery-assets/cloud-native/. You should see yellow districts on a
coast; click one for its name. The gallery’s
FlatGeobuf source shows it live.
Whole file or by view
Section titled “Whole file or by view”By default a file is read whole when it is small and by view when it is
large. load: changes that:
load: | What it reads | Needs |
|---|---|---|
none, or all (default) | Small files whole, once. Large ones by view (see Large files) when the file has a spatial index and the server supports range requests; otherwise the map says why and how to fix it | Any web server for a small file |
viewport | Only the features in view, read again as the map moves; nothing below minzoom (default 10), and nothing for a view holding more than maxFeatures (default 150,000) | A file with a spatial index, and a server with range requests |
sources: districts: type: geojson url: data/districts.fgb # small file: read whole parcels: type: geojson url: https://data.example.org/parcels.fgb load: { mode: viewport, minzoom: 14 } # big file: only what's in viewReading by view changes a few things:
fitToframes the extent recorded in the file’s header instead of the features, at the outermost zoom whose view is read (never belowminzoomnor overmaxFeatures), so the map opens with features drawn.classify:withmethod:needs the whole file. On a source read by view, write explicitbreaks:(Thematic maps).
Large files
Section titled “Large files”The limits
Section titled “The limits”- Read whole up to 50 MB and 400,000 features (the header’s count, so a file of many small features counts too). Between 10 and 50 MB a console note says it may take a few seconds. As a rule of thumb, a whole file is comfortable up to about 100,000 simple features (about 20 MB).
- Over either, a file with a spatial index is read by view instead, with a note for the author; one without an index, or on a server without range requests, is refused, and the notice says which and how to fix it.
- By view, a view holding more than
maxFeaturesfeatures (default 150,000) isn’t read; the file’s index tells how many before any feature is downloaded. Readers see “Too many features to show here — zoom in to see them”, also belowminzoom, until they zoom in.
Make it draw
Section titled “Make it draw”-
Say
load: viewport. A large indexed file is read by view anyway; saying so drops the note. -
Raise
maxFeaturesunderload:: the most features one read takes, by view, and above 400,000 it raises the whole-file feature limit too (load: { maxFeatures: 600000 }). It costs memory, so the notes suggest it only up to 600,000 features loaded whole, or 250,000 in a view, and say how much; the 50 MB limit stays. -
Add a spatial index so the file can be read by view:
Terminal window ogr2ogr -f FlatGeobuf -lco SPATIAL_INDEX=YES parcels-indexed.fgb parcels.fgb -
For a citywide view, convert it to PMTiles, which draws at every zoom. tylertoo reads GeoParquet, so convert first (
uvx --from geoparquet-io==1.7.0 gpio convert geoparquet parcels.fgb parcels.parquet, no GDAL needed), then follow the PMTiles guide.
npx maplibre-yaml@latest prepare parcels.fgb reads the file’s header and
prints the step that applies, with the YAML to paste
(Choose a format for large data).
Make a FlatGeobuf file
Section titled “Make a FlatGeobuf file”ogr2ogr -f FlatGeobuf out.fgb in.geojsonGDAL writes the spatial index by default. Add -t_srs EPSG:4326 if the data
is in another projection. In QGIS, use Export → Save Features As →
FlatGeobuf.
Limits
Section titled “Limits”- Coordinates must be longitude/latitude (EPSG:4326).
- Write the file uncompressed. A Zstandard-compressed file is refused
(
zstd -ddecompresses it). - Live data (
refresh:,stream:) isn’t available for FlatGeobuf; validation says so. Serve GeoJSON for data that changes (Live data).
Troubleshooting
Section titled “Troubleshooting”- “…is in EPSG:3857; MapLibre needs longitude/latitude (EPSG:4326)”: the
notice gives the fix,
ogr2ogr -t_srs EPSG:4326 out.fgb x.fgb. - “…has 500,000 features, more than a page loads whole … and it can’t be read by view”: the notice names what blocks reading by view (no spatial index, or a server without range requests) and the command that fixes it; see Large files for every way.
- “Too many features to show here — zoom in to see them”: the view holds
more than
maxFeatures, or the map is belowminzoom; zoom in, raisemaxFeaturesor lowerminzoomunderload:, or convert the file to PMTiles to draw it at every zoom. - “…was read as JSON, but it is a FlatGeobuf file”: the URL doesn’t end
in
.fgb. Rename the file, or addformat: flatgeobufto the source. - Hosting problems (range requests, CORS): see Large data › When something goes wrong.
Export to style.json: mlym emit writes the file’s features into the style
as GeoJSON (Large-data files in an exported style).