An interactive analytics map for exploring geo-search demand across Turkey
Jupyter Notebook
2
60 commits
updated May 24, 2026
An interactive analytics map for exploring geo-search demand across Turkey. The web app shows province-level heatmaps, filters requests by time and category, and supports demand forecasting with a machine-learning model.
Search-system request logs are cleaned and joined to city boundaries before the map can show demand.
turkey/
├── api/ # FastAPI backend (demand + ML endpoints)
├── cli/ # Data pipeline (python -m cli.run_map)
├── data/
│ └── tr-cities.json # Province boundaries (GeoJSON; see note below)
├── ds/ # Notebooks and offline preprocessing / ML
├── frontend/ # React + Vite + Leaflet UI
├── app.py # ASGI entrypoint (exports FastAPI app)
└── requirements.txt # Python dependencies
data/tr-cities.json)Province boundaries are not authored in this repo. tr-cities.json was taken from an external project:
Source: https://github.com/alpers/Turkey-Maps-GeoJSON
From the repository root:
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cd frontend
npm install
cd ..
The UI and API will start without prepared data, but the map and analytics will be empty until you run the pipeline.
Input logs are search-system request logs (JSONL). One command runs the full pipeline:
python -m cli.run_map --logs path/to/logs.jsonl --geojson data/tr-cities.json
Stages (in order):
org_* from model_response_fulltr-cities.json (adds province name, number)--exclude-queries, --drop-min-count)org_type_en and category on data/df_with_cat.csvdata/provinces/{number}.csv with org_name, time, category, org_ratingQuery cleanup is skipped unless you opt in (see below). See python -m cli.run_map --help for all flags (--regions-dir, --exclude-queries, etc.).
Translation model: the pipeline uses Helsinki-NLP/opus-mt-tr-en as a free, local example — it is not the best Turkish→English option. Replace it in cli/translate.py if you need higher quality (e.g. a larger Marian/NLLB model or a paid API). Categorization uses BAAI/bge-large-en-v1.5 on the translated labels.
Noisy high-volume prompts (often from other systems) are not removed by default. Use either or both flags:
| Flag | Effect |
|---|---|
--exclude-queries PATH | Drop rows whose query exactly matches a line in PATH (one query per line; # comments and blank lines ignored). |
--drop-min-count N | Drop every row whose query appears at least N times in the dataset after the spatial join. |
python -m cli.run_map --logs path/to/logs.jsonl --geojson data/tr-cities.json \
--exclude-queries data/bad_queries.txt
Example — drop any query that occurs 500+ times:
python -m cli.run_map --logs path/to/logs.jsonl --geojson data/tr-cities.json \
--drop-min-count 500
Both flags can be combined; exclusions run first, then the frequency threshold.
To re-run only categorization + split on an existing CSV: python ds/ml/categorize_org_types.py --input data/df.csv.
Further model training and notebooks live under ds/.
Build the frontend, then start the API (it serves the built static files):
cd frontend && npm run build && cd ..
uvicorn app:app --host 127.0.0.1 --port 8000
Open http://127.0.0.1:8000.
Use two terminals.
Terminal 1 — API:
source .venv/bin/activate
uvicorn app:app --reload --host 127.0.0.1 --port 8000
Terminal 2 — frontend dev server (proxies /api and /tr-cities.json to the API):
cd frontend
npm run dev
Open http://127.0.0.1:5173.
Demand prediction endpoints (/api/ml/*) expect a serialized model at api/rf_model.pkl. This file is not committed (see .gitignore). Train or export the model using notebooks under ds/ml/, then place rf_model.pkl in api/ before using the predict panel in the UI.
| Endpoint | Description |
|---|---|
GET /api/metrics | Available map metrics |
GET /api/categories | Category list for filters |
GET /api/demand/region-values | Province values for the heatmap |
GET /api/demand/overview | National summary for current filters |
GET /api/demand/provinces/{n} | Detail for province number n (1–81) |
GET /tr-cities.json | Province boundary GeoJSON |
POST /api/ml/predict | Single-step demand forecast |
POST /api/ml/predict-recursive | Multi-step recursive forecast |
Interactive API docs: http://127.0.0.1:8000/docs when the server is running.
| Path | Purpose |
|---|---|
cli/run_map.py | CLI entry (python -m cli.run_map) |
cli/pipeline.py | Pipeline orchestration |
cli/slim.py, cli/spatial.py, cli/queries.py | JSONL slimming, map filter, query cleanup |
cli/translate.py, cli/categorize.py, cli/split.py | HF translation, BGE labels, province CSVs |
ds/preprocessing/ | Cleaning and memory-efficient JSONL parsing |
ds/ml/ | Notebooks (result.ipynb, training, evaluation) |
ds/analysis/ | Exploratory analysis |
An interactive analytics map for exploring geo-search demand across Turkey
Jupyter Notebook
2
60 commits
updated May 24, 2026
An interactive analytics map for exploring geo-search demand across Turkey. The web app shows province-level heatmaps, filters requests by time and category, and supports demand forecasting with a machine-learning model.
Search-system request logs are cleaned and joined to city boundaries before the map can show demand.
turkey/
├── api/ # FastAPI backend (demand + ML endpoints)
├── cli/ # Data pipeline (python -m cli.run_map)
├── data/
│ └── tr-cities.json # Province boundaries (GeoJSON; see note below)
├── ds/ # Notebooks and offline preprocessing / ML
├── frontend/ # React + Vite + Leaflet UI
├── app.py # ASGI entrypoint (exports FastAPI app)
└── requirements.txt # Python dependencies
data/tr-cities.json)Province boundaries are not authored in this repo. tr-cities.json was taken from an external project:
Source: https://github.com/alpers/Turkey-Maps-GeoJSON
From the repository root:
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cd frontend
npm install
cd ..
The UI and API will start without prepared data, but the map and analytics will be empty until you run the pipeline.
Input logs are search-system request logs (JSONL). One command runs the full pipeline:
python -m cli.run_map --logs path/to/logs.jsonl --geojson data/tr-cities.json
Stages (in order):
org_* from model_response_fulltr-cities.json (adds province name, number)--exclude-queries, --drop-min-count)org_type_en and category on data/df_with_cat.csvdata/provinces/{number}.csv with org_name, time, category, org_ratingQuery cleanup is skipped unless you opt in (see below). See python -m cli.run_map --help for all flags (--regions-dir, --exclude-queries, etc.).
Translation model: the pipeline uses Helsinki-NLP/opus-mt-tr-en as a free, local example — it is not the best Turkish→English option. Replace it in cli/translate.py if you need higher quality (e.g. a larger Marian/NLLB model or a paid API). Categorization uses BAAI/bge-large-en-v1.5 on the translated labels.
Noisy high-volume prompts (often from other systems) are not removed by default. Use either or both flags:
| Flag | Effect |
|---|---|
--exclude-queries PATH | Drop rows whose query exactly matches a line in PATH (one query per line; # comments and blank lines ignored). |
--drop-min-count N | Drop every row whose query appears at least N times in the dataset after the spatial join. |
python -m cli.run_map --logs path/to/logs.jsonl --geojson data/tr-cities.json \
--exclude-queries data/bad_queries.txt
Example — drop any query that occurs 500+ times:
python -m cli.run_map --logs path/to/logs.jsonl --geojson data/tr-cities.json \
--drop-min-count 500
Both flags can be combined; exclusions run first, then the frequency threshold.
To re-run only categorization + split on an existing CSV: python ds/ml/categorize_org_types.py --input data/df.csv.
Further model training and notebooks live under ds/.
Build the frontend, then start the API (it serves the built static files):
cd frontend && npm run build && cd ..
uvicorn app:app --host 127.0.0.1 --port 8000
Open http://127.0.0.1:8000.
Use two terminals.
Terminal 1 — API:
source .venv/bin/activate
uvicorn app:app --reload --host 127.0.0.1 --port 8000
Terminal 2 — frontend dev server (proxies /api and /tr-cities.json to the API):
cd frontend
npm run dev
Open http://127.0.0.1:5173.
Demand prediction endpoints (/api/ml/*) expect a serialized model at api/rf_model.pkl. This file is not committed (see .gitignore). Train or export the model using notebooks under ds/ml/, then place rf_model.pkl in api/ before using the predict panel in the UI.
| Endpoint | Description |
|---|---|
GET /api/metrics | Available map metrics |
GET /api/categories | Category list for filters |
GET /api/demand/region-values | Province values for the heatmap |
GET /api/demand/overview | National summary for current filters |
GET /api/demand/provinces/{n} | Detail for province number n (1–81) |
GET /tr-cities.json | Province boundary GeoJSON |
POST /api/ml/predict | Single-step demand forecast |
POST /api/ml/predict-recursive | Multi-step recursive forecast |
Interactive API docs: http://127.0.0.1:8000/docs when the server is running.
| Path | Purpose |
|---|---|
cli/run_map.py | CLI entry (python -m cli.run_map) |
cli/pipeline.py | Pipeline orchestration |
cli/slim.py, cli/spatial.py, cli/queries.py | JSONL slimming, map filter, query cleanup |
cli/translate.py, cli/categorize.py, cli/split.py | HF translation, BGE labels, province CSVs |
ds/preprocessing/ | Cleaning and memory-efficient JSONL parsing |
ds/ml/ | Notebooks (result.ipynb, training, evaluation) |
ds/analysis/ | Exploratory analysis |