python-visualization / folium

Python Data. Leaflet.js Maps.
https://python-visualization.github.io/folium/
MIT License
6.91k stars 2.22k forks source link

Request Adding Leaflet.TileLayer.MBTiles Plugin Support #1318

Open and-viceversa opened 4 years ago

and-viceversa commented 4 years ago

Is your feature request related to a problem? Please describe. No. This feature request adds support for displaying raster tile base maps in .mbtiles format.

Describe the solution you'd like

  1. Make Leaflet.TileLayer.MBTiles it's own Folium plugin.
  2. Put the above plugin on a CDN so users aren't forced to store it locally. The original author's CDN link is broken I think.

Currently, you can use Folium and Leaflet.TileLayer.MBTiles to display .mbtiles format, but it's a bit hacky.

I used roughly the following snippet to override TileLayer._template

    # override defaults to use the plugin
    folium.raster_layers.TileLayer._template = Template(u"""
        {% macro script(this, kwargs) %}
            var {{ this.get_name() }} = L.tileLayer.mbTiles(         <--------
                {{ this.tiles|tojson }},
                {{ this.options|tojson }}
            ).addTo({{ this._parent.get_name() }});
        {% endmacro %}
        """)

   # make a Map with .mbtile basemap 
   m = folium.Map(
        location=[35.650787, -117.661728], 
        tiles='my_tiles.mbtiles', 
        attr=attr
    )

    # add open street map
    layer = folium.TileLayer(
        tiles='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
        name='OpenStreetMap Online',
        attr='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> '
             'contributors',
        overlay=True,
        control=True,
        show=False
    ).add_to(m)

    # function to return _template back to default after the fact.
    set_tile_layer_default(layer)

Even then, you have to override TileLayer._template back to it's default before m.save('index.html') for each additional (non-mbtiles) basemap you add.

It would be really nice to:

from folium.plugins import MBTiles

m = MBTiles.Map(
    location = [14.0, 15.0],
    tiles = 'your_tiles_url.mbtiles',
    attr = 'attr text'
)

Describe alternatives you've considered The only alternative I can see is something like the above snippet. There was some discussion in #351.

Additional context For those unfamiliar, one open source way to make your own tiles is by using TileMill. TileMill exports the usual {z}{x}{y} directory structure, as well as the Mapbox raster .mbtiles format. Mbtiles are preferable in some cases, because you only have to manage 1 file instead of (no joke) millions of .png files.

Also, it's great when one open source project ties cleanly into another.

Implementation folium is maintained by volunteers. Can you help make a PR if we want to implement this feature?

I would be happy to write this PR. I have working code which accomplishes the task, just not sure how to best turn it into a plugin.

If somebody could please comment:

  1. Is this idea acceptable and feasible?
  2. A general best practices and implementation list.

Thanks.

Conengmo commented 4 years ago

Hi @and-viceversa, your text is very clear, thanks for that. I like your proposal. We recently updated our contributing guide with some plugin acceptance criteria, you can see them here. I think this proposal passes the criteria. The plugin code on our side would be fairly simple, maybe inheriting from or reusing ideas from TileLayer. There hasn't been any recent activity on the plugin repo though, but it doesn't seem abandoned, since there are no critical open issues or PRs.

You can open a PR with just the plugin first. Then if that passes review you can add some more things, those are listed in the contributing guide.

and-viceversa commented 4 years ago

Two questions:

  1. The plugin's CDN linked at https://unpkg.com/leaflet.tilelayer.mbtiles@latest/Leaflet.TileLayer.MBTiles.js is no longer working. I'm not really a web dev, can someone please comment on how to fix this?

  2. The way folium.Map is written, I'm not sure there's a good way to create a Map with a subclass of TileLayer.

Consider

        if tiles:
            tile_layer = TileLayer(tiles=tiles, attr=attr,
                                   min_zoom=min_zoom, max_zoom=max_zoom)
            self.add_child(tile_layer, name=tile_layer.tile_name)

So you'd have to do something like this to use a subclass of TileLayer.

    m = folium.Map(
        location=[45.5236, -122.6750],
        tiles=None,     # None value to avoid creating TileLayer
        zoom_start=13
    )

    # MBTiles subclasses TileLayer
    MBTiles(
        location=[45.5236, -122.6750],
        tiles='path/to/your_tiles.mbtiles',
        zoom_start=13,

    ).add_to(m)

It'd be best to just tiles=url/to/your_tiles.mbtiles How can we do this without changing the logic in folium.Map? Or, does it make sense to add a little flag to check for .mbtiles etc?

Conengmo commented 4 years ago

Here's an unpkg url that works: https://unpkg.com/Leaflet.TileLayer.MBTiles@1.0.0/Leaflet.TileLayer.MBTiles.js It's a bit concerning though that the author has ignored this issue for 2 years: https://gitlab.com/IvanSanchez/Leaflet.TileLayer.MBTiles/-/issues/7 Seems like this library is abandoned after all. I don't want to shoot this down because of that, but do consider that if that library breaks the folium plugin is also gone.

Your second code snippet is the desired behavior IMO. That's already how users should go about if they want more advanced TileLayer settings. We don't want to overload Map any further. And since this is going to be a plugin, we don't want to import it in folium/folium.py.

martinfleis commented 1 year ago

There seems to be a new, maintained version of the mbtiles plugin https://github.com/0nza1101/leaflet-tilelayer-mbtiles-ts