Skip to content
Mulder90Public

About

A knowledge graph ecosystem for London, connecting housing data with transport, schools, crime, and amenities using Neo4j.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

16 Commits

Folders and files

Repository files navigation

London

A knowledge graph ecosystem for London, connecting housing data with transport, schools, and amenities using Neo4j.

Architecture

Domain APIs own their data lifecycle (ingestion, enrichment, serving via REST + SQLite):

  • services/postcodes-api -- ONS postcode data (ONSPD)
  • services/transport-api -- TfL station and line data with accessibility
  • services/schools-api -- GIAS school data with Ofsted ratings
  • services/properties-api -- Land Registry price paid data with price history
  • services/crime-api -- Police UK crime data with LSOA-level yearly statistics
  • services/imd-api -- Index of Multiple Deprivation (2025) at LSOA level

Graph Builder (graph/) collects from these APIs and builds a spatial knowledge graph in Neo4j, linking entities by proximity.

Query Engine (query-engine/) is an LLM-powered CLI that lets you ask natural language questions about the knowledge graph. It translates questions into Cypher queries, executes them, and synthesizes answers.

See docs/architecture.md for full details and docs/roadmap.md for planned work.

Full Setup Guide

Everything you need to go from a fresh clone to a working graph with all data.

Prerequisites

  • Go 1.24+
  • Docker (for Neo4j)

Data Downloads

You need these files before starting. All are free/open data:

Dataset Source Notes
ONSPD ONS Geoportal Download the CSV, ~1.3GB
Land Registry PPD gov.uk pp-complete.csv, ~4GB
Police UK crime data.police.uk Select Metropolitan Police + City of London, all dates
IoD 2025 File 7 gov.uk File_7_IoD2025_All_Ranks_Scores_Deciles_Population_Denominators.csv
TfL stations Fetched live via TfL API No download needed
GIAS schools Fetched live via GIAS API No download needed

Step 1: Clone and configure

git clone https://github.com/mulder90/london.git
cd london
cp .env.example .env
# Edit .env if you want to change Neo4j password or ports

Step 2: Start Neo4j

make docker-up

Step 3: Build all binaries

make build-all

Step 4: Ingest data into each API

Run these from the repo root. Order matters — postcodes must be ingested first since properties-api uses the postcodes database for geocoding.

# 1. Postcodes (must be first — other services depend on it)
cd services/postcodes-api && make ingest FILE=/path/to/ONSPD.csv BOROUGH=London
cd ..

# 2. Transport (fetches from TfL API — needs TFL_API_KEY in .env for full data)
cd services/transport-api && make ingest
cd ..

# 3. Schools (fetches from GIAS API)
cd services/schools-api && make ingest-gias
cd ..

# 4. Properties (uses postcodes DB for geocoding)
cd services/properties-api && POSTCODES_DB_PATH=../postcodes-api/db/postcodes.db make ingest FILE=/path/to/pp-complete.csv
cd ..

# 5. Crime (import CSVs, then aggregate by year)
cd services/crime-api && make ingest-dir DIR=data/crime
cd services/crime-api && make aggregate YEAR=2023
cd services/crime-api && make aggregate YEAR=2024
cd services/crime-api && make aggregate YEAR=2025
cd ..

# 6. IMD
cd services/imd-api && make ingest FILE=/path/to/File_7_IoD2025.csv
cd ..

Step 5: Start all APIs

Each API needs its own terminal:

# Terminal 1: Transport API (port 8080)
cd services/transport-api && make run

# Terminal 2: Schools API (port 8081)
cd services/schools-api && make run

# Terminal 3: Postcodes API (port 8082)
cd services/postcodes-api && make run

# Terminal 4: Properties API (port 8083)
cd services/properties-api && make run

# Terminal 5: Crime API (port 8084)
cd services/crime-api && make run

# Terminal 6: IMD API (port 8085)
cd services/imd-api && make run

Step 6: Build the graph

With all 6 APIs running:

cd graph

# Build the graph CLI
make build

# Collect data from all APIs into staging JSON files
./bin/urbangraph collect all

