Example #1
0
  static boolean setFlagCodingsAndBitmaskDefs(final Product product) {
    final FlagCoding flagCoding = new FlagCoding("GLOBCOLOUR");

    for (Flags flag : Flags.values()) {
      flagCoding.addFlag(flag.name(), flag.getMask(), flag.getDescription());
    }

    boolean codingAdded = false;

    for (final String name : product.getBandNames()) {
      if (name.endsWith("flags")) {
        final Band band = product.getBand(name);

        if (band == null || band.isFloatingPointType() || band.getFlagCoding() != null) {
          continue;
        }
        if (!product.getFlagCodingGroup().contains(flagCoding.getName())) {
          product.getFlagCodingGroup().add(flagCoding);
        }

        band.setSampleCoding(flagCoding);
        addBitmaskDefinitions(product, name);

        codingAdded = true;
      }
    }

    return codingAdded;
  }
  /**
   * This method copies all bands which contain a flagcoding from the source product to the target
   * product.
   *
   * @param sourceProduct the source product
   * @param targetProduct the target product
   */
  public static void copyDownscaledFlagBands(
      Product sourceProduct, Product targetProduct, float scalingFactor) {
    Guardian.assertNotNull("source", sourceProduct);
    Guardian.assertNotNull("target", targetProduct);
    if (sourceProduct.getFlagCodingGroup().getNodeCount() > 0) {

      ProductUtils.copyFlagCodings(sourceProduct, targetProduct);
      ProductUtils.copyMasks(sourceProduct, targetProduct);

      // loop over bands and check if they have a flags coding attached
      for (int i = 0; i < sourceProduct.getNumBands(); i++) {
        Band sourceBand = sourceProduct.getBandAt(i);
        FlagCoding coding = sourceBand.getFlagCoding();
        if (coding != null) {
          Band targetBand = AerosolHelpers.downscaleBand(sourceBand, scalingFactor);
          targetBand.setSampleCoding(coding);
          targetProduct.addBand(targetBand);
        }
      }
    }
  }
  /**
   * This method copies the flag bands from the synergy product to the target product
   *
   * @param synergyProduct - the Synergy product
   * @param targetProduct - the target product
   */
  public static void copySynergyFlagBands(Product synergyProduct, Product targetProduct) {
    final Band aatsrConfidFlagNadirBand =
        targetProduct.addBand(SynergyConstants.CONFID_NADIR_FLAGS_AATSR, ProductData.TYPE_INT16);
    final Band aatsrConfidFlagFwardBand =
        targetProduct.addBand(SynergyConstants.CONFID_FWARD_FLAGS_AATSR, ProductData.TYPE_INT16);
    final Band aatsrCloudFlagNadirBand =
        targetProduct.addBand(SynergyConstants.CLOUD_NADIR_FLAGS_AATSR, ProductData.TYPE_INT16);
    final Band aatsrCloudFlagFwardBand =
        targetProduct.addBand(SynergyConstants.CLOUD_FWARD_FLAGS_AATSR, ProductData.TYPE_INT16);
    final Band merisL1FlagsBand =
        targetProduct.addBand(SynergyConstants.L1_FLAGS_MERIS, ProductData.TYPE_INT16);
    final Band merisCloudFlagBand =
        targetProduct.addBand(SynergyConstants.CLOUD_FLAG_MERIS, ProductData.TYPE_INT16);

    final FlagCoding aatsrConfidNadirFlagCoding =
        synergyProduct.getFlagCodingGroup().get(SynergyConstants.CONFID_NADIR_FLAGS_AATSR);
    ProductUtils.copyFlagCoding(aatsrConfidNadirFlagCoding, targetProduct);
    aatsrConfidFlagNadirBand.setSampleCoding(aatsrConfidNadirFlagCoding);

    final FlagCoding aatsrConfidFwardFlagCoding =
        synergyProduct.getFlagCodingGroup().get(SynergyConstants.CONFID_FWARD_FLAGS_AATSR);
    ProductUtils.copyFlagCoding(aatsrConfidFwardFlagCoding, targetProduct);
    aatsrConfidFlagFwardBand.setSampleCoding(aatsrConfidFwardFlagCoding);

    final FlagCoding aatsrCloudNadirFlagCoding =
        synergyProduct.getFlagCodingGroup().get(SynergyConstants.CLOUD_NADIR_FLAGS_AATSR);
    ProductUtils.copyFlagCoding(aatsrCloudNadirFlagCoding, targetProduct);
    aatsrCloudFlagNadirBand.setSampleCoding(aatsrCloudNadirFlagCoding);

    final FlagCoding aatsrCloudFwardFlagCoding =
        synergyProduct.getFlagCodingGroup().get(SynergyConstants.CLOUD_FWARD_FLAGS_AATSR);
    ProductUtils.copyFlagCoding(aatsrCloudFwardFlagCoding, targetProduct);
    aatsrCloudFlagFwardBand.setSampleCoding(aatsrCloudFwardFlagCoding);

    final FlagCoding merisL1FlagsCoding =
        synergyProduct.getFlagCodingGroup().get(SynergyConstants.L1_FLAGS_MERIS);
    ProductUtils.copyFlagCoding(merisL1FlagsCoding, targetProduct);
    merisL1FlagsBand.setSampleCoding(merisL1FlagsCoding);

    final FlagCoding merisCloudFlagCoding =
        synergyProduct.getFlagCodingGroup().get(SynergyConstants.CLOUD_FLAG_MERIS);
    ProductUtils.copyFlagCoding(merisCloudFlagCoding, targetProduct);
    merisCloudFlagBand.setSampleCoding(merisCloudFlagCoding);
  }
  @Override
  protected void addFlagsAndMasks(Product product) {
    Band QFBand = product.getBand("l3m_qual");
    if (QFBand != null) {
      FlagCoding flagCoding = new FlagCoding("SST_Quality");
      flagCoding.addFlag("Best", 0x00, "Highest quality retrieval");
      flagCoding.addFlag("Good", 0x01, "Good quality retrieval");
      flagCoding.addFlag("Questionable", 0x02, "Questionable quality retrieval");
      flagCoding.addFlag("Bad", 0x03, "Bad quality retrieval");
      product.getFlagCodingGroup().add(flagCoding);
      QFBand.setSampleCoding(flagCoding);

      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Best",
                  "Highest quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l3m_qual == 0",
                  SeadasFileReader.Cornflower,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Good",
                  "Good quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l3m_qual == 1",
                  SeadasFileReader.LightPurple,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Questionable",
                  "Questionable quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l3m_qual == 2",
                  SeadasFileReader.BurntUmber,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Bad",
                  "Bad quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l3m_qual == 3",
                  SeadasFileReader.FailRed,
                  0.6));
    }
    QFBand = product.getBand("qual_sst");
    if (QFBand != null) {
      FlagCoding flagCoding = new FlagCoding("SST_Quality");
      flagCoding.addFlag("Best", 0x00, "Highest quality retrieval");
      flagCoding.addFlag("Good", 0x01, "Good quality retrieval");
      flagCoding.addFlag("Questionable", 0x02, "Questionable quality retrieval");
      flagCoding.addFlag("Bad", 0x03, "Bad quality retrieval");
      product.getFlagCodingGroup().add(flagCoding);
      QFBand.setSampleCoding(flagCoding);

      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Best",
                  "Highest quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst == 0",
                  SeadasFileReader.Cornflower,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Good",
                  "Good quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst == 1",
                  SeadasFileReader.LightPurple,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Questionable",
                  "Questionable quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst == 2",
                  SeadasFileReader.BurntUmber,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Bad",
                  "Bad quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst == 3",
                  SeadasFileReader.FailRed,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "No Data",
                  "No data retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst == -1",
                  SeadasFileReader.MediumGray,
                  0.6));
    }
    QFBand = product.getBand("qual_sst4");
    if (QFBand != null) {
      FlagCoding flagCoding = new FlagCoding("SST_Quality");
      flagCoding.addFlag("Best", 0x00, "Highest quality retrieval");
      flagCoding.addFlag("Good", 0x01, "Good quality retrieval");
      flagCoding.addFlag("Questionable", 0x02, "Questionable quality retrieval");
      flagCoding.addFlag("Bad", 0x03, "Bad quality retrieval");
      product.getFlagCodingGroup().add(flagCoding);
      QFBand.setSampleCoding(flagCoding);

      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Best",
                  "Highest quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst4 == 0",
                  SeadasFileReader.Cornflower,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Good",
                  "Good quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst4 == 1",
                  SeadasFileReader.LightPurple,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Questionable",
                  "Questionable quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst4 == 2",
                  SeadasFileReader.BurntUmber,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Bad",
                  "Bad quality retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst4 == 3",
                  SeadasFileReader.FailRed,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "No Data",
                  "No data retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst4 == -1",
                  SeadasFileReader.MediumGray,
                  0.6));
    }
  }
