0

I am creating a shiny app that:

  1. Allows the user to upload a shapefile (sf_object) "filemap".
  2. Based on the uploaded shapefile, the user will then be able to select a column/variable of interest to plot a an average temperature value Choropleth/classification map (mapview).

The app loads fine with no errors however the map is I think only showing a single value (last value to be precise) on the map, however the shapefile consists of 40 years (year column )of monthly (month column) data.

I guess part of the problem is that mapview is not averaging point values (those at the same point) rather it's mapping points with the same location on top of each other.

How can I fix this ?

Sample Data:

 structure(list(Info = c(NA_character_, NA_character_, NA_character_, 
    NA_character_, NA_character_), tmean = c(22.2395992279053, 22.7657985687256, 
    24.4260005950928, 19.601001739502, 17.5659999847412), CITYNAME = c("NORTH LAUDERDALE", 
    "NORTH LAUDERDALE", "NORTH LAUDERDALE", "NORTH LAUDERDALE", "NORTH LAUDERDALE"
    ), Model = c("PRISM", "PRISM", "PRISM", "PRISM", "PRISM"), Variable = c("tmean", 
    "tmean", "tmean", "tmean", "tmean"), Datatype = c("provisional", 
    "provisional", "stable", "stable", "stable"), Resolution = c("4kmM3", 
    "4kmM3", "4kmM3", "4kmM3", "4kmM3"), year = c(2021L, 2021L, 2020L, 
    2020L, 2020L), month = c(11L, 12L, 11L, 12L, 0L), TMin = c(0, 0, 
    0, 0, 0), TMax = c(0, 0, 0, 0, 0), geometry = structure(list(
        structure(c(-80.2083333327448, 26.2083333333333), class = c("XY", 
        "POINT", "sfg")), structure(c(-80.2083333327448, 26.2083333333333
        ), class = c("XY", "POINT", "sfg")), structure(c(-82.2083333327448, 
        28.2083333333333), class = c("XY", "POINT", "sfg")), structure(c(-82.2083333327448, 
        28.2083333333333), class = c("XY", "POINT", "sfg")), structure(c(-84.2083333327448, 
        30.2083333333333), class = c("XY", "POINT", "sfg"))), class = c("sfc_POINT", 
    "sfc"), precision = 0, bbox = structure(c(xmin = -84.2083333327448, 
    ymin = 26.2083333333333, xmax = -82.2083333327448, ymax = 28.2083333333333
    ), class = "bbox"), crs = structure(list(input = "WGS 84", wkt = "GEOGCRS[\"WGS 84\",\n    DATUM[\"World Geodetic System 1984\",\n        ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n            LENGTHUNIT[\"metre\",1]]],\n    PRIMEM[\"Greenwich\",0,\n        ANGLEUNIT[\"degree\",0.0174532925199433]],\n    CS[ellipsoidal,2],\n        AXIS[\"latitude\",north,\n            ORDER[1],\n            ANGLEUNIT[\"degree\",0.0174532925199433]],\n        AXIS[\"longitude\",east,\n            ORDER[2],\n            ANGLEUNIT[\"degree\",0.0174532925199433]],\n    ID[\"EPSG\",4326]]"), class = "crs"), n_empty = 0L)), row.names = c(NA, 
    -5L), class = c("sf", "data.frame"), sf_column = "geometry", agr = structure(c(Info = NA_integer_, 
    tmean = NA_integer_, CITYNAME = NA_integer_, Model = NA_integer_, 
    Variable = NA_integer_, Datatype = NA_integer_, Resolution = NA_integer_, 
    year = NA_integer_, month = NA_integer_, TMin = NA_integer_, 
    TMax = NA_integer_), class = "factor", .Label = c("constant", 
    "aggregate", "identity")))

UI

