MODIS and Landsat primer for Hannah

This is a quick primer on how to handle MODIS and Landsat NDVI data from Google Earth Engine (GEE) in R. It’s primarily for my Honours student, Hannah Simon, but I thought why not make it public for all to share? I first posted the MODIS section and had Hannah play with the Landsat data as a learning exercise, so full credit to her for the Landsat code.

For a general introduction for many of the functions used and handling spatial data in general see A Primer for Handling Spatial Data in R posted previously.

We start by getting the necessary libraries:

#Get libraries
library(raster) #to handle rasters
## Loading required package: sp
library(rgdal) #to handle shapefiles
## rgdal: version: 1.3-4, (SVN revision 766)
##  Geospatial Data Abstraction Library extensions to R successfully loaded
##  Loaded GDAL runtime: GDAL 2.1.3, released 2017/20/01
##  Path to GDAL shared files: /Library/Frameworks/R.framework/Versions/3.5/Resources/library/rgdal/gdal
##  GDAL binary built with GEOS: FALSE 
##  Loaded PROJ.4 runtime: Rel. 4.9.3, 15 August 2016, [PJ_VERSION: 493]
##  Path to PROJ.4 shared files: /Library/Frameworks/R.framework/Versions/3.5/Resources/library/rgdal/proj
##  Linking to sp version: 1.3-1
library(reshape) #for data wrangling
library(ggplot2) #for pretty plotting
library(animation) #for silly animations

datwd <- "/Users/jasper/Documents/GIS/CFR/MODISforHannah/"

Now let’s start with…

MODIS

#Get data
mNDVI <- stack(paste0(datwd, "2017043_MODIS_v1g_JNP_MOD_NDVI.tif")) #Note that the file has multiple bands so stack() is the easiest way to read all bands

mNDVI #See what the data look like
## class       : RasterStack 
## dimensions  : 15, 20, 300, 393  (nrow, ncol, ncell, nlayers)
## resolution  : 250, 250  (x, y)
## extent      : 361250, 366250, 6242250, 6246000  (xmin, xmax, ymin, ymax)
## coord. ref. : +proj=utm +zone=34 +south +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
## names       : X2017043_//MOD_NDVI.1, X2017043_//MOD_NDVI.2, X2017043_//MOD_NDVI.3, X2017043_//MOD_NDVI.4, X2017043_//MOD_NDVI.5, X2017043_//MOD_NDVI.6, X2017043_//MOD_NDVI.7, X2017043_//MOD_NDVI.8, X2017043_//MOD_NDVI.9, X2017043_//OD_NDVI.10, X2017043_//OD_NDVI.11, X2017043_//OD_NDVI.12, X2017043_//OD_NDVI.13, X2017043_//OD_NDVI.14, X2017043_//OD_NDVI.15, ... 
## min values  :                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768, ... 
## max values  :                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767, ...

So we’ve pulled in the full record of NDVI from the “MODIS/MOD13Q1 16-day composite vegetation indices” product from the dawn of time (i.e. the 18th February 2000) until as recent as possible (6th March 2017), but you’ll notice the names in the raster, e.g. X2017043_MODIS_v1g_JNP_MOD_NDVI.1 aren’t very informative, simply numbering the layers from 1 to 393, but not giving us the dates… For some reason I still haven’t worked out how to embed the dates in the .tif raster exported from GEE, so I save them manually from the console into a text file (stone age, I know, sorry…).

metdat <- read.delim(paste0(datwd, "MODIS_metadata.txt"), sep = " ", header = F)

head(metdat)
##   V1    V2                                   V3 V4    V5
## 1 0: Image MODIS/MOD13Q1/MOD13Q1_005_2000_02_18 (1 band)
## 2 1: Image MODIS/MOD13Q1/MOD13Q1_005_2000_03_05 (1 band)
## 3 2: Image MODIS/MOD13Q1/MOD13Q1_005_2000_03_21 (1 band)
## 4 3: Image MODIS/MOD13Q1/MOD13Q1_005_2000_04_06 (1 band)
## 5 4: Image MODIS/MOD13Q1/MOD13Q1_005_2000_04_22 (1 band)
## 6 5: Image MODIS/MOD13Q1/MOD13Q1_005_2000_05_08 (1 band)

Still not pretty, but getting closer…

date <- substr(metdat[,3], start = 27, stop = 37)
date[1:5]
## [1] "2000_02_18" "2000_03_05" "2000_03_21" "2000_04_06" "2000_04_22"

With a bit of squint-counting and trial and error we manage to use substr() to extract the dates!

But for R to treat them as dates we need to tell it they are dates with as.Date(), and specify the date format (see ?strptime).

date <- as.Date(date, format = "%Y_%m_%d")
date[1:5]
## [1] "2000-02-18" "2000-03-05" "2000-03-21" "2000-04-06" "2000-04-22"

Et voilà!

So we now have a stack of MODIS NDVI images through time, and we know their dates. We can embed the dates into the raster like so:

names(mNDVI) <- date

mNDVI #Have another look at the data
## class       : RasterStack 
## dimensions  : 15, 20, 300, 393  (nrow, ncol, ncell, nlayers)
## resolution  : 250, 250  (x, y)
## extent      : 361250, 366250, 6242250, 6246000  (xmin, xmax, ymin, ymax)
## coord. ref. : +proj=utm +zone=34 +south +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
## names       : X2000.02.18, X2000.03.05, X2000.03.21, X2000.04.06, X2000.04.22, X2000.05.08, X2000.05.24, X2000.06.09, X2000.06.25, X2000.07.11, X2000.07.27, X2000.08.12, X2000.08.28, X2000.09.13, X2000.09.29, ... 
## min values  :      -32768,      -32768,      -32768,      -32768,      -32768,      -32768,      -32768,      -32768,      -32768,      -32768,      -32768,      -32768,      -32768,      -32768,      -32768, ... 
## max values  :       32767,       32767,       32767,       32767,       32767,       32767,       32767,       32767,       32767,       32767,       32767,       32767,       32767,       32767,       32767, ...

Great! But now you’ll notice that the min and max values are rather strange (-32767 to 32767) when we know that NDVI can only range from -1 to 1, and rarely goes below 0. Very strange! Let’s have a look at our first image.

plot(mNDVI[[1]])

0 to 4000? Even stranger!

It turns out the issue is that GEE has exported the data in what appears to be a strange format to reduce the file size. We need to tell R how to interpret it by setting a gain and offset.

NAvalue(mNDVI)=-32767
offs(mNDVI)=0
gain(mNDVI)=.0001
plot(mNDVI[[1]])

Much more reasonable!

Ok, so now we can begin to look at our questions; namely, how does MODIS NDVI vary in space and time?

But first, we should set our focal area.

site <- readOGR(dsn=paste0(datwd, "Jonaskop 120m.kml"), layer="Jonaskop 120m.kml") # A Google Earth KML file
## OGR data source with driver: KML 
## Source: "/Users/jasper/Documents/GIS/CFR/MODISforHannah/Jonaskop 120m.kml", layer: "Jonaskop 120m.kml"
## with 1 features
## It has 2 fields
## Warning in readOGR(dsn = paste0(datwd, "Jonaskop 120m.kml"), layer =
## "Jonaskop 120m.kml"): Z-dimension discarded
site <- spTransform(site, CRS(proj4string(mNDVI))) #Reproject to match the projection of the MODIS data (UTM 34S in this case)

plot(mNDVI[[1]])
plot(site, add=T)

It looks like we’re wrangling a lot more MODIS data than we need to. Let’s crop to out focal area…

mNDVI <- crop(mNDVI, site, snap = "out") #"snap="out"" adds a buffer around the site

plot(mNDVI[[1]])
plot(site, add=T)

…and look at a few images through time.

plot(mNDVI) #Note that R limits it to the first 16 images

Lots of variation in space! What about time?

