leafem icon indicating copy to clipboard operation
leafem copied to clipboard

consider adding functionality to render mvt/mbtiles

Open tim-salabim opened this issue 5 years ago • 2 comments

A continuation from https://twitter.com/_davecooley/status/1316992144226144256

A function to read and render mbtiles files or their respective folder structure version (.pbf files) should be easy enough... With mapview in mind, the hard thing is to create these tiles on the fly from a e.g. sf object. tippecanoe is fast but for "deep zoom" scenarios it is still way too slow. Running in background doesn't help as the leaflet copies the folder(s) on map creation to a different place so every tile set created afterwards is lost. Maybe this could be somehow addressed in htmlwidgets?

In any case, here's what I have so far:

library(sf)
library(leaflet)
library(htmlwidgets)
library(htmltools)
library(mapboxapi)
library(processx)

url = "https://raw.githubusercontent.com/bjornharrtell/flatgeobuf/3.0.1/test/data/UScounties.fgb"

tst = st_read(url)
tst = tst[, -5] # tippecanoe has a problem with the NAME column (not being proper utf8)

fldr = tempfile()
fl = tempfile(fileext = ".geojson")
st_write(tst, fl, append = FALSE)

system.time({
    st_write(
        tst
        , dsn = fldr
        , driver = "MVT"
        , dataset_options = c(
            "FORMAT=DIRECTORY"
            , "COMPRESS=NO"
            , "MAXZOOM=10"
        )
        , delete_dsn = TRUE
    )
})

dir.create(fldr)
command = sprintf(
    "tippecanoe -zg -f -pC -e %s %s"
    , fldr
    , fl
)
system.time({
    system(command)
})

system.time({
    processx::run(
        "tippecanoe"
        , args = c("-z 10", "-f", "-pC", "-e", fldr, fl)
    )
})

p = processx::process$new(
    "tippecanoe"
    , args = c("-z 10", "-f", "-pC", "-e", fldr, fl)
)
p$is_alive()

leafgrid = htmlDependency(
    name = "Leaflet.VectorGrid"
    , version = "1.3.0"
    , src = c(href = "https://unpkg.com/leaflet.vectorgrid@latest/dist/")
    , script = "Leaflet.VectorGrid.bundled.js"
)

registerPlugin <- function(map, plugin) {
    map$dependencies <- c(map$dependencies, list(plugin))
    map
}

tiledDataDependency <- function(tiles_dir) {
    htmltools::htmlDependency(
        name = basename(tiles_dir),
        version = "0.0.1",
        src = c(file = tiles_dir)
    )
}


leaflet() %>%
    addTiles() %>%
    registerPlugin(leafgrid) %>%
    registerPlugin(tiledDataDependency(fldr)) %>%
    setView(lng = -105.644, lat = 51.618, zoom = 3) %>%
    onRender(
        paste0(
            "function(el, x) {
                var pbfLayer = L.vectorGrid.protobuf('lib/"
                , basename(fldr)
                , "-0.0.1/{z}/{x}/{y}.pbf');
                pbfLayer.addTo(this);
            }"
        )
    ) %>%
    leafem::addMouseCoordinates()

tim-salabim avatar Oct 16 '20 18:10 tim-salabim

This would be amazing. While your workaround works, I understand that it wouldn't work in Shiny when you want to update a tile using leaftletProxy (because of the onRender piece). Would that be feasible to implement?

kauebraga avatar Feb 20 '25 22:02 kauebraga

Care for creating a PR?

tim-salabim avatar Feb 21 '25 07:02 tim-salabim