walkerke / mapgl

R interface to Mapbox GL JS v3 and Maplibre GL JS
https://walker-data.com/mapgl
Other
91 stars 5 forks source link

PMTiles source integration? #8

Open emilydomanico opened 4 months ago

emilydomanico commented 4 months ago

Hi! First, so excited to see that you are developing tools for maplibre in R! It parallels well with work I've been doing to explore 1) creating PMTiles with tippecanoe, 2) storing them in an S3 bucket, and 3) adding them as a vector source to a maplibre map.

I see you have the add_vector_source() function set up for MapTiler tiles, and I am wondering if it is a small lift to allow it to render PMTiles read in directly from an S3 bucket link?

I have an html script adapted from the PMTiles source and protocol example (here: https://maplibre.org/maplibre-gl-js/docs/examples/pmtiles/), that works as a standalone. Would it be possible to integrate the PMTiles plugin with this work? Possibly in the add_vector_source() function?

If I can help move toward a pull request, let me know if that's something could see including in this package. Thanks!

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Map with PMTiles from S3 link</title>
    <meta property="og:description" content="Initialize a map in an HTML element with MapLibre GL JS. Add PM tiles plugin and protocol" />
    <meta charset='utf-8'>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel='stylesheet' href='https://unpkg.com/maplibre-gl@4.3.2/dist/maplibre-gl.css' />
    <script src='https://unpkg.com/maplibre-gl@4.3.2/dist/maplibre-gl.js'></script>
    <script src="https://unpkg.com/pmtiles@2.11.0/dist/index.js"></script>
    <style>
        body { margin: 0; padding: 0; }
        html, body, #map { height: 100%; }
    </style>
</head>
<body>
<div id="map"></div>
<script>

    // add the PMTiles plugin to the maplibregl global.
    const protocol = new pmtiles.Protocol();
    maplibregl.addProtocol('pmtiles', (request) => {
        return new Promise((resolve, reject) => {
            const callback = (err, data) => {
                if (err) {
                    reject(err);
                } else {
                    resolve({data});
                }
            };
            protocol.tile(request, callback);
        });
    });

    const PMTILES_URL = 'https://vector-tiles-testing.s3.us-east-2.amazonaws.com/brmpo-demographics.pmtiles';

    const p = new pmtiles.PMTiles(PMTILES_URL);
    // this is so we share one instance across the JS code and the map renderer
    protocol.add(p);

    // we first fetch the header so we can get the center lon, lat of the map.
    p.getHeader().then(h => {
        const map = new maplibregl.Map({
            container: 'map',
            zoom: h.maxZoom - 2,
            center: [h.centerLon, h.centerLat],
            style: {
                version: 8,
                sources: {
                    'brmpo_demo_tiles': {
                        type: 'vector',
                        url: `pmtiles://${PMTILES_URL}`,
                        attribution: '© <a href="https://openstreetmap.org">OpenStreetMap</a>'
                    }
                },
                layers: [
                    {
                        'id': 'demo',
                        'source': 'brmpo_demo_tiles',
                        'source-layer': 'brmpo_tract',
                        'type': 'fill',
                        'paint': {
                            'fill-color': [
                                "rgb",10,10,10
                                ],
                            'fill-outline-color': 'white',
                            'fill-opacity': .8

                        }
                    }
                ]
            }
        });

    });

</script>
</body>
</html>
walkerke commented 4 months ago

Yes, a PR would be great! I've heard elsewhere that PMTiles integration is a desirable feature.

I do want to keep the Mapbox and MapLibre interfaces consistent, so we'd need to handle with related but separate plugins (like I do with compare().

emilydomanico commented 4 months ago

I'll keep this on my to-do list, but it might be a couple of weeks. The pmtiles for maplibre integration seems do-able with all the references I've gathered.

I like the Mapbox and MapLibre consistency that you've set up in the package, but I can see some gaps in the pmtiles to mapbox support (although something like https://github.com/am2222/mapbox-pmtiles looks promising). If you have more thoughts on that front let me know.