Citysense
CitySense is an open-source Python library that bridges geospatial urban data with large language model (LLM) toolchains.
Ask AI about Citysense
Powered by Claude · Grounded in docs
I know everything about Citysense. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
CitySense
Geospatial RAG + MCP Toolkit for Urban Intelligence
Built for SDG 11 workflows, pilot countries, and research-grade city analytics.
QUICK LINKS
Documentation | PyPI | Issues | MCP
PARTNERS
PILOT COUNTRIES
PACKAGE
REPOSITORY
QUALITY
STANDARDS
TECH STACK
PLATFORM
Table of Contents
- Abstract
- Executive Summary
- Overview
- Current Implementation Status
- Architecture
- Requirements
- Installation
- Quick Start
- Configuration
- CLI Reference
- MCP Integration
- Known Limitations
- Pilot Countries
- Mathematical Foundations
- API Summary
- Data Sources and Connectors
- WUF13 Alignment
- H3 Spatial Index
- Dependencies
- Data Flow
- Troubleshooting
- Glossary
- Frequently Asked Questions
- Version History
- Development
- Security Considerations
- Performance Notes
- References
- License
- Links
- Acknowledgments
Abstract
CitySense is an open-source Python library for geospatial retrieval-augmented generation (RAG) and MCP-based AI tooling for urban analysis. It combines natural-language intent parsing, geospatial indexing, and an MCP server so applications can query urban context with minimal boilerplate. The project aligns with SDG 11, the New Urban Agenda, and WUF13 framing. The current codebase includes active pilot configs for Azerbaijan and Finland, OSM-powered indexing, and modular connectors for Sentinel-2, Mapillary, and KartaView integrations.
Executive Summary
| Aspect | Description |
|---|---|
| Primary function | Semantic geospatial retrieval and MCP server for urban AI |
| Target users | Urban AI developers, smart city researchers, policy tool builders |
| Core technologies | Python 3.12+, Qdrant, Shapely, GeoPandas, H3, FastEmbed, FastMCP |
| Implemented pilots | Azerbaijan (az), Finland (fi) |
| Current indexing path | OSM -> H3 -> embeddings -> Qdrant |
| Standards alignment | WUF13 framing, SDG 11 indicator support |
| License | EUPL-1.2 |
Overview
Design Philosophy
The core design philosophy of CitySense is that a developer should be able to express spatial intent in natural language and receive structured geospatial results without writing spatial query code. For example:
# find housing zones with low resilience scores near transit corridors in Baku
CitySense resolves such intent into structured geospatial results drawn from live and indexed sources.
Observational Layers
CitySense adds a third observational layer on top of traditional vector geospatial data:
| Layer | Source | Purpose |
|---|---|---|
| Vector | OpenStreetMap, national registers | Buildings, roads, land use, amenities |
| Street-level imagery | Mapillary API v4, KartaView REST API | Ground truth, validation |
| Satellite | Copernicus Data Space, Sentinel Hub | Spectral indices, change detection |
Key Features
| Capability | Description |
|---|---|
| Natural language queries | Intent parsing and semantic retrieval over geospatial knowledge bases |
| Multi-source architecture | OSM active CLI path; Sentinel-2, Mapillary, and KartaView connector modules included |
| MCP integration | Native Model Context Protocol server for Cursor, Claude, VS Code |
| WUF13 alignment | Pilot-aware schema and indicator model aligned to WUF13 framing |
| SDG 11 metrics | Land consumption rate (11.3.1), urban resilience composite score |
| Spectral indices | NDVI, NDWI, NDBI, EVI, MNDWI, BSI from Sentinel-2 L2A |
| Reciprocal Rank Fusion | Hybrid dense and sparse retrieval with configurable parameter k |
Primary User Groups
| Group | Use Case |
|---|---|
| Urban AI Developers | Build applications on city data with semantic access to geospatial knowledge bases |
| Smart City Researchers | Query, compare, and analyze datasets across multiple countries with consistent schemas |
| Urban Policy Tools Builders | Prototype AI-assisted planning tools aligned with UN New Urban Agenda, SDG 11, and WUF13 |
Current Implementation Status
| Area | Status | Notes |
|---|---|---|
| CLI commands | Implemented | pilot, index, query, serve, --version |
| MCP tools | Implemented | query_spatial_context tool is available |
| Indexing connectors in CLI | Partially implemented | index build currently uses OSM connector path |
| Pilot country configs | Implemented | az, fi in citysense.pilot |
| Additional pilot profiles | Planned | se, dk, no are parsed in intent logic but not yet shipped as pilot configs |
| SSE transport | Placeholder | serve --transport sse currently prints status message |
Architecture
System Stack
+----------------------------------------------------------------------------------+
| CitySense Stack |
+------------------------------+---------------------------------------------------+
| CLI | RAG pipeline |
| - pilot (init/status) | parse_intent -> embed_texts -> hybrid_search -> |
| - index (build/status) | rerank -> assemble_context |
| - query | |
| - serve (stdio, sse stub) | |
+------------------------------+---------------------------------------------------+
| MCP server (FastMCP) | Connectors |
| - query_spatial_context | OSM (active path), Sentinel-2, Mapillary, |
| | KartaView (module-level support) |
+------------------------------+---------------------------------------------------+
| Storage and geo foundation: Qdrant + H3 + Shapely + GeoPandas |
+----------------------------------------------------------------------------------+
Module Structure
| Module | Responsibility |
|---|---|
| citysense.cli | Typer-based command-line interface |
| citysense.core | Configuration, session, logging, registry, exceptions |
| citysense.geo | CRS, H3, bbox, geometry, OSM utilities |
| citysense.connectors | OSM, Mapillary, KartaView, Sentinel, base connector |
| citysense.rag | Intent parsing, retriever, embedder, reranker, assembler |
| citysense.imagery | Street and satellite processing |
| citysense.housing | Housing analytics primitives |
| citysense.governance | Governance-related scoring hooks |
| citysense.urban | SDG 11 indicators |
| citysense.climate | Resilience scoring |
| citysense.realtime | Realtime stream utilities |
| citysense.pilot | Country-specific configurations |
| citysense.mcp | MCP server and tools |
Design Principles
| Principle | Implementation |
|---|---|
| Schema consistency | Pilot configs enforce national CRS and normalised GeoDataFrame schema |
| Extensibility | Connector and pilot registries allow plug-in modules |
| Observability | structlog for structured logging; configurable log levels |
| Type safety | mypy strict mode; Pydantic for config and data validation |
Requirements
| Requirement | Version |
|---|---|
| Python | 3.12 or 3.13 |
| Qdrant | Default endpoint http://localhost:6333 |
| GDAL | 3.10+ (for rasterio, geopandas; conda recommended) |
Installation
pip
pip install citysense
Optional Extras
| Extra | Purpose |
|---|---|
| clip | CLIP ViT-B/32 for street imagery embedding |
| sentinelhub | Sentinel Hub Process API |
| dev | ruff, mypy, pytest, mkdocs, pre-commit |
pip install "citysense[clip]"
pip install "citysense[dev]"
conda-forge (Binary Compatibility)
For binary compatibility with GDAL, PROJ, GEOS:
conda env create -f environment.yml
conda activate citysense
pip install -e ".[dev]"
Editable Install
git clone https://github.com/olaflaitinen/citysense.git
cd citysense
pip install -e ".[dev]"
Quick Start
5-Minute Workflow
# 1) select pilot
citysense pilot init fi
# 2) build index (current CLI path: OSM)
citysense index build --city Helsinki --country fi --sources osm
# 3) check index status
citysense index status --country fi
# 4) run a natural-language query
citysense query "social housing zones within 1 km of metro stations in Helsinki"
# 5) start MCP server (stdio)
citysense serve --transport stdio
Optional: Enable Imagery Dependencies (Connector Modules)
pip install "citysense[clip]"
pip install "citysense[sentinelhub]"
Then set connector credentials (MAPILLARY_ACCESS_TOKEN, CDSE_CLIENT_ID, CDSE_CLIENT_SECRET) as needed.
Configuration
Configuration is read in priority order: environment variables (prefix CITYSENSE_), .env file at project root, defaults.
Environment Variables
| Variable | Default | Description |
|---|---|---|
| CITYSENSE_PILOT_COUNTRY | None | Pilot key (az, fi fully available; se, dk, no reserved for upcoming profiles) |
| CITYSENSE_VECTOR_STORE_URL | http://localhost:6333 | Qdrant URL |
| CITYSENSE_EMBEDDING_MODEL | BAAI/bge-m3 | Text embedding model |
| MAPILLARY_ACCESS_TOKEN | None | Mapillary API token |
| CDSE_CLIENT_ID | None | Copernicus Data Space client ID |
| CDSE_CLIENT_SECRET | None | Copernicus Data Space client secret |
Example .env
CITYSENSE_PILOT_COUNTRY=fi
CITYSENSE_VECTOR_STORE_URL=http://localhost:6333
MAPILLARY_ACCESS_TOKEN=your_token
CDSE_CLIENT_ID=your_id
CDSE_CLIENT_SECRET=your_secret
CLI Reference
| Command | Description |
|---|---|
| citysense --version | Print package version |
| citysense pilot init <country> | Initialize pilot country in .env (az, fi, se, dk, no) |
| citysense pilot status | Show active pilot country |
| citysense index build --city <name> [--country <iso2>] | Build index for selected city |
| citysense index status [--country <iso2>] | Show Qdrant collection point count |
| citysense query <natural language> [--country <iso2>] [--city <name>] | Execute semantic spatial query |
| citysense serve --transport stdio | Start FastMCP server for local MCP clients |
| citysense serve --transport sse --port 7832 | SSE transport placeholder (status output) |
Notes:
index buildcurrently uses the OSM connector path.--sourcesis present for forward compatibility and not fully wired yet.index buildcity routing is currently optimized for Helsinki and Baku.
CLI Examples
# Initialize Finland pilot
citysense pilot init fi
# Show current pilot status
citysense pilot status
# Build index for Helsinki (OSM path)
citysense index build --city Helsinki --country fi --sources osm
# Check index status in Qdrant
citysense index status --country fi
# Query with natural language
citysense query "metro stations within 500m of parks in Helsinki"
# Start MCP server for Cursor/Claude
citysense serve --transport stdio
# SSE transport status output (placeholder)
citysense serve --transport sse
MCP Integration
Cursor
Add to .cursor/mcp.json:
{
"mcpServers": {
"citysense": {
"command": "citysense",
"args": ["serve", "--transport", "stdio"],
"env": {
"CITYSENSE_PILOT_COUNTRY": "fi",
"CITYSENSE_VECTOR_STORE_URL": "http://localhost:6333",
"MAPILLARY_ACCESS_TOKEN": "your_token",
"CDSE_CLIENT_ID": "your_id",
"CDSE_CLIENT_SECRET": "your_secret"
}
}
}
}
Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"citysense": {
"command": "citysense",
"args": ["serve", "--transport", "stdio"],
"env": {
"CITYSENSE_PILOT_COUNTRY": "az",
"CITYSENSE_VECTOR_STORE_URL": "http://localhost:6333"
}
}
}
}
Prerequisites
- Qdrant running at configured URL
- Index built:
citysense index build --city Helsinki --sources osm - Optional tokens for Mapillary and CDSE if imagery connectors are enabled
MCP Tools
| Tool | Description |
|---|---|
| query_spatial_context | Execute natural language spatial query and return GeoJSON context |
Transport Modes
| Transport | Use case |
|---|---|
| stdio | Cursor, Claude Desktop, local scripts |
| sse | Placeholder in current CLI; reserved for future remote clients |
Known Limitations
citysense index buildcurrently indexes through the OSM connector path.- SSE transport is not a fully running server yet (
serve --transport sseis a placeholder output). - Pilot config modules currently shipped in code are
azandfi. - MCP server currently exposes one tool:
query_spatial_context.
Pilot Countries
| Country | Module Key | National CRS | Primary City |
|---|---|---|---|
| Azerbaijan | az | EPSG:32638 | Baku |
| Finland | fi | EPSG:3067 | Helsinki |
Planned pilot profiles (not yet shipped as config modules): Sweden (se), Denmark (dk), Norway (no).
Pilot Configuration Attributes
| Attribute | Description |
|---|---|
| country | ISO2 code |
| language | IETF BCP 47 tag |
| national_crs | EPSG string |
| default_cities | City name to BBox mapping |
| connector_priority | Ordered connector IDs |
| wuf13_primary_dimensions | WUF13 dimension tags |
| data_gaps | Known gaps and fallback strategies |
| informality_heuristic | SAR/spectral heuristic for tenure |
Mathematical Foundations
Sentinel-2 Band Reference
All spectral indices are computed from Sentinel-2 L2A surface reflectance bands.
| Band ID | Centre Wavelength (nm) | Resolution (m) |
|---|---|---|
| B02 | 490 (Blue) | 10 |
| B03 | 560 (Green) | 10 |
| B04 | 665 (Red) | 10 |
| B08 | 842 (NIR broad) | 10 |
| B11 | 1610 (SWIR-1) | 20 |
| B12 | 2190 (SWIR-2) | 20 |
Spectral Indices
NDVI (Normalized Difference Vegetation Index)
$$\mathrm{NDVI} = \frac{\rho_{\mathrm{NIR}} - \rho_{\mathrm{Red}}}{\rho_{\mathrm{NIR}} + \rho_{\mathrm{Red}}} = \frac{B_{08} - B_{04}}{B_{08} + B_{04}}$$
Range: $[-1, +1]$. Urban green: $> 0.3$. Built-up: $< 0.1$.
NDWI (McFeeters 1996)
$$\mathrm{NDWI} = \frac{B_{03} - B_{08}}{B_{03} + B_{08}}$$
Open water: $> 0.0$.
NDBI (Zha et al. 2003)
$$\mathrm{NDBI} = \frac{B_{11} - B_{08}}{B_{11} + B_{08}}$$
Built-up: $> 0.0$.
EVI (Huete et al. 2002)
$$\mathrm{EVI} = 2.5 \cdot \frac{B_{08} - B_{04}}{B_{08} + 6 \cdot B_{04} - 7.5 \cdot B_{02} + 1}$$
MNDWI (Modified NDWI)
$$\mathrm{MNDWI} = \frac{B_{03} - B_{11}}{B_{03} + B_{11}}$$
BSI (Rikimaru et al. 2002)
$$\mathrm{BSI} = \frac{(B_{11} + B_{04}) - (B_{08} + B_{02})}{(B_{11} + B_{04}) + (B_{08} + B_{02})}$$
Reciprocal Rank Fusion
$$\mathrm{RRF}(d) = \sum_{R \in {R_{\mathrm{dense}}, R_{\mathrm{sparse}}}} \frac{1}{k + \mathrm{rank}_R(d)}$$
Default: $k = 60$.
Cross-Encoder Reranking
$$s_i = \mathrm{CrossEncoder}(q, c_i)$$
Urban Resilience Composite Score
$$\mathrm{URCS}(c) = \sum_{d \in D} w_d \cdot R_d(c)$$
where $D = {\mathrm{physical}, \mathrm{climate}, \mathrm{social}, \mathrm{infrastructure}}$.
| Dimension | Weight $w_d$ |
|---|---|
| Physical | 0.30 |
| Climate | 0.30 |
| Social | 0.20 |
| Infrastructure | 0.20 |
Physical Resilience Sub-dimension
$$R_{\mathrm{physical}} = \alpha_s \cdot s_{\mathrm{condition}} + \alpha_a \cdot (1 - a_{\mathrm{norm}}) + \alpha_{\mathrm{SAR}} \cdot (1 - I_{\mathrm{informal}})$$
Default: $\alpha_s = 0.4$, $\alpha_a = 0.35$, $\alpha_{\text{SAR}} = 0.25$.
Segregation Indices
Dissimilarity Index
$$D = \frac{1}{2} \sum_{i=1}^{n} \left| \frac{g_i}{G} - \frac{m_i}{M} \right|$$
Isolation Index
$$P^* = \sum_{i=1}^{n} \left[ \frac{g_i}{G} \cdot \frac{g_i}{t_i} \right]$$
Transit Accessibility (SDG 11.2.1)
$$A(c) = \sum_{s \in S(c, r)} f(s) \cdot e^{-\lambda \cdot d(c, s)}$$
Default: $r = 800$ m, $\lambda = 0.003$.
Land Consumption Rate (SDG 11.3.1)
$$\mathrm{SDG}_{11.3.1} = \frac{\mathrm{LCR}}{\mathrm{PGR}}$$
$$\mathrm{LCR} = \frac{\ln(U_{\mathrm{land}}(t_1) / U_{\mathrm{land}}(t_0))}{t_1 - t_0}$$
$$\mathrm{PGR} = \frac{\ln(P(t_1) / P(t_0))}{t_1 - t_0}$$
Sustainable urban growth: ratio approximately 1. Ratio $> 1$: land consumption outpacing population growth. Ratio $< 1$: increasing density.
Formula Summary Table
| Formula | Parameters | Default |
|---|---|---|
| RRF | k | 60 |
| Transit accessibility | r (radius), lambda (decay) | 800 m, 0.003 |
| URCS physical | alpha_s, alpha_a, alpha_SAR | 0.4, 0.35, 0.25 |
| URCS dimensions | w_physical, w_climate, w_social, w_infrastructure | 0.30, 0.30, 0.20, 0.20 |
API Summary
Intent Parsing
from citysense.rag.intent import parse_intent, SpatialIntent
intent: SpatialIntent = parse_intent(
"social housing zones within 1 km of metro stations in Helsinki"
)
# intent.entity_types, intent.spatial_relations, intent.city_scope, ...
Spectral Indices
from citysense.imagery.satellite.indices import compute_index, compute_ndvi
# From band dict (B02, B03, B04, B08, B11)
ndvi_array = compute_index("NDVI", bands)
ndwi_array = compute_index("NDWI", bands)
Urban Resilience
from citysense.climate.resilience import compute_urcs, compute_physical_resilience
urcs = compute_urcs(physical=0.7, climate=0.6, social=0.5, infrastructure=0.8)
physical = compute_physical_resilience(
street_condition=0.8, building_age_norm=0.3, informality_score=0.1
)
SDG 11 Indicators
from citysense.urban.sdg11 import compute_sdg_1131, compute_land_consumption_rate
lcr = compute_land_consumption_rate(
urban_land_t0_km2=100, urban_land_t1_km2=120, years=10
)
ratio = compute_sdg_1131(
urban_land_t0_km2=100, urban_land_t1_km2=120,
pop_t0=1e6, pop_t1=1.2e6, years=10
)
Pilot Configuration
from citysense.pilot.az import AZ_CONFIG
from citysense.pilot.fi import FI_CONFIG
config = FI_CONFIG
print(config.country, config.national_crs, config.default_cities)
Data Sources and Connectors
| Connector | Data Type | API |
|---|---|---|
| OSM | Vector (buildings, roads, land use) | Overpass API |
| Sentinel-2 | Multispectral imagery | Copernicus Data Space, Sentinel Hub |
| Mapillary | Street-level imagery | Mapillary API v4 |
| KartaView | Street-level imagery | KartaView REST API |
| National registers | Cadastral, tenure | Country-specific |
Current CLI indexing path uses OSM. Other connectors are available as modules and are being wired into end-to-end CLI flows.
Connector Priority
Pilot configurations define connector_priority to resolve conflicts when multiple sources provide overlapping data.
WUF13 Alignment
The 13th World Urban Forum (WUF13) dialogue dimensions inform CitySense pilot configurations and indicator coverage.
WUF13 Dialogue Dimensions
| Dimension | Description | CitySense Coverage |
|---|---|---|
| Adequate housing | Access to safe, affordable housing | Housing zone analysis, tenure heuristics |
| Urban planning | Land use, zoning, spatial planning | OSM land use, pilot city schemas |
| Climate action | Resilience, adaptation, mitigation | URCS, spectral indices, flood/heat risk |
| Urban economy | Employment, economic opportunity | Transit accessibility, service proximity |
| Urban ecology | Green space, biodiversity | NDVI, NDWI, built-up indices |
| Urban governance | Participation, transparency | Data standards, open schemas |
| Urban finance | Revenue, expenditure, investment | Indicator framework for fiscal analysis |
Pilot-Specific Data Gaps
| Country | Known Gaps | Fallback Strategy |
|---|---|---|
| Azerbaijan | Peri-urban tenure | SAR texture + NDBI heuristic for informality |
| Finland | Informal settlements | Not applicable; use tenure from registers |
| Planned profiles (se, dk, no) | National profile tuning in progress | Add profile-specific data-gap maps during rollout |
SpatialIntent Structure
The SpatialIntent dataclass captures parsed query slots from natural language input.
| Attribute | Type | Description |
|---|---|---|
| entity_types | tuple[str, ...] | OSM-style feature types (e.g. bus_stop, railway_station) |
| spatial_relations | tuple[str, ...] | Relations (e.g. near, within_500m) |
| attributes | dict[str, str] | Attribute filters (e.g. resilience: low) |
| country_scope | str | None | ISO2 country code |
| city_scope | str | None | City name |
| bbox | BBox | None | Resolved bounding box |
| wuf13_dimension | str | None | WUF13 dialogue dimension |
| sdg_indicator | str | None | SDG indicator code (e.g. 11.2.1) |
H3 Spatial Index
CitySense uses Uber H3 for hexagonal spatial indexing. Benefits include uniform cell size, hierarchical resolution, and efficient neighborhood queries.
Resolution Reference
| Resolution | Avg hexagon area (km^2) | Use case |
|---|---|---|
| 5 | 252.9 | City-scale analysis |
| 6 | 36.1 | District-scale |
| 7 | 5.2 | Neighborhood-scale |
| 8 | 0.74 | Block-scale |
| 9 | 0.11 | Building-scale |
Antimeridian Handling
H3 cells crossing the antimeridian require special handling. CitySense pre-filters queries to avoid invalid geometries.
Dependencies
Core Dependencies
| Package | Version | Purpose |
|---|---|---|
| shapely | >=2.1.2, <3.0 | Geometry operations |
| geopandas | >=1.1.2, <2.0 | Geospatial DataFrames |
| pyproj | >=3.7.1, <4.0 | Coordinate transformations |
| pyogrio | >=0.10.0 | Vector I/O |
| numpy | >=2.2.0, <3.0 | Numerical arrays |
| pandas | >=2.3.0, <4.0 | Tabular data |
| h3 | >=4.1.0, <5.0 | Hexagonal spatial index |
| rasterio | >=1.4.3, <2.0 | Raster I/O |
| xarray | >=2023.1.0 | Multidimensional arrays |
| pydantic | >=2.12.0, <3.0 | Data validation |
| pydantic-settings | >=2.7.0, <3.0 | Configuration |
| qdrant-client | >=1.17.0, <2.0 | Vector store client |
| fastembed | >=0.4.2, <1.0 | Embedding models |
| rank-bm25 | >=0.2.2 | BM25 sparse retrieval |
| litellm | >=1.57.0, <2.0 | LLM abstraction |
| mcp | >=1.4.0, <2.0 | Model Context Protocol |
| pystac-client | >=0.8.5, <1.0 | STAC catalog access |
| rio-cogeo | >=5.4.0, <8.0 | Cloud-optimized GeoTIFF |
| httpx | >=0.28.0, <1.0 | HTTP client |
| Pillow | >=11.1.0, <13.0 | Image processing |
| APScheduler | >=3.11.0, <4.0 | Task scheduling |
| structlog | >=25.1.0, <26.0 | Structured logging |
| typer | >=0.15.0, <1.0 | CLI framework |
Optional Dependencies
| Extra | Packages | Purpose |
|---|---|---|
| clip | transformers, torch | CLIP ViT-B/32 for street imagery |
| sentinelhub | sentinelhub | Sentinel Hub Process API |
| dev | ruff, mypy, pytest, mkdocs, pre-commit | Development tooling |
Data Flow
Index Build Flow
City + country input
|
v
OSMConnector.fetch(bbox)
|
v
GeoDataFrame normalization
|
v
H3 assignment + chunk text
|
v
Embedding generation
|
v
Qdrant upsert
Query Flow
Natural-language query
|
v
parse_intent() -> SpatialIntent
|
v
embed_texts(query)
|
v
hybrid_search(Qdrant + BM25)
|
v
rerank(top_k)
|
v
assemble_context()
Troubleshooting
| Issue | Cause | Resolution |
|---|---|---|
| Qdrant connection refused | Qdrant not running | Start Qdrant: docker run -p 6333:6333 qdrant/qdrant |
| Index empty after build | Connector returned no data | Check city name, pilot config, OSM coverage |
| Mapillary 401 | Invalid or missing token | Set MAPILLARY_ACCESS_TOKEN |
| CDSE auth failure | Invalid credentials | Set CDSE_CLIENT_ID, CDSE_CLIENT_SECRET |
| CRS transform error | Mismatched EPSG | Verify pilot national_crs |
| H3 antimeridian error | Query crosses date line | Use bbox that does not span antimeridian |
Glossary
| Term | Definition |
|---|---|
| RAG | Retrieval-Augmented Generation; augmenting LLM context with retrieved documents |
| MCP | Model Context Protocol; standard for AI tools and context |
| H3 | Uber hexagonal hierarchical spatial index |
| CRS | Coordinate Reference System |
| URCS | Urban Resilience Composite Score |
| LCR | Land Consumption Rate (SDG 11.3.1 numerator) |
| PGR | Population Growth Rate (SDG 11.3.1 denominator) |
| RRF | Reciprocal Rank Fusion |
| NDVI | Normalized Difference Vegetation Index |
| NDWI | Normalized Difference Water Index |
| NDBI | Normalized Difference Built-up Index |
| WUF13 | 13th World Urban Forum |
| SDG 11 | UN Sustainable Development Goal 11 (Sustainable Cities) |
Frequently Asked Questions
Is CitySense suitable for production use?
CitySense is in Alpha (Development Status 3). Use for research, prototyping, and pilot deployments. Production hardening is ongoing.
Which embedding model is used?
Default: BAAI/bge-m3. Configurable via CITYSENSE_EMBEDDING_MODEL.
Does CitySense support private repositories?
The MCP server and CLI work with any data source. Vector store (Qdrant) can be self-hosted. No data is sent to external services except configured APIs (Mapillary, CDSE, etc.).
How is coverage computed?
Coverage excludes connectors and modules under active development. Run pytest tests/unit --cov=citysense --cov-report=term-missing for current metrics.
Can I add a new pilot country?
Yes. Add a new PilotConfig module under citysense.pilot, expose it in package imports, and extend CLI validation logic for country codes. See CONTRIBUTING.md.
Version History
| Version | Date | Highlights |
|---|---|---|
| 0.2.1 | 2026-02 | Current alpha release; core RAG pipeline, FastMCP server, and pilot configs for AZ/FI |
| 0.2.0 | 2026-02-28 | Initial WUF13-aligned specification baseline |
| Unreleased | - | Additional pilot profiles, richer MCP tool surface, expanded connector wiring |
Development
Setup
git clone https://github.com/olaflaitinen/citysense.git
cd citysense
pip install -e ".[dev,clip]"
pre-commit install
Linting and Type Checking
ruff check src tests
ruff format --check src tests
mypy src/citysense
Tests
pytest tests/unit -v
pytest tests/unit -v --cov=citysense --cov-report=term-missing
Documentation
mkdocs serve
Priority Contribution Areas
| Area | Description |
|---|---|
| Country connectors | Additional WUF13-relevant cities and national data sources |
| Non-Latin script | Arabic, Cyrillic handling for Azerbaijan and Central Asia |
| Informal settlement detection | Model improvements for tenure heuristics |
| Climate adaptation | Document parsers for national plans |
Python Version Compatibility
| Python | Status |
|---|---|
| 3.12 | Supported, tested in CI |
| 3.13 | Supported, tested in CI |
| 3.11 | Not supported (requires 3.12+) |
Commit Message Format
| Type | Version Bump | Example |
|---|---|---|
| feat: | Minor | feat(imagery): add Sentinel-3 LST connector |
| fix: | Patch | fix(rag): correct H3 pre-filter for antimeridian |
| feat!: | Major | feat!: remove deprecated search() method |
| docs:, chore:, test: | None | docs: add Baku pilot tutorial |
Security Considerations
| Concern | Mitigation |
|---|---|
| API tokens | Store in environment variables or secrets; never commit |
| Qdrant | Run locally or in private network; no default auth |
| Data provenance | All connectors document source and license |
| Dependency audit | Dependabot enabled; run pip audit periodically |
Performance Notes
| Operation | Typical scale | Notes |
|---|---|---|
| Index build (OSM, city) | 10k-100k features | Depends on city size, H3 resolution |
| Query latency | 100-500 ms | Dense + sparse + RRF; reranker adds ~50 ms |
| Embedding | BAAI/bge-m3 | ~50 docs/s on CPU; GPU accelerates |
| Qdrant | Local | Sub-10 ms for vector search at city scale |
References
Spectral Indices
- McFeeters, S. K. (1996). The use of the Normalized Difference Water Index (NDWI) in the delineation of open water features. International Journal of Remote Sensing, 17(7), 1425-1432.
- Zha, Y., Gao, J., & Ni, S. (2003). Use of normalized difference built-up index in automatically mapping urban areas from TM imagery. International Journal of Remote Sensing, 24(3), 583-594.
- Huete, A., et al. (2002). Overview of the radiometric and biophysical performance of the MODIS vegetation indices. Remote Sensing of Environment, 83(1-2), 195-213.
- Rikimaru, A., Roy, P. S., & Miyatake, S. (2002). Tropical forest cover density mapping. Tropical Ecology, 43(1), 39-47.
SDG and Urban Indicators
- UN-Habitat. (2020). SDG Indicator 11.3.1 - Land consumption rate. United Nations.
- UN-Habitat. (2020). SDG Indicator 11.2.1 - Proportion of population with convenient access to public transport. United Nations.
Retrieval
- Cormack, G. V., Clarke, C. L., & Buettcher, S. (2009). Reciprocal rank fusion outperforms condorcet and individual rank learning methods. SIGIR 2009.
Citation
If you use CitySense in academic work, please cite:
CitySense: Geospatial RAG and MCP Server for Urban AI Development.
Specification v0.2.1. WUF13 Aligned. 2026.
https://github.com/olaflaitinen/citysense
Spectral Index Interpretation Ranges
| Index | Low | Medium | High | Interpretation |
|---|---|---|---|---|
| NDVI | < 0.1 | 0.1-0.3 | > 0.3 | Vegetation density |
| NDWI | < 0 | 0 | > 0 | Open water presence |
| NDBI | < 0 | 0 | > 0 | Built-up intensity |
| EVI | < 0.2 | 0.2-0.4 | > 0.4 | Enhanced vegetation |
| BSI | < 0 | 0 | > 0 | Bare soil / impervious |
License
EUPL-1.2. See LICENSE.
Links
| Resource | URL |
|---|---|
| Documentation | citysense.readthedocs.io |
| Repository | github.com/olaflaitinen/citysense |
| Issues | github.com/olaflaitinen/citysense/issues |
| Changelog | CHANGELOG.md |
| Contributing | CONTRIBUTING.md |
| Security | SECURITY.md |
Acknowledgments
CitySense builds on open standards and community data: OpenStreetMap contributors, Copernicus Programme, Mapillary, KartaView, Qdrant, and the Model Context Protocol initiative. Pilot country configurations align with national spatial data infrastructures and WUF13 dialogue priorities.
