Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setting max native zoom automatically #880

Open
marine-ecologist opened this issue May 23, 2024 · 11 comments
Open

setting max native zoom automatically #880

marine-ecologist opened this issue May 23, 2024 · 11 comments

Comments

@marine-ecologist
Copy link

?tm_view mentions passing options to leafletOptions() via leaflet.option=, but I can't seem to get this to work for leaflet::providerTileOptions. Example below:

not working:

library(sf)
library(tmap)
library(leaflet)

# set coord
heron_island_coords <- st_sfc(st_point(c(151.9110, -23.4421)), crs = 4326) |> 
  st_transform(crs = 20355)

#tmap
tmp_map <- tm_basemap("Esri.WorldImagery") +
tm_shape(heron_island_coords) +
  tm_dots() +
tm_view(set.zoom.limits=c(18,30), leaflet.options = providerTileOptions(maxNativeZoom=18,maxZoom=100))

tmp_map

working via tmap_leaflet():

tmp_map |>  tmap_leaflet() |>
  leaflet::addProviderTiles('Esri.WorldImagery', options=leaflet::providerTileOptions(maxNativeZoom=18,maxZoom=100)) 
@mtennekes
Copy link
Member

Apparently, we have to pass the zoom limits (set.zoom.limits) not only to options of leaflet(), but also to providerTileOptions in addProviderTiles.

So far so good. However, the maxNativeZoom depends on the tile provider, and needs to be specified manually. Leaving it undefined gives a blank layer, e.g.

tmp_map |>  tmap_leaflet() |>
	leaflet::addProviderTiles('Esri.WorldImagery', options=leaflet::providerTileOptions(maxZoom=100)) 

does not work.

As a user, you always want to be able to zoom in to the most detailed zoom level available. In other words, I see no reason to set the general max zoom to 18, but the max native zoom level to 10 (if 18 is also available).

Relevant post: Leaflet/Leaflet#6316

@tim-salabim how did you deal with this issue in mapview?

@tim-salabim
Copy link

tim-salabim commented May 25, 2024

We don't use maxNativeZoom at all in mapview. The maxZoom for the basemaps is set to 52 here. I can't quite remember why we set this to 52 exactly, but I vaguely remember that some issues arose if it was set to something higher. The general reason for maxZoom to be set so high is that it sometimes helps to see very small sliver polygons that can be produced by operations like st_intersection et al.

The issue seems to be quite tough, as maxZoom can even vary within basemap providers. Take the following code and zoom into North America somewhere, you will see that you can zoom past 19 and still get some basemap. However, if you zoom somewhere into Africa, it stops at zoom 17 and hence will not enable zooming past that (you can zoom, but no imagery). If you set maxNativeZoom = 17 then you will be able to zoom further into Africa, but at the same time you loose zoom levels 18 & 19 in North America...

library(leaflet)

leaflet() |>
    leaflet::addProviderTiles('Esri.WorldImagery', options=leaflet::providerTileOptions(maxZoom=52, maxNativeZoom = 19)) |> leafem::addMouseCoordinates()

Not sure if there is a good one-fits-all solution here...

@marine-ecologist
Copy link
Author

Would it be feasible to set a max zoom internally for the common basemap providers?

providers_max_zoom <- list(
  "OpenStreetMap" = 19,
  "CartoDB.Positron" = 19,
  "Stamen.Toner" = 20,
  "Esri.WorldImagery" = 17
)

@tim-salabim
Copy link

That's already done internally on the JavaScript side by the leaflet.providers package if I'm not mistaken

mtennekes added a commit that referenced this issue Jun 10, 2024
@mtennekes
Copy link
Member

Good idea @marine-ecologist ! I can include such a list in tmap, but it is preferable to do maintain such a list somewhere upstream (JS side). Can't find it in the leaflet.providers @tim-salabim, at least from the R side.

I've added max.native.zoom to tm_tiles:

