# Quickstart
## The two usage patterns
### Stateless functions (simple, one-off queries)
Call `calc_isochrones` or `calc_route` directly. Each call internally fetches
and caches the graph, so the first call for an area is slow (network I/O) and
subsequent calls are fast (cache hit).
```python
import pysochrone
# Geocode a place name
lat, lon = pysochrone.geocode("Marienplatz, Munich, Germany")
# Isochrones: 5, 10, 15, 20 minutes driving
isos = pysochrone.calc_isochrones(lat, lon, [300, 600, 900, 1200], "Drive", "Concave")
# Route between two points
route = pysochrone.calc_route(lat, lon, 48.154560, 11.530840, "Drive")
```
### Stateful Graph object (multiple queries over the same area)
`build_graph` returns a `Graph` that you reuse for isochrones, routing, and POI
lookups without re-loading data from the cache each time.
```python
import pysochrone
graph = pysochrone.build_graph(48.137144, 11.575399, "Drive", max_dist=10_000)
print(graph) # Graph(nodes=6251, edges=15356, network_type=Drive)
# Compute isochrones for multiple origin points using the same graph
origins = [(48.137144, 11.575399), (48.154560, 11.530840)]
for lat, lon in origins:
isos = graph.isochrones(lat, lon, [300, 600, 900], "Concave")
pois = graph.fetch_pois(isos[-1]) # POIs within the largest isochrone
```
Prefer the `Graph` object whenever you make more than one query for the same area.
---
## Working with GeoJSON output
All results come back as GeoJSON strings. Parse them with the standard library:
```python
import json
# Isochrone geometry
iso = json.loads(isos[0])
print(iso["type"]) # "Polygon"
print(iso["coordinates"]) # [[lon, lat], ...]
# Route feature
route_data = json.loads(route)
props = route_data["properties"]
print(f"Distance: {props['distance_m']:.0f} m")
print(f"Duration: {props['duration_s']:.0f} s")
print(f"Waypoints: {len(props['cumulative_times_s'])}")
# POI FeatureCollection
pois_data = json.loads(pois)
for feature in pois_data["features"]:
tags = feature["properties"]
name = tags.get("name", "unnamed")
kind = tags.get("amenity") or tags.get("shop") or "?"
print(f"{name} ({kind})")
```
---
## Choosing a network type
| `"Drive"` | Public roads accessible to private cars; excludes service roads, driveways |
| `"DriveService"` | Like `Drive` but includes service roads |
| `"Walk"` | Footways, pedestrian paths, and shared roads where walking is permitted |
| `"Bike"` | Cycleways and roads open to bicycles |
| `"All"` | All highway types except private access |
| `"AllPrivate"` | All highway types including private access |
---
## Choosing a hull type
| `"Convex"` | Convex polygon | Fastest | Quick overviews, simple analytics |
| `"FastConcave"` | Approx. concave | Fast | Good balance |
| `"Concave"` | Tight concave | Slowest | Accurate reachability maps |
---
## Caching
pysochrone uses a three-level cache so repeated queries skip the network entirely:
```
Overpass API (network) → disk XML → in-memory XML → in-memory graph
```
Each level persists across process restarts (disk) or within a session (memory).
```python
print(pysochrone.cache_dir()) # shows the disk cache location
pysochrone.clear_cache() # wipe all three levels
```
Set `OSM_GRAPH_CACHE_DIR` to override the default cache location.