if(!file.exists("/Users/jasper/GIT/jslingsby.github.io_local/static/img/output/mNDVI.gif")) { # Check if the file exists
  saveGIF({
    for (i in 1:dim(mNDVI)[3]) plot(mNDVI[[i]],
                      main=names(mNDVI)[i],
                      legend.lab="NDVI",
                      col=rev(terrain.colors(length(c(seq(0,.7,.05),1)))),
                      breaks=seq(0,.7,.05))}, 
    movie.name = "/Users/jasper/GIT/jslingsby.github.io_local/static/img/output/mNDVI.gif", 
    ani.width = 600, ani.height = 500, 
    interval=.25)
}

*NOTE: this writes a .gif to file and does not render in R (or R notebook), so I had to insert it in the Markdown script.

You could make it a lot prettier, but still pretty fancy. Not very useful for the print edition though. How about just a simple line graph? this requires extracting the data to a data.frame.

dat <- as.data.frame(mNDVI)

dat[1:10,1:10] #look at the data
##    X2000.02.18 X2000.03.05 X2000.03.21 X2000.04.06 X2000.04.22 X2000.05.08
## 1       0.2889      0.3280      0.3723      0.3743      0.3979      0.3902
## 2       0.3127      0.3745      0.3850      0.3872      0.3680      0.4681
## 3       0.3205      0.3508      0.3909      0.3878      0.3647      0.4488
## 4       0.3256      0.3323      0.3823      0.4001      0.3542      0.4050
## 5       0.3206      0.3841      0.3934      0.3817      0.3864      0.3987
## 6       0.2866      0.3567      0.3723      0.3757      0.3799      0.4278
## 7       0.2866      0.3567      0.3832      0.3808      0.3510      0.4681
## 8       0.3340      0.3709      0.4215      0.3787      0.4191      0.4681
## 9       0.3205      0.4144      0.4215      0.4099      0.3900      0.4615
## 10      0.3256      0.4278      0.4363      0.4085      0.4115      0.4615
##    X2000.05.24 X2000.06.09 X2000.06.25 X2000.07.11
## 1       0.4644      0.4777      0.4730      0.4220
## 2       0.4704      0.4777      0.4730      0.4289
## 3       0.4698      0.4574      0.4340      0.4532
## 4       0.4596      0.4574      0.4636      0.4228
## 5       0.4522      0.4193      0.4636      0.4517
## 6       0.4493      0.4538      0.4091      0.4160
## 7       0.4043      0.4913      0.4730      0.4359
## 8       0.4885      0.4913      0.4616      0.4300
## 9       0.4318      0.4589      0.4908      0.4363
## 10      0.4212      0.4413      0.4697      0.4489

So each date is a column and each cell in space is a row in our data frame. This isn’t ideal for plotting so let’s reshape the data into long format.

rownames(dat) <- paste0("A",1:20) #Add dummy column labels

dat <- as.data.frame(t(dat)) #transpose data.frame

dat$Date <- as.Date(rownames(dat), format = "X%Y.%m.%d")

dat <- melt(dat, id=c("Date"))

head(dat)
##         Date variable  value
## 1 2000-02-18       A1 0.2889
## 2 2000-03-05       A1 0.3280
## 3 2000-03-21       A1 0.3723
## 4 2000-04-06       A1 0.3743
## 5 2000-04-22       A1 0.3979
## 6 2000-05-08       A1 0.3902

Now let’s make some pretty pictures!

ggplot(data = dat, aes(x = Date, y = value, colour = variable)) + geom_line()

Pretty neat! It looks like our site burnt in the summer of 2000/2001. Now let’s split them up.

ggplot(data = dat, aes(x = Date, y = value, colour = variable)) + geom_line() + facet_wrap(~variable)

Well there’s definitely clear seasonality and, while subtle, there are differences among the cells. What do we see with…

Landsat

First we read in the full record of data for Landsat 8 for our area of interest.

lsNDVI <- stack(paste0(datwd,"Landsat/2017045_v1_LC8_L1T_TOA_Jonaskop_daily__2013-04-15-2017-03-25.tif")) #note that I have used LC8 (LandSat 8)
lsNDVI
## class       : RasterStack 
## dimensions  : 114, 157, 17898, 154  (nrow, ncol, ncell, nlayers)
## resolution  : 30, 30  (x, y)
## extent      : 361320, 366030, 6242370, 6245790  (xmin, xmax, ymin, ymax)
## coord. ref. : +proj=utm +zone=34 +south +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
## names       : X2017045_//17.03.25.1, X2017045_//17.03.25.2, X2017045_//17.03.25.3, X2017045_//17.03.25.4, X2017045_//17.03.25.5, X2017045_//17.03.25.6, X2017045_//17.03.25.7, X2017045_//17.03.25.8, X2017045_//17.03.25.9, X2017045_//7.03.25.10, X2017045_//7.03.25.11, X2017045_//7.03.25.12, X2017045_//7.03.25.13, X2017045_//7.03.25.14, X2017045_//7.03.25.15, ... 
## min values  :                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768,                -32768, ... 
## max values  :                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767,                 32767, ...

Then the metadata with dates.

metdat2 <- read.delim(paste0(datwd,"Landsat/LC8_L1T_TOA_metadata.txt"), sep = " ", header = F) 
head(metdat2) #first few rows of metdat
##   V1         V2
## 1 0: 2013-04-15
## 2 1: 2013-04-22
## 3 2: 2013-05-17
## 4 3: 2013-05-24
## 5 4: 2013-06-09
## 6 5: 2013-06-18
tail(metdat2) #last few rows of metdat (our data goes up to 25th march, 2017)
##       V1         V2
## 149 148: 2017-02-12
## 150 149: 2017-02-21
## 151 150: 2017-02-28
## 152 151: 2017-03-09
## 153 152: 2017-03-16
## 154 153: 2017-03-25

Extract the dates, embed into the raster and have a look at the first scene.

date2 <- substr(metdat2[,2], start = 0, stop = 10) #will extract YYYY_MM_DD
names(lsNDVI) <- date2 #assign the date to the 'names' of each NDVI value
plot(lsNDVI[[1]]) 

Looks good, but the scale’s all wrong again, so we need to fix the gain and offset

NAvalue(lsNDVI)=0
offs(lsNDVI)=-1.5
gain(lsNDVI)=.001
plot(lsNDVI[[1]])
plot(site, add=T)

Better! But still suspiscious… Note that I hacked the offset at -1.5 (it’s usually 2) I’ll need to recheck what the gain and offset values should be and fix this later…

Now let’s zoom in on our focal site

lsNDVI <- crop(lsNDVI, site, snap = "out") #"snap="out"" adds a buffer around the site

plot(lsNDVI[[1]])
plot(site, add=T)

Now let’s have a look at the variation in both space and time.

plot(lsNDVI)

Lots of errors because of NA’s! It looks like some scenes are empty, while others are dominated by cloud (we applied a cloud masking algorithm in GEE). We’ll need to get rid of these. Note that this isn’t a problem with the MODIS data, because the MODIS data are an 8-day composite based on 2 scenes per day (i.e the choice of 16 scenes to make up one complete one). We can easily exclude the blank Landsat scenes by identifying those with NA values only (if the minimum or maximum value is NA, then there are no values other than NA).

metdat2 <- metdat2[-which(is.na(minValue(lsNDVI))),] #Drop empty scenes from metadata
lsNDVI <- lsNDVI[[-which(is.na(minValue(lsNDVI)))]] #Drop empty scenes from rasterStack

Now we can look at variation through time using an animation as we did before. Note that in the previous plots the colour range varies. For the animation we need to set a fixed colour range so that a particular shade of green or yellow represents the same value across scenes. For the Landsat data these range from 0.255 to 0.555

if(!file.exists("/Users/jasper/GIT/jslingsby.github.io_local/static/img/output/lsNDVI.gif")) { # Check if the file exists
  saveGIF({
    for (i in 1:dim(lsNDVI)[3]) plot(lsNDVI[[i]],
                      main=names(lsNDVI)[i],
                      legend.lab="NDVI",
                      col=rev(terrain.colors(length(c(seq(min(minValue(lsNDVI)),max(maxValue(lsNDVI)),.01),1)))),
                      breaks=seq(min(minValue(lsNDVI)),max(maxValue(lsNDVI)),.01))}, 
    movie.name = "/Users/jasper/GIT/jslingsby.github.io_local/static/img/output/lsNDVI.gif", 
    ani.width = 600, ani.height = 500, 
    interval=.25)
}

Looks like there’s generally lower NDVI in the lower righthand corner parhaps?

Let’s look at some time-series graphs. First the data wrangling, then plotting; following just about the same formula as for the MODIS data.

ldat <- as.data.frame(lsNDVI)

#ldat[1:10,1:10] #look at the data

rownames(ldat) <- paste0(1:nrow(ldat)) #dummy column labels

ldat <- as.data.frame(t(ldat)) #transpose data.frame

ldat$Date <- as.Date(rownames(ldat), format = "X%Y.%m.%d")

ldat <- melt(ldat, id=c("Date"))

colnames(ldat)<- c("Date","Pixel.Number","NDVI.Value")

#head(ldat)

NDVIplot2 <- ggplot(data = ldat, aes(x = Date, y = NDVI.Value, colour = Pixel.Number)) + geom_line() + theme(legend.position="none") #remove legend as plot space cannot accommodate it - there are >800 pixels!

NDVIplot2 

So we still pick up some nice seasonality! Note this is “zoom in” on just the last ~4 years. It is a bit messy with all >800 pixels though, so let’s select just a few.

Converting a rasterStack into a data.frame fills the dataframe with raster cell values by row from left to right and numbers them. We know there are 28 rows and 31 columns (= 868 pixels), so we can use this to sample cells of interest.

This is how the grid is numbered:

matrix(1:868, 28, 31, byrow = T)
##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
##  [1,]    1    2    3    4    5    6    7    8    9    10    11    12    13
##  [2,]   32   33   34   35   36   37   38   39   40    41    42    43    44
##  [3,]   63   64   65   66   67   68   69   70   71    72    73    74    75
##  [4,]   94   95   96   97   98   99  100  101  102   103   104   105   106
##  [5,]  125  126  127  128  129  130  131  132  133   134   135   136   137
##  [6,]  156  157  158  159  160  161  162  163  164   165   166   167   168
##  [7,]  187  188  189  190  191  192  193  194  195   196   197   198   199
##  [8,]  218  219  220  221  222  223  224  225  226   227   228   229   230
##  [9,]  249  250  251  252  253  254  255  256  257   258   259   260   261
## [10,]  280  281  282  283  284  285  286  287  288   289   290   291   292
## [11,]  311  312  313  314  315  316  317  318  319   320   321   322   323
## [12,]  342  343  344  345  346  347  348  349  350   351   352   353   354
## [13,]  373  374  375  376  377  378  379  380  381   382   383   384   385
## [14,]  404  405  406  407  408  409  410  411  412   413   414   415   416
## [15,]  435  436  437  438  439  440  441  442  443   444   445   446   447
## [16,]  466  467  468  469  470  471  472  473  474   475   476   477   478
## [17,]  497  498  499  500  501  502  503  504  505   506   507   508   509
## [18,]  528  529  530  531  532  533  534  535  536   537   538   539   540
## [19,]  559  560  561  562  563  564  565  566  567   568   569   570   571
## [20,]  590  591  592  593  594  595  596  597  598   599   600   601   602
## [21,]  621  622  623  624  625  626  627  628  629   630   631   632   633
## [22,]  652  653  654  655  656  657  658  659  660   661   662   663   664
## [23,]  683  684  685  686  687  688  689  690  691   692   693   694   695
## [24,]  714  715  716  717  718  719  720  721  722   723   724   725   726
## [25,]  745  746  747  748  749  750  751  752  753   754   755   756   757
## [26,]  776  777  778  779  780  781  782  783  784   785   786   787   788
## [27,]  807  808  809  810  811  812  813  814  815   816   817   818   819
## [28,]  838  839  840  841  842  843  844  845  846   847   848   849   850
##       [,14] [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24]
##  [1,]    14    15    16    17    18    19    20    21    22    23    24
##  [2,]    45    46    47    48    49    50    51    52    53    54    55
##  [3,]    76    77    78    79    80    81    82    83    84    85    86
##  [4,]   107   108   109   110   111   112   113   114   115   116   117
##  [5,]   138   139   140   141   142   143   144   145   146   147   148
##  [6,]   169   170   171   172   173   174   175   176   177   178   179
##  [7,]   200   201   202   203   204   205   206   207   208   209   210
##  [8,]   231   232   233   234   235   236   237   238   239   240   241
##  [9,]   262   263   264   265   266   267   268   269   270   271   272
## [10,]   293   294   295   296   297   298   299   300   301   302   303
## [11,]   324   325   326   327   328   329   330   331   332   333   334
## [12,]   355   356   357   358   359   360   361   362   363   364   365
## [13,]   386   387   388   389   390   391   392   393   394   395   396
## [14,]   417   418   419   420   421   422   423   424   425   426   427
## [15,]   448   449   450   451   452   453   454   455   456   457   458
## [16,]   479   480   481   482   483   484   485   486   487   488   489
## [17,]   510   511   512   513   514   515   516   517   518   519   520
## [18,]   541   542   543   544   545   546   547   548   549   550   551
## [19,]   572   573   574   575   576   577   578   579   580   581   582
## [20,]   603   604   605   606   607   608   609   610   611   612   613
## [21,]   634   635   636   637   638   639   640   641   642   643   644
## [22,]   665   666   667   668   669   670   671   672   673   674   675
## [23,]   696   697   698   699   700   701   702   703   704   705   706
## [24,]   727   728   729   730   731   732   733   734   735   736   737
## [25,]   758   759   760   761   762   763   764   765   766   767   768
## [26,]   789   790   791   792   793   794   795   796   797   798   799
## [27,]   820   821   822   823   824   825   826   827   828   829   830
## [28,]   851   852   853   854   855   856   857   858   859   860   861
##       [,25] [,26] [,27] [,28] [,29] [,30] [,31]
##  [1,]    25    26    27    28    29    30    31
##  [2,]    56    57    58    59    60    61    62
##  [3,]    87    88    89    90    91    92    93
##  [4,]   118   119   120   121   122   123   124
##  [5,]   149   150   151   152   153   154   155
##  [6,]   180   181   182   183   184   185   186
##  [7,]   211   212   213   214   215   216   217
##  [8,]   242   243   244   245   246   247   248
##  [9,]   273   274   275   276   277   278   279
## [10,]   304   305   306   307   308   309   310
## [11,]   335   336   337   338   339   340   341
## [12,]   366   367   368   369   370   371   372
## [13,]   397   398   399   400   401   402   403
## [14,]   428   429   430   431   432   433   434
## [15,]   459   460   461   462   463   464   465
## [16,]   490   491   492   493   494   495   496
## [17,]   521   522   523   524   525   526   527
## [18,]   552   553   554   555   556   557   558
## [19,]   583   584   585   586   587   588   589
## [20,]   614   615   616   617   618   619   620
## [21,]   645   646   647   648   649   650   651
## [22,]   676   677   678   679   680   681   682
## [23,]   707   708   709   710   711   712   713
## [24,]   738   739   740   741   742   743   744
## [25,]   769   770   771   772   773   774   775
## [26,]   800   801   802   803   804   805   806
## [27,]   831   832   833   834   835   836   837
## [28,]   862   863   864   865   866   867   868

So let’s take a few from the bottom right (where the NDVI is generally low):

lowc <- c(614, 617, 707, 710)
highc <- c(128, 131, 221, 224)

lowdat <- subset(ldat, Pixel.Number%in%lowc)
highdat <- subset(ldat, Pixel.Number%in%highc)

NDVIplot_low <- ggplot(data = lowdat, aes(x = Date, y = NDVI.Value, colour = Pixel.Number)) + geom_line() #+ theme(legend.position="none") #remove legend as plot space cannot accommodate it - there are >800 pixels!

NDVIplot_low 

And a few from the top left (where the NDVI is generally high):

NDVIplot_high <- ggplot(data = highdat, aes(x = Date, y = NDVI.Value, colour = Pixel.Number)) + geom_line() #+ theme(legend.position="none") #remove legend as plot space cannot accommodate it - there are >800 pixels!

NDVIplot_high

Jasper Slingsby Written by:

An ecologist working on global change in terrestrial ecosystems.

comments powered by Disqus