Evalscript Planet does not return nan in dataMask

Hi,

I’m trying to get two layers of data through the planet API, the NDVI and the WDVI. I manage to get both out, however, the dataMask outside my polygon is NaN at the NDVI (appearing as a transparent band) while the dataMask outside my polygon at the WDVI is 0, which is shown as a normal layer number. The only thing that differs is the eval script. See below for the two evalscripts. Can anyone of you explain what’s the difference between both?

Evalscript NDVI (returns Nan outside specified polygon):

function setup() {
  return {
    input: [{"bands": ["Blue","Green","Red", "NIR","UDM2_Clear",
    "UDM2_Snow","UDM2_Shadow","UDM2_LightHaze","UDM2_HeavyHaze","UDM2_Cloud","UDM2_Confidence", "dataMask"]}],
    output: [{id: "index", bands: 1, sampleType: "FLOAT32" },
            {id: "trueColor", bands: 4, sampleType: "FLOAT32" },
            {id: "usablePixels", bands: 1, sampleType: "FLOAT32" },
            {id: "cloudprobability", bands: 1, sampleType: "FLOAT32" },
            {id: "dataMask", bands: 1, sampleType: "FLOAT32" }
  ]
  }
}
  function evaluatePixel(sample) {
    let f = 10000
    let ndvi = ((sample.NIR/f) - (sample.Red/f)/(sample.NIR/f) + (sample.Red/f));
    
    
    return {
      index: [ndvi],
      trueColor: [sample.Red/f*2.5, sample.Green/f*2.5, sample.Blue/f*2.5, sample.dataMask],
      usablePixels : [sample.UDM2_Clear],
      cloudprobability: [cloudprob(sample)],
      dataMask: [sample.dataMask],
      
    }
  }


  
function cloudprob(sample) {
  if (sample.UDM2_Snow == 1) {
    // Saturated or defective pixel (red)
    return 80;
  } else if (sample.UDM2_Shadow == 1) {
    // Dark features / Shadows (very dark grey)
    return 50;
  } else if (sample.UDM2_LightHaze == 1) {
    // Cloud shadows (dark brown)
    return 40;
  } else if (sample.UDM2_HeavyHaze == 1) {
    // Vegetation (green)
    return 70;
  } else if (sample.UDM2_Cloud == 1) {
    // Not-vegetated (dark yellow)
    return 100;
  }
  return 0;
}

Evalscript WDVI (returns 0 outside specified polygon):

function setup() {
  return {
    input: [{"bands": ["Blue","Green","Red", "NIR","UDM2_Clear",
    "UDM2_Snow","UDM2_Shadow","UDM2_LightHaze","UDM2_HeavyHaze","UDM2_Cloud","UDM2_Confidence", "dataMask"]}],
    output: [{id: "index", bands: 1, sampleType: "FLOAT32" },
            {id: "trueColor", bands: 4, sampleType: "FLOAT32" },
            {id: "usablePixels", bands: 1, sampleType: "FLOAT32" },
            {id: "cloudprobability", bands: 1, sampleType: "FLOAT32" },
            {id: "dataMask", bands: 1, sampleType: "FLOAT32" }
  ]
  }
}
  function evaluatePixel(sample) {
    let a = 1.5 // factor to correct for soils, for soils, NIR is amost always higher than red. Therefore red has to be corrected
    // a = (NIRsoil/Redsoil) if some fields are off, this calibration should be specific per field on bare soil
    let f = 10000
    let wdvi = (sample.NIR/f) - a * (sample.Red/f);
    return {
      index: [wdvi],
      trueColor: [sample.Red/f*2.5, sample.Green/f*2.5, sample.Blue/f*2.5, sample.dataMask],
      usablePixels : [sample.UDM2_Clear],
      cloudprobability: [cloudprob(sample)],
      dataMask: [sample.dataMask],
      
    }
  }


  
function cloudprob(sample) {
  if (sample.UDM2_Snow == 1) {
    // Saturated or defective pixel (red)
    return 80;
  } else if (sample.UDM2_Shadow == 1) {
    // Dark features / Shadows (very dark grey)
    return 50;
  } else if (sample.UDM2_LightHaze == 1) {
    // Cloud shadows (dark brown)
    return 40;
  } else if (sample.UDM2_HeavyHaze == 1) {
    // Vegetation (green)
    return 70;
  } else if (sample.UDM2_Cloud == 1) {
    // Not-vegetated (dark yellow)
    return 100;
  }
  return 0;
}


Hi @jitsr,

In short, when you try to apply NDVI formula for the pixels outside of your polygon the value of the denominator will be zero. Because division by zero is not defined, you get NaNs.for those pixels. In the WDVI formula there are no divisions, so you do not run int this problem. If you apply the WDVI formula for the pixels outside of your polygon you get 0 (because pixel value of input bands is zero outside of polygon).

BR, Anja

Wauw, that’s too logical. I should have come up with that myself. Thank you very much.
However, I still got the question how I could have the data outside my polygon transparent. I thought the dataMask layer should take care of this. Obviously this is not the case. So my question is, how can I make sure that the pixels outside my polygon are given NaN-values? A work around would be to create a formula like I accidentally did with the NDVI, but that’s not the prettiest solution I assume.

Yes, you will have to use dataMask but it is up to you what values will you output for no data pixels. If you are requesting tif format, you can add something like this to your evaluatePixel function:

...
  if (sample.dataMask == 1)  {
    wdvi = (sample.NIR/f) - a * (sample.Red/f)
  } else {
    wdvi = NaN
 }
...

I suggest checking the guide about dataMask: https://docs.sentinel-hub.com/api/latest/user-guides/datamask/