r-spatial / leafem

leaflet extensions for mapview
https://r-spatial.github.io/leafem/
Other
108 stars 30 forks source link

Enable viewing of locally saved pmtiles #73

Open Robinlovelace opened 1 year ago

Robinlovelace commented 1 year ago

I've just tried the following to no avail:

library(leafem)
library(leaflet)
url_rivers = "https://vector-tiles-data.s3.eu-central-1.amazonaws.com/rivers_africa.pmtiles"
leaflet() %>%
  addTiles() %>%
  addPMPolylines(
    url = url_rivers
    , layerId = "rivers"
    , group = "rivers"
    , style = paintRules(
      layer = "rivers_africa"
      , color = "blue"
    )
  ) %>%
  setView(24, 2.5, 4)
f_rivers = basename(url_rivers)
if (!file.exists(f_rivers)) {
  download.file(url_rivers, f_rivers)
}
leaflet() %>%
  addTiles() %>%
  addPMPolylines(
    # url = paste0("pmtiles://", f_rivers)
    url = f_rivers
    , layerId = "rivers"
    , group = "rivers"
    , style = paintRules(
      layer = "rivers_africa"
      , color = "blue"
    )
  ) %>%
  setView(24, 2.5, 4)

I know that hosting pmtiles locally can work: https://github.com/protomaps/PMTiles/discussions/234

Thoughts @tim-salabim or anyone? Would be super useful if possible and imagine it would work as a simple solution when shipping a .pmtiles file alongside a map for a quick solution.

tim-salabim commented 1 year ago

I've investigated that last year. IIRC, we would need to serve the tiles via a local server. I tried with servr but that doesn't work as it apparently does not support http range requests. At least that was what @bdon told me. I guess if we find a server solution that supports http range requests locally that we can invoke from R, then supporting PMTiles should be straight forward.

Robinlovelace commented 1 year ago

Ah yes, a parallel server is needed, that makes sense. In my tests I use VS Live Server extension FYI: https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer

I imagine something like {servr} would be ideal, worth opening an issue in the issue tracker? Various alternatives out there, wish I had more time to test them out!

tim-salabim commented 1 year ago

I've already provided a thumbs up here https://github.com/rstudio/httpuv/issues/259

Have you tried serving locally and then using addPM* functions?

Robinlovelace commented 1 year ago

Nope, can do! Also, out of interest, I tried uploading the .pmtiles file to GitHub pages, am I missing something obvious here?

u_routes = "https://itsleeds.github.io/netvis/rnet_limerick.pmtiles"
leaflet() %>%
  addTiles() %>%
  addPMPolylines(
    # url = paste0("pmtiles://", f_rivers)
    url = u_routes
    , layerId = "rnet_limerick"
    # , group = "rivers"
    , style = paintRules(
      layer = "rnet_limerick"
      , color = "blue"
    )
  ) %>%
  setView(24, 2.5, 4)

No luck yet, should show lines in Limerick :crossed_fingers:

tim-salabim commented 1 year ago

Did you enable cors? What does the browser console say?

Robinlovelace commented 1 year ago

Didn't enable cors but should be fine when both on GH pages. Here's a test: https://itsleeds.github.io/netvis/pmtiles.html

Doesn't seem to be asking for the tiles so probably something up with my code. Here are the tiles: https://itsleeds.github.io/netvis/rnet_limerick.pmtiles

And here's an example of it working with GH pages: https://altilunium.github.io/vmap/

Food for thought...

Robinlovelace commented 1 year ago

Update: pmtiles working! I guess the challenge now is just to get R to output the html, right?

https://itsleeds.github.io/netvis/pmtiles-test.html

Relevant bit of html:


        <script type="text/javascript">
            // add the PMTiles plugin to the maplibregl global.
            let protocol = new pmtiles.Protocol();
            maplibregl.addProtocol("pmtiles",protocol.tile);

            let PMTILES_URL = "rnet_limerick.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: 14,
                    center: [-8.63, 52.66],
                    style: {
                        version:8,
                        sources: {
                            "example_source": {
                                type: "vector",
                                url: "pmtiles://" + PMTILES_URL,
                                attribution: '© <a href="https://openstreetmap.org">OpenStreetMap</a>'
                            }
                        },
                        layers: [
                            {
                                "id":"rnet_limerick",
                                "source": "example_source",
                                "source-layer":"rnet_limerick",
                                "type": "line",
                                "paint": {
                                    "line-color": "red"
                                }
                            }
                        ]
                    }
                });
                map.showTileBoundaries = true;
            })
        </script>

Source: https://github.com/ITSLeeds/netvis/blob/main/pmtiles-test.html

tim-salabim commented 1 year ago

That's probably because the PMTiles dependency hasn't been updated for quite some time...