rfor-loopr-leaflet

Loop through dataframe and produce individual location maps


I want to loop through a dataframe that contains different monitoring sites and produce an individual leaflet map for each site.

See the minimal example below. I realize the loop, as written, will just overwrite each map. In my actual file, each site will have its own individual .html file with some ancillary information specific to each site produced at the end of the loop.

Is it possible to create leaflet maps in this way while retaining the for-loop? Do I need to add in a step that filters the larger dataframe based on the current site in the loop, and use that?

library(leaflet)
library(dataRetrieval)

# Create dataframe with 3 example sites
site1 <- readNWISsite(372322081241501)
site2 <- readNWISsite(380252079472801)
site3 <- readNWISsite(372223080234801)

df <- rbind(site1, site2)
df <- rbind(df, site3)

# Create a location map for each individual site in df
map <- leaflet()

q <- length(df$site_no)
for (j in 1:q) {
    map %>%
      addCircleMarkers(
        lng = df[j]$dec_long_va,
        lat = df[j]$dec_lat_va,
        radius = 9.0,
        color = "black",
        weight = 1,
        fillColor = "gray",
        stroke = T,
        fillOpacity = 1.0
      ) 
}

# Error in validateCoords(lng, lat, funcName) : 
#  addCircleMarkers requires non-NULL longitude/latitude values

Solution

  • First, the issue with your code is that you have use df[j, ] instead of df[j] to get the first row of your data. But IMHO you could achieve your result much easier by using a list and to use lapply to read your data and to create your maps. Not sure what's the next step in your workflow. As you say that you want to add the maps to a HTML document per site you could for example loop over the list of maps to add them to create your documents.

    library(leaflet)
    library(dataRetrieval)
    
    siteNumbers <- c(372322081241501, 380252079472801, 372223080234801)
    names(siteNumbers) <- paste0("site", seq_along(siteNumbers))
    sites <- lapply(siteNumbers, readNWISsite)
    
    lapply(sites, function(x) {
      leaflet() %>%
        addCircleMarkers(
          data = x,
          lng = ~dec_long_va,
          lat = ~dec_lat_va,
          radius = 9.0,
          color = "black",
          weight = 1,
          fillColor = "gray",
          stroke = T,
          fillOpacity = 1.0
        ) 
    })
    #> $site1
    #> 
    #> $site2
    #> 
    #> $site3
    

    But just in case. If you want to stick with the for loop you could achieve the same result like so:

    library(leaflet)
    library(dataRetrieval)
    
    # Create dataframe with 3 example sites
    site1 <- readNWISsite(372322081241501)
    site2 <- readNWISsite(380252079472801)
    site3 <- readNWISsite(372223080234801)
    
    df <- rbind(site1, site2)
    df <- rbind(df, site3)
    
    map <- leaflet()
    maps <- list()
    for (j in seq(nrow(df))) {
      maps[[j]] <- map %>%
        addCircleMarkers(
          lng = df[j, "dec_long_va"],
          lat = df[j, "dec_lat_va"],
          radius = 9.0,
          color = "black",
          weight = 1,
          fillColor = "gray",
          stroke = T,
          fillOpacity = 1.0
        ) 
    }
    maps
    #> [[1]]
    #> 
    #> [[2]]
    #> 
    #> [[3]]