Skip to content

View or edit on GitHub

This page is synchronized from trase/data/indonesia/wood_pulp/indicators/q3_2025/qa/gaveau_ioppitp_expansion_2024.md. Last modified on 2025-12-16 22:20 CET by Harry Biddle. Please view or edit the original file there; changes should be reflected here after a midnight build (CET time), or manually triggering it with a GitHub action (link).

Incoming data from Gaveau et al. for expansion IOPPITP expansion and

forest cover change Adelina Chandra 2025-06-12

Purpose

This script checks the incoming data, transforms it, and prepares it for further analysis.

  • Checks geodatabase and transforms into format that suits GEE
  • Quality checks the forest cover change data
  • Saves the data to S3

Preparation

# Load required packages
library(dplyr)
Attaching package: 'dplyr'

The following objects are masked from 'package:stats':

    filter, lag

The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
library(ggplot2)
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ lubridate 1.9.4     ✔ tibble    3.2.1
✔ purrr     1.0.4     ✔ tidyr     1.3.1
✔ readr     2.1.5

── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(sf)
Linking to GEOS 3.13.0, GDAL 3.8.5, PROJ 9.5.1; sf_use_s2() is TRUE
library(terra)
terra 1.8.42

Attaching package: 'terra'

The following object is masked from 'package:tidyr':

    extract
library(purrr)
library(patchwork)
Attaching package: 'patchwork'

The following object is masked from 'package:terra':

    area
library(aws.signature)
library(aws.s3)

# Sign into S3
aws.signature::use_credentials()
Sys.setenv("AWS_DEFAULT_REGION" = "eu-west-1")
bucket <- "trase-storage"

Read data

# IOPPITP data
ioppitp_gdb_path <- "/vsis3/trase-storage/indonesia/wood_pulp/indicators/ori/q3_2025/Data For Trase_06112025/IOPPITP_Expansion_2024.gdb"
layers <- st_layers(ioppitp_gdb_path)
ioppitp_gdb <- st_read(ioppitp_gdb_path, layer = "IDN_IOPPITP_Expansion2001to2024_Updated_15May2025")
Reading layer `IDN_IOPPITP_Expansion2001to2024_Updated_15May2025' from data source `/vsis3/trase-storage/indonesia/wood_pulp/indicators/ori/q3_2025/Data For Trase_06112025/IOPPITP_Expansion_2024.gdb' 
  using driver `OpenFileGDB'

Warning in CPL_read_ogr(dsn, layer, query, as.character(options), quiet, : GDAL
Message 1: organizePolygons() received a polygon with more than 100 parts. The
processing may be really slow.  You can skip the processing by setting
METHOD=SKIP, or only make it analyze counter-clock wise parts by setting
METHOD=ONLY_CCW if you can assume that the outline of holes is counter-clock
wise defined

Simple feature collection with 3562602 features and 47 fields
Geometry type: MULTIPOLYGON
Dimension:     XYZ
Bounding box:  xmin: 95.39768 ymin: -8.000588 xmax: 140.9463 ymax: 5.498833
z_range:       zmin: 0 zmax: 0
Geodetic CRS:  WGS 84

Check data (Skip for faster processing)

glimpse(ioppitp_gdb) #3,562,602 rows; 47 columns; WGS 84 - massive data 
Rows: 3,562,602
Columns: 48
$ year         <chr> "2000", "2000", "2000", "2000", "2000", "2000", "2000", "…
$ Class        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ gridcode     <int> 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 30…
$ F1980s       <chr> " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " …
$ F1990        <chr> " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " …
$ F2000        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2001        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2002        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2003        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2004        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2005        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2006        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2007        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2008        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2009        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2010        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2011        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2012        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2013        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2014        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2015        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2016        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2017        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2018        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2019        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2020        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2021        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2022        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2023        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ F2024        <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "…
$ CompDriven   <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ YearITP      <chr> " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " …
$ Note         <chr> " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " …
$ Koreksi      <chr> " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " …
$ YrBfor2000   <chr> " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " …
$ YrOP         <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ TDelay       <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ CID          <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ District     <chr> "Pandeglang", "Pandeglang", "Pandeglang", "Pandeglang", "…
$ Province     <chr> "Banten", "Banten", "Banten", "Banten", "Banten", "Banten…
$ Country      <chr> "Indonesia", "Indonesia", "Indonesia", "Indonesia", "Indo…
$ Region       <chr> "Java", "Java", "Java", "Java", "Java", "Java", "Java", "…
$ Area_Ha      <dbl> 2.276075e+01, 6.520691e-03, 2.413157e+01, 9.488771e+00, 3…
$ OBJECTID     <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ Shape_Leng   <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ Shape_Length <dbl> 4.755183e-02, 3.191463e-04, 5.596155e-02, 3.162875e-02, 2…
$ Shape_Area   <dbl> 1.861893e-05, 5.333890e-09, 1.973965e-05, 7.761763e-06, 2…
$ Shape        <MULTIPOLYGON [°]> MULTIPOLYGON Z (((105.762 -..., MULTIPOLYGON…
# drop geometry - for quicker checks 
opt_df <- ioppitp_gdb  %>% 
    as_tibble()  %>% 
    select(-Shape_Leng, -Shape_Area, -Shape_Length, -Shape)

# sapply(opt_df[, sapply(opt_df, is.character)], unique)
# sapply(opt_df, function(x) sum(is.na(x)))
unique(opt_df$gridcode) # this is the value that indicates the type of forest cover change (see the raster data)
  [1]  300  304  306  307  308  309  310  311  312  313  314  315  316  317  318
 [16]  319  320  301  305  302  303  323    0  321  324  112  114  113  111  322
 [31]  100  117  118  120  108  106  104  115  102  110  116  101  109  105  122
 [46]  119  123  121  107  124  103  602  400  412  416  419  420  418  417  421
 [61]  608  607  606  611  609  612  610  615  613  415  413  619  620  624  409
 [76]  601  604  605  621  600  614  616  617  618  622  603  623  422  424 7000
 [91] 7005 7014 7015 7016 7017 7018 7019 7020 7021 7004 7007 7008 7009 7011 7012
[106] 7013 5000 5002 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015
[121] 5016 5017 5018 5019 5020 7001 7002 7003 7006 7010 7022 5021 5022 5023 7023
[136] 5001  411 7024 5024  410    1 5003  414  423  408  405  407 9000 9001 9002
[151] 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017
[166] 9018 9019 9020 9021 9022 9023 9024  403  401  406
length(unique(opt_df$gridcode))
[1] 175
# names(opt_df)
# head(opt_df)

# check if CompDriven == 2024 is where F2023 == Forest & F2024 == IOPP/ITP
opt_df %>% 
    filter(CompDriven == 2024) %>% 
    select(Class, year, gridcode, F2023, F2024) %>% 
    group_by(Class, year, gridcode, F2023, F2024) %>% 
    summarise(n = n()) %>% 
    ungroup() 
`summarise()` has grouped output by 'Class', 'year', 'gridcode', 'F2023'. You
can override using the `.groups` argument.

# A tibble: 6 × 6
  Class year  gridcode F2023  F2024     n
  <chr> <chr>    <int> <chr>  <chr> <int>
1 IOPP  2024       124 Forest IOPP   9921
2 IOPP  2024       424 Forest IOPP     54
3 IOPP  2024       624 Forest IOPP    811
4 ITP   2024       124 Forest ITP    3027
5 ITP   2024       424 Forest ITP       2
6 ITP   2024       624 Forest ITP     528
# Check if the data is consistent across years
years <- 2001:2024
result_list <- map(years, function(y) {
  opt_df %>% 
    filter(CompDriven == y) %>%
    select(Class, year, gridcode, 
           !!sym(paste0("F", y-1)), !!sym(paste0("F", y))) %>%
    group_by(Class, year, gridcode, 
             !!sym(paste0("F", y-1)), !!sym(paste0("F", y))) %>%
    summarise(n = n(), .groups = "drop") %>%
    mutate(CompDriven = y)})

all_results <- bind_rows(result_list)

#View(all_results)
sapply(all_results[, sapply(all_results, is.character)], unique)
$Class
[1] "IOPP" "ITP"

$year
 [1] "2001" "2002" "2003" "2004" "2005" "2006" "2007" "2008" "2009" "2010"
[11] "2011" "2012" "2013" "2014" "2015" "2016" "2017" "2018" "2019" "2020"
[21] "2021" "2022" "2023" "2024"

$F2000
[1] "Forest" NA

$F2001
[1] "IOPP"   "ITP"    "Forest" NA

$F2002
[1] NA       "IOPP"   "ITP"    "Forest"

$F2003
[1] NA       "IOPP"   "ITP"    "Forest"

$F2004
[1] NA       "IOPP"   "ITP"    "Forest"

$F2005
[1] NA       "IOPP"   "ITP"    "Forest"

$F2006
[1] NA       "IOPP"   "ITP"    "Forest"

$F2007
[1] NA       "IOPP"   "ITP"    "Forest"

$F2008
[1] NA       "IOPP"   "ITP"    "Forest"

$F2009
[1] NA       "IOPP"   "ITP"    "Forest"

$F2010
[1] NA       "IOPP"   "ITP"    "Forest"

$F2011
[1] NA       "IOPP"   "ITP"    "Forest"

$F2012
[1] NA       "IOPP"   "ITP"    "Forest"

$F2013
[1] NA       "IOPP"   "ITP"    "Forest"

$F2014
[1] NA       "IOPP"   "ITP"    "Forest"

$F2015
[1] NA       "IOPP"   "ITP"    "Forest"

$F2016
[1] NA       "IOPP"   "ITP"    "Forest"

$F2017
[1] NA       "IOPP"   "ITP"    "Forest"

$F2018
[1] NA       "IOPP"   "ITP"    "Forest"

$F2019
[1] NA       "IOPP"   "ITP"    "Forest"

$F2020
[1] NA       "IOPP"   "ITP"    "Forest"

$F2021
[1] NA       "IOPP"   "ITP"    "Forest"

$F2022
[1] NA       "IOPP"   "ITP"    "Forest"

$F2023
[1] NA       "IOPP"   "ITP"    "Forest"

$F2024
[1] NA     "IOPP" "ITP"
# all consistent that the data shows forest transition to IOPP/ITP - meaning rapid conversion

#check NA in f2024
# all_results  %>% filter(is.na(F2024)) # NA is the case for the years after expansion

unique(all_results$gridcode) # however, the gridcode is not clear due to zero values 
 [1] 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
[20] 601 121 602 403 603 100 604 605 606 122 120 123 608 609 614 615 600 607 407
[39] 408 412 610 409 414 611 612 124 410 422 613 400 421 411 413 416 417 418 423
[58] 424 419 616 617 619 621 622 618 620 415 420   0 623 624
zero_gridcode <- all_results  %>% filter(gridcode == 0)
sapply(zero_gridcode[, sapply(zero_gridcode, is.character)], unique)
$Class
[1] "IOPP" "ITP"

$year
[1] "2016" "2017" "2018" "2019" "2020" "2021" "2022" "2023"

$F2000
[1] NA

$F2001
[1] NA

$F2002
[1] NA

$F2003
[1] NA

$F2004
[1] NA

$F2005
[1] NA

$F2006
[1] NA

$F2007
[1] NA

$F2008
[1] NA

$F2009
[1] NA

$F2010
[1] NA

$F2011
[1] NA

$F2012
[1] NA

$F2013
[1] NA

$F2014
[1] NA

$F2015
[1] "Forest" NA

$F2016
[1] "IOPP"   "Forest" NA

$F2017
[1] NA       "IOPP"   "Forest"

$F2018
[1] NA       "IOPP"   "Forest"

$F2019
[1] NA       "IOPP"   "Forest"

$F2020
[1] NA       "IOPP"   "Forest"

$F2021
[1] NA       "ITP"    "Forest"

$F2022
[1] NA       "IOPP"   "ITP"    "Forest"

$F2023
[1] NA     "IOPP" "ITP"

$F2024
[1] NA
# view(zero_gridcode) # there are classes where forest transition happened but gricode is zero

# thus the most important columns are compdriven, class
sum(is.na(opt_df$CompDriven)) 
[1] 0
unique(opt_df$CompDriven)
 [1]    0 2013 2012 2011 2010 2009 2015 2014 2016 2017 2018 2019 2020 2021 2023
[16] 2006 2024 2001 2002 2005 2007 2003 2004 2008 2022
nrow(opt_df) == length(unique(opt_df$OBJECTID)) # false, needs to create unique ID
[1] FALSE

further check stats

# calculate area directly in hectares and add as a new column - compare to Area_Ha
# gdb_data_merc <- st_transform(ioppitp_gdb, 3395)
# gdb_data_merc$calc_area_ha <- as.numeric(st_area(gdb_data_merc)) / 10000
# sum(gdb_data_merc$calc_area_ha[gdb_data_merc$Class == "IOPP"]) # 11080549
# sum(gdb_data_merc$calc_area_ha[gdb_data_merc$Class == "IOPP" & gdb_data_merc$CompDriven > 0]) # 2308282

# View the first few area values
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "IOPP"]) # 11062436 - similar to the above - good to go
[1] 11062436
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "IOPP" & ioppitp_gdb$CompDriven > 0]) # 2303917 rapid conversion by industry
[1] 2303917
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "IOPP" & ioppitp_gdb$CompDriven == 2024]) # 31096.53 rapid expansion only 2024
[1] 31096.53
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "IOPP" & ioppitp_gdb$year < 2020]) # 10541982 oil palm up to 2019 - looking at gaveau et al this is industrial only
[1] 10541982
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "IOPP"  & ioppitp_gdb$year > 2000]) # 6370069 # oil palm expansion after 2000 
[1] 6890523
# remember that F2024 is either IOPP or ITP, thus checking the F2023 
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "IOPP" & ioppitp_gdb$CompDriven == 0 & ioppitp_gdb$F2023 == "Forest"]) # 555.27 - what is this then? 
[1] 555.2736
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "IOPP" & ioppitp_gdb$CompDriven == 0 & ioppitp_gdb$F2023 == "Non Forest"]) # 84646.55 - also this non-forest to palm transition 
[1] 84646.55
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "IOPP" & ioppitp_gdb$CompDriven == 2024 & ioppitp_gdb$F2023 == "Forest"]) # 31096.53
[1] 31096.53
# how about ITP? similar to https://nusantara-atlas.org/industrial-wood-pulp-deforestation-in-indonesia-slows-in-2024/?
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "ITP"]) # 3334775
[1] 3334775
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "ITP" & ioppitp_gdb$CompDriven == 2024]) # 13211.67 - 13,647 in the blogpost
[1] 13211.67
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "ITP" & ioppitp_gdb$CompDriven == 2023]) # 30399.84 - 30,385 in the blogpost
[1] 30399.84
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "ITP" & ioppitp_gdb$CompDriven == 0 & ioppitp_gdb$F2023 == "Non Forest"]) # 59907.58
[1] 59907.58
sum(ioppitp_gdb$Area_Ha[ioppitp_gdb$Class == "ITP" & ioppitp_gdb$CompDriven == 0 & ioppitp_gdb$F2022 == "Non Forest"]) - 59907.58 # 69653.45
[1] 69653.45

Notes on inconsistencies - First, the info on transition in FXXXX is not clear. There are some cases where F2000-2023 is Forest but CompDriven == 0. Why is this not classified as rapid conversion? What does this class represent? - Second, what seems to be consistent is that CompDriven == 2024 and CompDriven == 0 but F2023 was Non-Forest.See the graph in their blog: for 2023 and 2024, the composition from Forest to ITP and Non-Forest to ITP looks somewhat similar. - There are also cases where F2023 is Forest and F2024 is IOPP/ITP, but the gridcode is 0, 100, 400, or 600 (see below). However, for CompDriven data, gridcode is consistently 124, 424, or 624.

Based on information from the Gaveau et al. team: - CompDriven indicates the drivers of forest loss by pulpwood (one year interval). - Thus, for all polygons where the previous year was forest, they should fall under CompDriven != 0. - However, there are many cases where this is not true! In theory, we should exclude them because Gaveau et al. did not categorize them as rapid conversion.

What is clear is: - Where F2023 = Forest, CompDriven == 0, and year == 2024: these should be excluded, since all polygons under this category are classified as forest in 2024 (in the image). The total area for ITP is 432 ha.

More details on errors are in the linked Google Doc.

Clarification from Gaveau et al:
We need to exclude areas with gridcode 0.

# further checking non-forest to palm and wp
opt_df <- ioppitp_gdb  %>% 
    as_tibble()  %>% 
    select(-Shape_Leng, -Shape_Area, -Shape_Length, -Shape)

#further checks misclassified forests
opt_df_misfor <- opt_df %>% 
    filter(Class == "IOPP")  %>% 
    filter(CompDriven == 0)  %>% 
    count(F2000,F2001,F2002, F2003, F2004, F2005, F2006, F2007, F2008, F2009, F2010,
    F2011, F2012, F2013, F2014, F2015, F2016, F2017, F2018, F2019, F2020,
    F2021, F2022, F2023, F2024, gridcode)  %>%  # non IOPP/ITP data 
    filter(gridcode %in% c(100,400,600))
# view(opt_df_misfor)
#ok all consistent that this should be corrected y-1 forest

opt_df_nonfor <- opt_df %>% 
    filter(Class == "IOPP")  %>% 
    #filter(CompDriven == 0)  %>% 
    count(F2000,F2023,F2024,CompDriven) # non IOPP/ITP data 
head(opt_df_nonfor)
# A tibble: 6 × 5
  F2000  F2023  F2024 CompDriven      n
  <chr>  <chr>  <chr>      <int>  <int>
1 Forest Forest IOPP           0   4376
2 Forest Forest IOPP        2024  10786
3 Forest IOPP   IOPP           0 193111
4 Forest IOPP   IOPP        2001   3438
5 Forest IOPP   IOPP        2002   4846
6 Forest IOPP   IOPP        2003   3412
opt_df_nonfor_case <- opt_df %>% 
    filter(Class == "IOPP")  %>% 
    #filter(CompDriven == 0)  %>% 
    #filter(year == 2024) %>% 
    count(F2001,F2002, F2003, F2004, F2005, F2006, F2007, F2008, F2009, F2010,
    F2011, F2012, F2013, F2014, F2015, F2016, F2017, F2018, F2019, F2020,
    F2021, F2022, F2023, F2024, CompDriven, gridcode)   %>%  # non IOPP/ITP data 
    filter(F2023 == "Forest")

head(opt_df_nonfor_case)
# A tibble: 6 × 27
  F2001  F2002 F2003 F2004 F2005 F2006 F2007 F2008 F2009 F2010 F2011 F2012 F2013
  <chr>  <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
2 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
3 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
4 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
5 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
6 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
# ℹ 14 more variables: F2014 <chr>, F2015 <chr>, F2016 <chr>, F2017 <chr>,
#   F2018 <chr>, F2019 <chr>, F2020 <chr>, F2021 <chr>, F2022 <chr>,
#   F2023 <chr>, F2024 <chr>, CompDriven <int>, gridcode <int>, n <int>

this is super insteresting why does some of the data have CompDriven == 0 but F2023 == Forest and F2024 == IOPP/ITP

Summary of meeting with Husna on 7th aug I found some misclassifications within the expansion data (GDB) from Gaveau et al., as the sum of areas does not add up and does not match the figures reported in their blog posts.

ITP blog: https://nusantara-atlas.org/industrial-wood-pulp-deforestation-in-indonesia-slows-in-2024/

IOPP blog: https://nusantara-atlas.org/industrial-palm-oil-deforestation-in-indonesia-slows-slightly-in-2024/

All industry-driven expansions are classified by assigning a year value in the CompDriven field. However, there are thousands of hectares that should fall under this category but are not included. During the meeting, we calculated the totals together, and it was evident that if we combine both the classified and misclassified areas, the sum roughly matches the values reported in the blog post. Note that we only calculated for 2023 and 2024 during the call. This issue applies to both figures for IOPP and ITP.

IOPP: CompDriven == 2024: 31,096.53 ha (while in blog: 31,314 ha) BUT when adding 358 ha misclassified, totals match CompDriven == 2023: 34,410.11 ha (while in blog: 34,353 ha) adding 2.3 ha misclassified, insignificant) ITP: CompDriven == 2024: 13,211.67 ha (blog: 13,647 ha) but adding 432.9 ha misclassified, totals match) CompDriven == 2023: 30,399.84 ha (blog: 30,385 ha; no misclassified areas)

This discrepancy makes sense, as Husna explained that she used the forest image layer and then ‘corrected’ it using digitized industrial pulpwood areas. However, she mentioned that the value in small areas often did not get updated during the reclassification.

Since the calculation now add up, she agreed that I should proceed with correcting the data. We also agreed to remove Gridcode == 0 Other topic: Peat data used to calculate PeatSwamp extent was MoEF’s data.

sapply(opt_df_nonfor[, sapply(opt_df_nonfor, is.character)], unique)
$F2000
[1] "Forest"     "IOPP"       "ITP"        "Non Forest"

$F2023
[1] "Forest"     "IOPP"       "Non Forest"

$F2024
[1] "IOPP"
opt_df_nonfor  %>% select(CompDriven)
# A tibble: 36 × 1
   CompDriven
        <int>
 1          0
 2       2024
 3          0
 4       2001
 5       2002
 6       2003
 7       2004
 8       2005
 9       2006
10       2007
# ℹ 26 more rows
unique(opt_df_nonfor$CompDriven) # 0 
 [1]    0 2024 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013
[16] 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023
#check itp
opt_df_case_itp <- opt_df %>% 
    filter(Class == "ITP")  %>% 
    #filter(CompDriven == 0)  %>% 
    #filter(year == 2024) %>% 
    count(F2001,F2002, F2003, F2004, F2005, F2006, F2007, F2008, F2009, F2010,
    F2011, F2012, F2013, F2014, F2015, F2016, F2017, F2018, F2019, F2020,
    F2021, F2022, F2023, F2024, CompDriven, gridcode)   %>%  # non IOPP/ITP data 
    filter(F2023 == "Forest")

head(opt_df_case_itp)
# A tibble: 6 × 27
  F2001  F2002 F2003 F2004 F2005 F2006 F2007 F2008 F2009 F2010 F2011 F2012 F2013
  <chr>  <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
2 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
3 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
4 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
5 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
6 Forest Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore… Fore…
# ℹ 14 more variables: F2014 <chr>, F2015 <chr>, F2016 <chr>, F2017 <chr>,
#   F2018 <chr>, F2019 <chr>, F2020 <chr>, F2021 <chr>, F2022 <chr>,
#   F2023 <chr>, F2024 <chr>, CompDriven <int>, gridcode <int>, n <int>
#should be excluded? 
opt_df_exclude_itp <- opt_df %>% 
    filter(Class == "ITP" & CompDriven == 0)  %>% 
    #filter(year == 2023)  %>% 
    filter(gridcode %in% c(100,400,600,0)) 

sum(opt_df_exclude_itp$Area_Ha) # 2400 ha - quite huge! so why excluding 100,400,600 then?
[1] 2400.162
opt_df_exclude_itp_count <- opt_df_exclude_itp %>%
    count(F2001,F2002, F2003, F2004, F2005, F2006, F2007, F2008, F2009, F2010,
    F2011, F2012, F2013, F2014, F2015, F2016, F2017, F2018, F2019, F2020,
    F2021, F2022, F2023, F2024, CompDriven, year, gridcode)    # non IOPP/ITP data   

head(opt_df_exclude_itp)
# A tibble: 6 × 44
  year  Class gridcode F1980s F1990 F2000  F2001  F2002  F2003 F2004 F2005 F2006
  <chr> <chr>    <int> <chr>  <chr> <chr>  <chr>  <chr>  <chr> <chr> <chr> <chr>
1 2024  ITP          0 <NA>   <NA>  Forest Forest Forest Fore… Fore… Fore… Fore…
2 2024  ITP        100 <NA>   <NA>  Forest Forest Forest Fore… Fore… Fore… Fore…
3 2024  ITP        100 <NA>   <NA>  Forest Forest Forest Fore… Fore… Fore… Fore…
4 2024  ITP        100 <NA>   <NA>  Forest Forest Forest Fore… Fore… Fore… Fore…
5 2024  ITP        100 <NA>   <NA>  Forest Forest Forest Fore… Fore… Fore… Fore…
6 2024  ITP        100 <NA>   <NA>  Forest Forest Forest Fore… Fore… Fore… Fore…
# ℹ 32 more variables: F2007 <chr>, F2008 <chr>, F2009 <chr>, F2010 <chr>,
#   F2011 <chr>, F2012 <chr>, F2013 <chr>, F2014 <chr>, F2015 <chr>,
#   F2016 <chr>, F2017 <chr>, F2018 <chr>, F2019 <chr>, F2020 <chr>,
#   F2021 <chr>, F2022 <chr>, F2023 <chr>, F2024 <chr>, CompDriven <int>,
#   YearITP <chr>, Note <chr>, Koreksi <chr>, YrBfor2000 <chr>, YrOP <int>,
#   TDelay <int>, CID <int>, District <chr>, Province <chr>, Country <chr>,
#   Region <chr>, Area_Ha <dbl>, OBJECTID <int>

ok in the oil palm 2024 update the data capture when forest or non forest is converted to IOPP but not when forest is converted to non-forest and subsequently into IOPP lets follow this stucture

Transform data

# create unique ID for IOPP and ITP 
ioppitp_gdb_uid <- ioppitp_gdb  %>% 
    mutate(uid = case_when(Class == "IOPP" ~ paste0("P_", row_number()),
                            Class == "ITP" ~ paste0("T_", row_number()),
                            TRUE ~ "problem"))
length(unique(ioppitp_gdb_uid$uid))
[1] 3562602
sum(ioppitp_gdb_uid$uid == 'problem')
[1] 0
# separate IOPP and ITP data 
iopp_df <- ioppitp_gdb_uid  %>%  
    filter(Class == "IOPP")
# nrow(iopp_df) # 2,615,627

itp_df <- ioppitp_gdb_uid  %>%  
    filter(Class == "ITP") 
# nrow(itp_df) # 946,975

Prepare for exporting

# dropping 3d geometry due to error in st_write
iopp_df_2d <- st_zm(iopp_df, drop = TRUE, what = "ZM")
itp_df_2d <- st_zm(itp_df, drop = TRUE, what = "ZM")

# check if the data is valid 
sum(st_is_valid(iopp_df_2d)) # many invalid geometries -600k
[1] 2508944
sum(st_is_valid(itp_df_2d)) # 40k invalid geometries
[1] 906894
# quick fix for invalid geometries
iopp_df_2d_valid <- st_make_valid(iopp_df_2d)
itp_df_2d_valid <- st_make_valid(itp_df_2d)

# check again before exporting
sum(st_is_valid(iopp_df_2d_valid)) 
[1] 2615627
sum(st_is_valid(itp_df_2d_valid)) 
[1] 946975
# glimpse(itp_df_2d_valid)

# empty geometries
sum(st_is_empty(iopp_df_2d_valid)) #331   
[1] 331
which(st_is_empty(iopp_df_2d_valid))
  [1]     660    2254   11164   36863   36864   36865   36937   36938   36939
 [10]   38250   43250   63403   64845   67059   67060   69106   69948   72999
 [19]   73306   82709   82735   88195   89610   93319   93581   97592  103295
 [28]  103869  104032  104033  107993  112741  112742  112745  112746  112747
 [37]  113297  113298  113487  113905  113906  114754  115259  125372  134582
 [46]  134596  134597  134611  148232  160443  169846  175735  177583  178069
 [55]  178072  178073  178074  178775  183797  183883  183884  184021  196405
 [64]  203813  204281  208824  216240  226618  238095  238097  239720  240558
 [73]  240595  243313  243314  243315  243316  255588  258116  260801  262259
 [82]  290686  294221  295688  295689  295690  295691  295693  295701  295703
 [91]  295706  295707  295712  295713  295715  295716  295717  295718  295719
[100]  295720  295721  295723  295724  295725  295726  295733  295734  295735
[109]  295736  295737  295740  295743  295744  295747  295759  295782  295783
[118]  295784  295788  295795  295796  295801  295803  295807  295836  295837
[127]  298615  342168  343561  353045  387610  415386  435049  436187  436190
[136]  436191  436192  436194  436195  439307  440266  447701  447707  447709
[145]  460843  469430  469433  469434  469446  495241  510094  528031  529261
[154]  534922  540522  540525  540526  540527  540528  540529  540530  541949
[163]  546259  547810  549388  566686  570163  632603  634195  642288  715540
[172]  777309  801355  834963  853939  853940  853941  856200  895544  898274
[181]  903397  905449  969461  976726 1045224 1059907 1063657 1069761 1077906
[190] 1088800 1094423 1097609 1131672 1131995 1174470 1177970 1181600 1210176
[199] 1212761 1256283 1259171 1262277 1283985 1283986 1286620 1290865 1315078
[208] 1331865 1343060 1344823 1348430 1363698 1412608 1415315 1416814 1422138
[217] 1427490 1430958 1457489 1461893 1461902 1463774 1467382 1477753 1480513
[226] 1480515 1487424 1490842 1521420 1527940 1527944 1527948 1527949 1527950
[235] 1532796 1532797 1533071 1533072 1535490 1535491 1535494 1535512 1535527
[244] 1535532 1539289 1542863 1542864 1542865 1542866 1542867 1542868 1542869
[253] 1542870 1542871 1542872 1542873 1542875 1542876 1542877 1542878 1552578
[262] 1555012 1568832 1578546 1585404 1585408 1594974 1611486 1616062 1641992
[271] 1665895 1713669 1737612 1743802 1786206 1790481 1821011 1842230 1851944
[280] 1857400 1878026 1897093 1905909 1933386 1957037 1969993 1974052 1997753
[289] 2001185 2045909 2063538 2108701 2125339 2131507 2168158 2191027 2213391
[298] 2225318 2240443 2266511 2268144 2369005 2369006 2369068 2369071 2384408
[307] 2388274 2425597 2441476 2493585 2499474 2501126 2509828 2547620 2554558
[316] 2576649 2576650 2577763 2578063 2578972 2579291 2580523 2582767 2585909
[325] 2590598 2597113 2597975 2598264 2598542 2600894 2607977
sum(st_is_empty(itp_df_2d_valid))  #79 
[1] 79
which(st_is_empty(itp_df_2d_valid))
 [1]  40305  57430  74630  83793  92907 113336 128984 131254 131255 134123
[11] 140100 140101 140102 140103 142197 142200 157447 213518 213519 213520
[21] 215738 250624 256119 291982 292016 299289 312156 314758 318821 334784
[31] 352260 357787 363763 369662 410240 412038 416034 416035 440095 455302
[41] 458200 505079 537734 561045 571153 610910 614241 628677 628678 628679
[51] 629535 647917 655125 684946 686739 699798 703658 704289 704820 735141
[61] 747542 751129 753504 768275 768277 768279 768281 768283 768286 778160
[71] 797104 797513 797795 806717 817008 818618 828987 904193 905167
# should we be worried? 
# iopp_df_2d_empty <- iopp_df_2d_valid[st_is_empty(iopp_df_2d_valid), ]
# sum(iopp_df_2d_empty$Area_Ha) # 0.01378832 ha
# itp_df_2d_empty <- itp_df_2d_valid[st_is_empty(itp_df_2d_valid), ]
# sum(itp_df_2d_empty$Area_Ha) # 0.000396351 ha #very small areas - will be excluded since rasterization will not work with empty geometries

# remove empty geometries
iopp_df_2d_valid <- iopp_df_2d_valid[!st_is_empty(iopp_df_2d_valid), ]
itp_df_2d_valid <- itp_df_2d_valid[!st_is_empty(itp_df_2d_valid), ]

Prepare clean shapefile and perform correction for misclassified polygons as agreed on meeting with Husna on 7th August

iopp_df_final <- iopp_df_2d_valid %>% 
    select(uid, year, gridcode, starts_with("F20"), CompDriven, 
    District, Province, Country, Region, Area_Ha)  %>% 
    filter(gridcode != 0)  %>% #exclude gridcode 0  
    rename("CompDriven_init" = "CompDriven")  %>% #change CompDriven initial (before correction) to CompDriven_init just to makesure we're keeping record
    mutate(CompDriven = ifelse(CompDriven_init == 0 & year != 2000 & gridcode %in% c(100,400,600), year, CompDriven_init), #correct the misclassified
          CompDriven = as.numeric(CompDriven))   

# check_rev_p <- iopp_df_final  %>% 
#   filter(CompDriven_init == 0 & CompDriven>0) %>% 
#   select(year, CompDriven_init, CompDriven, gridcode) 

# sum(check_rev_p$CompDriven_init != check_rev_p$CompDriven, na.rm = TRUE) #14926 polygon changed classification

itp_df_final <- itp_df_2d_valid %>% 
    select(uid, year, gridcode, starts_with("F20"), CompDriven, 
    District, Province, Country, Region, Area_Ha)  %>% 
    filter(gridcode != 0)  %>% #exclude gridcode 0  
    rename("CompDriven_init" = "CompDriven")  %>% #change CompDriven initial (before correction) to CompDriven_init just to makesure we're keeping record
    mutate(CompDriven = ifelse(CompDriven_init == 0 & year != 2000 & gridcode %in% c(100,400,600), year, CompDriven_init), #correct the misclassified
          CompDriven = as.numeric(CompDriven))   

glimpse(itp_df_final)
Rows: 946,882
Columns: 36
$ uid             <chr> "T_7326", "T_7327", "T_7328", "T_7329", "T_7330", "T_7…
$ year            <chr> "2000", "2000", "2000", "2000", "2000", "2000", "2000"…
$ gridcode        <int> 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300,…
$ F2000           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2001           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2002           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2003           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2004           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2005           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2006           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2007           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2008           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2009           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2010           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2011           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2012           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2013           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2014           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2015           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2016           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2017           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2018           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2019           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2020           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2021           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2022           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2023           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ F2024           <chr> "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP", "ITP"…
$ CompDriven_init <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ District        <chr> "Way Kanan", "Way Kanan", "Way Kanan", "Way Kanan", "W…
$ Province        <chr> "Lampung", "Lampung", "Lampung", "Lampung", "Lampung",…
$ Country         <chr> "Indonesia", "Indonesia", "Indonesia", "Indonesia", "I…
$ Region          <chr> "Sumatra", "Sumatra", "Sumatra", "Sumatra", "Sumatra",…
$ Area_Ha         <dbl> 6.921000e-01, 2.239206e-01, 3.180442e-02, 4.808438e-01…
$ Shape           <POLYGON [°]> POLYGON ((104.5593 -4.58316..., POLYGON ((104.…
$ CompDriven      <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
# check_rev_t <- itp_df_final  %>% 
#   filter(CompDriven_init == 0 & CompDriven>0) %>% 
#   select(year, CompDriven_init, CompDriven, gridcode) 

# sum(check_rev_t$CompDriven_init != check_rev_t$CompDriven, na.rm = TRUE) #4569 polygon changed classification

RAPID conversion only

clean shapefile for conversion to image where first digit indicates the current land cover (palm), and the two last are the year when it was established thus the loss year for palm 400, for timber 300 shows other transition (non-forest to pulp, and if already timber in 2000 no history prior 2000, also ITP to palm (only for iopp data) - should be excluded in the deforestation analysis)

unique(iopp_df_final$CompDriven) # 2001-2024.
 [1]    0 2013 2012 2011 2010 2009 2015 2014 2016 2017 2018 2019 2020 2021 2023
[16] 2006 2024 2001 2002 2005 2007 2003 2004 2008 2022
`%nin%` <- Negate(`%in%`)

iopp_df_rapid <- iopp_df_final %>%
  mutate(foresttopalm = case_when(
      CompDriven == 0  ~ 400, #other transitions (indirect, eventually converted or already itp in 2000)
      CompDriven >= 2000   ~ 200 + (CompDriven - 2000),
      TRUE                 ~ NA_real_))
glimpse(iopp_df_rapid)
Rows: 2,614,975
Columns: 37
$ uid             <chr> "P_1", "P_2", "P_3", "P_4", "P_5", "P_6", "P_7", "P_8"…
$ year            <chr> "2000", "2000", "2000", "2000", "2000", "2000", "2000"…
$ gridcode        <int> 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300,…
$ F2000           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2001           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2002           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2003           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2004           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2005           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2006           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2007           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2008           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2009           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2010           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2011           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2012           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2013           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2014           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2015           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2016           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2017           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2018           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2019           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2020           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2021           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2022           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2023           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ F2024           <chr> "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP", "IOPP"…
$ CompDriven_init <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ District        <chr> "Pandeglang", "Pandeglang", "Pandeglang", "Pandeglang"…
$ Province        <chr> "Banten", "Banten", "Banten", "Banten", "Banten", "Ban…
$ Country         <chr> "Indonesia", "Indonesia", "Indonesia", "Indonesia", "I…
$ Region          <chr> "Java", "Java", "Java", "Java", "Java", "Java", "Java"…
$ Area_Ha         <dbl> 2.276075e+01, 6.520691e-03, 2.413157e+01, 9.488771e+00…
$ Shape           <POLYGON [°]> POLYGON ((105.762 -6.806987..., POLYGON ((105.…
$ CompDriven      <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ foresttopalm    <dbl> 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400,…
sum(iopp_df_rapid$Area_Ha[iopp_df_rapid$foresttopalm == 223])
[1] 34366.39
itp_df_rapid <- itp_df_final %>%
  mutate(foresttotimber = case_when(
      CompDriven == 0  ~ 300, #other transitions (indirect)
      CompDriven >= 2000   ~ 100 + (CompDriven - 2000),
      TRUE                 ~ NA_real_))
sum(itp_df_rapid$Area_Ha[itp_df_rapid$foresttotimber== 124]) # great
[1] 13644.66
unique(itp_df_rapid$foresttotimber)
 [1] 300 121 123 110 111 119 120 106 107 108 109 112 113 114 124 103 104 105 122
[20] 115 116 101 102 117 118
unique(itp_df_rapid$foresttotimber)
 [1] 300 121 123 110 111 119 120 106 107 108 109 112 113 114 124 103 104 105 122
[20] 115 116 101 102 117 118

RAPID and INDIRECT (ultimately CONVERTED)

AXYY for Woodpulp A = 1 = Rapid conversion - one year interval 3 = eventually converted for Palm A = 2 = Rapid conversion - one year interval 4 = eventually converted X = 1 = forest in mineral soil, 4 = mangrove forest, 6 = peat swamp forest, 3 = non forest, 5 = ITP (applies only for palm), 0 = already IOPP/ITP in 2000 Y = year when converstion to plantation happened. When running on GEE be sure to exclude any 1000, 2000, 31YY, 34YY, 36YY, 41YY, 44YY, 46YY because the 31,34,36,41,44,46 should have been under CompDriven != 0 if they are meant to be included by Gaveau et al. Rapid and indirect conversion IOPP

# quick check
iopp_df  %>% filter(is.na(year)) #no NA! 
Simple feature collection with 0 features and 48 fields
Bounding box:  xmin: NA ymin: NA xmax: NA ymax: NA
Geodetic CRS:  WGS 84
 [1] year         Class        gridcode     F1980s       F1990       
 [6] F2000        F2001        F2002        F2003        F2004       
[11] F2005        F2006        F2007        F2008        F2009       
[16] F2010        F2011        F2012        F2013        F2014       
[21] F2015        F2016        F2017        F2018        F2019       
[26] F2020        F2021        F2022        F2023        F2024       
[31] CompDriven   YearITP      Note         Koreksi      YrBfor2000  
[36] YrOP         TDelay       CID          District     Province    
[41] Country      Region       Area_Ha      OBJECTID     Shape_Leng  
[46] Shape_Length Shape_Area   Shape        uid         
<0 rows> (or 0-length row.names)
check00 <- itp_df  %>% filter(year == 2000) 
unique(check00$F2000)
[1] "ITP"
unique(check00$gridcode)
 [1]  300  301  302  303  304  305  306  307  308  309  310  311  312  313  314
[16]  315  316  317  318  319  320  321  101  105  106  108  109  111  112  113
[31]  114  118  119  323  322  102  104  107  110  115  116  117  103  100 5000
[46] 5001 5002 5011 5013 7000 7001 7002 7003 7004 7005 7006 7007 7009 7010 7011
[61] 7012 7013 7014 7015 7016 7017 7018 7019 7020 5009 5010 7008 7021  120
# ITP
itp_df_clean <- itp_df_final %>%  
  #as_tibble() %>% #initally used to make it faster in checking the logic
  #slice_sample(n = 50000) %>% #initally used to make it faster in checking the logic
  rowwise() %>%
  mutate(
    year_num = as.numeric(year),
    prev_cover = ifelse(year_num > 2000,
                        get(paste0("F", year_num - 1)),
                        ifelse(year_num == 2000 & F2000 == "ITP", F2000, NA_character_)),
    conversion_itp = case_when(
  # Rapid conversion: forest to ITP in one year interval year (CompDriven value is the year)
  CompDriven > 0 & prev_cover == "Forest" ~
    paste0("1", str_sub(as.character(gridcode), 1, 1), str_pad(year_num %% 100, 2, pad = "0")),

  # Conversion from non-forest to ITP
  CompDriven == 0 & prev_cover == "Non Forest" ~
    paste0("33", str_pad(year_num %% 100, 2, pad = "0")),

  # already ITP in 2000, not a new conversion - second digit is 0
  CompDriven == 0 & prev_cover == "ITP" ~ 
  paste0("30", str_pad(year_num %% 100, 2, pad = "0")),

  # !!! After correction - this should not exist, but we correct it just to be sure! 
  # forest and shows history to plantation, but CompDriven == 0 but forest in y-1)
  CompDriven == 0 & prev_cover == "Forest" & year_num > 2000 ~  
  paste0("1", str_sub(as.character(gridcode), 1, 1), str_pad(year_num %% 100, 2, pad = "0")),

  # Everything else is not handled
  TRUE ~ NA_character_
    )
  ) %>%
  ungroup()

# check if the classification is ok!
# view(itp_df_clean  %>% select(year_num, gridcode, CompDriven, prev_cover, conversion_itp))
unique(itp_df_clean$prev_cover) 
[1] "ITP"        "Non Forest" "Forest"
unique(itp_df_clean$conversion_itp)
 [1] "3000" "3323" "3324" "3301" "3302" "3303" "3304" "3305" "3306" "3307"
[11] "3308" "3309" "3310" "3311" "3312" "3313" "3314" "3315" "3316" "3317"
[21] "3318" "3319" "3320" "3321" "3322" "1121" "1123" "1110" "1111" "1119"
[31] "1120" "1106" "1107" "1108" "1109" "1112" "1113" "1114" "1124" "1103"
[41] "1104" "1105" "1122" "1621" "1622" "1623" "1115" "3016" "1116" "1101"
[51] "1102" "1117" "1118" "1624" "1619" "1620" "1421" "1616" "1423" "1614"
[61] "1615" "1613" "1612" "1605" "1604" "1611" "1610" "1606" "1411" "1410"
[71] "1617" "1618" "1412" "1413" "1414" "1415" "1417" "1420" "1424"
problem <- itp_df_clean  %>% filter(conversion_itp == "problem"|is.na(conversion_itp)) %>% 
      select(year_num, gridcode, CompDriven, prev_cover, F2020, F2021, F2023, F2024, conversion_itp, Area_Ha)
sum(problem$Area_Ha) 
[1] 0
sum(is.na(itp_df_clean$conversion_itp))
[1] 0
# unique(problem$gridcode) #exclude - around 10ha
unique(problem$year_num)
numeric(0)
sum(is.na(itp_df_clean$conversion_itp))
[1] 0
# unique(problem$gridcode) #exclude - around 10ha
unique(problem$year_num)
numeric(0)
# SOLVED: what still does not make sense is if we include any polygons that is forest in y-1 BUT 
# classified as CompDriven == 0 - in theory they should be CompDriven == 1 (rapid forest conversion)
# thus I am excluding this class - anything starts with 31,34, or 36 for ITP (41,44, and 46 for IOPP)
# clean_itp <- itp_df_clean  %>% filter(!str_starts(conversion_itp, "31"), 
#                                       !str_starts(conversion_itp, "34"),
#                                       !str_starts(conversion_itp, "36"))
# unique(clean_itp$conversion_itp)
# sum(clean_itp$Area_Ha) #3333656 
# sum(itp_df_clean$Area_Ha) #3334338 
# 682 ha excluded
# CLEANED
itp_df_all_transition <- itp_df_clean

Rapid and indirect conversion IOPP

iopp_df_clean <- iopp_df_final %>%  
  rowwise() %>%
  mutate(
    year_num = as.numeric(year),
    prev_cover = ifelse(year_num > 2000,
                        get(paste0("F", year_num - 1)),
                        ifelse(year_num == 2000 & F2000 == "IOPP", F2000, NA_character_)),
    conversion_iopp = case_when(
  # Rapid conversion: forest to IOPP in one year interval year (CompDriven value is the year)
  CompDriven > 0 ~ #& prev_cover == "Forest" ~
    paste0("2", str_sub(as.character(gridcode), 1, 1), str_pad(year_num %% 100, 2, pad = "0")),

  # Conversion from non-forest to IOPP
  CompDriven == 0 & prev_cover == "Non Forest" ~
    paste0("43", str_pad(year_num %% 100, 2, pad = "0")),

  # Conversion from ITP to IOPP - only palm has this transition
  CompDriven == 0 & prev_cover == "ITP" ~
    paste0("45", str_pad(year_num %% 100, 2, pad = "0")),

  # already IOPP in 2000, not a new conversion - second digit is 0
  CompDriven == 0 & prev_cover == "IOPP" ~ 
  paste0("40", str_pad(year_num %% 100, 2, pad = "0")),

  # !!! Part of correction to makesure that it is consistent 
  CompDriven == 0 & prev_cover == "Forest" & year_num > 2000 ~ 
  paste0("2", str_sub(as.character(gridcode), 1, 1), str_pad(year_num %% 100, 2, pad = "0")),

  # Everything else is not handled
  TRUE ~ NA_character_
    )
  ) %>%
  ungroup()

unique(iopp_df_clean$prev_cover)
[1] "IOPP"       "Non Forest" "Forest"     "ITP"
unique(iopp_df_clean$conversion_iopp)
  [1] "4000" "4301" "4302" "4303" "4304" "4305" "4306" "4307" "4308" "4309"
 [11] "4310" "4311" "4318" "4321" "4312" "4313" "2113" "4314" "4315" "4316"
 [21] "4317" "4319" "4320" "4322" "4323" "4324" "2112" "2111" "2110" "2109"
 [31] "2115" "2114" "2116" "2117" "2118" "2119" "2120" "2121" "2123" "2106"
 [41] "4508" "4514" "2124" "2101" "2102" "2105" "2107" "2103" "2104" "2108"
 [51] "4512" "4513" "4509" "4510" "4511" "4515" "2415" "4516" "2418" "2419"
 [61] "2420" "2122" "2607" "2611" "2612" "2417" "2624" "4518" "2621" "2619"
 [71] "2620" "2622" "2618" "2617" "2421" "2422" "2623" "2424" "2609" "2613"
 [81] "2411" "2412" "4501" "4504" "4505" "4506" "4507" "4517" "2423" "2608"
 [91] "2615" "2616" "2605" "2610" "2407" "2409" "2410" "2408" "2614" "2403"
[101] "2602" "2606" "4502" "4503" "2601" "2604" "2603" "2413" "2414" "2416"
iopp_df_clean  %>% filter(prev_cover == 'ITP')
Simple feature collection with 4694 features and 38 fields
Geometry type: GEOMETRY
Dimension:     XY
Bounding box:  xmin: 99.88457 ymin: -3.617235 xmax: 116.3442 ymax: 1.372087
Geodetic CRS:  WGS 84
# A tibble: 4,694 × 39
   uid      year  gridcode F2000 F2001 F2002 F2003 F2004 F2005 F2006 F2007 F2008
 * <chr>    <chr>    <int> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
 1 P_136904 2008       300 ITP   ITP   ITP   ITP   ITP   ITP   ITP   ITP   IOPP 
 2 P_136905 2008       300 ITP   ITP   ITP   ITP   ITP   ITP   ITP   ITP   IOPP 
 3 P_136906 2008       300 ITP   ITP   ITP   ITP   ITP   ITP   ITP   ITP   IOPP 
 4 P_136907 2008       300 ITP   ITP   ITP   ITP   ITP   ITP   ITP   ITP   IOPP 
 5 P_136908 2008       300 ITP   ITP   ITP   ITP   ITP   ITP   ITP   ITP   IOPP 
 6 P_136909 2008       300 ITP   ITP   ITP   ITP   ITP   ITP   ITP   ITP   IOPP 
 7 P_136910 2008       300 ITP   ITP   ITP   ITP   ITP   ITP   ITP   ITP   IOPP 
 8 P_136911 2008       300 ITP   ITP   ITP   ITP   ITP   ITP   ITP   ITP   IOPP 
 9 P_136912 2008       300 Non … Non … Non … Non … ITP   ITP   ITP   ITP   IOPP 
10 P_136913 2008       300 Non … Non … Non … Non … ITP   ITP   ITP   ITP   IOPP 
# ℹ 4,684 more rows
# ℹ 27 more variables: F2009 <chr>, F2010 <chr>, F2011 <chr>, F2012 <chr>,
#   F2013 <chr>, F2014 <chr>, F2015 <chr>, F2016 <chr>, F2017 <chr>,
#   F2018 <chr>, F2019 <chr>, F2020 <chr>, F2021 <chr>, F2022 <chr>,
#   F2023 <chr>, F2024 <chr>, CompDriven_init <int>, District <chr>,
#   Province <chr>, Country <chr>, Region <chr>, Area_Ha <dbl>,
#   Shape <POLYGON [°]>, CompDriven <dbl>, year_num <dbl>, prev_cover <chr>, …
problem_iopp <- iopp_df_clean  %>% filter(conversion_iopp == "problem"|is.na(conversion_iopp)) %>% 
      select(year_num, gridcode, CompDriven, prev_cover, starts_with('F2'), conversion_iopp, Area_Ha)
problem_iopp_count <- problem_iopp  %>%  
    count(year_num, CompDriven, gridcode, prev_cover, conversion_iopp)
sum(problem_iopp$Area_Ha) 
[1] 0
sum(is.na(iopp_df_clean$conversion_iopp))
[1] 0
# SOLVED - unnecessary: excluding any y-1 forest but not rapid conversion 
# clean_iopp <- iopp_df_clean  %>% filter(!str_starts(conversion_iopp, "41"), !str_starts(conversion_iopp, "44"),
#                                       !str_starts(conversion_iopp, "46"))
# unique(clean_iopp$conversion_iopp)
# sum(clean_iopp$Area_Ha) #11041732
# sum(iopp_df_clean$Area_Ha)  #11061716 (19984ha excluded)

# Cleaned
iopp_df_all_transition <- iopp_df_clean

Visualization

# quartz(width = 10, height = 6)

# prep oil palm data for visualization
order <- c("Non Forest", "ITP", "Peat Swamp Forest", "Mangrove Forest", "Forest")
iopp_df_visu  <- iopp_df_all_transition %>% 
    mutate(prev_land_cover = str_sub(as.character(conversion_iopp), 2, 2), 
          prev_land_cover = case_when(prev_land_cover == 1 ~ "Forest",
                                      prev_land_cover == 4 ~ "Mangrove Forest",
                                      prev_land_cover == 6 ~ "Peat Swamp Forest",
                                      prev_land_cover == 3 ~ "Non Forest",
                                      prev_land_cover == 5 ~ "ITP",
                                      prev_land_cover == 0 ~ "Already palm in 2000",
                                      TRUE ~ "Others")) %>%
    mutate(conv_type = ifelse(CompDriven > 0, "Rapid", "Indirect"))  %>%  #indirect means ultimately converted
    group_by(conv_type, year, prev_land_cover) %>% 
    summarise(Area_Ha = sum(Area_Ha, na.rm = TRUE)) %>% 
    ungroup()
`summarise()` has grouped output by 'conv_type', 'year'. You can override using
the `.groups` argument.
head(iopp_df_visu)
Simple feature collection with 6 features and 4 fields
Geometry type: GEOMETRY
Dimension:     XY
Bounding box:  xmin: 95.49075 ymin: -7.60486 xmax: 140.8524 ymax: 5.088223
Geodetic CRS:  WGS 84
# A tibble: 6 × 5
  conv_type year  prev_land_cover        Area_Ha                           Shape
  <chr>     <chr> <chr>                    <dbl>                  <GEOMETRY [°]>
1 Indirect  2000  Already palm in 2000 4171891.  MULTIPOLYGON (((120.4202 -2.55…
2 Indirect  2001  ITP                     1208.  MULTIPOLYGON (((101.0242 0.554…
3 Indirect  2001  Non Forest             91273.  MULTIPOLYGON (((103.4517 -2.95…
4 Indirect  2002  ITP                       87.7 MULTIPOLYGON (((101.3762 0.892…
5 Indirect  2002  Non Forest             89704.  MULTIPOLYGON (((110.8515 -2.42…
6 Indirect  2003  ITP                      144.  POLYGON ((101.3877 0.8705, 101…
ggplot(iopp_df_visu  %>% filter(prev_land_cover != "Already palm in 2000"), 
aes(x = as.factor(year), y = Area_Ha/1000, fill = factor(prev_land_cover, levels = order))) +
    geom_bar(stat = "identity")+ #position = "dodge") +
    scale_fill_viridis_d(option = "D") +
    labs(title = "IOPP expansion over time",
         x = "Clearing year", y = "Total expansion area (thousand Ha)", fill = "Previous land cover") +
    #facet_wrap(~conv_type)+
    theme_minimal()

# prepare wood pulp data for visualization
order_itp <- c("Non Forest", "Peat Swamp Forest", "Mangrove Forest", "Forest")
itp_df_visu  <- itp_df_all_transition %>% 
    #filter(CompDriven != 0) %>% 
    mutate(prev_land_cover = str_sub(as.character(conversion_itp), 2, 2), 
          prev_land_cover = case_when(prev_land_cover == 1 ~ "Forest",
                                      prev_land_cover == 4 ~ "Mangrove Forest",
                                      prev_land_cover == 6 ~ "Peat Swamp Forest",
                                      prev_land_cover == 3 ~ "Non Forest",
                                      prev_land_cover == 0 ~ "Already woodpulp in 2000",
                                      TRUE ~ "Others")) %>%
    mutate(conv_type = ifelse(CompDriven > 0, "Rapid", "Indirect"))  %>%  #indirect means ultimately converted
    group_by(conv_type, year, prev_land_cover) %>% 
    summarise(Area_Ha = sum(Area_Ha, na.rm = TRUE)) %>% 
    ungroup()
`summarise()` has grouped output by 'conv_type', 'year'. You can override using
the `.groups` argument.
head(itp_df_visu)
Simple feature collection with 6 features and 4 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 95.54244 ymin: -6.60075 xmax: 140.6248 ymax: 5.498833
Geodetic CRS:  WGS 84
# A tibble: 6 × 5
  conv_type year  prev_land_cover           Area_Ha                        Shape
  <chr>     <chr> <chr>                       <dbl>           <MULTIPOLYGON [°]>
1 Indirect  2000  Already woodpulp in 2000 1130291. (((101.4303 0.8559026, 101.…
2 Indirect  2001  Non Forest                 30141. (((101.5717 -0.2137218, 101…
3 Indirect  2002  Non Forest                 26604. (((103.6612 -3.47648, 103.6…
4 Indirect  2003  Non Forest                 13268. (((101.8499 0.08275, 101.84…
5 Indirect  2004  Non Forest                 68657. (((103.5184 -3.643, 103.518…
6 Indirect  2005  Non Forest                 80277. (((103.5195 -3.50975, 103.5…
ggplot(itp_df_visu  %>% filter(prev_land_cover != "Already woodpulp in 2000"), 
aes(x = as.factor(year), y = Area_Ha/1000, fill = factor(prev_land_cover, levels = order))) +
    geom_bar(stat = "identity")+ #position = "dodge") +
    scale_fill_viridis_d(option = "D") +
    labs(title = "ITP expansion over time",
         x = "Clearing year", y = "Total expansion area (thousand Ha)", fill = "Previous land cover") +
    #facet_wrap(~conv_type)+
    theme_minimal()

Map the data

# map the IOPP and ITP data 
itp_map <- itp_df_visu  %>% filter(prev_land_cover != "Already woodpulp in 2000")
map_itp_all_conv <- ggplot() +
    geom_sf(data = itp_map, aes(fill = prev_land_cover), alpha = 0.5, color = NA) +
    scale_fill_viridis_d(option = "D") +
    labs(title = "ITP Expansion Areas") +
    theme_minimal() +
    theme(legend.position = "bottom")

map_itp_all_conv

iopp_map <- iopp_df_visu  %>% filter(prev_land_cover != "Already woodpulp in 2000")
map_iopp_all_conv <- ggplot() +
    geom_sf(data = iopp_map, aes(fill = prev_land_cover), alpha = 0.5, color = NA) +
    scale_fill_viridis_d(option = "D") +
    labs(title = "IOPP Expansion Areas") +
    theme_minimal() +
    theme(legend.position = "bottom")

map_itp_all_conv

Map Rapid expansion by province

names(itp_map)
[1] "conv_type"       "year"            "prev_land_cover" "Area_Ha"        
[5] "Shape"
province_itp <- unique(itp_df_rapid$Province)
map_itp_rapid_conv <- map(province_itp, function(prov) {
  d <- itp_df_rapid %>% filter(foresttotimber != 100, Province == prov)
  ggplot() +
    geom_sf(data = d, aes(fill = factor(foresttotimber)), alpha = 0.5, color = NA) +
    scale_fill_viridis_d(option = "D") +
    labs(title = prov) +
    theme_minimal() +
    theme(
      legend.position = "none",      
      axis.text = element_blank(),
      axis.title = element_blank(),
      axis.ticks = element_blank()
    )
})

wrap_plots(map_itp_rapid_conv[1:16], ncol = 4, guides = "collect") & 
theme(legend.position = "right", panel.grid = element_blank())

province_iopp <- unique(iopp_df_rapid$Province)
map_iopp_rapid_conv <- map(province_iopp, function(prov) {
  d <- iopp_df_rapid %>% filter(foresttopalm != 200, Province == prov)
  ggplot() +
    geom_sf(data = d, aes(fill = factor(foresttopalm)), alpha = 0.5, color = NA) +
    scale_fill_viridis_d(option = "D") +
    labs(title = prov) +
    theme_minimal() +
    theme(
      legend.position = "none",      
      axis.text = element_blank(),
      axis.title = element_blank(),
      axis.ticks = element_blank()
    )
})
wrap_plots(map_iopp_rapid_conv[1:26], ncol = 6, guides = "collect") & 
theme(legend.position = "right")

Rasterize and upload to S3

# rasterize 
rast_upload <- function(sf_obj, field, s3_bucket, s3_path, res = 0.00025) {
  crs_val <- st_crs(sf_obj)
  v <- vect(sf_obj)
  template <- rast(ext = st_bbox(sf_obj), resolution = res, crs = crs_val$wkt)
  r <- rasterize(v, template, field = field)
  print(summary(r))
  print(plot(r))

  temp_file <- tempfile(fileext = ".tif") # write a temp file
  writeRaster(r, temp_file, overwrite = TRUE)

  put_object(file = temp_file, object = s3_path, bucket = s3_bucket) # upload to S3

  unlink(temp_file)
  return(r)
}

# define path and bucket
s3_bucket <- "trase-storage"  
s3_path_iopp_rapid <- "indonesia/wood_pulp/indicators/in/q3_2025/gaveau_iopp_foresttopalm_rapid_2024.tif"
s3_path_itp_rapid <- "indonesia/wood_pulp/indicators/in/q3_2025/gaveau_itp_foresttotimber_rapid_2024.tif"
s3_path_iopp <- "indonesia/wood_pulp/indicators/in/q3_2025/gaveau_iopp_expansion_2024.tif"
s3_path_itp <- "indonesia/wood_pulp/indicators/in/q3_2025/gaveau_itp_expansion_2024.tif"

# rasterize and upload to S3
r_itp <- rast_upload(
  sf_obj = itp_df_rapid,
  field = "foresttotimber",
  s3_bucket = s3_bucket,
  s3_path = s3_path_itp_rapid)

r_iopp <- rast_upload(
  sf_obj = iopp_df_rapid,
  field = "foresttopalm",
  s3_bucket = s3_bucket,
  s3_path = s3_path_iopp_rapid)

# all conversion
unique(itp_df_all_transition$conversion_itp) #still has 3000 can be excluded easily in GEE
# rasterize and upload to S3
r_itp_all <- rast_upload(
  sf_obj = itp_df_all_transition,
  field = "conversion_itp",
  s3_bucket = s3_bucket,
  s3_path = s3_path_itp)

unique(iopp_df_all_transition$conversion_iopp) #still has 4000 can be excluded easily in GEE
r_iopp_all <- rast_upload(
  sf_obj = iopp_df_all_transition,
  field = "conversion_iopp",
  s3_bucket = s3_bucket,
  s3_path = s3_path_iopp)

Shapefile export & save to S3

shp_to_s3 <- function(sf_obj, s3_bucket, s3_prefix, shp_name) {
  temp_dir <- tempdir()
  temp_shp <- file.path(temp_dir, shp_name)
  st_write(sf_obj, temp_shp, delete_dsn = TRUE)
  base_name <- tools::file_path_sans_ext(shp_name)
  shp_files <- list.files(temp_dir, pattern = paste0("^", base_name, "\\."), full.names = TRUE)
  for (f in shp_files) {
    put_object(
      file = f,
      object = file.path(s3_prefix, basename(f)),
      bucket = s3_bucket
    )
  }
  file.remove(shp_files)
}
# raw data that has been geometrically corrected
#only rapid
shp_to_s3(
  iopp_df_rapid %>% select(uid, year, gridcode, CompDriven, foresttopalm, District, Province, Area_Ha),
  s3_bucket,
  "indonesia/wood_pulp/indicators/in/q3_2025",
  "gaveau_iopp_rapid_expansion_2024.shp"
)

shp_to_s3(
  itp_df_rapid %>% select(uid, year, gridcode, CompDriven, foresttotimber, District, Province, Area_Ha),
  s3_bucket,
  "indonesia/wood_pulp/indicators/in/q3_2025",
  "gaveau_itp_rapid_expansion_2024.shp"
)

# all transitions
shp_to_s3(
  iopp_df_all_transition %>% select(uid, year, gridcode, CompDriven, prev_cover, conversion_iopp, District, Province, Area_Ha),
  s3_bucket,
  "indonesia/wood_pulp/indicators/in/q3_2025",
  "gaveau_iopp_all_expansion_2024.shp"
)

shp_to_s3(
  itp_df_all_transition %>% select(uid, year, gridcode, CompDriven, prev_cover, conversion_itp, District, Province, Area_Ha),
  s3_bucket,
  "indonesia/wood_pulp/indicators/in/q3_2025",
  "gaveau_itp_all_expansion_2024.shp"
)