# Download & Build

This guide covers how to download OSM data and build `.yoromaps` files.

## How it works

The build process:

1. **Download** — Fetches the latest `.osm.pbf` file from [Geofabrik](https://download.geofabrik.de/) for the chosen country.
2. **Extract** — Parses the PBF in two passes using pyosmium: first collecting highway ways and node references, then resolving node coordinates.
3. **Build graph** — Inserts nodes (intersections) and edges (road segments) into the SQLite database, with distance and estimated travel time.
4. **Tiles** (optional) — Downloads map tiles from OpenStreetMap tile servers and stores them in MBTiles format.

## CLI usage

### Basic build

```bash
yoromaps download ML --output mali.yoromaps
```

### With tiles

```bash
yoromaps download ML --output mali.yoromaps --tiles --zoom-max 14
```

### Custom zoom range

```bash
yoromaps download BF --output bf.yoromaps --tiles --zoom-min 8 --zoom-max 12
```

### Using an existing PBF file

If you already have a PBF file (e.g., downloaded manually from Geofabrik):

```bash
yoromaps download ML --output mali.yoromaps --pbf /data/mali-latest.osm.pbf
```

### List available countries

```bash
yoromaps countries
```

## Python API

### Basic build

```python
from yoromaps.download import build

build("ML", "mali.yoromaps")
```

### With tiles and progress

```python
from yoromaps.download import build

def on_progress(message, current, total):
    if total > 0:
        print(f"{message}: {current / total * 100:.0f}%")
    else:
        print(message)

build(
    "ML",
    "mali.yoromaps",
    include_tiles=True,
    zoom_min=6,
    zoom_max=14,
    progress=on_progress,
)
```

### Using an existing PBF

```python
from yoromaps.download import build

build("ML", "mali.yoromaps", pbf_path="/data/mali-latest.osm.pbf")
```

### Download PBF only

```python
from yoromaps.download import download_pbf

pbf_path = download_pbf("ML", "/tmp/downloads")
print(f"Downloaded to: {pbf_path}")
```

## Road types

The extractor recognizes these OSM highway types, with estimated speeds used for routing duration:

| Highway type     | Speed (km/h) |
|------------------|---------------|
| motorway         | 110           |
| motorway_link    | 60            |
| trunk            | 90            |
| trunk_link       | 50            |
| primary          | 70            |
| primary_link     | 40            |
| secondary        | 60            |
| secondary_link   | 35            |
| tertiary         | 50            |
| tertiary_link    | 30            |
| residential      | 30            |
| unclassified     | 40            |
| living_street    | 20            |
| service          | 20            |
| track            | 15            |
| path             | 5             |

One-way streets are respected: if an OSM way has `oneway=yes`, only a forward edge is created.

## Updating an existing database

After the initial download, use `update` to refresh the road graph with the latest OSM data. Only re-downloads if Geofabrik has published a newer PBF file.

### CLI

```bash
# First time: re-downloads (no previous date stored)
yoromaps update mali.yoromaps

# Second time: skips if nothing changed
yoromaps update mali.yoromaps
# → "Already up to date" — instant, no download

# Custom PBF cache directory
yoromaps update mali.yoromaps --cache-dir /data/pbf-cache
```

### Python API

```python
import yoromaps

result = yoromaps.update("mali.yoromaps")

if result["updated"]:
    print(f"Updated: {result['nodes']:,} nodes, {result['edges']:,} edges")
    print(f"Previous: {result['previous_date']}")
    print(f"New:      {result['new_date']}")
else:
    print(f"Already up to date (since {result['previous_date']})")
```

### What happens during an update

1. Checks `osm_update_date` in the database metadata
2. Sends HTTP `If-Modified-Since` to Geofabrik — if the PBF hasn't changed, **stops immediately** (no download)
3. If newer data exists, downloads the new PBF
4. **Clears** the road graph (nodes + edges tables)
5. **Re-extracts** from the new PBF
6. **Preserves** all POI data and tiles — only the road graph is rebuilt

!!! tip "Automate updates"
    Set up a cron job or Django management command to run `yoromaps.update()` weekly:
    ```python
    # management/commands/update_maps.py
    from django.core.management.base import BaseCommand
    import yoromaps

    class Command(BaseCommand):
        def handle(self, **options):
            result = yoromaps.update("/data/mali.yoromaps")
            if result["updated"]:
                self.stdout.write(f"Updated: {result['nodes']:,} nodes")
            else:
                self.stdout.write("Already up to date")
    ```

## File sizes

Tested file sizes (road graph only, no tiles):

| Country | PBF size | Nodes | Edges | .yoromaps size | Build time |
|---------|----------|-------|-------|----------------|------------|
| Togo | 59 MB | 2.2M | 2.3M | 436 MB | ~55s |
| Mali | 163 MB | 6.4M | 6.7M | 1.3 GB | ~145s |

Adding tiles at zoom 6-14 adds 200-500 MB depending on the country area.

!!! note
    File sizes depend on OSM data completeness. Countries with more mapped roads produce larger databases.
