The fcb command-line tool
Table of contents
- Installation
- Commands
- Conversion CityJSONSeq => FlatCityBuf
- Conversion FlatCityBuf => CityJSONSeq
- Inspecting a file
- Other conversions
If you have CityJSON files, you first need to convert them to the CityJSONSeq format.
fcb converts between CityJSON/CityJSONSeq and FlatCityBuf, and inspects .fcb files. It is written in Rust (code on GitHub); the crate-level reference is docs/rust.md.
Installation
Option 1: install from crates.io
cargo install fcb_cli --locked
This installs the fcb binary into your Cargo bin directory (usually ~/.cargo/bin/).
Option 2: build from source
git clone https://github.com/cityjson/flatcitybuf.git
cd flatcitybuf/src/rust
cargo build --release -p fcb_cli
The binary is then at target/release/fcb.
If your fcb still has a separate info subcommand, it predates fcb_cli 0.7.7 — inspect absorbed it. Re-run cargo install fcb_cli --locked to update.
Commands
$ fcb --help
CLI tool for CityJSON <-> FCB conversion
Usage: fcb <COMMAND>
Commands:
ser Convert CityJSON to FCB
deser Convert FCB to CityJSON
cbor Convert CityJSON to CBOR
bson Convert CityJSON to BSON
inspect Inspect an FCB file or URL: a terminal UI on a TTY, a static report otherwise
help Print this message or the help of the given subcommand(s)
Input and output are positional arguments: the input comes first, the output last. Older versions of the CLI used -i/-o flags; those are gone.
Both accept - for stdin/stdout, so fcb composes in a pipeline.
Conversion CityJSONSeq => FlatCityBuf
$ fcb ser delft.city.jsonl delft.fcb
━━━ FlatCityBuf Serialization
━━━ ━━━━━━━━━━━━━━━━━━━━━━━━
▶ Configuration
Input: 1 file(s)
1. delft.city.jsonl
Output: delft.fcb
Spatial Index: enabled
Geospatial Extent: not set
▶ Reading CityJSON...
✓ 1115 features
▶ Building attribute schema...
✓ 44 unique attributes found
▶ Building indices...
✓ Spatial R-tree index (node size: 16)
▶ Writing FCB file...
✓ File written successfully
━━━ Serialization Complete
The spatial (R-tree) index is written by default; --no-spatial-index turns it off.
Multiple inputs and glob patterns
ser takes any number of inputs before the output, so it merges as it converts. When the inputs have different coordinate transforms, vertices are aligned to the first file’s transform.
# several files -- the last positional is always the output
fcb ser file1.city.jsonl file2.city.jsonl merged.fcb
# glob patterns (quote them, so fcb expands them itself)
fcb ser 'data/*.city.jsonl' output.fcb
fcb ser 'cities/**/*.city.json' all_cities.fcb
# a plain CityJSON file works too
fcb ser city.city.json output.fcb
# stdin to stdout
cat input.city.jsonl | fcb ser - - > output.fcb
With attribute indexing
To enable fast queries on specific attributes:
$ fcb ser delft.city.jsonl delft.fcb \
--attr-index identificatie,b3_h_dak_50p,b3_is_glas_dak \
--attr-branching-factor 256
--attr-index (-a) takes a comma-separated list of attribute names to index. The branching factor (default: 256) controls the B+tree structure – higher values mean flatter trees and fewer round trips per query, but slightly larger files. See the performance tips.
Index all attributes
If you want to index every attribute found in the dataset:
$ fcb ser delft.city.jsonl delft.fcb -A
This is convenient but increases file size and conversion time.
Filtering by bounding box
You can filter features during conversion to create a subset:
$ fcb ser delft.city.jsonl filtered.fcb \
--bbox "84227.77,445377.33,85323.23,446334.69"
The bounding box format is minx,miny,maxx,maxy, in the CRS of the data.
All ser options
| Option | Meaning |
|---|---|
-a, --attr-index <LIST> | comma-separated attribute names to index |
-A, --index-all-attributes | index every attribute found |
--attr-branching-factor <N> | B+tree branching factor (default 256) |
-s, --no-spatial-index | do not write the R-tree |
--index-node-size <N> | R-tree node size (default 16) |
-b, --bbox "minx,miny,maxx,maxy" | keep only features inside the bbox |
-g, --ge | compute and write the geographical extent in the header |
--attr-branching-factor and --index-node-size are unrelated knobs: the first is the attribute B+tree’s, the second the spatial R-tree’s.
Conversion FlatCityBuf => CityJSONSeq
$ fcb deser delft.fcb delft.city.jsonl
Successfully decoded to CityJSON
The output is a CityJSONSeq document: one metadata line, then one CityJSONFeature per line, in stored (Hilbert) order.
Inspecting a file
fcb inspect reads the header of a local file or an HTTP(S) URL. For a remote file it fetches only the header bytes with a range request, so pointing it at a 68GB file on cloud storage is instant.
fcb inspect delft.fcb
fcb inspect https://flatcitybuf.open3d.city/data/3dbag_all_index.fcb
fcb inspect --static — a one-shot report
With --static, or whenever stdout is not a terminal (a pipe, a redirect, CI), inspect prints a plain-text report instead of the UI, so it composes in a script:
$ fcb inspect --static delft.fcb
▶ File Details
Source: delft.fcb
Size: 7.31 MB
Version: 2.0
Title: 3DBAG
▶ Dataset
Features: 1115
Columns: 44
Geospatial Extent: Yes
Min: [84501.55, 445805.03, -3.75]
Max: [85675.23, 446983.47, 95.04]
Dimensions: 1173.68 × 1178.44 × 98.79
▶ Indices
Spatial R-tree: Yes (node size: 16)
Attribute Indices: 44 (B+Tree)
1. b3_bag_bag_overlap
2. b3_dak_type
3. b3_h_dak_50p
... 41 more attributes...
▶ Coordinate Reference System
CRS: EPSG:7415
▶ Coordinate Transform
Scale: [0.001000, 0.001000, 0.001000]
Translate: [85088.390625, 446394.250000, 45.648003]
The list of attribute indices is the list of attributes you can actually query: an attribute that was not indexed at write time is still readable, but not queryable.
fcb inspect — an interactive terminal UI
On a terminal, the same command opens a full-screen UI with three tabs. Metadata:
┌Header Categories─────────────────────────────────────────────────────────────┐
│ Metadata │ Columns │ Map │
└──────────────────────────────────────────────────────────────────────────────┘
┌Metadata──────────────────────────────────────────────────────────────────────┐
│Title: 3DBAG │
│CityJSON Version: 2.0 │
│Features: 1115 │
│Columns: 44 │
│Spatial Index R-Tree Node Size: 16 │
│Attribute Indices: 44 │
│Bounds: [84501.5547, 445805.0312, -3.7470] .. [85675.2344, 446983.4688, ...] │
│Dimensions: 1173.68 x 1178.44 x 98.79 │
│Scale: [0.001000, 0.001000, 0.001000] │
│Translate: [85088.391, 446394.250, 45.648] │
│CRS Code: EPSG:7415 │
└──────────────────────────────────────────────────────────────────────────────┘
Columns — the attribute schema, scrollable:
┌Columns (1 of 44)─────────────────────────────────────────────────────────────┐
│Name Type Description Nullable Primary Key ... │
│b3_bag_bag_overlap Double - true false │
│b3_dak_type String - true false │
│b3_h_dak_50p Double - true false │
│b3_kas_warenhuis Bool - true false │
│b3_pw_datum ULong - true false │
└──────────────────────────────────────────────────────────────────────────────┘
Map — for a geographic CRS, the dataset’s extent drawn on a world coastline; for a projected CRS (like the EPSG:7415 above) it prints the extent instead of guessing a reprojection.
Keys: Tab/←/→ (or h/l) switch tabs, ↑/↓ (or k/j) scroll, g/G jump to top/bottom, q/Esc/Ctrl-C quit. The UI needs a real terminal; without one you get the static report above rather than escape codes in your file.
Other conversions
fcb cbor and fcb bson convert CityJSON to CBOR and BSON. They exist mainly to compare encodings and file sizes, and do not produce FlatCityBuf files:
fcb cbor city.city.json city.cbor
fcb bson city.city.json city.bson