cityjson-json 0.7.2

Serde adapter for CityJSON 2.0, providing (de)serialization on top of the `cityjson` crate.
docs.rs failed to build cityjson-json-0.7.2
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

cityjson-json

cityjson-json is the CityJSON 2.0 JSON boundary crate around the cityjson crate. It reads and writes owned CityJSON models with explicit document and feature-stream APIs.

It also exposes the JSON-aware helper layer consumed by cityjson-lib:

  • probe and RootKind for cheap root sniffing
  • staged reconstruction helpers for feature-plus-base workflows
  • JSON-backed cleanup, extract, append, and merge helpers

Benchmarks

Read benchmarks against serde_json::Value for acquired real-world data and synthetic stress cases. The table below is a historical snapshot; refresh the current suite with just bench.

Acquired data

Case cityjson-json serde_json::Value Factor
io_basisvoorziening_3d_cityjson 284.1 MiB/s 276.9 MiB/s 1.03x
io_3dbag_cityjson_cluster_4x 185.6 MiB/s 326.1 MiB/s 0.57x
io_3dbag_cityjson 193.8 MiB/s 341.4 MiB/s 0.57x

Stress cases

Case cityjson-json serde_json::Value Factor
stress_attribute_heavy_heterogenous 158.2 MiB/s 197.8 MiB/s 0.80x
stress_attribute_heavy_homogenous 168.9 MiB/s 212.5 MiB/s 0.79x
stress_boundary_heavy 319.8 MiB/s 212.9 MiB/s 1.50x
stress_geometry_heavy 278.9 MiB/s 218.7 MiB/s 1.28x
stress_hierarchy_heavy 194.7 MiB/s 229.9 MiB/s 0.85x
stress_resource_heavy 162.8 MiB/s 234.0 MiB/s 0.70x
stress_vertex_heavy 357.4 MiB/s 246.5 MiB/s 1.45x

Full benchmark tables and plots are written to benches/results/benchmark_summary.md. Use just bench-local /path/to/file-or-directory for ad hoc local inputs without rewriting this README snapshot.

Installation

cargo add cityjson-json

Getting Started

Read A Document

use cityjson_json::v2_0::{ReadOptions, read_model};

let json_bytes = br#"{
  "type": "CityJSON",
  "version": "2.0",
  "transform": {"scale": [1.0, 1.0, 1.0], "translate": [0.0, 0.0, 0.0]},
  "CityObjects": {},
  "vertices": []
}"#;

let model = read_model(json_bytes, &ReadOptions::default())?;
# Ok::<(), cityjson_json::Error>(())

Write A Document

use cityjson_json::v2_0::{WriteOptions, to_vec};

let bytes = to_vec(&model, &WriteOptions::default())?;
# Ok::<(), cityjson_json::Error>(())

CityJSONSeq

Read a newline-delimited CityJSONSeq stream. The first item must be a CityJSON header; each subsequent item is a self-contained CityJSONFeature:

use std::io::Cursor;
use cityjson_json::v2_0::{ReadOptions, read_feature_stream};

let seq = concat!(
    r#"{"type":"CityJSON","version":"2.0","transform":{"scale":[0.001,0.001,0.001],"translate":[0.0,0.0,0.0]},"CityObjects":{},"vertices":[]}"#, "\n",
    r#"{"type":"CityJSONFeature","id":"f1","CityObjects":{"f1":{"type":"Building"}},"vertices":[]}"#, "\n",
);
let features = read_feature_stream(Cursor::new(seq.as_bytes()), &ReadOptions::default())?
    .collect::<cityjson_json::Result<Vec<_>>>()?;
// each element is an OwnedCityModel (CityJSONFeature) with the header transform merged in
# Ok::<(), cityjson_json::Error>(())

Write a strict CityJSONSeq stream from feature models. The writer derives the header from their shared root state and quantizes vertices with explicit options:

use cityjson_json::v2_0::{
    CityJsonSeqWriteOptions, FeatureStreamTransform, ReadOptions, read_feature_with_base,
    read_model, write_feature_stream,
};
use cityjson::v2_0::Transform;

let base_input = br#"{"type":"CityJSON","version":"2.0","transform":{"scale":[0.001,0.001,0.001],"translate":[0.0,0.0,0.0]},"CityObjects":{},"vertices":[]}"#;
let base = read_model(base_input, &ReadOptions::default())?;
let feature = read_feature_with_base(
    br#"{"type":"CityJSONFeature","id":"f1","CityObjects":{"f1":{"type":"Building","geometry":[{"type":"MultiPoint","boundaries":[0]}]}},"vertices":[[10,20,30]]}"#,
    &base,
    &ReadOptions::default(),
)?;

let mut output: Vec<u8> = Vec::new();
let report = write_feature_stream(
    &mut output,
    [feature],
    &CityJsonSeqWriteOptions {
        transform: FeatureStreamTransform::Explicit(Transform::new()),
        ..CityJsonSeqWriteOptions::default()
    },
)?;
// report.feature_count == 1, report.geographical_extent covers all feature vertices
# Ok::<(), cityjson_json::Error>(())

Documentation

todo: link to docs.rs

Library Layout

Module Contents
v2_0 CityJSON 2.0 read/write entry points and feature-stream helpers
errors Error and Result types surfaced by the adapter
(root) Convenience re-exports: read_*, write_*, option types, and model types

Core types re-exported from cityjson:

  • OwnedCityModel: A CityJSON model with owned String storage. Self-contained and doesn't depend on external lifetimes.

Design

The adapter is optimized around a small number of core decisions:

  • deserialization is split into root preparation and streamed model construction
  • CityObjects and geometry boundaries avoid large intermediate JSON structures
  • attributes deserialize directly into backend value types
  • serialization streams from the model with a shared write context instead of building a DOM first
  • the public JSON boundary is explicitly owned-model oriented

The full design description lives in docs/design.md.

API Stability

This crate follows semantic versioning (MAJOR.MINOR.PATCH):

  • MAJOR: incompatible API changes
  • MINOR: backwards-compatible feature additions
  • PATCH: backwards-compatible fixes

Minimum Rust Version

The minimum supported rustc version is 1.93.0.

Development

Development setup, test configuration, and benchmark workflow are documented in docs/development.md.

Contributing

Contributions are welcome in all forms. Please open an issue to discuss any potential changes before working on a patch. You can submit LLM-generated PRs for bug fixes and documentation improvements. Regardless of handwritten or LLM-generated code, the PR should follow these guidelines:

  • relatively small, focused changes, otherwise I won't be able to review it,
  • follow the existing style and conventions,
  • include unit tests and documentation for new features and bug fixes,
  • the patched code should pass:
    • just ci
  • if you remove or merge tests or examples or benchmarks, please explain why and update the documentation accordingly.

License

Licensed under either:

  • Apache License, Version 2.0 (LICENSE-APACHE)
  • MIT license (LICENSE-MIT)

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in cityjson-json by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without additional terms or conditions.

Use of AI in this project

This crate was originally hand written. It was later refactored to the cityjson-rs API with AI assistance. AI was also used for documentation, and for the test and benchmark harnesses.

Roadmap

There are no major features planned for the near future, beyond bug fixes, test coverage, performance optimization, and documentation improvements.