tm_basemap("Esri.WorldImagery", max.native.zoom = 18) +
	tm_shape(heron_island_coords) +
	tm_dots() +
	tm_mouse_coordinates() +
	tm_view(set.view = 16, set.zoom.limits=c(2,20))

@mtennekes mtennekes changed the title correctly passing leaflet.options to set.view() setting max native zoom automatically Jun 10, 2024
@tim-salabim
Copy link

This
https://github.com/leaflet-extras/leaflet-providers/blob/master/leaflet-providers.js#L78-L1217

Just to mention it here, maybe python xyzservices has a solution to the maxNativeZoom issue? @martinfleis
https://github.com/geopandas/xyzservices

@martinfleis
Copy link

Not sure I follow what the actual issue is here. xyzservices stores metadata for min_zoom and max_zoom. When this gets consumed by folium (Python leaflet.js wrapper), these get used within the map unless a user overrides any of them manually. maxNativeZoom is, by default, equal to max_zoom but you can also override that.

We don't use maxNativeZoom at all in mapview.

I believe that if you don't set it, tiles at higher zoom levels just disappear. If you do, the tiles are autoscaled (blurred).

@tim-salabim
Copy link

I believe that if you don't set it, tiles at higher zoom levels just disappear. If you do, the tiles are autoscaled (blurred).

In theory, yes. But if you take the example from #880 (comment) you will see that it only autoscales in regions within the map where the map provider actually has tiles until maxNativeZoom (North America in this example). If there are no tiles at this depth (as in Africa in the example), then can zoom until maxNativeZoom is reached, but you get the same behaviour as if maxNativeZoom was not set (i.e. tiles disappear; no autoscaling). Hence, I think given the current implementation of the JavaScript code in leaflet(Providers), there is no consistent way of setting maxNativeZoom to get identical behaviour everywhere. Does that clarify the issue at hand @martinfleis ?

@marine-ecologist
Copy link
Author

@tim-salabim - I hadn't realised maxNativeZoom was dynamic. One solution/workaround implemented elsewhere is Leaflet.TileLayer.Fallback that replaces missing Tiles (404 error) with scaled lower zoom Tiles.

I've tried implementing this via leaflet and htmltools (with a West Africa coast) as a Dependency but to with limited success either inline or via unpkg:

library(leaflet)
library(htmlwidgets)
library(htmltools)



# Create the JavaScript dependency
js_dep <- htmlDependency(
  name = "leaflet-tilelayer-fallback",
  version = "1.0.4",
  src = c(href = "https://unpkg.com/[email protected]/dist/"),
  script = "leaflet.tilelayer.fallback.js"
)


# Create the leaflet map
map <- leaflet() %>%
  addProviderTiles(
    providers$Esri.WorldImagery,
    options = providerTileOptions(maxNativeZoom = 18, maxZoom = 100)
  ) %>%
  addMarkers(lng = 10.54925, lat = -51.16226) %>%
  onRender("
    function(el, x) {
      var map = this;
      L.tileLayer.fallback('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        minNativeZoom: 0,
        maxNativeZoom: 20,
        maxZoom: 100
      }).addTo(map);
    }
  ")


# Attach the JavaScript dependency to the map
map <- htmltools::attachDependencies(map, js_dep)

map

@martinfleis
Copy link

In theory, yes. But if you take the example from #880 (comment) you will see that it only autoscales in regions within the map where the map provider actually has tiles until maxNativeZoom

That is not what is happening. The issue with these specific ESRI tiles is that they do provide tiles for higher zoom levels but those images just say "no data...". If you test on something else, like OpenStreetMap.HOT, it actually behaves the way I described. The issue you are facing here is with tiles not software.

@tim-salabim
Copy link

@martinfleis thanks for the clarification. I think there's not much we can do here (apart from utilising @marine-ecologist "s suggestion using Leaflet.TileLayer.Fallback ), though, for me, this is not high priority at the moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants