This vignette shows how the neonhs package can be combined with the neonUtilities and geoNEON packages to programmatically access woody vegetation data from NEON ground plots, and extract spectra from hyperspectral imagery.

First, we need to download vegetation data (data acquisition steps below are adapted from the documentation in the neonUtilities and geoNEON packages):

zipsByProduct(dpID = "DP1.10098.001", 
              site = "TALL", 
              savepath = ".", 
              check.size = FALSE)
stackByTable("filesToStack10098", folder = TRUE)

Then, we can read the mapping data and apparent individual data. We will restrict our focus to one year’s worth of data, to simplify the process of matching ground plot data in one year to the same year’s imagery.

Merge mapping and individual data, and filter to live plants with coordinates:

veg <- right_join(vegind, vegmap, 
             by = c("individualID", "namedLocation",
                    "domainID", "siteID", "plotID")) %>%
  filter(!is.na(adjEasting), !is.na(adjNorthing), plantStatus == "Live")

Using this spatial data, we can acquire tiles of hyperspectral data that intersect the plants that were mapped on the ground.

Now, create a SpatialPointsDataFrame of these points.

spdf <- SpatialPointsDataFrame(veg[, c('adjEasting', 'adjNorthing')], 
                               data = veg, 
                               proj4string = CRS(hs_proj4string(hs_paths[1])))

Now, let’s visualize the extents of these hyperspectral images and the locations of the mapped plants:

extents <- lapply(hs_paths, hs_extent)

plot(do.call(raster::merge, extents), bty = 'n', 
     xlab = 'Easting', ylab = 'Northing')
plot(spdf, add = TRUE)
for (e in extents) {
  plot(e, add = TRUE)
}

Each mapped plant location is covered by an extent object for a hyperspectral image. We can use the neonhs package to extract hyperspectral data at these locations.

out <- list()
for (i in seq_along(hs_paths)) {
  res <- hs_extract_pts(hs_paths[i], pts = spdf, bands = 1:426)
  first_band <- grep('^band1', names(res), value = TRUE)[1]
  na_vals <- is.na(res[[first_band]])
  out[[i]] <- res[!na_vals, ]
}

Now let’s create a tibble for easy plotting:

hs_df <- lapply(out, as.data.frame) %>% 
  bind_rows %>% 
  as_tibble %>%
  select(uid.x, adjEasting, adjNorthing, plantStatus, scientificName, 
         starts_with('band')) %>%
  filter(plantStatus == 'Live') %>%
  distinct
hs_df
#> # A tibble: 20 x 431
#>    uid.x adjEasting adjNorthing plantStatus scientificName band1_384nm
#>    <chr>      <dbl>       <dbl> <chr>       <chr>                <dbl>
#>  1 f1a5…    462671.    3645859. Live        Pinus palustr…      0.0427
#>  2 b3d2…    462671.    3645859. Live        Pinus palustr…      0.0427
#>  3 d286…    462758.    3645667. Live        Cornus florid…      0.0317
#>  4 1193…    462758.    3645667. Live        Cornus florid…      0.0317
#>  5 7b27…    462831.    3646134. Live        Pinus echinat…      0.054 
#>  6 6836…    462831.    3646134. Live        Pinus echinat…      0.054 
#>  7 1dd0…    462865.    3646029. Live        Pinus palustr…      0.0574
#>  8 3f01…    462865.    3646029. Live        Pinus palustr…      0.0574
#>  9 f186…    462865.    3646029. Live        Pinus palustr…      0.0574
#> 10 c011…    462719.    3646114. Live        Quercus velut…      0.0565
#> 11 b082…    462719.    3646114. Live        Quercus velut…      0.0565
#> 12 3b66…    462733.    3646108. Live        Cornus florid…      0.0421
#> 13 2afd…    462733.    3646108. Live        Cornus florid…      0.0421
#> 14 559e…    463029.    3645994. Live        Cornus florid…      0.0606
#> 15 aa1b…    463033.    3646187. Live        Cornus florid…      0.046 
#> 16 4c43…    463033.    3646187. Live        Cornus florid…      0.046 
#> 17 1e30…    463033.    3646187. Live        Cornus florid…      0.046 
#> 18 fe7d…    463033.    3646187. Live        Cornus florid…      0.046 
#> 19 2101…    463037.    3646191. Live        Pinus echinat…      0.0747
#> 20 7ed2…    463037.    3646191. Live        Pinus echinat…      0.0747
#> # … with 425 more variables: band2_389nm <dbl>, band3_394nm <dbl>,
#> #   band4_399nm <dbl>, band5_404nm <dbl>, band6_409nm <dbl>,
#> #   band7_414nm <dbl>, band8_419nm <dbl>, band9_424nm <dbl>,
#> #   band10_429nm <dbl>, band11_434nm <dbl>, band12_439nm <dbl>,
#> #   band13_444nm <dbl>, band14_449nm <dbl>, band15_454nm <dbl>,
#> #   band16_459nm <dbl>, band17_464nm <dbl>, band18_469nm <dbl>,
#> #   band19_474nm <dbl>, band20_479nm <dbl>, band21_484nm <dbl>,
#> #   band22_489nm <dbl>, band23_494nm <dbl>, band24_499nm <dbl>,
#> #   band25_504nm <dbl>, band26_509nm <dbl>, band27_514nm <dbl>,
#> #   band28_519nm <dbl>, band29_524nm <dbl>, band30_529nm <dbl>,
#> #   band31_534nm <dbl>, band32_539nm <dbl>, band33_544nm <dbl>,
#> #   band34_549nm <dbl>, band35_554nm <dbl>, band36_559nm <dbl>,
#> #   band37_564nm <dbl>, band38_569nm <dbl>, band39_574nm <dbl>,
#> #   band40_579nm <dbl>, band41_584nm <dbl>, band42_589nm <dbl>,
#> #   band43_594nm <dbl>, band44_599nm <dbl>, band45_604nm <dbl>,
#> #   band46_609nm <dbl>, band47_614nm <dbl>, band48_619nm <dbl>,
#> #   band49_624nm <dbl>, band50_629nm <dbl>, band51_634nm <dbl>,
#> #   band52_639nm <dbl>, band53_644nm <dbl>, band54_649nm <dbl>,
#> #   band55_654nm <dbl>, band56_659nm <dbl>, band57_664nm <dbl>,
#> #   band58_669nm <dbl>, band59_674nm <dbl>, band60_679nm <dbl>,
#> #   band61_684nm <dbl>, band62_689nm <dbl>, band63_694nm <dbl>,
#> #   band64_699nm <dbl>, band65_704nm <dbl>, band66_709nm <dbl>,
#> #   band67_714nm <dbl>, band68_719nm <dbl>, band69_724nm <dbl>,
#> #   band70_729nm <dbl>, band71_734nm <dbl>, band72_739nm <dbl>,
#> #   band73_744nm <dbl>, band74_749nm <dbl>, band75_754nm <dbl>,
#> #   band76_759nm <dbl>, band77_764nm <dbl>, band78_769nm <dbl>,
#> #   band79_774nm <dbl>, band80_779nm <dbl>, band81_784nm <dbl>,
#> #   band82_789nm <dbl>, band83_794nm <dbl>, band84_799nm <dbl>,
#> #   band85_804nm <dbl>, band86_809nm <dbl>, band87_814nm <dbl>,
#> #   band88_819nm <dbl>, band89_824nm <dbl>, band90_829nm <dbl>,
#> #   band91_834nm <dbl>, band92_839nm <dbl>, band93_844nm <dbl>,
#> #   band94_849nm <dbl>, band95_854nm <dbl>, band96_859nm <dbl>,
#> #   band97_864nm <dbl>, band98_869nm <dbl>, band99_874nm <dbl>,
#> #   band100_879nm <dbl>, band101_884nm <dbl>, …

And gather the hyperspectral columns (converting the data from wide to long form).

Finally, we can plot the signatures for each species: