ropensci/MODISTools

Interpreting QC

Opened this issue · 2 comments

I didn't find much useful information for how to interpret the quality control information accessible through MODISTools downloads of respective bands. I wrote the following code (see also here for MOD13Q1 and here for MCD15A3H). I thought I post it here not as an actual "issue", and also not as an definite instruction for others, but rather as a call for others to point out possible errors or misinterpretations here. (I hard-coded a qc-flag-based filtering in the ingestr package, which provides yet another wrapper around MODISTools and more, see here).

For MCD15A3H (band FparLai_QC) interpreted according to this document:

df <- df %>%

      ## separate into bits
      rowwise() %>%
      mutate(qc_bitname = intToBits( qc )[1:8] %>% rev() %>% as.character() %>% paste(collapse = "")) %>%

      ## MODLAND_QC bits
      ## 0: Good  quality (main algorithm with or without saturation)
      ## 1: Other quality (backup  algorithm or fill values)
      mutate(qc_bit0 = substr( qc_bitname, start=8, stop=8 )) %>%
      mutate(good_quality = ifelse( qc_bit0=="0", TRUE, FALSE )) %>%

      ## Sensor
      ## 0: Terra
      ## 1: Aqua
      mutate(qc_bit1 = substr( qc_bitname, start=7, stop=7 )) %>%
      mutate(terra = ifelse( qc_bit1=="0", TRUE, FALSE )) %>%

      ## Dead detector
      ## 0: Detectors apparently  fine  for up  to  50% of  channels  1,  2
      ## 1: Dead  detectors caused  >50%  adjacent  detector  retrieval
      mutate(qc_bit2 = substr( qc_bitname, start=6, stop=6 )) %>%
      mutate(dead_detector = ifelse( qc_bit2=="1", TRUE, FALSE )) %>%

      ## CloudState
      ## 00 0  Significant clouds  NOT present (clear)
      ## 01 1  Significant clouds  WERE  present
      ## 10 2  Mixed cloud present in  pixel
      ## 11 3  Cloud state not defined,  assumed clear
      mutate(qc_bit3 = substr( qc_bitname, start=4, stop=5 )) %>%
      mutate(CloudState = ifelse( qc_bit3=="00", 0, ifelse( qc_bit3=="01", 1, ifelse( qc_bit3=="10", 2, 3 ) ) )) %>%

      ## SCF_QC (five level confidence score)
      ## 000 0 Main (RT) method used, best result possible (no saturation)
      ## 001 1 Main (RT) method used with saturation. Good, very usable
      ## 010 2 Main (RT) method failed due to bad geometry, empirical algorithm used
      ## 011 3 Main (RT) method failed due to problems other than geometry, empirical algorithm used
      ## 100 4 Pixel not produced at all, value couldn???t be retrieved (possible reasons: bad L1B data, unusable MOD09GA data)
      mutate(qc_bit4 = substr( qc_bitname, start=1, stop=3 )) %>%
      mutate(SCF_QC = ifelse( qc_bit4=="000", 0, ifelse( qc_bit4=="001", 1, ifelse( qc_bit4=="010", 2, ifelse( qc_bit4=="011", 3, 4 ) ) ) ))

And for MOD13Q1 (band 250m_16_days_VI_Quality) based on this document:

df <- df %>%

      ## separate into bits
      rowwise() %>%
      mutate(qc_bitname = intToBits( qc ) %>% as.character() %>% paste(collapse = "")) %>%

      ## Bits 0-1: VI Quality
      ##   00 VI produced with good quality
      ##   01 VI produced, but check other QA
      mutate(vi_quality = substr( qc_bitname, start=1, stop=2 )) %>%

      ## Bits 2-5: VI Usefulness
      ##   0000 Highest quality
      ##   0001 Lower quality
      ##   0010 Decreasing quality
      ##   0100 Decreasing quality
      ##   1000 Decreasing quality
      ##   1001 Decreasing quality
      ##   1010 Decreasing quality
      ##   1100 Lowest quality
      ##   1101 Quality so low that it is not useful
      ##   1110 L1B data faulty
      ##   1111 Not useful for any other reason/not processed
      mutate(vi_useful = substr( qc_bitname, start=3, stop=6 )) %>%

      ## Bits 6-7: Aerosol Quantity
      ##  00 Climatology
      ##  01 Low
      ##  10 Intermediate
      ##  11 High
      mutate(aerosol = substr( qc_bitname, start=7, stop=8 )) %>%

      ## Bit 8: Adjacent cloud detected
      ##  0 No
      ##  1 Yes
      mutate(adjcloud = substr( qc_bitname, start=9, stop=9 )) %>%

      ## Bits 9: Atmosphere BRDF Correction
      ##   0 No
      ##   1 Yes
      mutate(brdf_corr = substr( qc_bitname, start=10, stop=10 )) %>%

      ## Bits 10: Mixed Clouds
      ##   0 No
      ##   1 Yes
      mutate(mixcloud = substr( qc_bitname, start=11, stop=11 )) %>%

      ## Bits 11-13: Land/Water Mask
      ##  000 Shallow ocean
      ##  001 Land (Nothing else but land)
      ##  010 Ocean coastlines and lake shorelines
      ##  011 Shallow inland water
      ##  100 Ephemeral water
      ##  101 Deep inland water
      ##  110 Moderate or continental ocean
      ##  111 Deep ocean
      mutate(mask = substr( qc_bitname, start=12, stop=14 )) %>%

      ## Bits 14: Possible snow/ice
      ##  0 No
      ##  1 Yes
      mutate(snowice = substr( qc_bitname, start=15, stop=15 )) %>%

      ## Bits 15: Possible shadow
      ##  0 No
      ##  1 Yes
      mutate(shadow = substr( qc_bitname, start=16, stop=16 ))

Here df is a data frame with column qc containing integers downloaded as the respective QC band.

Thanks a million, this is great.

I'll add it to the vignette if I find the time!

Pinned it so people find it quickly!