library(shiny)
library(shinydashboard)
library(shinythemes)
library(tidyverse)
library(RColorBrewer)
library(png)
library(shinyWidgets)
library(sf)
library(mapview)
library(leaflet)


 

       # Define UI for application that draws an interactive ggplot
        options(shiny.maxRequestSize=30*1024^2)
        ui =   navbarPage("Temperature Analysis", theme = shinytheme("sandstone"),
                        tabPanel("Temperature",
                        icon = icon("chart-area"),
                        sidebarLayout(sidebarPanel(fileInput("filemap", label = "Input Shapefile 
                                                             (.shp,.dbf,.sbn,.sbx,.shx,.prj)",
                                                             multiple=TRUE,
                                                             accept = c(".shp",
                                                                        ".dbf",
                                                                        ".sbn",
                                                                        ".sbx",
                                                                        ".shx",
                                                                        ".prj",
                                                                        ".cpg",
                                                                        ".xml"))),
                        mainPanel(selectInput(inputId = "Temp", label = "Select Temperature Variable", choices = c("Mean Temperature" = "TMean", "Minimum Temperature" = "TMin", "Maximum Temperature" = "TMax")),
                                  leafletOutput("mapview")))))

Server

# Tell the server how to assemble inputs into outputs
    server = function(input, output, session) {
Temp_map =   reactive({
            req(input$filemap)
          df = Read_Shapefile(input$filemap)
          df
        })
          pal_fun = colorQuantile("YlOrRd", NULL, n = 5) # Define color palette and classes
          
          observe({
            dff = Temp_map()
            updateSelectInput(session, "Temp", choices = names(dff))
          })
          
          #t_popup = paste0("Air Temperature", input$Temp) # Popup depends on the "Temp" variable selected 
          
          output$mapview =  renderLeaflet({
            Map_data = Temp_map() 
            Mapview_map = mapview(Map_data, zcol=input$Temp)   
            Mapview_map@map
          })    
}
Ed_Gravy
  • 1,841
  • 2
  • 11
  • 34

2 Answers2

1

This is because your example data is all on the exact same location, there is no reason the mapping function would be expected to average overlapping points. As well if you did you would not have the data from all the different years. I think what you are looking for is clustering, which I don't think is possible in mapview, you can do it with leaflet or tmap though, ex:

output$mapview =  renderLeaflet({
  Map_data <-  Temp_map() 
  TMAP_MAP <- tm_basemap()+
    tm_shape(Map_data)+
    tm_dots(clustering=TRUE, col=input$Temp, popup.vars=TRUE)
  tmap_leaflet(TMAP_MAP)
}) 


BEVAN
  • 426
  • 2
  • 12
  • My original timeseries dataset consists of a `4 km` grid spatially distributed data. Each grid point has monthly values for each year for a 40 year period. So, I guess it should be possible to average the values for each point and map those values. In the sample dataset, it should be possible to average the `temperature` value of the points with the same location. The code above is clustering the points and showing count of points rather than the average value. – Ed_Gravy Jun 17 '22 at 19:41
  • If you click on the cluster it will expand the cluster and give the value for each overlapping points. if you want to average the 40 years for each point the you would want to do that to your spatial data before you start mapping it. – BEVAN Jun 17 '22 at 20:01
  • I did find a solution by using `tm_bubbles(size=input$Temp, col = "red", popup.vars=TRUE)` instead of `tm_dots`. Now I just need to find a way to define a `color palette` to like `YlOrRd` so that the bubbles `change color` according to the `temperature value (high/low) ` – Ed_Gravy Jun 17 '22 at 20:07
  • Sorry `tm_bubble` does not fix it either, it just picks up the highest value and displays that on the map. – Ed_Gravy Jun 17 '22 at 20:29
1

Check in library(leaflet) the function markerClusterOptions. There is an argument spiderfyOnMaxZoom.

Look at this functionality in the original JS library http://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-realworld.388.html

G. Cocca
  • 2,456
  • 1
  • 12
  • 13
  • Thank you, since I have timeseries data so a single coordinate has multiple values thus leading to multiple overlapping points. Hence, I was looking for a way to show the average value for each coordinate on the interactive map. For example, let's say I have a grid of 4 points and each point has a value for let's say each of the four years, thus the total points would be 16. So, then an average (for each set of four overlapping points) would be calculated and showed on the map. – Ed_Gravy Jun 20 '22 at 13:25
  • So I don't think `markerClusterOptions` would do this ^right? – Ed_Gravy Jun 20 '22 at 13:46
  • 1
    That's correct. `spiderfyOnMaxZoom` does not do that. What is possible to do is to reshape your data first in order to calculate a mean over the 4 years for each of the overlapping points and pass that value to a leaflet object with `markerClusterOptions` and `spiderfyOnMaxZoom`. However, it depends on your dataset. A graph in the pop-up is another alternative or even better `library(leaflet.minicharts)` – G. Cocca Jun 21 '22 at 07:20