Geospatial RAG and MCP server for urban AI development; WUF13 aligned.
Project description
CitySense
Geospatial RAG and MCP Server Library for Urban AI Development
Specification v0.2.1 | February 2026 | WUF13 AlignedTable of Contents
- Abstract
- Executive Summary
- Overview
- Architecture
- Requirements
- Installation
- Quick Start
- Configuration
- CLI Reference
- MCP Integration
- Pilot Countries
- Mathematical Foundations
- API Summary
- Data Sources and Connectors
- WUF13 Alignment
- SpatialIntent Structure
- H3 Spatial Index
- Dependencies
- Data Flow
- Troubleshooting
- Glossary
- Frequently Asked Questions
- Version History
- Development
- Security Considerations
- Performance Notes
- References
- License
- Links
Abstract
CitySense is an open-source Python library that bridges geospatial urban data with large language model (LLM) toolchains. It provides two tightly integrated components: a geospatial retrieval-augmented generation (RAG) framework that semantically indexes spatial datasets and retrieves relevant context for natural language queries, and a Model Context Protocol (MCP) server that exposes city intelligence directly into AI-assisted development environments. The library is aligned with the United Nations Sustainable Development Goal 11 (Sustainable Cities and Communities), the New Urban Agenda, and the 13th World Urban Forum (WUF13) dialogue dimensions. CitySense supports five pilot countries (Azerbaijan, Finland, Sweden, Denmark, Norway) with consistent schemas and national coordinate reference systems. The framework implements spectral indices (NDVI, NDWI, NDBI, EVI, MNDWI, BSI) from Sentinel-2 L2A, Reciprocal Rank Fusion for hybrid retrieval, Urban Resilience Composite Score, and SDG 11.3.1 Land Consumption Rate. Street-level imagery (Mapillary, KartaView) and multispectral satellite data (Copernicus Data Space, Sentinel Hub) extend traditional vector geospatial analysis.
Executive Summary
| Aspect | Description |
|---|---|
| Primary function | Semantic geospatial retrieval and MCP server for urban AI |
| Target users | Urban AI developers, smart city researchers, urban policy tools builders |
| Core technologies | Python 3.12+, Qdrant, Shapely, GeoPandas, H3, fastembed, MCP |
| Data sources | OpenStreetMap, Sentinel-2, Mapillary, KartaView, Copernicus Data Space |
| Standards alignment | WUF13, SDG 11, UN New Urban Agenda |
| 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 indexing | OpenStreetMap, Sentinel-2, Mapillary, KartaView, national registers |
| MCP integration | Native Model Context Protocol server for Cursor, Claude, VS Code |
| WUF13 alignment | Pilot countries with consistent schemas and national CRS |
| 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 |
Architecture
System Stack
┌─────────────────────────────────────────────────────────────────────────────┐
│ CitySense Stack │
├─────────────────────────────────────────────────────────────────────────────┤
│ CLI (citysense) │
│ pilot init | index build | query | serve | export | imagery | watch │
├─────────────────────────────────────────────────────────────────────────────┤
│ MCP Server (stdio/SSE) │ Geospatial RAG Pipeline │
│ Tools: query_spatial_context, │ Intent Parser → H3 Filter → │
│ analyze_housing_zone, │ Dense Retrieval + Sparse (BM25) → │
│ get_resilience_score, bounds │ RRF Fusion → Reranker → Assembler │
├─────────────────────────────────────────────────────────────────────────────┤
│ Connectors: OSM, Sentinel-2, Mapillary, KartaView, CDSE │
├─────────────────────────────────────────────────────────────────────────────┤
│ Vector Store (Qdrant) │ Geometry (Shapely, GeoPandas, H3) │
└─────────────────────────────────────────────────────────────────────────────┘
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.urban | SDG 11 indicators |
| citysense.climate | Resilience scoring |
| 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
Minimal Workflow
citysense pilot init fi
citysense index build --city Helsinki --sources osm
citysense query "social housing zones within 1 km of metro stations in Helsinki"
citysense serve --transport stdio
With Imagery (CLIP)
pip install "citysense[clip]"
citysense pilot init fi
citysense index build --city Helsinki --sources osm,mapillary
With Sentinel Hub
pip install "citysense[sentinelhub]"
# Set SENTINELHUB_INSTANCE_ID, SENTINELHUB_CLIENT_ID, SENTINELHUB_CLIENT_SECRET
citysense index build --city Baku --sources osm,sentinel
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 module key: az, fi, se, dk, no |
| 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 pilot init <country> | Initialize pilot configuration |
| citysense index build --city <name> --sources <list> | Build vector index |
| citysense query <natural language> | Execute semantic spatial query |
| citysense serve --transport stdio | Start MCP server (stdio) |
| citysense serve --transport sse | Start MCP server (SSE) |
| citysense export <format> | Export indexed data |
| citysense imagery <subcommand> | Street/satellite imagery operations |
| citysense watch | Watch mode for real-time updates |
CLI Examples
# Initialize Finland pilot
citysense pilot init fi
# Build index for Helsinki from OSM only
citysense index build --city Helsinki --sources osm
# Build index with multiple sources (requires tokens)
citysense index build --city Baku --sources osm,mapillary,sentinel
# Query with natural language
citysense query "metro stations within 500m of parks in Stockholm"
# Start MCP server for Cursor/Claude
citysense serve --transport stdio
# Start MCP server with SSE for web clients
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 - API tokens for Mapillary and CDSE if using imagery
MCP Tools
| Tool | Description |
|---|---|
| query_spatial_context | Execute natural language spatial query and return GeoJSON context |
| analyze_housing_zone | Analyze housing zone with resilience and SDG metrics |
| get_resilience_score | Compute URCS for a given location or zone |
| get_bounds | Return bounding box for pilot city or region |
Transport Modes
| Transport | Use case |
|---|---|
| stdio | Cursor, Claude Desktop, local scripts |
| sse | Web applications, remote clients |
Pilot Countries
| Country | Module Key | National CRS | Primary City |
|---|---|---|---|
| Azerbaijan | az | EPSG:32638 | Baku |
| Finland | fi | EPSG:3067 | Helsinki |
| Sweden | se | EPSG:3006 | Stockholm |
| Denmark | dk | EPSG:25832 | Copenhagen |
| Norway | no | EPSG:25833 | Oslo |
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), λ (decay) | 800 m, 0.003 |
| URCS physical | α_s, α_a, α_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 import get_pilot_config
config = get_pilot_config("fi")
# 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 |
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 |
| Sweden | (Per national profile) | (Per national profile) |
| Denmark | (Per national profile) | (Per national profile) |
| Norway | (Per national profile) | (Per national profile) |
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²) | 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, <3.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, <6.0 | Cloud-optimized GeoTIFF |
| httpx | >=0.28.0, <1.0 | HTTP client |
| Pillow | >=11.1.0, <12.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
OSM Overpass / STAC / Connectors
│
▼
Geometry + Metadata (GeoDataFrame)
│
▼
H3 Cell Assignment (resolution 7 default)
│
▼
Chunking + Text Serialization
│
▼
Dense Embedding (fastembed) + Sparse (BM25)
│
▼
Qdrant Upsert (vectors + payload)
Query Flow
Natural Language Query
│
▼
parse_intent() → SpatialIntent
│
▼
H3 Filter (bbox → H3 cells)
│
▼
Dense + Sparse Retrieval (top-k each)
│
▼
RRF Fusion (k=60)
│
▼
Reranker (optional)
│
▼
Assembled Context (GeoJSON, summary)
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. Implement a PilotConfig in citysense.pilot, register in the pilot registry, and add to the CLI init options. See CONTRIBUTING.md.
Version History
| Version | Date | Highlights |
|---|---|---|
| 0.2.0 | 2026-02-28 | WUF13 alignment, five pilot countries, SDG 11 indicators, mathematical foundations |
| Unreleased | - | License EUPL-1.2, initial structure per Specification v0.2.0 |
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.0. 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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file citysense-0.2.1.tar.gz.
File metadata
- Download URL: citysense-0.2.1.tar.gz
- Upload date:
- Size: 73.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b14f0fc2f617d09244abebe78b8505c53c907b6139246f67338d53bf3ec73f24
|
|
| MD5 |
8cadbc670151a7ae77711e423b8c5271
|
|
| BLAKE2b-256 |
f0093345f6b8dd01fc251abd8a97d07ecdb360e484f16f8fd846bb85d8aa16af
|
Provenance
The following attestation bundles were made for citysense-0.2.1.tar.gz:
Publisher:
release.yml on olaflaitinen/citysense
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
citysense-0.2.1.tar.gz -
Subject digest:
b14f0fc2f617d09244abebe78b8505c53c907b6139246f67338d53bf3ec73f24 - Sigstore transparency entry: 1005028455
- Sigstore integration time:
-
Permalink:
olaflaitinen/citysense@9a086f910da8945914fa283915f83517ef678e6f -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/olaflaitinen
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9a086f910da8945914fa283915f83517ef678e6f -
Trigger Event:
push
-
Statement type:
File details
Details for the file citysense-0.2.1-py3-none-any.whl.
File metadata
- Download URL: citysense-0.2.1-py3-none-any.whl
- Upload date:
- Size: 59.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c85095f105c1bb234c6b3bff6192a93623f749214032d4c2ee6c754745cf8c42
|
|
| MD5 |
b509ad5f8792c004abb525baa4a88c79
|
|
| BLAKE2b-256 |
25602caae1bd404795318471d5ae2cd6bbc4930d5bebe3a4d27d50f5c066116d
|
Provenance
The following attestation bundles were made for citysense-0.2.1-py3-none-any.whl:
Publisher:
release.yml on olaflaitinen/citysense
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
citysense-0.2.1-py3-none-any.whl -
Subject digest:
c85095f105c1bb234c6b3bff6192a93623f749214032d4c2ee6c754745cf8c42 - Sigstore transparency entry: 1005028476
- Sigstore integration time:
-
Permalink:
olaflaitinen/citysense@9a086f910da8945914fa283915f83517ef678e6f -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/olaflaitinen
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9a086f910da8945914fa283915f83517ef678e6f -
Trigger Event:
push
-
Statement type: