Skip to main content

Extract continuous street-level 'blue line' coverage (Google/Mapillary/KartaView) as GeoJSON — official car Street View only, never photospheres.

Project description

tracelines

Extract the blue lines, never the circles.

Turn an area into GeoJSON polylines of continuous official street-level coverage — the solid blue Street View lines — while rigorously excluding user photospheres / photopets (the dots). Multi-source: Google (via streetlevel), Mapillary, and KartaView, fused onto one map.

CI PyPI Python License: AGPL v3 Docs GUI

🗺️ Live GUI · 📖 Documentation · 🔬 How the HARD RULE works · ✅ Reproduce the verification


Disclaimer. Research / educational tool, provided as-is, without warranty. The Google provider relies on undocumented internal endpoints (via streetlevel) that may break without notice. You are responsible for complying with each data source's Terms of Service and your local law. Respect rate limits — don't hammer the endpoints. Not affiliated with or endorsed by Google, Meta/Mapillary, or KartaView.

The HARD RULE

Output contains only genuine official Google car Street View coverage — never a user photosphere / photopet / photo-path, even a Google-hosted one.

Enforced in three defense-in-depth layers (each verified — see the filter contract and verification):

Layer Guard Stops
0 — Source Nodes come only from the z17 get_coverage_tile layer; never search_third_party. Photospheres are structurally absent there. Verified: 7,433 / 7,433 tile panos official (2026-07-13 snapshot; the count drifts as Google updates coverage, the 0-leak invariant does not — re-check with scripts/verify_hardrule.py).
1 — ID whitelist is_official_panoid(): 22 chars, 16-byte decode, terminal ∈ {A,Q,g,w}, not CIHM/CIAB/AF1Q. Legacy 22-char photospheres + the new CIAB UGC scheme. Verified: 753/753 official accepted, 3/3 photospheres rejected.
2 — Source allowlist (--precision) source == 'launch' (car), fetched per-pano. Trekker (scout), indoor (innerspace), every photos:* upload.

Honest caveat: launch is the road-snapped layer Google draws as solid blue lines; Google's own metadata says launch is "sometimes trekker" and scout is "sometimes car" — so no field perfectly separates car from trekker. That fuzz is orthogonal to the photosphere exclusion, which is airtight.

Install

pip install tracelines            # Google-capable out of the box (no API key)
pip install "tracelines[mapillary]"   # + Mapillary vector-tile source
pip install "tracelines[all]"     # + Mapillary + OSM road-snapping

Optional env vars: MAPILLARY_TOKEN (free, for the Mapillary source); MAPS_API_TOKEN (optional Google metadata enrichment — not required).

Quickstart

# Nearest official car coverage to a point (never a photosphere)
tracelines nearest 44.435072 26.050430

# Extract blue lines over an area -> GeoJSON
tracelines extract --area bucharest-city --sources google --out bucharest.geojson
tracelines extract --bbox 26.09,44.45,26.11,44.46 --sources google,mapillary --out out.geojson

# Pure car only (drops trekker/indoor; slower, one metadata call per pano)
tracelines extract --area bucharest-city --sources google --precision --out cars.geojson

# Density probe: covered km / segments / median year per source
tracelines probe --bbox 26.09,44.45,26.11,44.46 --sources google,mapillary
from tracelines import Settings, AREAS
from tracelines.pipeline import extract
from tracelines.export import write_geojson, summary_stats

segments, meta = extract(AREAS["bucharest-city"], ["google"], Settings())
write_geojson(segments, "bucharest.geojson")
print(summary_stats(segments))

The GUI

A static cockpit on GitHub Pages — because the Google extraction can't run in a browser (undocumented, CORS-blocked, Python-only), the page is a control surface, not the engine:

  • View any GeoJSON the CLI produced (drag & drop) on an interactive map.
  • Build a command — draw a bbox, copy the exact tracelines extract … command.
  • Live Mapillary overlay (paste your own free token — stored only in your browser).
  • Live Google works out-of-the-box via a shared hosted demo proxy — or run your own free.
  • Diff sources — where does Google have coverage Mapillary lacks, and vice-versa.

