Architecture¶
tracelines is a small asyncio pipeline. Each source is a provider that returns either
panos (nodes to stitch) or segments (native lines); the pipeline stitches, fuses, and exports.
Pipeline¶
flowchart TD
BBOX[bbox / named area] --> TILES[enumerate z17 / z14 tiles]
TILES --> GP[GoogleStreetlevelProvider]
TILES --> MP[MapillaryProvider]
BBOX --> KP[KartaViewProvider]
GP -->|async fetch + diskcache| GN[panos + links]
GN -->|Layer 1 whitelist| GN2[official panos]
GN2 -->|--precision: source==launch| GN3[car panos]
GN3 --> ST[stitch.py: line_merge]
MP -->|MVT decode + per-sequence merge| MS[segments]
KP -->|tolerant bbox parse| KS[segments]
ST --> FUSE[fuse.py: union + provenance + optional dedup]
MS --> FUSE
KS --> FUSE
FUSE --> EXPORT[export.py: GeoJSON FeatureCollection]
Modules¶
flowchart LR
cli --> pipeline --> providers
pipeline --> stitch --> models
pipeline --> fuse --> models
providers --> tiles
providers --> cache
providers --> models
cli --> export --> models
nearest --> models
probe --> providers
| Module | Responsibility |
|---|---|
models |
Pano, CoverageSegment, Source, is_official_panoid, haversine/length |
tiles |
slippy-map tile math (no runtime dependency on mercantile) |
config |
BBox, named areas, Settings, env token resolution |
cache |
resumable tile cache (diskcache, in-memory fallback) |
providers/google_sl |
Google coverage graph-walk (the core) |
providers/mapillary |
Mapillary sequence vector-tile decode + per-sequence merge |
providers/kartaview |
KartaView bbox polylines (tolerant, degrades gracefully) |
stitch |
panoid graph → merged polylines via shapely.ops.linemerge |
fuse |
multi-source union + provenance + optional cross-source dedup |
export |
GeoJSON (stdlib json) + summary stats + optional GPKG |
pipeline / probe / nearest |
orchestration, density probe, point query |
Key design points¶
- No API key for Google. The core provider walks the z17 coverage tiles directly.
- Concurrency + resume. An
asyncio.Semaphorebounds in-flight requests; adiskcachetile cache makes a large crawl interruptible/resumable (re-running a bbox reuses cached tiles). - Cross-tile continuity is free. Google returns cross-tile
linksand duplicates boundary panos into adjacent tiles, so when a whole bbox is fetched, all links resolve within the set — the graph connects across tile boundaries without a special join. - Stitching by
line_merge. Every graph edge becomes a 2-point line;shapely.ops.linemergefuses runs and splits at junctions (degree ≥ 3) automatically. Isolated nodes never form an edge, so they drop for free. - Geometry is the driven path (like Google's blue lines), not road-snapped centerlines. Optional
OSM snapping is available behind the
[snap]extra. - Provider isolation. A provider that fails (e.g. KartaView endpoint drift) is caught in the pipeline and returns empty with a stats note; the run continues on the other sources.
Provider contract¶
class Provider(abc.ABC):
source: Source
def available(self) -> tuple[bool, str]: ...
async def fetch(self, bbox, settings, cache) -> ProviderResult: ...
@dataclass
class ProviderResult:
source: Source
panos: list[Pano] = [] # point sources -> stitched downstream
segments: list[CoverageSegment] = [] # native-line sources -> pass through
stats: dict = {}