Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

ChronoJSON

A time-aware extension of the GeoJSON specification.

GeoJSON answers where. ChronoJSON answers where — and when.

PyPI version Python versions License Code style: black Spec Status

Documentation · Quick Start · Specification · Roadmap · Contributing


Table of Contents


🌍 Overview

ChronoJSON is an open specification and reference Python implementation for representing time-aware geographic data. It extends the GeoJSON ecosystem with a temporal dimension, while remaining fully interoperable with existing GIS tooling.

It targets any domain where geography changes over time:

Domain Example use case
🏛️ Historical GIS Empire and border evolution
📜 Digital Humanities Annotated historical narratives
🗺️ Historical Atlases Interactive time-sliced maps
⛏️ Archaeology Site occupation timelines
🏺 Cultural Heritage Monument and site lifecycle tracking
🧭 Historical Cartography Map digitization over eras
🎓 Education & Research Teaching historical change spatially
🎮 Strategy Games Territory control over turns/time
🛰️ UAV / Live Tracking Flight paths, live vehicle location

In one sentence: ChronoJSON stores change, not repetition.


💡 Motivation

GIS formats are excellent at describing space. They are not designed to describe change over time.

When a country's borders shift every few decades, the conventional workaround is to create a new, complete GeoJSON file for every date. This approach seems simple, but it does not scale:

  • 📁 Duplicated geometries across every snapshot
  • 🧾 Duplicated properties, repeated for every version
  • 💾 Growing file sizes as history accumulates
  • 🔧 Painful maintenance — one change means editing N files

ChronoJSON was designed to solve this at the format level, not with external tooling bolted on afterward.


❓ Why ChronoJSON?

  • Single source of truth per feature. One entity, one file, full history.
  • Inheritance, not duplication. Only changes are recorded; everything else is inherited automatically.
  • GeoJSON-compatible core. Familiar Feature / FeatureCollection structures, extended — not replaced.
  • Point-in-time queries. Ask "what did this feature look like in year X?" and get a resolved, immutable state.
  • Tool-agnostic. Plays well with existing GIS and mapping ecosystems by design.

Why not plain GeoJSON?

GeoJSON has no concept of time. Any temporal modeling on top of it is a convention your team invents and maintains by hand — multiple files, naming schemes, manual diffing. ChronoJSON turns that convention into a first-class, validated data model.


⏳ Timeline Inheritance Model

Instead of repeating an entire feature for every date, ChronoJSON stores a base feature plus an ordered list of milestones. Any property or geometry not explicitly changed at a milestone is inherited from the previous state.

Example — Ottoman Beylik → Ottoman Empire

Year Event Result
1299 creation Beylik founded, capital: Söğüt
1326 property_change Capital becomes Bursa
1365 property_change Capital becomes Edirne
1453 geometry_change + property_change Geometry updated, capital → Istanbul, name → "Ottoman Empire"
1699 geometry_change Territorial contraction after treaty
1922 deletion State dissolved
timeline
    title Ottoman Beylik → Ottoman Empire
    1299 : Creation (Capital: Söğüt)
    1326 : Capital → Bursa
    1365 : Capital → Edirne
    1453 : Geometry change, Capital → Istanbul, Name → Ottoman Empire
    1699 : Geometry change
    1922 : Deletion

Querying feature.at(1500) resolves every property and geometry by walking the timeline up to that date — no duplication, no manual bookkeeping.


✨ Features

Implemented

  • ✅ GeoJSON-compatible format
  • ✅ FeatureCollection and Feature objects
  • ✅ Dataset-level metadata support
  • ✅ Chrono and Milestone objects
  • ✅ TimeRange support
  • ✅ Multiple milestone types
  • ✅ JSON loading and saving
  • ✅ Timeline resolution engine
  • ✅ Property inheritance
  • ✅ Geometry inheritance
  • ✅ Immutable resolved state
  • ✅ Feature.at(year) point-in-time queries
  • ✅ Internal resolution cache

Planned — see Roadmap


📦 Installation

pip install chronojson

Requires Python 3.9+.


🚀 Quick Start

import chronojson

# Load a ChronoJSON dataset
collection = chronojson.load("ottoman.geojson")

# Pick a feature from the collection
feature = collection.features[0]

# Resolve its state at a specific point in time
state = feature.at(1500)

print(state.properties["capital"])  # Istanbul
print(state.properties["name"])     # Ottoman Empire

Every call to .at(year) returns an immutable, fully-resolved snapshot — geometry and properties included — computed by walking the feature's milestone timeline.


🧬 Data Model

classDiagram
    class FeatureCollection {
        +metadata: Metadata
        +features: List~Feature~
    }
    class Feature {
        +id: str
        +base_properties: dict
        +base_geometry: Geometry
        +chrono: Chrono
        +at(year) FeatureState
    }
    class Chrono {
        +milestones: List~Milestone~
    }
    class Milestone {
        +type: MilestoneType
        +date: TimeRange
        +properties: dict
        +geometry: Geometry
    }
    class FeatureState {
        +properties: dict
        +geometry: Geometry
        +year: int
    }

    FeatureCollection "1" --> "*" Feature
    Feature "1" --> "1" Chrono
    Chrono "1" --> "*" Milestone
    Feature ..> FeatureState : resolves to

🏷️ Milestone Types

Type Status Description
creation ✅ Stable Marks the start of a feature's existence
deletion ✅ Stable Marks the end of a feature's existence
property_change ✅ Stable Updates one or more properties
geometry_change ✅ Stable Replaces the feature's geometry
event ✅ Stable Non-structural annotation (e.g. a battle, a treaty)
merge 🚧 Planned Combines two or more features into one
split 🚧 Planned Divides a feature into multiple features

🗂️ Metadata

Every ChronoJSON dataset can declare descriptive metadata alongside its features:

{
  "metadata": {
    "title": "Ottoman Empire",
    "description": "Territorial evolution of the Ottoman state, 1299–1922.",
    "author": "Jane Doe",
    "license": "CC-BY-4.0",
    "created": "2024-01-01",
    "updated": "2024-06-01",
    "version": "1.0.0"
  }
}

⚖️ GeoJSON vs ChronoJSON

Aspect GeoJSON ChronoJSON
Spatial representation ✅ Yes ✅ Yes
Temporal representation ❌ No ✅ Yes
Change over time Requires separate files Native, via milestones
Duplication High (one file per date) Minimal (inheritance-based)
Point-in-time queries Manual / external tooling Feature.at(year) built-in
Backward compatibility — Fully compatible superset
File size at scale Grows linearly with snapshots Grows with actual changes only

📁 Project Structure

chronojson/
├── __init__.py
├── schema.py          # Core schema definitions
├── enums.py           # Milestone types and enums
├── io.py              # Load / save operations
├── timeline.py        # Timeline resolution engine
├── validation.py       # Schema and data validation
├── query.py           # Query API (in progress)
├── geometry.py         # Geometry inheritance logic
├── fuzzy.py            # Fuzzy / uncertain date handling
├── exceptions.py       # Custom exception types
│
├── converters/
│   ├── geojson.py      # GeoJSON <-> ChronoJSON conversion
│   ├── csv.py           # CSV import/export
│   └── pandas.py        # pandas DataFrame integration
│
└── utils/
    ├── cache.py         # Resolution caching
    ├── dates.py         # Date/time parsing utilities
    └── ids.py            # Feature ID generation

🏗️ Architecture

flowchart LR
    A[ChronoJSON File] --> B[io.py<br/>Load]
    B --> C[schema.py<br/>Parse into Feature/Chrono objects]
    C --> D[validation.py<br/>Validate structure]
    C --> E[timeline.py<br/>Resolution Engine]
    E --> F[geometry.py<br/>Geometry inheritance]
    E --> G[Property inheritance]
    F --> H[utils/cache.py]
    G --> H
    H --> I[Feature.at year]
    I --> J[Resolved FeatureState]

🧩 API Overview

# Loading and saving
collection = chronojson.load("path/to/file.geojson")
chronojson.save(collection, "output.geojson")

# Accessing features
feature = collection.features[0]

# Resolving state at a point in time
state = feature.at(1453)
state.properties   # dict of resolved properties
state.geometry     # resolved geometry object

# Inspecting the timeline directly
for milestone in feature.chrono.milestones:
    print(milestone.type, milestone.date)

📘 A full API reference will be published alongside the Query API release.


📐 Specification Overview

ChronoJSON is designed as an open specification, not just a Python library. The reference implementation in this repository is one conformant implementation; others (e.g. a future JavaScript SDK) are expected to follow the same document model:

  • FeatureCollection → Feature → Chrono → Milestone
  • All coordinate and geometry structures remain RFC 7946 (GeoJSON) compatible
  • Temporal resolution is deterministic and order-dependent on milestone dates

A formal specification document is tracked under Roadmap.


🗺️ Roadmap

  • Validation Engine (schema + semantic validation)
  • merge and split milestone support
  • Query API (filter features by time range, property, region)
  • JavaScript SDK
  • Complete formal specification document
  • CLI tool
  • GeoPandas integration
  • Leaflet integration
  • OpenLayers integration
  • MapLibre integration
  • Temporal GIS utility functions

🤝 Contributing

Contributions are welcome, whether you're fixing a bug, improving documentation, or proposing an addition to the specification.

How to contribute
  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Make your changes and add tests where applicable
  4. Run the test suite: pytest
  5. Open a pull request with a clear description of the change

Please open an issue first for significant changes, especially anything touching the core specification, so it can be discussed before implementation work begins.


📄 License

This project is licensed under the MIT License. See LICENSE for details.


👤 Author

Maintained by Ahmet Koçyiğit. Feel free to open an issue or start a discussion for questions, ideas, or feedback.


🙏 Acknowledgements

  • The GeoJSON specification, for the spatial foundation this project builds on
  • The broader GIS and Digital Humanities communities whose use cases shaped this project's direction

ChronoJSON — because geography has a history.

Metadata

Release files for chronojson 0.1.0b2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for chronojson 0.1.0b2
File Size Uploaded
chronojson-0.1.0b2.tar.gz 15.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for chronojson 0.1.0b2
File Interpreter ABI Platform
chronojson-0.1.0b2-py3-none-any.whl Python 3 none any Details

Total release size: 26.8 kB

Release files / chronojson-0.1.0b2.tar.gz

Download URL chronojson-0.1.0b2.tar.gz
Size 15.7 kB
Tags Source
SHA-256 checksum
How to use checksums
e668da26608587dc80965361a2ae5e317cda2dca0c0b1a52de7d615246fe745e
BLAKE2b-256 checksum
How to use checksums
48ffe75ec4a2ade690575c5f235210889bc67b7d0311c6dc868ef7d0b71406d3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.0

Release files / chronojson-0.1.0b2-py3-none-any.whl

Download URL chronojson-0.1.0b2-py3-none-any.whl
Size 11.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fe90bb764e4f9eefaf930bef89c527b27a20969a7f571ed2541a91595dc4fa35
BLAKE2b-256 checksum
How to use checksums
d5fe9e32c7a6cf16aeebd2e9d48a2bbc7a29951854d4a1ce2eb2e44d7fee3e36
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.0

Release history Release notifications | RSS feed

This release

0.1.0b2 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page