{"id":18019216,"url":"https://github.com/jonathom/detect_deforestation","last_synced_at":"2025-04-04T16:28:28.879Z","repository":{"id":53556673,"uuid":"338410381","full_name":"jonathom/detect_deforestation","owner":"jonathom","description":"AOSD Final Project of WS20/21","archived":false,"fork":false,"pushed_at":"2021-03-24T18:53:23.000Z","size":59930,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-10T02:13:11.276Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jonathom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-12T19:15:02.000Z","updated_at":"2024-03-27T17:48:55.000Z","dependencies_parsed_at":"2022-09-21T10:21:56.390Z","dependency_job_id":null,"html_url":"https://github.com/jonathom/detect_deforestation","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathom%2Fdetect_deforestation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathom%2Fdetect_deforestation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathom%2Fdetect_deforestation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathom%2Fdetect_deforestation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonathom","download_url":"https://codeload.github.com/jonathom/detect_deforestation/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247208661,"owners_count":20901604,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-30T05:09:31.364Z","updated_at":"2025-04-04T16:28:28.856Z","avatar_url":"https://github.com/jonathom.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"Near Real-Time Monitoring of Deforestation using Optical Remote Sensing Data\n=============\nBrian Pondi \u0026 Jonathan Bahlmann\n\nPrerequisites\n=============\n\nBecause most computations are taking some time, a lot of them have been\nprecomputed and uploaded to this repository. Input imagery however is\nnot available via this repository due to size. To build the markdown,\nclone this repository locally and download the Landsat time series data\n[from sciebo](https://uni-muenster.sciebo.de/s/d9BKPd1sVtFqvW4) and\nextracted into the repository folder. Alongside with the toplevel files\nlike `main.Rmd`, that folder should then contain `landsat_monthly` and\n`landsat_quarterly`. If there is a problem with the input data, please\ncontact us.\n\nIntroduction\n============\n\nForests are known to be crucial part of the ecosystem as they purify\nwater and air. They are key in mitigating climate changes as they act as\na carbon sink and apart from that there are varieties of land-based\nspecies that live in the forest (Pacheco et al., 2021). Forests in the\ntropical are under threat due to deforestation. Deforestation in this\ncontext refers to (UNFCCC 2001) definition which is the direct\nhuman-induced conversion of forested land to non-forested land.\n\nIn this research we focused on the Amazonia of Brazil because\ndeforestation that occurs in that region leads to loss of environmental\nservices that, while affecting Brazil the most, affect the whole world\n(Fearnside, 1997a, 2008a). Environmental services of Amazonian forest\nhere include its roles in storing carbon, which avoids global warming\n(Fearnside, 2000, 2016a; Nogueira et al., 2015), recycling of water in\nalso non-Amazonian areas (Arraut et al., 2012), and in maintenance of\nbiodiversity (Fearnside, 1999).\n\nCarrying out near real-time monitoring of deforestation can help to curb\ndeforestation. Satellite sensors are greatly capable for this task\nbecause they provide repeatable measurements that are consistent in both\nspatial and temporal scale. This capability enables capturing of many\nprocesses that can cause change, including natural cases like fires and\nanthropogenic disturbances such as deforestation (Jin and Sader, 2005).\n\nOur research focused on utilizing Optical Multi-spectral Remote Sensing\nImagery to carry out near real-time monitoring of deforestation. The\nmain challenge of optical satellite data specifically on the tropics is\nthat they cannot penetrate cloud cover. We therefore explored new\ntechniques such as a the Gapfill algorithm to predict missing values in\noptical imagery time series data, i.e. to fill the cloud gaps. We then\nused `bfastmonitor` of the bfast algorithm family to detect disturbances\nin the gapfilled optical time series.\n\nOf special interest was the presence of the mentioned cloud gaps, and\nhow they affect deforestation detection, i.e. we asked the question:\nDoes `bfastmonitor` perform better with stronger aggregated data? To\nanswer this question, we compared the same procedure for monthly and for\nquarterly aggregated data sets.\n\nWe validated our results by using\n[INPE](http://terrabrasilis.dpi.inpe.br/en/home-page/)\n[PRODES](http://terrabrasilis.dpi.inpe.br/download/dataset/legal-amz-prodes/vector/yearly_deforestation.zip)\n(more conservative, so very low false positive rate) and\n[DETER](http://terrabrasilis.dpi.inpe.br/file-delivery/download/deter-amz/shape)\n(automatized system, some false positives expected) deforestation data\nas reference.\n\nMethods\n=======\n\nInput Data and Preparations\n---------------------------\n\nThe time series of Landsat 8 satellite images used for this research\nwere already provided in a state where cloud cover was already removed\nsufficiently. The data covers the period 01-01-2013 to 31-12-2019 in row\n001, paths 066 and 067.\n\nTo investigate the influence of different temporal aggregation\nintervals, the input data was aggregated to a) a monthly and b) a\nquarterly NDVI (Normalized Difference Vegetation Index) time series. For\nthis, the median was used. This resulted in a) 12 NDVI images per year\nand b) 4 NDVI images per year, respectively.\n\nAggregating the different temporal intervals as seen in the [course\nmaterial](https://github.com/edzer/astd/blob/master/st.Rmd), only that\nwe chose another area of interest. Because `Gapfill` is very intense on\ncomputation time, we had to settle for only 140x140 pixels. We then\nselected an area with a diverse range of deforestation dates to be able\nto do a differentiated evaluation.\n\n    v = cube_view(srs=\"EPSG:3857\", extent=list(left = -7338335, right = -7329987, top = -1018790, bottom = -1027138, t0 =\"2013-01-01\", t1 = \"2019-12-31\"), dx=60, dy=60, dt = \"P1M\", resampling = \"average\", aggregation = \"median\") # dt = \"P3M\"\n\n\n    # calculate NDVI and export as GeoTIFF files at subfolder \"L8cube_subregion\"\n    raster_cube(col, v, L8.clear_mask) %\u003e%\n     select_bands(c(\"B04\", \"B05\")) %\u003e%\n     apply_pixel(\"(B05-B04)/(B05+B04)\") %\u003e%\n     write_tif(\"smaller_monthly\",prefix = \"NDVI_\")\n\nThe aggregated imagery time series are loaded as `stars` objects from\ntheir directories. They are plotted to get an idea of what we are\ndealing with here. The monthly aggregation can be seen on the left, the\nquarterly aggregation on the right.\n\n``` r\nlibrary(stars)\nlibrary(gapfill)\nlibrary(bfast)\nlibrary(zoo)\nlibrary(raster)\nlibrary(viridis)\n```\n\n``` r\nsubdir = \"landsat_monthly\"\nf = paste0(subdir, \"/\", list.files(subdir))\nst = merge(read_stars(f)) # make stars object\nplot(st)\nsubdir = \"landsat_quarterly\"\nf = paste0(subdir, \"/\", list.files(subdir))\nst_q = merge(read_stars(f)) # make stars object\nplot(st_q)\n```\n\n\u003cimg src=\"main_files/figure-markdown_github/st-flag-1, load-data-1.png\" width=\"50%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/st-flag-1, load-data-2.png\" width=\"50%\" /\u003e\n\nThe reference PRODES and DETER data were then loaded and cropped.\n\n``` r\n# load PRODES data\nprod \u003c- read_sf(\"./yearly_deforestation/yearly_deforestation.shp\")\nprod_3857 \u003c- st_make_valid(st_transform(prod, crs = st_crs(st)))\nprod_crop \u003c- st_crop(prod_3857, st) # clip\nwrite_sf(prod_crop, \"./yearly_deforestation/PRODES_cropped.shp\", overwrite = TRUE)\n\ndeter \u003c- read_sf(\"./yearly_deforestation/deter_public.shp\")\ndeter_3857 \u003c- st_make_valid(st_transform(deter, crs = st_crs(st)))\ndeter_crop \u003c- st_crop(deter_3857, st)\nwrite_sf(deter_crop, \"./yearly_deforestation/DETER_cropped.shp\", overwrite = TRUE)\n```\n\nAn overview is given here, with the deforestation in our area of\ninterest colored by the year it occurred. There is no deforestation\nprior to 2016, which promises a stable history period for applying\n`bfastmonitor`. We also observe that in the less conservative DETER\ndata, more deforestation areas were detected.\n\n``` r\nprod \u003c- read_sf(\"./deforestation_shapes/PRODES_cropped.shp\")\ndete \u003c- read_sf(\"./deforestation_shapes/DETER_cropped.shp\")\n\ncols \u003c- viridis::magma(4)\ndete$VIEW_DATE \u003c- as.numeric(format(as.Date(dete$VIEW_DATE, format=\"%d/%m/%Y\"),\"%Y\")) # year as date\ndete \u003c- dete[dete$VIEW_DATE \u003c 2020,] # defo. after 2019 is not of interest here\n\nplot(prod[\"YEAR\"], pal = cols[2:4], main = \"PRODES Deforestation Data Colored by Year\")\nplot(dete[\"VIEW_DATE\"], pal = cols, main = \"DETER Deforestation Data Colored by Year\")\n```\n\n\u003cimg src=\"main_files/figure-markdown_github/plot-AOI-1.png\" width=\"50%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/plot-AOI-2.png\" width=\"50%\" /\u003e\n\nGapfill\n-------\n\nPrediction of missing values in satellite data are carried out using the\n`gapfill` package in R. The gapfill approach was designed to carry out\npredictions on satellite data that were recorded at equally spaced\npoints of time. Based on Gerber et. al 2016, they applied the algorithm\nto MODIS NDVI data with cloud cover scenarios of up to 50% missing data.\n\nGapfill was appealing to this research because it’s capable of handling\nlarge amounts of spatio-temporal data, it’s user friendly and tailored\nto specific features of satellite imagery. The predictions of the\nmissing values are based on a subset-predict procedure, i.e. each\nmissing value is predicted separately by (1) selecting subsets of the\ndata that are in a neighborhood around the missing point in space and\ntime and (2) predicting the missing value based on the subset (Gerber\net. al, 2016). If a selected subset doesn’t fullfil the requirements\n(enough non-empty images and non-missing values), the neighbourhood is\nsimply increased. If a suitable subset is found, a linear quantile\nregression is used to interpolate the missing value. The temporal\nneighbourhood is also used to adjust for seasonality (Gerber et. al,\n2016).\n\n### Prepare for Gapfill\n\n`Gapfill` documentation tells us that as input, a 4-dimensional numeric\narray is needed, with dimensions x, y, seasonal index (doy) and year.\nThese arrays are extracted as numeric vector from the input `stars` data\nand then put into an array of the requested dimensions. An x-y-axis flip\nis needed such that the function `Image`, that can render the\nmultidimensional arrays, displays the aoi in the correct orientation,\nsaving time and effort to convert the arrays back to `stars` objects.\n\n``` r\nprep_gapfill \u003c- function(st, doy, ts) {\n # st is stars object, doy is day of year vector, ts is number of timesteps per year\n \n # get pixels of whole dataset\n imgdata \u003c- c(st[,,,][[1]])\n\n # make labels\n xlab \u003c- seq(from = attr(st, \"dimensions\")[[1]]$offset, by = attr(st, \"dimensions\")[[1]]$delta, length.out = attr(st, \"dimensions\")[[1]]$to)\n ylab \u003c- seq(from = attr(st, \"dimensions\")[[2]]$offset, by = attr(st, \"dimensions\")[[2]]$delta, length.out = attr(st, \"dimensions\")[[2]]$to)\n years \u003c- seq(2013,2019,1)\n\n # make array, transpose\n h \u003c- array(imgdata, dim = c(140, 140, ts, 7), dimnames = list(xlab, ylab, doy, years))\n # x, y is switched between stars and these arrays\n h \u003c- aperm(h, c(2,1,3,4))\n return(h)\n}\n\ndoy_12 \u003c- c(1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)\ndoy_4 \u003c- c(1, 91, 182, 274)\n\nma_monthly \u003c- prep_gapfill(st, doy_12, 12)\nma_quarter \u003c- prep_gapfill(st_q, doy_4, 4)\n```\n\nIn this research we also explored to tailor gapfill by customizing the\n`iMax` parameter. It gives the maximum number of iterations of the\nsubset-predict procedure until `NA` is returned as predicted value\n(Gerber, 2016). As it is defaulting to `Inf`, `Gapfill` can take hours\nupon hours of computation. This is why we settled on using `iMax = 5`. A\ncomparison of the (negligible) effect of different `iMax` values can be\nfound in Appendix A).\n\n``` r\nd \u003c- Gapfill(ma_monthly, iMax = 5)\nsaveRDS(d, \"./monthly_iMax5_140_gapfilled.rds\")\ne \u003c- Gapfill(ma_quarter, iMax = 5)\nsaveRDS(e, \"./quarterly_iMax5_140_gapfilled.rds\")\n```\n\n### Gapfill Results\n\nTo save computation time, gapfilled data was precomputed. Here is an\noverview of the resulting imagery using the function `Image()` of\npackage `gapfill` that lets us visualize satllite data that is contained\nin arrays with no spatial reference stored. The x-axis shows day of year\nwhile the y-axis shows the year.\n\n``` r\ngf_monthly \u003c- readRDS(\"monthly_iMax5_140_gapfilled.rds\")\nImage(gf_monthly$fill, zlim = c(0.2, 1)) + ggtitle(\"Gapfilled Monthly Data\")\ngf_quarterly \u003c- readRDS(\"quarterly_iMax5_140_gapfilled.rds\")\nImage(gf_quarterly$fill, zlim = c(0.2, 1)) + ggtitle(\"Gapfilled Quarterly Data\")\n```\n\n\u003cimg src=\"main_files/figure-markdown_github/load-gapfill-1.png\" width=\"50%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/load-gapfill-2.png\" width=\"50%\" /\u003e\n\n### Gapfill Results - Closeup\n\nTo have a closer look at what `Gapfill` does, the time period of October\nto December 2013 is plotted here for comparison. First, the input data\nis plotted. Below that, the gapfilled datasets are plotted.\n\n``` r\n# plot input data matrices\nImage(ma_monthly[,,10:12,1], zlim = c(0.2, 1), colbarTitle = \"NDVI\") + ggtitle(\"Monthly Input Data, Oct - Dec 2013\")\nImage(ma_quarter[,,4,1], zlim = c(0.2, 1), colbarTitle = \"NDVI\") + ggtitle(\"Quarterly Input Data, Last Quarter 2013\")\n\n# plot gapfilled data matrices\nImage(gf_monthly$fill[,,10:12,1], zlim = c(0.2, 1), colbarTitle = \"NDVI\") + ggtitle(\"Monthly Gapfilled Data, Oct - Dec 2013, iMax = 5\")\nImage(gf_quarterly$fill[,,4,1], zlim = c(0.2, 1), colbarTitle = \"NDVI\") + ggtitle(\"Quarterly Gapfilled Data, Last Quarter 2013, iMax = 5\")\n```\n\n\u003cimg src=\"main_files/figure-markdown_github/zoom-gapfill-input-1.png\" width=\"50%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/zoom-gapfill-input-2.png\" width=\"50%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/zoom-gapfill-input-3.png\" width=\"50%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/zoom-gapfill-input-4.png\" width=\"50%\" /\u003e\n\nJust to see what the Gapfill algorithm is capable of achieving, observe\nwhat it yields when letting `iMax` default to infity. This allows the\nfunction to endlessly increase the neighbourhood for predicting `NA`\nvalues, resulting in an image with no cloud gaps whatsoever (as long as\nsome input pixels are given, gapfill can not fill empty images).\n\n``` r\ngf_quarterly_inf \u003c- readRDS(\"./appendix/quarterly_iMaxInf_140_gapfilled.rds\")\nImage(gf_quarterly_inf$fill[,,4,1], zlim = c(0.2, 1), colbarTitle = \"NDVI\") + ggtitle(\"Quarterly Gapfilled Data, Last Quarter 2013, with iMax=inf\") # plotting quarterly gapfilled data with iMax=Inf\n```\n\n\u003cimg src=\"main_files/figure-markdown_github/plot-inf-gf-1.png\" width=\"50%\" style=\"display: block; margin: auto;\" /\u003e\n\nBFAST\n-----\n\nNear-real time monitoring of deforestation being the main object of this\nstudy, we looked into a generic change detection approach for time\nseries by detecting and characterizing Breaks For Additive Seasonal and\nTrend (BFAST). (Verbesselt et al., 2010) first applied BFAST in forested\nareas of South Eastern Australia and it was able to detect and\ncharacterize spatial and temporal changes in a forested landscape. BFAST\npackage is now publicly available on CRAN. Besides BFAST there exists a\nfunction component named `bfastmonitor`, which is capable of carrying\nout near real-time disturbance detection in satellite image time series\neven if the data is not gap-filled (Verbesselt et al., 2013). A short\ninvestigation into whether using Gapfill was actually helpful or not is\ndone in Appendix C).\n\n`bfastmonitor` proves to be useful because gap-filling algorithm was not\nable to completely predict all the missing values in the time series\ndata used in this study as some had some satellite images that had 100%\ncloud cover, and bfast is able to handle gaps in the data. In\n`bfastmonitor`, the data is split into a history and a monitoring\nperiod. The “piecewise linear trend and seasonal model” (Verbesselt et.\nal, 2010) used in bfast is then fitted to the part of the history that\nis considered stable. A monitoring procesdure then checks the monitoring\ntimesteps for breaks. The algorithm was used in both monthly and\nquarterly time series data.\n\n### `bfastmonitor` Example\n\nLet’s have a look at what `bfastmonitor` does by plotting two example\ntime series. We select a border area of an area that is deforested\n(subset of time series in first plot). Then we let `bfastmonitor` run on\ntwo example pixels (top-left and bottom-right corner). As expected, a\nbreak is detected in the latter time series.\n\n``` r\next \u003c- extent(-7337562,-7337134,-1020218,-1019648) # extent drawn on raster and then recreated here\nplot(st_geometry(prod), main = \"Overview of Example Time Series\") # plot prodes shape\nplot(as(st[,,,80], \"Raster\"), add = TRUE, ext = ext) # add clipped raster\n\nImage(gf_monthly$fill[9:13, 16:20,6:10,7], colbarTitle = \"NDVI\", zlim = c(0.2, 1)) +\n ggtitle(\"Example Time Series Around Deforestation Edge. June - Oct 2019\")\n```\n\n\u003cimg src=\"main_files/figure-markdown_github/st-flag-4-1.png\" width=\"50%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/st-flag-4-2.png\" width=\"50%\" /\u003e\nIn the above plot, we can observe the deforestation process in detail:\nHow it progresses and first changes the NDVI gradually, then suddenly\n(indicating clearcut). We show the two resulting `bfastmonitor` time\nseries below, the first one indicating no significantly large change,\nand the second one detecting a break in late 2019.\n\n``` r\nx \u003c- as.vector(gf_monthly$fill[9,16,,]) # ts of top-left pixel\ny \u003c- as.ts(zoo(x, seq(2013, by = .08333333, length.out = 84))) # as ts object\nbf \u003c- bfastmonitor(y, start = 2019) # bfmonitor\nplot(bf) # plot\n\nx \u003c- as.vector(gf_monthly$fill[13,20,,]) # ts of bottom-right pixel\ny \u003c- as.ts(zoo(x, seq(2013, by = .08333333, length.out = 84))) # as ts object\nbf \u003c- bfastmonitor(y, start = 2019) # bfmonitor\nplot(bf) # plot\n```\n\n\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-1-1.png\" width=\"50%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-1-2.png\" width=\"50%\" /\u003e\n\n### `bfastmonitor` on the Complete Tile\n\nThe above demonstrated `bfastmonitor` is then run on all pixels of the\naoi. This is done by the function `bfast_on_tile`, defined in the\nfollowing code block. It returns a matrix that is `TRUE` for all pixels\nfor which a breakpoint is detected and `FALSE` for all where no break is\nfound.\n\n``` r\nbfast_on_tile \u003c- function(gapfill_matrix, by, ts, order) {\n # gapfill_matrix is a x*y*doy*year matrix, by is 1/doy, ts is # of timesteps, order is bfastmonitor order\n dims \u003c- dim(gapfill_matrix)\n result \u003c- matrix(rep(FALSE, dims[1]*dims[2]), ncol = dims[1]) # result is all FALSE\n for (i in 1:dims[1]) { # looping through x\n for (j in 1:dims[2]) { # looping through y\n  raw_px_ts \u003c- as.vector(gapfill_matrix[i,j,,]) # create pixel timeseries vector\n  px_ts_obj \u003c- as.ts(zoo(raw_px_ts, seq(2013, by = by, length.out = ts))) # make into ts object\n  bfm_obj \u003c- bfastmonitor(px_ts_obj, start = 2019, order = order) # bfastmonitor of pixel timeseries\n  brkpoint \u003c- bfm_obj$breakpoint\n  if(!is.na(brkpoint)) { # if breakpoint is available..\n  result[i,j] \u003c- TRUE # .. write TRUE to solution raster\n  } else {\n  # FALSE\n  }\n }\n }\n return(result)\n}\n```\n\nThis function is then run on our monthly and quarterly input data. While\nthe monthly time series is longer and narrowly timed, the quarterly data\nhas less timesteps with bigger intervals between them.\n\n``` r\nbfast_monthly2 \u003c- bfast_on_tile(gf_monthly$fill, by = .08333333, ts = 84, order = 2)\nbfast_quarter2 \u003c- bfast_on_tile(gf_quarterly$fill, by = 0.25, ts = 28, order = 2)\n# order = 2 was chosen because order 3 doesn't work on our quarterly aggreggated data\nsaveRDS(bfast_monthly2, \"bfast_monthly2.rds\")\nsaveRDS(bfast_quarter2, \"bfast_quarter2.rds\")\n# warning: too few observations in history period\n```\n\nPrecomputed BFAST tiles can then be loaded, but are not plotted yet.\n\n``` r\nbfast_monthly \u003c- readRDS(\"bfast_monthly2.rds\")\nbfast_quarter \u003c- readRDS(\"bfast_quarter2.rds\")\n```\n\nTo eliminate errors that may appear due to previously deforested areas\n(\\\u003c 2019), these areas are simply excluded, according to PRODES\nreference data. This is done only for PRODES data and not also for DETER\npolygons to ensure that only pixels that were actually deforested are\ntaken out, as the goal of this research is to investigate whether\n(Gapfil and) BFAST is able to detect deforestation. This task includes\nbeing robust to other forest disturbances. We chose to take advantage of\nthe PRODES program here, since an actual near real-time monitoring\nsystem could also incorporate PRODES data.\n\n``` r\n# to mask out previous deforestation\n# \u003c2019 = TRUE, !\u003c2019 = FALSE\nras \u003c- rasterize(prod, as(st[,,,5], \"Raster\"), \"YEAR\")\nprodes_prev \u003c- aperm(matrix(ras[], ncol = 140), c(2,1))\nprodes_prev[prodes_prev \u003c 2019] \u003c- TRUE\nprodes_prev[prodes_prev == 2019] \u003c- FALSE\nprodes_prev[is.na(prodes_prev)] \u003c- FALSE\n\nbfast_monthly[prodes_prev == 1] \u003c- NA\nbfast_quarter[prodes_prev == 1] \u003c- NA\n```\n\nValidation\n----------\n\nThe reference data is rasterized to the same array format that the\nresult data is held in, to make the plots comparable.\n\n``` r\n# rasterize reference data\n# 2019 = TRUE, !2019 = FALSE\nras \u003c- rasterize(prod, as(st[,,,5], \"Raster\"), \"YEAR\")\nprodes \u003c- aperm(matrix(ras[], ncol = 140), c(2,1))\nprodes[prodes \u003c 2019] \u003c- FALSE\nprodes[prodes == 2019] \u003c- TRUE\nprodes[is.na(prodes)] \u003c- FALSE\n\nrus \u003c- rasterize(dete, as(st[,,,5], \"Raster\"), \"VIEW_DATE\")\nrus[rus \u003c 2019] \u003c- 0\nrus[rus \u003e 2019] \u003c- 0\nrus[is.na(rus[])] \u003c- 0\nrus[rus != 0] \u003c- 1\ndeter \u003c- aperm(matrix(rus[], ncol = 140), c(2,1))\n\nreference \u003c- deter | prodes\n```\n\nError matrices and various accuracies are calculated for each\nclassification. For this, the function `accuracies` is written, which\nreturns a list, containing Overall Accuracy, Producer’s Accuracies,\nUser’s Accuracies and Kappa value.\n\n``` r\ntable1 \u003c- addmargins(table(bfast_monthly, reference))\ntable2 \u003c- addmargins(table(bfast_quarter, reference))\n\naccuracies \u003c- function(table1) {\n # overall accuracy\n P0 \u003c- (table1[1] + table1[5]) / table1[9]\n # producer's accuracy, Probability of classifying a pixel correctly\n pa_f \u003c- table1[1] / table1[3] # FALSE\n pa_t \u003c- table1[5] / table1[6] # TRUE\n # user's accuracy, Probability of a pixel being the classified type\n ua_f \u003c- table1[1] / table1[7] # FALSE\n ua_t \u003c- table1[5] / table1[8] # TRUE\n # kappa\n # chance that both TRUE / FALSE randomly\n tr \u003c- (table1[8] / table1[9]) * (table1[6] / table1[9])\n fr \u003c- (table1[7] / table1[9]) * (table1[3] / table1[9])\n Pe \u003c- tr + fr\n kappa \u003c- (P0 - Pe) / (1 - Pe)\n \n return(list(\"Overall Accuracy\" = P0*100, \"Prod. Acc. FALSE\" = pa_f*100, \"Prod. Acc. TRUE\" = pa_t*100, \"User's Acc. FALSE\" = ua_f*100, \"User's Acc. TRUE\" = ua_t*100, \"Kappa\" = kappa))\n}\n```\n\nThis concludes the applied methods of applying the combination of\n`Gapfill` and `bfastmonitor` on the complete 7-year time series. That\nleaves the question whether this combination could, in general, be used\nin a near real-time monitoring system. A short investigation of this\nquestion is done in Appendix D).\n\nResults\n=======\n\n`Gapfill` and `bfastmonitor` were applied to both monthly and quarterly\naggregated Landsat time series to detect deforestation. As mentioned\nearlier, PRODES and DETER Shapefiles were used to validate the results.\n\nFirst, overview maps of the `bfastmonitor` - classifications are\nprinted. `TRUE/FALSE` are in red/purple, while `NA` values are black. In\nthe row below, rasterized reference data is shown: PRODES on the left,\nand both PRODES and DETER data on the right.\n\n``` r\n# plot results\nImage(bfast_monthly, colbarTitle = \"TRUE/FALSE\") + ggtitle(\"Monthly Data\") + theme(plot.title = element_text(size=22))\nImage(bfast_quarter, colbarTitle = \"TRUE/FALSE\") + ggtitle(\"Quarterly Data\") + theme(plot.title = element_text(size=22))\n\n# plot reference data\nImage(prodes, colbarTitle = \"TRUE/FALSE\") + ggtitle(\"PRODES Data\") + theme(plot.title = element_text(size=22))\nImage(reference, colbarTitle = \"TRUE/FALSE\") + ggtitle(\"PRODES and DETER Data\") + theme(plot.title = element_text(size=22))\n```\n\n\u003cimg src=\"main_files/figure-markdown_github/results-1.png\" width=\"50%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/results-2.png\" width=\"50%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/results-3.png\" width=\"50%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/results-4.png\" width=\"50%\" /\u003e\nComparing the monthly aggregated result to the reference data below, we\nobserve that the general shape, count and area of deforestation pixels\nis reflected in the result plot. There are also some scattered pixels\npresent that do not align with the reference data. Additionally, some\nareas inside the areas classified as deforestation are wrongly marked\n`FALSE`.\n\nWhen looking at the quarterly data, we find an increase of the above\nmentioned errors. There are more scattered pixels with no corresponding\nreference areas and also some more areas that were falsely classified as\nnot deforested.\n\nAs for the reference data, the outcome of the research was closer to\nDETER data compared to PRODES data which did not fully cover the\ndeforestation scenario (e.g in the areas east and west from the center\nof the aoi). This is expected to some extent, as PRODES data is known to\nbe more conservative.\n\nAdditionally to the raster plots, error matrices and according accuracy\nmeasurements were produced, plotted below.\n\n``` r\naddmargins(table(bfast_monthly, reference)) # monthly data error matrix\n```\n\n    ##              reference\n    ## bfast_monthly FALSE  TRUE   Sum\n    ##         FALSE 14309   468 14777\n    ##         TRUE    929  2596  3525\n    ##         Sum   15238  3064 18302\n\n``` r\naddmargins(table(bfast_quarter, reference)) # quarterly data error matrix\n```\n\n    ##              reference\n    ## bfast_quarter FALSE  TRUE   Sum\n    ##         FALSE 14069   727 14796\n    ##         TRUE   1169  2337  3506\n    ##         Sum   15238  3064 18302\n\n``` r\narray(c(accuracies(table1), accuracies(table2)), dim = c(6,2), dimnames = list(c(\"Overall Accuracy\", \"Prod. Acc. FALSE\", \"Prod. Acc. TRUE\", \"User's Acc. FALSE\", \"User's Acc. TRUE\", \"Kappa\"), c(\"monthly\", \"quarterly\"))) # comparison of accuracies\n```\n\n    ##                   monthly   quarterly\n    ## Overall Accuracy  92.36695  89.64048 \n    ## Prod. Acc. FALSE  93.9034   92.32839 \n    ## Prod. Acc. TRUE   84.72585  76.27285 \n    ## User's Acc. FALSE 96.83292  95.08651 \n    ## User's Acc. TRUE  73.64539  66.65716 \n    ## Kappa             0.7417141 0.6486349\n\nWhen comparing the error matrices for monthly and quarterly data, we\nnotice an increase in false positives and false negatives in the\nquarterly error matrix. The effect of said increase can be read from the\naccuracies table.\n\nFor example had the monthly solution an error of omission value of 6.1%\nfor incorrectly classifying forested areas as deforested. It also had an\nerror of omission of 15.3% classifying deforested as forested. An\nevaluation using error of commission, forested areas had 3.2% incorrect\nclassification and deforested areas had 26.4% incorrect classification.\n\nEvaluating the quarterly data accuracy metrics, an error of omission\nvalue of 7.7% for incorrectly classifying forested areas as deforested\nis reported. The error of omission for classifying deforested as\nforested was 23.7%. An evaluation using error of commission, forested\nareas had 4.9% incorrect classification and deforested areas had 33.4%\nincorrect classification.\n\nIt becomes clear that both Producer’s and User’s accuracies for the\ndeforestation class (`TRUE`) are worse than for forested areas, meaning\nthat deforestation itself is underestimated. We also observe a general\ndecline in accuracy (increase in error measurements) over both\naggregations that is especially strong for the above mentioned\naccuracies, meaning deforestation is even more underestimated in the\nmonthly aggregation.\n\nFinally, the results are summarized by taking a look at the Kappa value,\nthat is .1 better for monthly data (0.74 instead of 0.64).\n\nDiscussion\n==========\n\nWhile we conducted this research, we found several noteworthy things\nabout the combination of `Gapfill` and `bfastmonitor`: First, the\napplication of `Gapfill` and the resulting decrease in cloud gaps does\nhave a positive influence on the classification, as a .04 increase in\nKappa value was observed (Appendix C). The exact extent to which\ngapfilling is done however does not matter as much (Appendix A).\n\nIn the previously stated results we further found that while\ndeforestation is in general underestimated, the effect increases when\nusing quarterly aggregated time series data. On the one hand, this\nconfirms the claim that BFAST is independent from data gaps (less cloud\ngaps through stronger aggregation). On the other hand it raises the\nquestion why quarterly data performs worse to such an extent.\n\nThe issue of data availability might play a role here, as found by\nSchultz et. al 2016, where data availability was identified as a key\nsource of error in bfast deforestation detection. Considering that BFAST\nfits a model on the part of a time series history that is considered\nstable, it could be that that part becomes smaller and less stable with\ndecreasing number of observations, thus introducing error. This is\nbacked by the warning `\"too few observations in history period\"` that\nwas occasionally given by `bfastmonitor` on the quarterly aggregated\ndata.\n\nNot taken into account here are e.g. the influence of the aggregation\nmethod (median).\n\nConclusion\n==========\n\nIn this research, we a) applied `gapfill` to cover for cloud gaps in a\nmulti-temporal dataset to then b) detect deforestation via\n`bfastmonitor`, on both monthly and quarterly aggregated data.\n\nAs for the applied gapfilling, we found that computational load poses an\nissue, as we had to settle for some remaining cloud gaps due to the very\nintense time requirement of `Gapfill` to replace all gaps, even on such\na small area of interest. However, the problem of cloud gaps was at the\ncore of this research, which is why we also tried to eliminate gaps by\naggregating much stronger, as mentioned. Nonetheless, does the `Gapfill`\nalgorithm prove to be an interesting algorithm for gap-filling time\nseries data.\n\nVia the `bfastmonitor` classification and PRODES and DETER reference\ndata we could then evaluate how that aggregation influences the quality\nof a deforestation detection. We found that deforestation is in general\nunderestimated, but more so in the quarterly aggregated data. BFAST\nitself proves to be a robust tool for such a detection, on the one hand\nbecause of good overall results, on the other hand due to capabilities\nof integration into a near real-time system (proof of concept in\nAppendix D).\n\nSources of error that we not account for are e.g. the chosen aggregation\nmethod and the deforestation reference data, which even though it is\nbenefiting from quite a strong methodology, is also subject to\nmisinterpretation and errors. We saw that using only PRODES data leads\nto an underestimation of forest disturbance, while using DETER data\nintroduces uncertainty about the characteristics of the disturbance\nevent.\n\nIn conclusion, we advise against aggregating a time series too strongly\nfor deforestation detection with `bfastmonitor`, as to allow for a rich\nand stable history time series, but we do advise towards using gapfill\nmethodology, as `Gapfill` has proven its capabilities.\n\nReferences\n==========\n\nArraut, J. M., Nobre, C., Barbosa, H. M., Obregon, G., and Marengo, J.\n(2012). Aerial rivers and lakes: looking at large-scale moisture\ntransport and its relation to Amazonia and to subtropical rainfall in\nSouth America. Journal of Climate, 25:543–556.\n\nFearnside, P.M. 1997a. Environmental services as a strategy for\nsustainable development in rural Amazonia. Ecological Economics\n20(1):53-70.\n\nFearnside, P.M. 1999. Biodiversity as an environmental service in\nBrazil’s Amazonianforests: Risks, value and conservation. Environmental\nConservation 26(4):305-21.\n\nFearnside, P.M. 2000. Global warming and tropical land-use change:\nGreenhouse gas emissions from biomass burning, decomposition and soils\nin forest conversion, shifting cultivation and secondary vegetation.\nClimatic Change 46(1-2):115-158\n\nFearnside, P.M. 2008a. Amazon forest maintenance as a source of\nenvironmental services. Anais da Academia Brasileira de Ciências\n80(1):101-114.\n\nGerber F, Furrer R, Schaepman-Strub G, de Jong R, Schaepman ME (2016)\nPredicting missing values in spatio-temporal satellite data.\n\nJin, S. M., Sader, S. A., 2005. MODIS time-series imagery for forest\ndisturbance detection and quantification of patch size effects. Remote\nSensing of Environment 99 (4), 462–470.\n\nNogueira, E.M., A.M. Yanai, F.O.R. Fonseca, and P.M. Fearnside. 2015.\nCarbon stock loss from deforestation through 2013 in Brazilian Amazonia.\nGlobal Change Biology 21:1271–1292.\n\nPacheco, P., Mo, K., Dudley, N., Shapiro, A., Aguilar-Amuchastegui, N.,\nLing, P.Y., Anderson, C. and Marx, A. 2021. Deforestation fronts:\nDrivers and responses in a changing world. WWF, Gland, Switzerland.\n\nSchultz, M., Verbesselt, J., Avitabile, V., Souza, C. and Herold, M.,\n“Error Sources in Deforestation Detection Using BFAST Monitor on Landsat\nTime Series Across Three Tropical Sites,” in IEEE Journal of Selected\nTopics in Applied Earth Observations and Remote Sensing, vol. 9, no. 8,\npp. 3667-3679, Aug. 2016, doi: 10.1109/JSTARS.2015.2477473.\n\nUNFCCC 2001 Seventh Conf. of Parties: The Marrakech Accords (Bonn:\nUNFCCC Secretariat) available at\n\u003ca href=\"https://unfccc.int/\" class=\"uri\"\u003ehttps://unfccc.int/\u003c/a\u003e\n\nVerbesselt, J., Hyndman, R., Newnham, G., \u0026 Culvenor, D. (2010).\nDetecting trend and seasonal changes in satellite image time series.\n\nVerbesselt, J., Zeileis, A., \u0026 Herold, M. (2013). Near real-time\ndisturbance detection using satellite image time series.\n\nAppendix\n========\n\nA) Investigate `iMax` Parameter of `Gapfill` Function\n-----------------------------------------------------\n\nWe investigated different values for the `iMax` parameter in Gapfill\nalgorithm, and found that results were not significantly different (did\nneither improve nor impair accuracies), although the calculation using\n`iMax = infinite`, i.e. completely gapfilled data, resulted in the\nhighest accuracies. The code for this is found here.\n\nCreate gapfilled datasets and calculate bfast on tiles.\n\n``` r\nf \u003c- Gapfill(ma_quarter) # iMax defaults to infinite\nsaveRDS(f, \"./appendix/quarterly_iMaxInf_140_gapfilled.rds\")\ng \u003c- Gapfill(ma_quarter, iMax = 1) #\nsaveRDS(g, \"./appendix/quarterly_iMax1_140_gapfilled.rds\")\n\nbfast_quarter_inf \u003c- bfast_on_tile(f$fill, by = 0.25, ts = 28, order = 2)\nbfast_quarter_1 \u003c- bfast_on_tile(g$fill, by = 0.25, ts = 28, order = 2)\nsaveRDS(bfast_quarter_inf, \"./appendix/bfast_quarter_inf.rds\")\nsaveRDS(bfast_quarter_1, \"./appendix/bfast_quarter_1.rds\")\n```\n\nLoad results, see above for details.\n\n``` r\n# load bfast results\nbfast_quarter_inf \u003c- readRDS(\"./appendix/bfast_quarter_inf.rds\")\nbfast_quarter_1 \u003c- readRDS(\"./appendix/bfast_quarter_1.rds\")\n# exclude existing deforestation\nbfast_quarter_inf[prodes_prev == 1] \u003c- NA\nbfast_quarter_1[prodes_prev == 1] \u003c- NA\n# create accuracy tables\ntable3 \u003c- addmargins(table(bfast_quarter_inf, reference))\ntable4 \u003c- addmargins(table(bfast_quarter_1, reference))\n# print\narray(c(accuracies(table4), accuracies(table2), accuracies(table3)), dim = c(6,3), dimnames = list(c(\"Overall Accuracy\", \"Prod. Acc. FALSE\", \"Prod. Acc. TRUE\", \"User's Acc. FALSE\", \"User's Acc. TRUE\", \"Kappa\"), c(\"iMax = 1\", \"iMax = 5\", \"iMax = inf\")))\n```\n\n    ##                   iMax = 1  iMax = 5  iMax = inf\n    ## Overall Accuracy  89.61862  89.64048  89.77707  \n    ## Prod. Acc. FALSE  92.3087   92.32839  92.49902  \n    ## Prod. Acc. TRUE   76.24021  76.27285  76.24021  \n    ## User's Acc. FALSE 95.07909  95.08651  95.08871  \n    ## User's Acc. TRUE  66.59065  66.65716  67.14573  \n    ## Kappa             0.6479805 0.6486349 0.6521101\n\nB) Investigate `order` Parameter of Function `bfastmonitor`\n-----------------------------------------------------------\n\nTo make sure that by changing the value of parameter `order` from 3\n(default) to 2, no completely unexpected effects are introduced, a quick\ntry-out is done here. The value 2 actually leads to the worst accuracy,\nbut the difference is not considered significant.\n\n``` r\nbfast_monthly1 \u003c- bfast_on_tile(gf_monthly$fill, by = .08333333, ts = 84, order = 1)\nsaveRDS(bfast_monthly1, \"./appendix/bfast_monthly1.rds\")\nbfast_monthly3 \u003c- bfast_on_tile(gf_monthly$fill, by = .08333333, ts = 84, order = 3)\nsaveRDS(bfast_monthly3, \"./appendix/bfast_monthly3.rds\")\n```\n\n``` r\nbfast_monthly1 \u003c- readRDS(\"./appendix/bfast_monthly1.rds\")\nbfast_monthly3 \u003c- readRDS(\"./appendix/bfast_monthly3.rds\")\n\nbfast_monthly1[prodes_prev == 1] \u003c- NA\nbfast_monthly3[prodes_prev == 1] \u003c- NA\n# create accuracy tables\ntable5 \u003c- addmargins(table(bfast_monthly1, reference))\ntable6 \u003c- addmargins(table(bfast_monthly3, reference))\n# print\narray(c(accuracies(table5), accuracies(table1), accuracies(table6)), dim = c(6,3), dimnames = list(c(\"Overall Accuracy\", \"Prod. Acc. FALSE\", \"Prod. Acc. TRUE\", \"User's Acc. FALSE\", \"User's Acc. TRUE\", \"Kappa\"), c(\"order = 1\", \"order = 2\", \"order = 3\")))\n```\n\n    ##                   order = 1 order = 2 order = 3\n    ## Overall Accuracy  92.41613  92.36695  92.59097 \n    ## Prod. Acc. FALSE  93.92309  93.9034   94.17246 \n    ## Prod. Acc. TRUE   84.92167  84.72585  84.72585 \n    ## User's Acc. FALSE 96.87288  96.83292  96.84168 \n    ## User's Acc. TRUE  73.75283  73.64539  74.51206 \n    ## Kappa             0.7434727 0.7417141 0.7480239\n\nC) Gapfill vs No Gapfill\n------------------------\n\nTo investigate what kind of effect the Gapfill function has in the first\nplace, since BFAST doesn’t necessarily need a gapfilling method.\n\n``` r\nbfast_monthly_nofill \u003c- bfast_on_tile(ma_monthly, by = .08333333, ts = 84, order = 2)\nsaveRDS(bfast_monthly_nofill, \"./appendix/bfast_monthly_nofill.rds\")\n```\n\n``` r\nbfast_monthly_nofill \u003c- readRDS(\"./appendix/bfast_monthly_nofill.rds\")\n\nbfast_monthly_nofill[prodes_prev == 1] \u003c- NA\n# create accuracy tables\ntable7 \u003c- addmargins(table(bfast_monthly_nofill, reference))\n# print\narray(c(accuracies(table1), accuracies(table7)), dim = c(6,2), dimnames = list(c(\"Overall Accuracy\", \"Prod. Acc. FALSE\", \"Prod. Acc. TRUE\", \"User's Acc. FALSE\", \"User's Acc. TRUE\", \"Kappa\"), c(\"Gapfilled Data\", \"Original Data\")))\n```\n\n    ##                   Gapfilled Data Original Data\n    ## Overall Accuracy  92.36695       90.92449     \n    ## Prod. Acc. FALSE  93.9034        92.01339     \n    ## Prod. Acc. TRUE   84.72585       85.50914     \n    ## User's Acc. FALSE 96.83292       96.93052     \n    ## User's Acc. TRUE  73.64539       68.28251     \n    ## Kappa             0.7417141      0.7042521\n\nD) Near Real-Time Proof of Concept\n----------------------------------\n\nPreviously we have tested the methods on complete time series and\nstarted the BFAST algorithm at the beginning of 2019. This makes sense\nas we wanted to compare the suitability of monthly vs. quarterly data\nfor using bfast, mainly in an effort to reduce cloud gaps via\naggregation + a gapfilling method. But what about evaluating each new\nacquired image separately? This approach is tested here only on the\nmonthly aggregated data, since even that could hardly be called “near\nreal-time”. So in order to evaluate how `bfastmonitor` performs if the\nvery last pixel of the time series contains the deforestation event, the\ntime series are cut short. This is done on the original data, no gapfill\nis applied.\n\n``` r\nplot(st[,,,73:84]) # complete year 2019\n```\n\n\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-9-1.png\" width=\"50%\" style=\"display: block; margin: auto;\" /\u003e\n\nCode for calculating `bfastmonitor` on time series with variable length\nis hidden since it is taken from the `bfast_on_tile` function seen\nabove.\n\nThe idea here is to run `bfastmonitor` each time a new image comes in,\nwhich in this case is a monthly aggregated image (no gapfilling done).\nAs we see above, most timesteps of 2019 are useless anyway. Regardless,\nbfast is run on timesteps for which DETER actually detected\ndeforestation, and results are then plotted next to their reference\ndata. (Code is also hidden, see `main.Rmd`). The output below is in the\nfollowing order:\n\n1.  timesteps for which DETER detected deforestation in 2019\n2.  accuracy measures as more data is added to the time series that is\n    fed to `bfastmonitor`\n3.  last tile of the input time series data, `bfastmonitor` detection\n    and DETER reference data plotted by month\n\n\u003c!-- --\u003e\n\n    ## [1] \"2019-09-03\" \"2019-09-09\" \"2019-08-08\" \"2019-06-06\" \"2019-07-10\"\n    ## [6] \"2019-07-22\" \"2019-07-25\" \"2019-07-30\"\n\n    ##                   june      july      august    september\n    ## Overall Accuracy  93.18878  88.95408  89.09184  89.68878 \n    ## Prod. Acc. FALSE  96.6318   95.44839  93.79212  91.6612  \n    ## Prod. Acc. TRUE   25.96859  27.37968  49.71292  78.02469 \n    ## User's Acc. FALSE 96.2241   92.57152  93.98535  96.10381 \n    ## User's Acc. TRUE  28.3105   38.81729  48.87112  61.27424 \n    ## Kappa             0.2352352 0.2629319 0.4317756 0.6257878\n\n\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-1.png\" width=\"33%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-2.png\" width=\"33%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-3.png\" width=\"33%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-4.png\" width=\"33%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-5.png\" width=\"33%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-6.png\" width=\"33%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-7.png\" width=\"33%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-8.png\" width=\"33%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-9.png\" width=\"33%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-10.png\" width=\"33%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-11.png\" width=\"33%\" /\u003e\u003cimg src=\"main_files/figure-markdown_github/unnamed-chunk-11-12.png\" width=\"33%\" /\u003e\n\nE) Statement of Work\n--------------------\n\nBrian:\n\n-   Project idea and development, initial research\n-   Paper drafting\n\nJonathan:\n\n-   Implementation\n-   Draft review and finalization\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonathom%2Fdetect_deforestation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonathom%2Fdetect_deforestation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonathom%2Fdetect_deforestation/lists"}