Deploy your own proxy (free)

The GUI ships pointed at a shared hosted demo proxy (Google Cloud Run) so Live Google works immediately. It's hardened (tiny bbox cap, per-IP rate limit) and may sleep or hit limits — for real use, run your own. A public Google-extraction proxy is an open relay (CORS doesn't stop curl), so keep the hardening env tight and treat it as best-effort, not a service.

Host Free? How
Render (recommended) ✅ no card Deploy to Render — one click; uses render.yaml
Docker anywhere docker build -t tracelines-proxy . && docker run -p 8000:8000 -e TRACELINES_CORS_ORIGINS="https://<you>.github.io" tracelines-proxy
Google Cloud Run ✅ free tier gcloud run deploy tracelines-proxy --source . --allow-unauthenticated --max-instances 1
Hugging Face Space ⚠️ needs HF PRO now files in spaces/

Then paste your URL into the GUI's Proxy tab. Hardening knobs: TRACELINES_MAX_BBOX_DEG2, TRACELINES_RATE_PER_MIN, TRACELINES_CORS_ORIGINS, and the kill switch TRACELINES_DISABLED=1.

Sources & what you may do with the output

Source Live in browser? Output license
Google (streetlevel) ❌ (CLI or self-hosted proxy) No redistribution license — generate for yourself (details)
Mapillary ✅ (your token) CC-BY-SA 4.0 (attribute + share-alike)
KartaView ✅ best-effort CC-BY-SA 4.0 (attribute + share-alike)

See DATA_LICENSES.md — the AGPL license covers the code, not the data you extract.

How it works

bbox → z17 tile enumerate → async fetch (cached, concurrent)
     → Layer-1 official-id whitelist → panoid graph (links = edges)
     → [--precision] source=='launch' allowlist
     → shapely line_merge (splits at junctions) → CoverageSegments
   Mapillary sequence tiles ─┐
   KartaView bbox polylines ─┴→ fuse (union + provenance) → GeoJSON

Full internals: Architecture.

Documentation

Development

pip install -e ".[dev]"
pytest                 # hermetic tests (no network)
pytest -m network      # live tests (hit Google/Mapillary)
ruff check tracelines tests
python scripts/verify_hardrule.py    # re-run the HARD-RULE sweep

Acknowledgements

  • streetlevel by sk-zk — the MIT library this stands on; it does the actual Google coverage-tile walking.
  • matdoes.dev Street View / pano-id write-ups — invaluable for the id taxonomy.
  • Mapillary and KartaView for open, documented street-level APIs.

License

AGPL-3.0-or-later — see LICENSE. If you run a modified version as a network service (e.g. the proxy backend), the AGPL requires you to share your source. Third-party notices in THIRD_PARTY_LICENSES.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tracelines-0.2.0.tar.gz (53.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tracelines-0.2.0-py3-none-any.whl (50.4 kB view details)

Uploaded Python 3

File details

Details for the file tracelines-0.2.0.tar.gz.

File metadata

  • Download URL: tracelines-0.2.0.tar.gz
  • Upload date:
  • Size: 53.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for tracelines-0.2.0.tar.gz
Algorithm Hash digest
SHA256 0cf9fc381325d8bba9eb0e0a80c1aabb9a48149c937e237dca5b28ed41e293e9
MD5 68d1ddced38b251a9636041b68961105
BLAKE2b-256 1eec7ed5525d53d7716177eb68628195f398ed7cdcb8ee56c852685a7c9f4a8e

See more details on using hashes here.

File details

Details for the file tracelines-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: tracelines-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 50.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for tracelines-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3961bc7ae299f704008e8eb9f09b07ac6196aebe6e224b07f626542fe4e325c1
MD5 5d6c17b0427a2705a0123f15ab02ebb4
BLAKE2b-256 84eab3e07a506b30d9dea6c4387664fee539fc5dc61aeb6e0d0ce2fb1a45cd70

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page