Example #5
0
  protected void addFlagsAndMasks(Product product) {
    Band QFBand = product.getBand("l2_flags");
    if (QFBand != null) {
      FlagCoding flagCoding = new FlagCoding("L2Flags");
      flagCoding.addFlag("ATMFAIL", 0x01, "Atmospheric correction failure");
      flagCoding.addFlag("LAND", 0x02, "Land");
      flagCoding.addFlag("PRODWARN", 0x04, "One (or more) product algorithms generated a warning");
      flagCoding.addFlag("HIGLINT", 0x08, "High glint determined");
      flagCoding.addFlag("HILT", 0x10, "High (or saturating) TOA radiance");
      flagCoding.addFlag("HISATZEN", 0x20, "Large satellite zenith angle");
      flagCoding.addFlag("COASTZ", 0x40, "Shallow water (<30m)");
      flagCoding.addFlag("SPARE8", 0x80, "Unused");
      flagCoding.addFlag("STRAYLIGHT", 0x100, "Straylight determined");
      flagCoding.addFlag("CLDICE", 0x200, "Cloud/Ice determined");
      flagCoding.addFlag("COCCOLITH", 0x400, "Coccolithophores detected");
      flagCoding.addFlag("TURBIDW", 0x800, "Turbid water determined");
      flagCoding.addFlag("HISOLZEN", 0x1000, "High solar zenith angle");
      flagCoding.addFlag("SPARE14", 0x2000, "Unused");
      flagCoding.addFlag("LOWLW", 0x4000, "Low Lw @ 555nm (possible cloud shadow)");
      flagCoding.addFlag("CHLFAIL", 0x8000, "Chlorophyll algorithm failure");
      flagCoding.addFlag("NAVWARN", 0x10000, "Navigation suspect");
      flagCoding.addFlag("ABSAER", 0x20000, "Absorbing Aerosols determined");
      flagCoding.addFlag("SPARE19", 0x40000, "Unused");
      flagCoding.addFlag("MAXAERITER", 0x80000, "Maximum iterations reached for NIR iteration");
      flagCoding.addFlag("MODGLINT", 0x100000, "Moderate glint determined");
      flagCoding.addFlag("CHLWARN", 0x200000, "Chlorophyll out-of-bounds (<0.01 or >100 mg m^-3)");
      flagCoding.addFlag(
          "ATMWARN", 0x400000, "Atmospheric correction warning; Epsilon out-of-bounds");
      flagCoding.addFlag("SPARE24", 0x800000, "Unused");
      flagCoding.addFlag("SEAICE", 0x1000000, "Sea ice determined");
      flagCoding.addFlag("NAVFAIL", 0x2000000, "Navigation failure");
      flagCoding.addFlag("FILTER", 0x4000000, "Insufficient data for smoothing filter");
      flagCoding.addFlag("SSTWARN", 0x8000000, "Sea surface temperature suspect");
      flagCoding.addFlag("SSTFAIL", 0x10000000, "Sea surface temperature algorithm failure");
      flagCoding.addFlag("HIPOL", 0x20000000, "High degree of polariztion determined");
      flagCoding.addFlag(
          "PRODFAIL", 0x40000000, "One (or more) product algorithms produced a failure");
      flagCoding.addFlag("SPARE32", 0x80000000, "Unused");

      product.getFlagCodingGroup().add(flagCoding);
      QFBand.setSampleCoding(flagCoding);

      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "ATMFAIL",
                  "Atmospheric correction failure",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.ATMFAIL",
                  FailRed,
                  0.0));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "LAND",
                  "Land",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.LAND",
                  LandBrown,
                  0.0));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "PRODWARN",
                  "One (or more) product algorithms generated a warning",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.PRODWARN",
                  DeepBlue,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "HILT",
                  "High (or saturating) TOA radiance",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.HILT",
                  Color.GRAY,
                  0.2));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "HIGLINT",
                  "High glint determined",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.HIGLINT",
                  BrightPink,
                  0.2));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "HISATZEN",
                  "Large satellite zenith angle",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.HISATZEN",
                  LightCyan,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "COASTZ",
                  "Shallow water (<30m)",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.COASTZ",
                  BurntUmber,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "STRAYLIGHT",
                  "Straylight determined",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.STRAYLIGHT",
                  Color.YELLOW,
                  0.2));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "CLDICE",
                  "Cloud/Ice determined",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.CLDICE",
                  Color.WHITE,
                  0.0));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "COCCOLITH",
                  "Coccolithophores detected",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.COCCOLITH",
                  Color.CYAN,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "TURBIDW",
                  "Turbid water determined",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.TURBIDW",
                  LightBrown,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "HISOLZEN",
                  "High solar zenith angle",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.HISOLZEN",
                  Purple,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "LOWLW",
                  "Low Lw @ 555nm (possible cloud shadow)",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.LOWLW",
                  Cornflower,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "CHLFAIL",
                  "Chlorophyll algorithm failure",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.CHLFAIL",
                  FailRed,
                  0.0));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "NAVWARN",
                  "Navigation suspect",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.NAVWARN",
                  Color.MAGENTA,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "ABSAER",
                  "Absorbing Aerosols determined",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.ABSAER",
                  Color.ORANGE,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "MAXAERITER",
                  "Maximum iterations reached for NIR correction",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.MAXAERITER",
                  MediumGray,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "MODGLINT",
                  "Moderate glint determined",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.MODGLINT",
                  LightPurple,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "CHLWARN",
                  "Chlorophyll out-of-bounds (<0.01 or >100 mg m^-3)",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.CHLWARN",
                  Color.LIGHT_GRAY,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "ATMWARN",
                  "Atmospheric correction warning; Epsilon out-of-bounds",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.ATMWARN",
                  Color.MAGENTA,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "SEAICE",
                  "Sea ice determined",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.SEAICE",
                  Color.DARK_GRAY,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "NAVFAIL",
                  "Navigation failure",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.NAVFAIL",
                  FailRed,
                  0.0));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "FILTER",
                  "Insufficient data for smoothing filter",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.FILTER",
                  Color.LIGHT_GRAY,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "SSTWARN",
                  "Sea surface temperature suspect",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.SSTWARN",
                  Color.MAGENTA,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "SSTFAIL",
                  "Sea surface temperature algorithm failure",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.SSTFAIL",
                  FailRed,
                  0.1));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "HIPOL",
                  "High degree of polariztion determined",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.HIPOL",
                  Color.PINK,
                  0.5));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "PRODFAIL",
                  "One (or more) product algorithms produced a failure",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "l2_flags.PRODFAIL",
                  FailRed,
                  0.1));
    }
    Band QFBandSST = product.getBand("qual_sst");
    if (QFBandSST != null) {
      //            FlagCoding flagCoding = new FlagCoding("SSTFlags");
      //            product.getFlagCodingGroup().add(flagCoding);
      //
      //            QFBandSST.setSampleCoding(flagCoding);

      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Best",
                  "Highest quality SST retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst == 0",
                  SeadasFileReader.Cornflower,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Good",
                  "Good quality SST retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst == 1",
                  SeadasFileReader.LightPurple,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Questionable",
                  "Questionable quality SST retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst == 2",
                  SeadasFileReader.BurntUmber,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Bad",
                  "Bad quality SST retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst == 3",
                  SeadasFileReader.FailRed,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "No SST Retrieval",
                  "No SST retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst == 4",
                  SeadasFileReader.FailRed,
                  0.6));
    }
    Band QFBandSST4 = product.getBand("qual_sst4");
    if (QFBandSST4 != null) {
      //            FlagCoding flagCoding = new FlagCoding("SST4Flags");
      //            product.getFlagCodingGroup().add(flagCoding);
      //            QFBandSST4.setSampleCoding(flagCoding);

      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Best",
                  "Highest quality SST4 retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst4 == 0",
                  SeadasFileReader.Cornflower,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Good",
                  "Good quality SST4 retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst4 == 1",
                  SeadasFileReader.LightPurple,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Questionable",
                  "Questionable quality SST4 retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst4 == 2",
                  SeadasFileReader.BurntUmber,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "Bad",
                  "Bad quality SST4 retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst4 == 3",
                  SeadasFileReader.FailRed,
                  0.6));
      product
          .getMaskGroup()
          .add(
              Mask.BandMathsType.create(
                  "No SST Retrieval",
                  "No SST retrieval",
                  product.getSceneRasterWidth(),
                  product.getSceneRasterHeight(),
                  "qual_sst4 == 4",
                  SeadasFileReader.FailRed,
                  0.6));
    }
  }
  protected void addBandsToProduct(Product product) {
    Debug.assertNotNull(getSourceProduct());
    Debug.assertNotNull(product);
    for (int i = 0; i < getSourceProduct().getNumBands(); i++) {
      Band sourceBand = getSourceProduct().getBandAt(i);
      String bandName = sourceBand.getName();
      if (isNodeAccepted(bandName)) {
        Band destBand;
        boolean treatVirtualBandsAsRealBands = false;
        if (getSubsetDef() != null && getSubsetDef().getTreatVirtualBandsAsRealBands()) {
          treatVirtualBandsAsRealBands = true;
        }

        // @todo 1 se/se - extract copy of a band or virtual band to create deep clone of band and
        // virtual band
        if (!treatVirtualBandsAsRealBands && sourceBand instanceof VirtualBand) {
          VirtualBand virtualSource = (VirtualBand) sourceBand;
          destBand =
              new VirtualBand(
                  bandName,
                  sourceBand.getDataType(),
                  getSceneRasterWidth(),
                  getSceneRasterHeight(),
                  virtualSource.getExpression());
        } else {
          destBand =
              new Band(
                  bandName,
                  sourceBand.getDataType(),
                  getSceneRasterWidth(),
                  getSceneRasterHeight());
        }
        if (sourceBand.getUnit() != null) {
          destBand.setUnit(sourceBand.getUnit());
        }
        if (sourceBand.getDescription() != null) {
          destBand.setDescription(sourceBand.getDescription());
        }
        destBand.setScalingFactor(sourceBand.getScalingFactor());
        destBand.setScalingOffset(sourceBand.getScalingOffset());
        destBand.setLog10Scaled(sourceBand.isLog10Scaled());
        destBand.setSpectralBandIndex(sourceBand.getSpectralBandIndex());
        destBand.setSpectralWavelength(sourceBand.getSpectralWavelength());
        destBand.setSpectralBandwidth(sourceBand.getSpectralBandwidth());
        destBand.setSolarFlux(sourceBand.getSolarFlux());
        if (sourceBand.isNoDataValueSet()) {
          destBand.setNoDataValue(sourceBand.getNoDataValue());
        }
        destBand.setNoDataValueUsed(sourceBand.isNoDataValueUsed());
        destBand.setValidPixelExpression(sourceBand.getValidPixelExpression());
        FlagCoding sourceFlagCoding = sourceBand.getFlagCoding();
        IndexCoding sourceIndexCoding = sourceBand.getIndexCoding();
        if (sourceFlagCoding != null) {
          String flagCodingName = sourceFlagCoding.getName();
          FlagCoding destFlagCoding = product.getFlagCodingGroup().get(flagCodingName);
          Debug.assertNotNull(
              destFlagCoding); // should not happen because flag codings should be already in
                               // product
          destBand.setSampleCoding(destFlagCoding);
        } else if (sourceIndexCoding != null) {
          String indexCodingName = sourceIndexCoding.getName();
          IndexCoding destIndexCoding = product.getIndexCodingGroup().get(indexCodingName);
          Debug.assertNotNull(
              destIndexCoding); // should not happen because index codings should be already in
                                // product
          destBand.setSampleCoding(destIndexCoding);
        } else {
          destBand.setSampleCoding(null);
        }
        if (isFullScene(getSubsetDef()) && sourceBand.isStxSet()) {
          copyStx(sourceBand, destBand);
        }
        product.addBand(destBand);
        bandMap.put(destBand, sourceBand);
      }
    }
    for (final Map.Entry<Band, RasterDataNode> entry : bandMap.entrySet()) {
      copyImageInfo(entry.getValue(), entry.getKey());
    }
  }
  @Override
  public void initialize() throws OperatorException {
    if (category == DataCategory.NDVI_MAXCOMPOSIT_NEW) {
      Arrays.sort(sourceProducts, new ProductNameComparator());
      sortedDataSourceProducts = sourceProducts;
    } else {
      final String sourceDataProductFilter = "_data";
      final String sourceFlagProductFilter = "_flag";
      sortedDataSourceProducts =
          DiversityAuxdataUtils.sortProductsByMonth(sourceProducts, sourceDataProductFilter, 2, 7);
      sortedFlagSourceProducts =
          DiversityAuxdataUtils.sortProductsByMonth(sourceProducts, sourceFlagProductFilter, 2, 7);
    }

    final Product yearlyNdviProduct = createYearlyProduct();

    if (writeFlags) {
      final FlagCoding ndviFlagCoding = DiversityAuxdataUtils.createNdviFlagCoding();
      yearlyNdviProduct.getFlagCodingGroup().add(ndviFlagCoding);
      TARGET_BAND_PREFIX += "flag_";
      DiversityAuxdataUtils.addPatternToAutoGrouping(yearlyNdviProduct, "ndvi_max_flag");
    } else {
      DiversityAuxdataUtils.addPatternToAutoGrouping(yearlyNdviProduct, "ndvi_max");
    }

    halfmonthlyDataProductsMap = new HashMap<String, List<Product>>();
    halfmonthlyFlagProductsMap = new HashMap<String, List<Product>>();

    for (int i = 0; i < Constants.MONTHS.length; i++) {
      final String month = Constants.MONTHS[i];
      List<Product> thisMonthDataProducts = new ArrayList<Product>();
      List<Product> thisMonthFlagProducts = new ArrayList<Product>();
      for (Product product : sortedDataSourceProducts) {
        if (category == DataCategory.NDVI_MAXCOMPOSIT_NEW) {
          if (product.getName().contains(String.format("_%02d_", i + 1))) {
            thisMonthDataProducts.add(product);
          }
        } else {
          if (product.getName().contains(month)) {
            thisMonthDataProducts.add(product);
          }
        }
      }
      if (category == DataCategory.NDVI_MAXCOMPOSIT) {
        for (Product product : sortedFlagSourceProducts) {
          if (product.getName().contains(month)) {
            thisMonthFlagProducts.add(product);
          }
        }
      }
      if (thisMonthDataProducts.size() == 2
          && (thisMonthFlagProducts.size() == 2 || category == DataCategory.NDVI_MAXCOMPOSIT_NEW)) {
        // the normal case
        halfmonthlyDataProductsMap.put(month, thisMonthDataProducts);
        halfmonthlyFlagProductsMap.put(month, thisMonthFlagProducts);

        Band targetBand =
            new Band(TARGET_BAND_PREFIX + month, ProductData.TYPE_FLOAT32, width, height);
        targetBand.setNoDataValue(Constants.NDVI_INVALID_VALUE);
        targetBand.setNoDataValueUsed(true);
        yearlyNdviProduct.addBand(targetBand);
      } else {
        System.err.println(
            "Warning: NDVI products for '" + month + "' missing or incomplete - skipping.");
      }
    }

    setTargetProduct(yearlyNdviProduct);
  }
  public static void addAerosolFlagBand(Product targetProduct, int rasterWidth, int rasterHeight) {
    FlagCoding aerosolFlagCoding = new FlagCoding(SynergyConstants.aerosolFlagCodingName);
    aerosolFlagCoding.addFlag(
        SynergyConstants.flagCloudyName,
        SynergyConstants.cloudyMask,
        SynergyConstants.flagCloudyDesc);
    aerosolFlagCoding.addFlag(
        SynergyConstants.flagOceanName, SynergyConstants.oceanMask, SynergyConstants.flagOceanDesc);
    aerosolFlagCoding.addFlag(
        SynergyConstants.flagSuccessName,
        SynergyConstants.successMask,
        SynergyConstants.flagSuccessDesc);
    aerosolFlagCoding.addFlag(
        SynergyConstants.flagBorderName,
        SynergyConstants.borderMask,
        SynergyConstants.flagBorderDesc);
    aerosolFlagCoding.addFlag(
        SynergyConstants.flagFilledName,
        SynergyConstants.filledMask,
        SynergyConstants.flagFilledDesc);
    aerosolFlagCoding.addFlag(
        SynergyConstants.flagNegMetricName,
        SynergyConstants.negMetricMask,
        SynergyConstants.flagNegMetricDesc);
    aerosolFlagCoding.addFlag(
        SynergyConstants.flagAotLowName,
        SynergyConstants.aotLowMask,
        SynergyConstants.flagAotLowDesc);
    aerosolFlagCoding.addFlag(
        SynergyConstants.flagErrHighName,
        SynergyConstants.errHighMask,
        SynergyConstants.flagErrHighDesc);
    aerosolFlagCoding.addFlag(
        SynergyConstants.flagCoastName, SynergyConstants.coastMask, SynergyConstants.flagCoastDesc);
    targetProduct.getFlagCodingGroup().add(aerosolFlagCoding);
    ProductNodeGroup<Mask> maskGroup = targetProduct.getMaskGroup();
    maskGroup.add(
        Mask.BandMathsType.create(
            SynergyConstants.flagCloudyName,
            SynergyConstants.flagCloudyDesc,
            rasterWidth,
            rasterHeight,
            SynergyConstants.aerosolFlagCodingName + "." + SynergyConstants.flagCloudyName,
            Color.lightGray,
            0.2f));
    maskGroup.add(
        Mask.BandMathsType.create(
            SynergyConstants.flagOceanName,
            SynergyConstants.flagOceanDesc,
            rasterWidth,
            rasterHeight,
            SynergyConstants.aerosolFlagCodingName + "." + SynergyConstants.flagOceanName,
            Color.blue,
            0.2f));
    maskGroup.add(
        Mask.BandMathsType.create(
            SynergyConstants.flagSuccessName,
            SynergyConstants.flagSuccessDesc,
            rasterWidth,
            rasterHeight,
            SynergyConstants.aerosolFlagCodingName + "." + SynergyConstants.flagSuccessName,
            Color.pink,
            0.2f));
    maskGroup.add(
        Mask.BandMathsType.create(
            SynergyConstants.flagBorderName,
            SynergyConstants.flagBorderDesc,
            rasterWidth,
            rasterHeight,
            SynergyConstants.aerosolFlagCodingName + "." + SynergyConstants.flagBorderName,
            Color.orange,
            0.2f));
    maskGroup.add(
        Mask.BandMathsType.create(
            SynergyConstants.flagFilledName,
            SynergyConstants.flagFilledDesc,
            rasterWidth,
            rasterHeight,
            SynergyConstants.aerosolFlagCodingName + "." + SynergyConstants.flagFilledName,
            Color.magenta,
            0.2f));
    maskGroup.add(
        Mask.BandMathsType.create(
            SynergyConstants.flagNegMetricName,
            SynergyConstants.flagNegMetricDesc,
            rasterWidth,
            rasterHeight,
            SynergyConstants.aerosolFlagCodingName + "." + SynergyConstants.flagNegMetricName,
            Color.magenta,
            0.2f));
    maskGroup.add(
        Mask.BandMathsType.create(
            SynergyConstants.flagAotLowName,
            SynergyConstants.flagAotLowDesc,
            rasterWidth,
            rasterHeight,
            SynergyConstants.aerosolFlagCodingName + "." + SynergyConstants.flagAotLowName,
            Color.magenta,
            0.2f));
    maskGroup.add(
        Mask.BandMathsType.create(
            SynergyConstants.flagErrHighName,
            SynergyConstants.flagErrHighDesc,
            rasterWidth,
            rasterHeight,
            SynergyConstants.aerosolFlagCodingName + "." + SynergyConstants.flagErrHighName,
            Color.magenta,
            0.2f));
    maskGroup.add(
        Mask.BandMathsType.create(
            SynergyConstants.flagCoastName,
            SynergyConstants.flagCoastDesc,
            rasterWidth,
            rasterHeight,
            SynergyConstants.aerosolFlagCodingName + "." + SynergyConstants.flagCoastName,
            Color.magenta,
            0.2f));

    Band targetBand =
        new Band(
            SynergyConstants.aerosolFlagCodingName,
            ProductData.TYPE_UINT16,
            rasterWidth,
            rasterHeight);
    targetBand.setDescription(SynergyConstants.aerosolFlagCodingDesc);
    targetBand.setSampleCoding(aerosolFlagCoding);
    targetProduct.addBand(targetBand);
  }