fcb_core 0.1.2

FlatCityBuf is a library for reading and writing CityJSON with FlatBuffers. Contains code derived from FlatGeobuf (BSD-2-Clause) for spatial indexing.
Documentation

FlatCityBuf Core Library

A high-performance Rust library for encoding and decoding CityJSON data to the FlatCityBuf (FCB) binary format. FCB uses FlatBuffers for efficient serialization with support for spatial and attribute indexing.

Features

  • Binary Format: Efficient storage using FlatBuffers
  • Spatial Indexing: Fast spatial queries with R-tree indexing
  • Attribute Indexing: Query features by attribute values
  • HTTP Support: Stream features over HTTP with range requests
  • Memory Efficient: Streaming readers for large datasets
  • CityJSON Compatibility: Full support for CityJSON 2.0 specification

Installation

Add this to your Cargo.toml:

[dependencies]
fcb_core = "0.1.0"

# For HTTP support
fcb_core = { version = "0.1.0", features = ["http"] }

Quick Start

Writing FCB Files

use fcb_core::{
    FcbWriter, HeaderWriterOptions, AttributeSchema, AttributeSchemaMethods,
    read_cityjson_from_reader, CJTypeKind
};
use std::fs::File;
use std::io::{BufReader, BufWriter};

// read cityjson data
let input_file = File::open("input.city.jsonl")?;
let input_reader = BufReader::new(input_file);
let cj_seq = read_cityjson_from_reader(input_reader, CJTypeKind::Seq)?;

if let CJType::Seq(cj_seq) = cj_seq {
    // build attribute schema
    let mut attr_schema = AttributeSchema::new();
    for feature in cj_seq.features.iter() {
        for (_, co) in feature.city_objects.iter() {
            if let Some(attributes) = &co.attributes {
                attr_schema.add_attributes(attributes);
            }
        }
    }

    // configure writer options
    let attr_indices = vec![
        ("building_type".to_string(), None),
        ("height".to_string(), None),
    ];

    let header_options = HeaderWriterOptions {
        write_index: true,
        feature_count: cj_seq.features.len() as u64,
        index_node_size: 16,
        attribute_indices: Some(attr_indices),
        geographical_extent: None,
    };

    // create writer and add features
    let output_file = File::create("output.fcb")?;
    let output_writer = BufWriter::new(output_file);

    let mut fcb = FcbWriter::new(
        cj_seq.cj,
        Some(header_options),
        Some(attr_schema),
        None
    )?;

    for feature in cj_seq.features.iter() {
        fcb.add_feature(feature)?;
    }

    fcb.write(output_writer)?;
}

you can also use the fcb_cli to serialize CityJSON to FCB. Check the CLI README for more details.

Reading FCB Files

Read All Features

use fcb_core::{FcbReader, deserializer::to_cj_metadata};
use std::fs::File;
use std::io::BufReader;

let input_file = File::open("input.fcb")?;
let input_reader = BufReader::new(input_file);

let mut reader = FcbReader::open(input_reader)?.select_all()?;
let header = reader.header();
let cj_metadata = to_cj_metadata(&header)?;

println!("features: {}", header.features_count());

while let Some(feature_buf) = reader.next()? {
    let cj_feature = feature_buf.cur_cj_feature()?;
    // process feature
    println!("feature id: {}", cj_feature.id);
}

Spatial Queries (Bounding Box)

use fcb_core::{FcbReader, packed_rtree::Query};

let mut reader = FcbReader::open(input_reader)?
    .select_query(Query::BBox(minx, miny, maxx, maxy))?;

while let Some(feature_buf) = reader.next()? {
    let cj_feature = feature_buf.cur_cj_feature()?;
    // process spatially filtered feature
}

Attribute Queries

use fcb_core::{FcbReader, KeyType, Operator, FixedStringKey, Float};

let query = vec![
    (
        "height".to_string(),
        Operator::Gt,
        KeyType::Float64(Float(10.0)),
    ),
    (
        "building_type".to_string(),
        Operator::Eq,
        KeyType::StringKey50(FixedStringKey::from_str("residential")),
    ),
];

let mut reader = FcbReader::open(input_reader)?.select_attr_query(query)?;

while let Some(feature_buf) = reader.next()? {
    let cj_feature = feature_buf.cur_cj_feature()?;
    // process filtered features
}

HTTP Streaming

use fcb_core::HttpFcbReader;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let http_reader = HttpFcbReader::open("https://example.com/data.fcb").await?;

    // spatial query over http
    let mut iter = http_reader
        .select_query(Query::BBox(minx, miny, maxx, maxy))
        .await?;

    while let Some(feature) = iter.next().await? {
        let cj_feature = feature.cj_feature()?;
        // process feature streamed over http
    }

    Ok(())
}

Attribution

Portions of this software are derived from FlatGeobuf (BSD 2-Clause License). See ATTRIBUTION.md for detailed attribution information.

API Reference

Core Types

FcbWriter<'a>

Main writer for serializing CityJSON to FCB format.

Methods:

  • new(cj, header_options, attr_schema, semantic_attr_schema) -> Result<Self>
  • add_feature(&mut self, feature) -> Result<()>
  • write(self, output) -> Result<()>

FcbReader<R>

Reader for deserializing FCB files.

Methods:

  • open(reader) -> Result<Self>
  • select_all(self) -> Result<FeatureIter<R, Seekable>>
  • select_query(self, query) -> Result<FeatureIter<R, Seekable>>
  • select_attr_query(self, query) -> Result<FeatureIter<R, Seekable>>
  • select_all_seq(self) -> Result<FeatureIter<R, NotSeekable>>
  • select_query_seq(self, query) -> Result<FeatureIter<R, NotSeekable>>
  • select_attr_query_seq(self, query) -> Result<FeatureIter<R, NotSeekable>>

HttpFcbReader<T>

HTTP-based streaming reader.

Methods:

  • open(url) -> Result<Self>
  • select_all(self) -> Result<AsyncFeatureIter<T>>
  • select_query(self, query) -> Result<AsyncFeatureIter<T>>
  • select_attr_query(self, query) -> Result<AsyncFeatureIter<T>>

Configuration

HeaderWriterOptions

Configuration for FCB header writing.

pub struct HeaderWriterOptions {
    pub write_index: bool,
    pub feature_count: u64,
    pub index_node_size: u16,
    pub attribute_indices: Option<Vec<(String, Option<u16>)>>,
    pub geographical_extent: Option<[f64; 6]>,
}

AttributeSchema

Schema for managing attribute types and indexing.

Methods:

  • new() -> Self
  • add_attributes(&mut self, attributes)
  • get(&self, name) -> Option<(u16, ColumnType)>

Query Types

Spatial Queries

use fcb_core::packed_rtree::Query;

// bounding box query
let bbox_query = Query::BBox(minx, miny, maxx, maxy);

Attribute Queries

use fcb_core::{KeyType, Operator, FixedStringKey, Float};

type AttrQuery = Vec<(String, Operator, KeyType)>;

// numeric comparison
let height_query = (
    "height".to_string(),
    Operator::Gt,
    KeyType::Float64(Float(10.0))
);

// string exact match
let type_query = (
    "building_type".to_string(),
    Operator::Eq,
    KeyType::StringKey50(FixedStringKey::from_str("residential"))
);

// datetime comparison
let date_query = (
    "registration_date".to_string(),
    Operator::Gt,
    KeyType::DateTime(chrono::DateTime::from_str("2020-01-01T00:00:00Z")?)
);

Supported Operators

  • Operator::Eq - equals
  • Operator::Gt - greater than
  • Operator::Lt - less than
  • Operator::Gte - greater than or equal
  • Operator::Lte - less than or equal

Supported Key Types

  • KeyType::Float64(Float) - 64-bit floating point
  • KeyType::StringKey50(FixedStringKey) - fixed-length strings up to 50 chars
  • KeyType::DateTime(chrono::DateTime<Utc>) - datetime values
  • KeyType::UByte(u8) - unsigned 8-bit integer

Error Handling

The library uses thiserror for structured error handling:

use fcb_core::error::{Error, Result};

match fcb_reader.select_all() {
    Ok(reader) => {
        // process features
    }
    Err(Error::NoIndex) => {
        println!("file has no spatial index");
    }
    Err(Error::AttributeIndexNotFound) => {
        println!("no attribute index found");
    }
    Err(e) => {
        println!("error: {}", e);
    }
}

Features

Enable optional features in Cargo.toml:

[dependencies]
fcb_core = { version = "0.1.0", features = ["http"] }
  • http - enables HTTP streaming capabilities

Examples

See the tests/ directory for comprehensive examples:

  • tests/attr_index.rs - attribute indexing and querying
  • tests/http.rs - HTTP streaming examples
  • tests/e2e.rs - end-to-end serialization/deserialization
  • tests/read.rs - various reading patterns

Related

License

MIT License - see LICENSE file for details.