# Tile Providers

Yoro works with any map tile provider. This guide shows how to configure different tile sources for various use cases.

## Quick Reference

=== "OpenStreetMap"

    Best for general use. Free, no API key needed.

    ```javascript
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; OpenStreetMap contributors',
        maxZoom: 19,
    }).addTo(map);
    ```

=== "Satellite (ESRI)"

    Free satellite imagery. Great for verifying parcel boundaries.

    ```javascript
    L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
        attribution: '&copy; Esri, Maxar, Earthstar Geographics',
        maxZoom: 18,
    }).addTo(map);
    ```

=== "Terrain (OpenTopoMap)"

    Free topographic map. Useful for altitude and terrain context.

    ```javascript
    L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; OpenTopoMap',
        maxZoom: 17,
    }).addTo(map);
    ```

=== "CartoDB Positron"

    Minimal light theme. Excellent for data overlays and the Yoro grid.

    ```javascript
    L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
        attribution: '&copy; CARTO',
        maxZoom: 20,
    }).addTo(map);
    ```

## Complete Example: Multi-Layer Map

A full example with layer switching — ideal for field applications:

```html
<!DOCTYPE html>
<html>
<head>
    <title>Yoro Multi-Layer Map</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9/dist/leaflet.css" />
    <style>
        body { margin: 0; }
        #map { height: 100vh; }
    </style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/leaflet@1.9/dist/leaflet.js"></script>
<script>
    // Base layers
    const osm = L.tileLayer(
        'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
        { attribution: '&copy; OSM', maxZoom: 19 }
    );
    const satellite = L.tileLayer(
        'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
        { attribution: '&copy; Esri', maxZoom: 18 }
    );
    const topo = L.tileLayer(
        'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
        { attribution: '&copy; OpenTopoMap', maxZoom: 17 }
    );
    const light = L.tileLayer(
        'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',
        { attribution: '&copy; CARTO', maxZoom: 20 }
    );

    // Initialize with OSM
    const map = L.map('map', {
        center: [7.5, -5.5],
        zoom: 7,
        layers: [osm],
    });

    // Layer control
    L.control.layers({
        'OpenStreetMap': osm,
        'Satellite': satellite,
        'Topographic': topo,
        'Light': light,
    }).addTo(map);
</script>
</body>
</html>
```

## Choosing the Right Tiles

| Use Case | Provider | Why |
|----------|----------|-----|
| General navigation | OpenStreetMap | Free, detailed roads/buildings |
| **Parcel verification** | **ESRI Satellite** | **See actual land, trees, buildings** |
| Terrain analysis | OpenTopoMap | Elevation contours, slopes |
| Data visualization | CartoDB Positron | Clean background for Yoro grid |
| Offline/field use | MBTiles | Pre-downloaded, no internet needed |

## Offline Tiles (MBTiles)

For field agents without internet connectivity, pre-download tiles as MBTiles:

```python
# In your Django app, serve offline tiles
# Store .mbtiles files and serve via a tile proxy view

from django.http import HttpResponse
import sqlite3

def offline_tile(request, z, x, y):
    """Serve tiles from a local .mbtiles file."""
    y_tms = (1 << z) - 1 - y  # TMS to XYZ conversion

    conn = sqlite3.connect('/path/to/region.mbtiles')
    cursor = conn.execute(
        'SELECT tile_data FROM tiles WHERE zoom_level=? AND tile_column=? AND tile_row=?',
        (z, x, y_tms)
    )
    row = cursor.fetchone()
    conn.close()

    if row:
        return HttpResponse(row[0], content_type='image/png')
    return HttpResponse(status=404)
```

Then in Leaflet:

```javascript
const offlineTiles = L.tileLayer('/tiles/{z}/{x}/{y}.png', {
    maxZoom: 16,
    attribution: 'Offline tiles',
});
```

!!! tip "Combining Yoro with offline maps"
    Yoro codes are computed purely from math — they work offline without any API call. Combined with pre-downloaded MBTiles, field agents can use the full addressing system with zero connectivity.

## Satellite Imagery for EUDR

For EUDR (EU Deforestation Regulation) compliance, satellite imagery is critical to verify that parcels are not on deforested land:

```javascript
// NICFI Planet basemaps (free for tropical forests, requires signup)
// https://www.planet.com/nicfi/
const nicfi = L.tileLayer(
    'https://tiles.planet.com/basemaps/v1/planet-tiles/planet_medres_normalized_analytic_{period}_mosaic/gmap/{z}/{x}/{y}.png?api_key={key}',
    {
        attribution: '&copy; Planet Labs',
        maxZoom: 17,
        period: '2024-01',  // Monthly mosaics available
        key: 'YOUR_NICFI_API_KEY',
    }
);
```

This lets you overlay Yoro codes on satellite imagery to visually verify each parcel's forest status.
