r-spatial / mapview

Interactive viewing of spatial data in R
https://r-spatial.github.io/mapview/
GNU General Public License v3.0
515 stars 91 forks source link

legend scientific notation #258

Open FrogBoss74 opened 4 years ago

FrogBoss74 commented 4 years ago

Is there a way to display legend when values plotted are relatively low say 1e-9 to 1e-3? ideally, I'd like to display region colour legends in range bands eg (1e-3, 1e-4], (1e-4, 1e-5], (1e-5, 1e-6], etc. log10 scale or not. Right now it shows 0 as values are truncated below 0.001. The data looks like a grid of grid points with each point having a value ranging 1e-9 to 1e-3. For presentation purpose I don't want to show the log10 values in the legend. More control on the legend display will be useful. This is the first issue.

the data is plotted as follows:

jetc <-   colorRampPalette(    c("#00007F", "blue", "#007FFF", "cyan", "#7FFF7F", "yellow", "#FF7F00", "red", "#7F0000"))
data%>%mapview(zcol="IR",     # Variable to plot
                 legend=F, cex=1, # cosmetics
                 col.regions=jetc(6), at=rev(10^seq(-9,-3,1)), alpha=0.2)

The second issue comes as I try a workaround to this by overlaying isocontours on top of the data for which I can better control the legend and colour. I have tried to generate iso countours add one more column called label with this category however if I use this method to plot the label column instead I am not able to match the legend colour and the geometry object colours (ie it is hard to match the legend colour with the plotted colours). I get matching colours with the colour regions only if I plot the level column. If I plot the range column I strangely have no matching colour between the legend colour and the countour colours.

The iso contour data looks like:

Simple feature collection with 4 features and 2 fields
geometry type:  MULTILINESTRING
dimension:      XY
bbox:           xmin: -99941.94 ymin: 6663022 xmax: -72867.65 ymax: 6677443
epsg (SRID):    3857
proj4string:    +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs
  level                       geometry            range
1    -4 MULTILINESTRING ((-86034.96... (0.001 to 1e-04]
2    -5 MULTILINESTRING ((-89885.6 ... (1e-04 to 1e-05]
3    -6 MULTILINESTRING ((-94484.28... (1e-05 to 1e-06]
4    -7 MULTILINESTRING ((-99935.43... (1e-06 to 1e-07]

the following generates a mismatch in colours between legends and contours

jetcrev <-colorRampPalette(rev(c("#00007F", "blue", "#007FFF", "cyan", "#7FFF7F", "yellow", "#FF7F00", "red", "#7F0000")))
mapview(IRiso, zcol="range", legend=T, color=jetcrev, layer.name="IR", burst=F)

the following generates a colour matching but I am back to displaying log10 values which is not very aestetic

mapview(IRiso, zcol="level", legend=T, color=jetcrev, layer.name="IR 10^", burst=F)
tim-salabim commented 4 years ago

Can you somehow share this data or produce a reproducible example using some openly available data? Everything else is just guessing on my side...

FrogBoss74 commented 4 years ago

I could not reproduce the second colour issue. I ll share data if i manage to reproduce. The main point is about control of format for the legend.

library(sf)
study_area = st_as_sf(data.frame(geom=c("POLYGON((0 0, 0 500, 500 500, 500 0, 0 0))"), 
                               stringsAsFactors = F), wkt="geom", crs=3857)

gr = st_sf(grid = st_make_grid(study_area, n = dim(volcano), square =T, what="centers"))%>%
  rownames_to_column(var="gr.id")%>%
  mutate(risk = 10^(5*log10(c(volcano)-min(volcano))-14))%>%filter(risk < 1e-3)

jetc <-   colorRampPalette(c("#00007F", "blue", "#007FFF", "cyan", "#7FFF7F", "yellow", "#FF7F00", "red", "#7F0000"))
mc <-pretty(c(-7,-3))

# First issue for legend scientific format
gr %>% mapview(zcol="risk",     # Variable to plot
               legend=T, cex=1.5, # cosmetics
               col.regions=jetc(4), at=rev(10^mc), alpha=0.2, layer.name="risk")

Rplot

Desired output for instance using sci notation (here I workaround the issue overlaying iso contours on top), better using 10^x format but i could not find any workaround for that. Rplot02

tim-salabim commented 4 years ago

This should now be partially solved. At least we can now pass any valid argument to leaflet::addLegend via ... Means, we can now at least show more than 3 digits in the legend:

gr %>% mapview(zcol="risk",     # Variable to plot
               legend=T, cex=1.5, # cosmetics
               col.regions=jetc(4), at=rev(10^mc), alpha=0.2, layer.name="risk",
               labFormat = leaflet::labelFormat(digits = 6))

Honestly, I have no clue how to realise the scientific notation. If it's possible in leaflet, it should be possible here, but I couldn't find anything on how to specify it. Maybe using format somehow?

FrogBoss74 commented 3 years ago

Sorry for late follow up and thanks for highlighting the labFormat route. I have done some further digging at it appears that leaflet::labelFormat calls an in internal function formatNum which has a scientific=F parameter. Somehow leaflet::labelFormat does not allow to set that parameter.

This would fix the issue, though still need to set digits to high number say 20:

labelFormatCustom = function (prefix = "", suffix = "", between = " &ndash; ", 
          digits = 3, big.mark = ",", transform = identity, scientific=T) 
{
  formatNum <- function(x) {
    format(round(transform(x), digits), trim = TRUE, scientific = scientific,#TRUE, 
           big.mark = big.mark)
  }
  function(type, ...) {
    switch(type, numeric = (function(cuts) {
      paste0(prefix, formatNum(cuts), suffix)
    })(...), bin = (function(cuts) {
      n <- length(cuts)
      paste0(prefix, formatNum(cuts[-n]), between, formatNum(cuts[-1]), 
             suffix)
    })(...), quantile = (function(cuts, p) {
      n <- length(cuts)
      p <- paste0(round(p * 100), "%")
      cuts <- paste0(formatNum(cuts[-n]), between, formatNum(cuts[-1]))
      paste0("<span title=\"", cuts, "\">", 
             prefix, p[-n], between, p[-1], suffix, "</span>")
    })(...), factor = (function(cuts) {
      paste0(prefix, as.character(transform(cuts)), suffix)
    })(...))
  }
}

gr %>% mapview(zcol="risk",     # Variable to plot
               legend=T, cex=1.5, # cosmetics
               col.regions=jetc(4), at=rev(10^mc), alpha=0.2, layer.name="risk",
               labFormat = labelFormatCustom(digits = 20, scientific=T))
tim-salabim commented 3 years ago

If that solves the issue, I think you should open an issue with leaflet. mapview will then allow you set it too.