# Apply Neo4j schema (constraints + indexes)
./bin/urbangraph schema

# Build everything: ingest nodes, create spatial links, aggregate stats
./bin/urbangraph build --link-all --aggregate-prices --aggregate-crime --aggregate-imd --years=2023,2024,2025

Step 7: Query

# CLI queries (graph builder)
./bin/urbangraph query "W12 8BS"                                     # postcode details
./bin/urbangraph query "W12 8BS" --show-nearby                       # nearby stations + schools
./bin/urbangraph query "W12 8BS" --show-nearby --nearby-type=Station # stations only
./bin/urbangraph query "W12 8BS" --show-nearby --radius=500          # within 500m

# Or open Neo4j Browser at http://localhost:7474 and run Cypher queries
# See docs/queries.md for example queries

Step 8: Query Engine (natural language)

The query engine uses an LLM to translate natural language questions into Cypher queries against the graph.

# Configure LLM provider in .env (Groq free tier, OpenAI, or Ollama — see .env.example)
# Then:
make build-query-engine

# Interactive REPL
./query-engine/bin/qe

# Single question
./query-engine/bin/qe "What are the safest boroughs near a tube station?"

# Verbose mode (shows Cypher queries and tool calls)
./query-engine/bin/qe -v "Are prices going up or down in Wimbledon?"

Example questions:

  • "What are the safest boroughs near a tube station?"
  • "Best area for a family with young kids near the District line?"
  • "Are prices going up or down in Wimbledon?"
  • "Compare schools in Richmond vs Hackney"

Clean Rebuild

If you need to start from scratch (e.g. after schema changes):

# Wipe Neo4j data
make docker-down
docker rm london-neo4j 2>/dev/null
docker volume rm london_neo4j_data london_neo4j_logs london_neo4j_plugins 2>/dev/null
make docker-up

# Rebuild graph (APIs must be running)
cd graph
make build
./bin/urbangraph schema
./bin/urbangraph collect all
./bin/urbangraph build --link-all --aggregate-prices --aggregate-crime --aggregate-imd --years=2023,2024,2025

Project Structure

london/
├── go.work                     # Go workspace
├── docker-compose.yml          # Neo4j
├── Makefile                    # Root orchestration
├── docs/
│   ├── architecture.md         # System architecture
│   ├── queries.md              # Example Cypher queries
│   ├── roadmap.md              # Development roadmap
│   └── future/                 # Future design docs
├── services/
│   ├── postcodes-api/          # Postcode REST API (port 8082)
│   ├── transport-api/          # Transport REST API (port 8080)
│   ├── schools-api/            # Schools REST API (port 8081)
│   ├── properties-api/         # Properties & Prices REST API (port 8083)
│   ├── crime-api/              # Crime statistics REST API (port 8084)
│   └── imd-api/                # IMD deprivation REST API (port 8085)
├── graph/                      # Graph builder (Neo4j)
└── query-engine/               # LLM-powered natural language query engine

Make Targets

Target Description
build-all Build all projects
test-all Run all tests
clean-all Remove all build artifacts
tidy-all Run go mod tidy on all modules
docker-up Start Neo4j
docker-down Stop Neo4j
build-graph Build graph builder only
build-postcodes Build postcodes API only
build-transport Build transport API only
build-schools Build schools API only
build-properties Build properties API only
build-crime Build crime API only
build-imd Build IMD API only
build-query-engine Build query engine
test-query-engine Run query engine tests
ingest-postcodes Ingest postcodes (requires FILE=)
ingest-transport Ingest transport data from TfL API
ingest-schools Ingest schools data from GIAS
ingest-properties Ingest properties (requires FILE=)
ingest-crime Ingest crime CSVs (requires DIR=)
ingest-imd Ingest IMD data (requires FILE=)

Data Attribution

Contains HM Land Registry data Crown copyright and database right. This data is licensed under the Open Government Licence v3.0.

About

A knowledge graph ecosystem for London, connecting housing data with transport, schools, crime, and amenities using Neo4j.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages