flatcitybuf


Nameflatcitybuf JSON
Version 0.1.2 PyPI version JSON
download
home_pageNone
SummaryPython bindings for FlatCityBuf - a cloud-optimized binary format for 3D city models
upload_time2025-08-12 20:15:55
maintainerNone
docs_urlNone
authorHideBa
requires_python>=3.8.1
licenseMIT
keywords gis 3d cityjson citymodel flatbuffers
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # FlatCityBuf Python Bindings

Python bindings for [FlatCityBuf](../../README.md), a cloud-optimized binary format for storing and retrieving 3D city models with full CityJSON compatibility.

## Features

- **Fast reading** of FlatCityBuf (.fcb) files with zero-copy access
- **Local and HTTP** file support with async iteration
- **CityJSON integration** with transform, metadata, and proper structure
- **Spatial queries** using bounding boxes with R-tree indexing
- **Attribute queries** for filtering features with B+tree indices
- **Async iterators** for efficient streaming from HTTP sources
- **Nested boundaries** support for complex 3D geometries
- **Pythonic API** with comprehensive type support

## Installation

### From Source

```bash
# Prerequisites: Rust toolchain and maturin
pip install maturin

# Build and install with HTTP support
cd src/rust/fcb_py
maturin develop --features http
```

### Using pip (when available)

```bash
pip install flatcitybuf
```

## Quick Start

### Local File Access

```python
import flatcitybuf as fcb

# Read a local file
reader = fcb.Reader("data.fcb")

# Get file information
info = reader.info()
print(f"Features: {info.feature_count}")

# Get CityJSON header with transform and metadata
cityjson = reader.cityjson_header()
print(f"CityJSON version: {cityjson.version}")
print(f"Transform: {cityjson.transform.scale}, {cityjson.transform.translate}")

# Iterate all features (CityJSON format)
for feature in reader:
    print(f"ID: {feature.id}, Type: {feature.type}")
    print(f"City Objects: {len(feature.city_objects)}")
    print(f"Vertices: {len(feature.vertices)}")

# Spatial query
features = list(reader.query_bbox(84227.77, 445377.33, 85323.23, 446334.69))
print(f"Found {len(features)} features in bounding box")

# Attribute query
id_filter = fcb.AttrFilter("identificatie", fcb.Operator.Eq, "building_123")
buildings = list(reader.query_attr([id_filter]))
print(f"Found {len(buildings)} matching buildings")
```

### HTTP Access with Async Iterators

```python
import asyncio
import flatcitybuf as fcb

async def main():
    # Create async reader for HTTP URL
    async_reader = fcb.AsyncReader("https://example.com/data.fcb")
    opened_reader = await async_reader.open()
    
    # Get file info
    info = opened_reader.info()
    print(f"Features: {info.feature_count}")
    
    # Async iteration - efficient streaming
    async_iter = opened_reader.select_all()
    
    # Process features one by one
    count = 0
    while count < 10:  # Get first 10 features
        feature = await async_iter.next()
        if feature is None:
            break
        print(f"Feature {count}: {feature.id}")
        count += 1
    
    # Or collect all at once
    all_features = await opened_reader.select_all().collect()
    print(f"Total features: {len(all_features)}")
    
    # Async spatial query
    bbox_iter = opened_reader.query_bbox(84227.77, 445377.33, 85323.23, 446334.69)
    spatial_features = await bbox_iter.collect()
    print(f"Spatial query result: {len(spatial_features)} features")

asyncio.run(main())
```

## API Reference

### Reader (Synchronous)

Main class for reading local FlatCityBuf files.

```python
reader = fcb.Reader(path: str)
```

**Methods:**
- `info() -> FileInfo` - Get file metadata and statistics
- `cityjson_header() -> CityJSON` - Get CityJSON header with transform/metadata
- `query_bbox(min_x, min_y, max_x, max_y) -> Iterator[Feature]` - Spatial query
- `query_attr(filters: List[AttrFilter]) -> Iterator[Feature]` - Attribute query
- `__iter__() -> Iterator[Feature]` - Iterate all features

### AsyncReader (HTTP)

Asynchronous reader for HTTP-based FlatCityBuf files.

```python
async_reader = fcb.AsyncReader(url: str)
opened_reader = await async_reader.open()
```

**Methods:**
- `info() -> FileInfo` - Get file metadata
- `cityjson_header() -> CityJSON` - Get CityJSON header information
- `select_all() -> AsyncFeatureIterator` - Get all features as async iterator
- `query_bbox(min_x, min_y, max_x, max_y) -> AsyncFeatureIterator` - Async spatial query
- `query_attr(filters: List[AttrFilter]) -> AsyncFeatureIterator` - Async attribute query

### AsyncFeatureIterator

Efficient async iterator for streaming features from HTTP sources.

```python
# Get features one by one
feature = await async_iter.next()  # Returns Feature or None

# Collect all remaining features
features = await async_iter.collect()  # Returns List[Feature]
```

### Feature (CityJSON Format)

Represents a CityJSON feature with proper structure.

```python
class Feature:
    id: str                           # Feature ID
    type: str                         # Feature type  
    vertices: List[List[float]]       # 3D vertices as [x, y, z] arrays
    city_objects: Dict[str, CityObject]  # Dictionary of city objects
```

### CityObject

Individual city object within a feature.

```python
class CityObject:
    type: str                         # Object type (e.g., "Building")
    geometry: List[Geometry]          # List of geometries
    attributes: Dict[str, Any]        # Object attributes
    children: List[str]               # Child object IDs
    parents: List[str]                # Parent object IDs
```

### Geometry

3D geometry with nested boundary structure.

```python
class Geometry:
    type: str                         # Geometry type
    boundaries: PyObject              # Nested boundary arrays (preserves structure)
    semantics: Optional[Any]          # Semantic information
```

### CityJSON Header

CityJSON metadata and transform information.

```python
class CityJSON:
    type: str                         # Always "CityJSON"
    version: str                      # CityJSON version
    transform: Transform              # Coordinate transformation
    metadata: Optional[Metadata]      # Optional metadata
    feature_count: int                # Number of features
```

### Transform

Coordinate transformation parameters.

```python
class Transform:
    scale: List[float]                # Scale factors [x, y, z]
    translate: List[float]            # Translation [x, y, z]
```

### Query Types

```python
# Bounding box
bbox = fcb.BBox(min_x=0, min_y=0, max_x=100, max_y=100)

# Attribute filters
filter_eq = fcb.AttrFilter("type", fcb.Operator.Eq, "building")
filter_gt = fcb.AttrFilter("height", fcb.Operator.Gt, 50.0)
filter_le = fcb.AttrFilter("floors", fcb.Operator.Le, 10)

# Available operators
fcb.Operator.Eq    # Equal
fcb.Operator.Ne    # Not equal  
fcb.Operator.Gt    # Greater than
fcb.Operator.Ge    # Greater than or equal
fcb.Operator.Lt    # Less than
fcb.Operator.Le    # Less than or equal
```

## Performance

The Python bindings leverage Rust's zero-copy deserialization and efficient indexing:

- **10-20× faster** than parsing equivalent JSON formats
- **2-6× less memory** usage compared to text formats  
- **Efficient spatial indexing** with packed R-tree queries
- **HTTP range requests** for cloud-optimized partial access
- **Async streaming** minimizes memory usage for large datasets
- **Nested boundary preservation** avoids flattening overhead

## Nested Boundaries

The bindings properly preserve CityJSON's nested boundary structure:

```python
# Boundaries can be nested arrays at arbitrary depth
# Example: [[0, 1, 2, 3]] for a single surface
# Example: [[[0, 1, 2, 3], [4, 5, 6, 7]]] for surfaces with holes
geometry.boundaries  # Preserves exact nesting from CityJSON
```

## HTTP Optimization

AsyncReader is optimized for HTTP access:

- **Persistent connections** - HTTP client maintained across iterator calls
- **Range request batching** - Efficient partial downloads
- **Prefetching strategies** - Reduces round-trip latency
- **Async streaming** - Process features as they arrive

## Examples

See the [examples/](examples/) directory for comprehensive usage examples including:
- Basic local file reading
- HTTP access with async iteration
- CityJSON header processing
- Spatial and attribute queries
- Nested geometry handling

## Development

### Building from Source

```bash
# Install development dependencies
pip install maturin pytest pytest-asyncio

# Build in development mode
maturin develop --features http

# Run tests
pytest tests/

# Run with coverage
pytest tests/ --cov=flatcitybuf
```

### Testing

```bash
# Run all tests
python -m pytest tests/test_e2e.py -v

# Run only sync tests
python -m pytest tests/test_e2e.py::TestE2EIntegration -v

# Run only async tests  
python -m pytest tests/test_e2e.py::TestAsyncReaderE2E -v
```

### Project Structure

```
src/rust/fcb_py/
├── python/             # Python stub files
├── src/                # Rust PyO3 bindings
│   ├── lib.rs         # Main module
│   ├── reader.rs      # Sync reader
│   ├── async_reader.rs # Async reader
│   ├── types.rs       # Python types
│   ├── utils.rs       # Conversion utilities
│   └── query.rs       # Query types
├── tests/             # Python tests
├── examples/          # Usage examples
├── Cargo.toml         # Rust dependencies
└── pyproject.toml     # Python project config
```

## License

MIT License - see [LICENSE](../../LICENSE) file for details.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "flatcitybuf",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8.1",
    "maintainer_email": null,
    "keywords": "gis, 3d, cityjson, citymodel, flatbuffers",
    "author": "HideBa",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/eb/17/2e18344ebf1fd2ebc5f1c5a4074d6e3cee4bb6e447a58428a0bae0d41d1f/flatcitybuf-0.1.2.tar.gz",
    "platform": null,
    "description": "# FlatCityBuf Python Bindings\n\nPython bindings for [FlatCityBuf](../../README.md), a cloud-optimized binary format for storing and retrieving 3D city models with full CityJSON compatibility.\n\n## Features\n\n- **Fast reading** of FlatCityBuf (.fcb) files with zero-copy access\n- **Local and HTTP** file support with async iteration\n- **CityJSON integration** with transform, metadata, and proper structure\n- **Spatial queries** using bounding boxes with R-tree indexing\n- **Attribute queries** for filtering features with B+tree indices\n- **Async iterators** for efficient streaming from HTTP sources\n- **Nested boundaries** support for complex 3D geometries\n- **Pythonic API** with comprehensive type support\n\n## Installation\n\n### From Source\n\n```bash\n# Prerequisites: Rust toolchain and maturin\npip install maturin\n\n# Build and install with HTTP support\ncd src/rust/fcb_py\nmaturin develop --features http\n```\n\n### Using pip (when available)\n\n```bash\npip install flatcitybuf\n```\n\n## Quick Start\n\n### Local File Access\n\n```python\nimport flatcitybuf as fcb\n\n# Read a local file\nreader = fcb.Reader(\"data.fcb\")\n\n# Get file information\ninfo = reader.info()\nprint(f\"Features: {info.feature_count}\")\n\n# Get CityJSON header with transform and metadata\ncityjson = reader.cityjson_header()\nprint(f\"CityJSON version: {cityjson.version}\")\nprint(f\"Transform: {cityjson.transform.scale}, {cityjson.transform.translate}\")\n\n# Iterate all features (CityJSON format)\nfor feature in reader:\n    print(f\"ID: {feature.id}, Type: {feature.type}\")\n    print(f\"City Objects: {len(feature.city_objects)}\")\n    print(f\"Vertices: {len(feature.vertices)}\")\n\n# Spatial query\nfeatures = list(reader.query_bbox(84227.77, 445377.33, 85323.23, 446334.69))\nprint(f\"Found {len(features)} features in bounding box\")\n\n# Attribute query\nid_filter = fcb.AttrFilter(\"identificatie\", fcb.Operator.Eq, \"building_123\")\nbuildings = list(reader.query_attr([id_filter]))\nprint(f\"Found {len(buildings)} matching buildings\")\n```\n\n### HTTP Access with Async Iterators\n\n```python\nimport asyncio\nimport flatcitybuf as fcb\n\nasync def main():\n    # Create async reader for HTTP URL\n    async_reader = fcb.AsyncReader(\"https://example.com/data.fcb\")\n    opened_reader = await async_reader.open()\n    \n    # Get file info\n    info = opened_reader.info()\n    print(f\"Features: {info.feature_count}\")\n    \n    # Async iteration - efficient streaming\n    async_iter = opened_reader.select_all()\n    \n    # Process features one by one\n    count = 0\n    while count < 10:  # Get first 10 features\n        feature = await async_iter.next()\n        if feature is None:\n            break\n        print(f\"Feature {count}: {feature.id}\")\n        count += 1\n    \n    # Or collect all at once\n    all_features = await opened_reader.select_all().collect()\n    print(f\"Total features: {len(all_features)}\")\n    \n    # Async spatial query\n    bbox_iter = opened_reader.query_bbox(84227.77, 445377.33, 85323.23, 446334.69)\n    spatial_features = await bbox_iter.collect()\n    print(f\"Spatial query result: {len(spatial_features)} features\")\n\nasyncio.run(main())\n```\n\n## API Reference\n\n### Reader (Synchronous)\n\nMain class for reading local FlatCityBuf files.\n\n```python\nreader = fcb.Reader(path: str)\n```\n\n**Methods:**\n- `info() -> FileInfo` - Get file metadata and statistics\n- `cityjson_header() -> CityJSON` - Get CityJSON header with transform/metadata\n- `query_bbox(min_x, min_y, max_x, max_y) -> Iterator[Feature]` - Spatial query\n- `query_attr(filters: List[AttrFilter]) -> Iterator[Feature]` - Attribute query\n- `__iter__() -> Iterator[Feature]` - Iterate all features\n\n### AsyncReader (HTTP)\n\nAsynchronous reader for HTTP-based FlatCityBuf files.\n\n```python\nasync_reader = fcb.AsyncReader(url: str)\nopened_reader = await async_reader.open()\n```\n\n**Methods:**\n- `info() -> FileInfo` - Get file metadata\n- `cityjson_header() -> CityJSON` - Get CityJSON header information\n- `select_all() -> AsyncFeatureIterator` - Get all features as async iterator\n- `query_bbox(min_x, min_y, max_x, max_y) -> AsyncFeatureIterator` - Async spatial query\n- `query_attr(filters: List[AttrFilter]) -> AsyncFeatureIterator` - Async attribute query\n\n### AsyncFeatureIterator\n\nEfficient async iterator for streaming features from HTTP sources.\n\n```python\n# Get features one by one\nfeature = await async_iter.next()  # Returns Feature or None\n\n# Collect all remaining features\nfeatures = await async_iter.collect()  # Returns List[Feature]\n```\n\n### Feature (CityJSON Format)\n\nRepresents a CityJSON feature with proper structure.\n\n```python\nclass Feature:\n    id: str                           # Feature ID\n    type: str                         # Feature type  \n    vertices: List[List[float]]       # 3D vertices as [x, y, z] arrays\n    city_objects: Dict[str, CityObject]  # Dictionary of city objects\n```\n\n### CityObject\n\nIndividual city object within a feature.\n\n```python\nclass CityObject:\n    type: str                         # Object type (e.g., \"Building\")\n    geometry: List[Geometry]          # List of geometries\n    attributes: Dict[str, Any]        # Object attributes\n    children: List[str]               # Child object IDs\n    parents: List[str]                # Parent object IDs\n```\n\n### Geometry\n\n3D geometry with nested boundary structure.\n\n```python\nclass Geometry:\n    type: str                         # Geometry type\n    boundaries: PyObject              # Nested boundary arrays (preserves structure)\n    semantics: Optional[Any]          # Semantic information\n```\n\n### CityJSON Header\n\nCityJSON metadata and transform information.\n\n```python\nclass CityJSON:\n    type: str                         # Always \"CityJSON\"\n    version: str                      # CityJSON version\n    transform: Transform              # Coordinate transformation\n    metadata: Optional[Metadata]      # Optional metadata\n    feature_count: int                # Number of features\n```\n\n### Transform\n\nCoordinate transformation parameters.\n\n```python\nclass Transform:\n    scale: List[float]                # Scale factors [x, y, z]\n    translate: List[float]            # Translation [x, y, z]\n```\n\n### Query Types\n\n```python\n# Bounding box\nbbox = fcb.BBox(min_x=0, min_y=0, max_x=100, max_y=100)\n\n# Attribute filters\nfilter_eq = fcb.AttrFilter(\"type\", fcb.Operator.Eq, \"building\")\nfilter_gt = fcb.AttrFilter(\"height\", fcb.Operator.Gt, 50.0)\nfilter_le = fcb.AttrFilter(\"floors\", fcb.Operator.Le, 10)\n\n# Available operators\nfcb.Operator.Eq    # Equal\nfcb.Operator.Ne    # Not equal  \nfcb.Operator.Gt    # Greater than\nfcb.Operator.Ge    # Greater than or equal\nfcb.Operator.Lt    # Less than\nfcb.Operator.Le    # Less than or equal\n```\n\n## Performance\n\nThe Python bindings leverage Rust's zero-copy deserialization and efficient indexing:\n\n- **10-20\u00d7 faster** than parsing equivalent JSON formats\n- **2-6\u00d7 less memory** usage compared to text formats  \n- **Efficient spatial indexing** with packed R-tree queries\n- **HTTP range requests** for cloud-optimized partial access\n- **Async streaming** minimizes memory usage for large datasets\n- **Nested boundary preservation** avoids flattening overhead\n\n## Nested Boundaries\n\nThe bindings properly preserve CityJSON's nested boundary structure:\n\n```python\n# Boundaries can be nested arrays at arbitrary depth\n# Example: [[0, 1, 2, 3]] for a single surface\n# Example: [[[0, 1, 2, 3], [4, 5, 6, 7]]] for surfaces with holes\ngeometry.boundaries  # Preserves exact nesting from CityJSON\n```\n\n## HTTP Optimization\n\nAsyncReader is optimized for HTTP access:\n\n- **Persistent connections** - HTTP client maintained across iterator calls\n- **Range request batching** - Efficient partial downloads\n- **Prefetching strategies** - Reduces round-trip latency\n- **Async streaming** - Process features as they arrive\n\n## Examples\n\nSee the [examples/](examples/) directory for comprehensive usage examples including:\n- Basic local file reading\n- HTTP access with async iteration\n- CityJSON header processing\n- Spatial and attribute queries\n- Nested geometry handling\n\n## Development\n\n### Building from Source\n\n```bash\n# Install development dependencies\npip install maturin pytest pytest-asyncio\n\n# Build in development mode\nmaturin develop --features http\n\n# Run tests\npytest tests/\n\n# Run with coverage\npytest tests/ --cov=flatcitybuf\n```\n\n### Testing\n\n```bash\n# Run all tests\npython -m pytest tests/test_e2e.py -v\n\n# Run only sync tests\npython -m pytest tests/test_e2e.py::TestE2EIntegration -v\n\n# Run only async tests  \npython -m pytest tests/test_e2e.py::TestAsyncReaderE2E -v\n```\n\n### Project Structure\n\n```\nsrc/rust/fcb_py/\n\u251c\u2500\u2500 python/             # Python stub files\n\u251c\u2500\u2500 src/                # Rust PyO3 bindings\n\u2502   \u251c\u2500\u2500 lib.rs         # Main module\n\u2502   \u251c\u2500\u2500 reader.rs      # Sync reader\n\u2502   \u251c\u2500\u2500 async_reader.rs # Async reader\n\u2502   \u251c\u2500\u2500 types.rs       # Python types\n\u2502   \u251c\u2500\u2500 utils.rs       # Conversion utilities\n\u2502   \u2514\u2500\u2500 query.rs       # Query types\n\u251c\u2500\u2500 tests/             # Python tests\n\u251c\u2500\u2500 examples/          # Usage examples\n\u251c\u2500\u2500 Cargo.toml         # Rust dependencies\n\u2514\u2500\u2500 pyproject.toml     # Python project config\n```\n\n## License\n\nMIT License - see [LICENSE](../../LICENSE) file for details.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Python bindings for FlatCityBuf - a cloud-optimized binary format for 3D city models",
    "version": "0.1.2",
    "project_urls": {
        "Bug Tracker": "https://github.com/cityjson/flatcitybuf/issues",
        "Documentation": "https://github.com/cityjson/flatcitybuf/blob/main/README.md",
        "Source Code": "https://github.com/cityjson/flatcitybuf"
    },
    "split_keywords": [
        "gis",
        " 3d",
        " cityjson",
        " citymodel",
        " flatbuffers"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "cf965334995703809d734656d19bef19b7d9a987296fe77b4420ff51c1d9b051",
                "md5": "994720940aa720f496879ee261b76bdc",
                "sha256": "75c3b21581222bb6f157e7e00b47cad82e9aea6c6e32556b56081c89aefef9f6"
            },
            "downloads": -1,
            "filename": "flatcitybuf-0.1.2-cp313-cp313-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "994720940aa720f496879ee261b76bdc",
            "packagetype": "bdist_wheel",
            "python_version": "cp313",
            "requires_python": ">=3.8.1",
            "size": 1971028,
            "upload_time": "2025-08-12T20:15:53",
            "upload_time_iso_8601": "2025-08-12T20:15:53.608584Z",
            "url": "https://files.pythonhosted.org/packages/cf/96/5334995703809d734656d19bef19b7d9a987296fe77b4420ff51c1d9b051/flatcitybuf-0.1.2-cp313-cp313-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "124521a14f7f246abfad9aeaa010125713f32116bf6a3eb5d2b8a8882c6f5ad9",
                "md5": "01bcdd4a05293e02f2b3d7d0fcea66ab",
                "sha256": "1590104af1410de9fa8845027c5f06b0fbb2dc0f01964e70f56562a32f7c0c5f"
            },
            "downloads": -1,
            "filename": "flatcitybuf-0.1.2-cp39-cp39-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "01bcdd4a05293e02f2b3d7d0fcea66ab",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8.1",
            "size": 2079810,
            "upload_time": "2025-08-12T20:19:10",
            "upload_time_iso_8601": "2025-08-12T20:19:10.183219Z",
            "url": "https://files.pythonhosted.org/packages/12/45/21a14f7f246abfad9aeaa010125713f32116bf6a3eb5d2b8a8882c6f5ad9/flatcitybuf-0.1.2-cp39-cp39-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "eb172e18344ebf1fd2ebc5f1c5a4074d6e3cee4bb6e447a58428a0bae0d41d1f",
                "md5": "6ac18526cdac9d79bc7f767d9484110e",
                "sha256": "78f36db2d61ad9cffab805bbf6311bcfa4120e30da447b5af848bf6097d45d0b"
            },
            "downloads": -1,
            "filename": "flatcitybuf-0.1.2.tar.gz",
            "has_sig": false,
            "md5_digest": "6ac18526cdac9d79bc7f767d9484110e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8.1",
            "size": 1673826,
            "upload_time": "2025-08-12T20:15:55",
            "upload_time_iso_8601": "2025-08-12T20:15:55.765288Z",
            "url": "https://files.pythonhosted.org/packages/eb/17/2e18344ebf1fd2ebc5f1c5a4074d6e3cee4bb6e447a58428a0bae0d41d1f/flatcitybuf-0.1.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-12 20:15:55",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "cityjson",
    "github_project": "flatcitybuf",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "flatcitybuf"
}
        
Elapsed time: 0.85370s