/**
   * Creates a {@link GridCoverage} for the provided {@link PlanarImage} using the {@link
   * #raster2Model} that was provided for this coverage.
   *
   * <p>This method is vital when working with coverages that have a raster to model transformation
   * that is not a simple scale and translate.
   *
   * @param coverageName
   * @param image contains the data for the coverage to create.
   * @param raster2Model is the {@link MathTransform} that maps from the raster space to the model
   *     space.
   * @return a {@link GridCoverage}
   * @throws IOException
   */
  protected final GridCoverage2D createImageCoverage(
      String coverageName, PlanarImage image, MathTransform raster2Model) throws IOException {

    // creating bands
    final SampleModel sm = image.getSampleModel();
    final ColorModel cm = image.getColorModel();
    final int numBands = sm.getNumBands();
    final GridSampleDimension[] bands = new GridSampleDimension[numBands];
    // setting bands names.
    for (int i = 0; i < numBands; i++) {
      final ColorInterpretation colorInterpretation = TypeMap.getColorInterpretation(cm, i);
      if (colorInterpretation == null) throw new IOException("Unrecognized sample dimension type");
      bands[i] = new GridSampleDimension(colorInterpretation.name()).geophysics(true);
    }
    // creating coverage
    if (raster2Model != null) {
      return coverageFactory.create(
          coverageName,
          image,
          getCoordinateReferenceSystem(coverageName),
          raster2Model,
          bands,
          null,
          null);
    }
    return coverageFactory.create(
        coverageName,
        image,
        new GeneralEnvelope(getOriginalEnvelope(coverageName)),
        bands,
        null,
        null);
  }
  private GridSampleDimension[] getSampleDimensions(final RenderedImage coverageRaster)
      throws IOException {

    GridSampleDimension[] bands = rasterInfo.getGridSampleDimensions();

    // may the image have been promoted? build the correct band info then
    final int imageBands = coverageRaster.getSampleModel().getNumBands();
    if (bands.length == 1 && imageBands > 1) {
      LOGGER.fine(
          coverageName
              + " was promoted from 1 to "
              + coverageRaster.getSampleModel().getNumBands()
              + " bands, returning an appropriate set of GridSampleDimension");
      // stolen from super.createCoverage:
      final ColorModel cm = coverageRaster.getColorModel();
      bands = new GridSampleDimension[imageBands];

      // setting bands names.
      for (int i = 0; i < imageBands; i++) {
        final ColorInterpretation colorInterpretation;
        colorInterpretation = TypeMap.getColorInterpretation(cm, i);
        if (colorInterpretation == null) {
          throw new IOException("Unrecognized sample dimension type");
        }
        bands[i] = new GridSampleDimension(colorInterpretation.name()).geophysics(true);
      }
    }

    return bands;
  }
  /**
   * Creates a {@link SimpleFeatureType} that exposes a coverage as a collections of feature points,
   * mapping the centre of each pixel as a point plus all the bands as attributes.
   *
   * <p>The FID is the long that combines x+y*width.
   *
   * @param gc2d the {@link GridCoverage2D} to wrap.
   * @param geometryClass the class for the geometry.
   * @return a {@link SimpleFeatureType} or <code>null</code> in case we are unable to wrap the
   *     coverage
   */
  public static SimpleFeatureType createFeatureType(
      final GridCoverage2D gc2d, final Class<? extends Geometry> geometryClass) {

    // checks
    Utilities.ensureNonNull("gc2d", gc2d);

    // building a feature type for this coverage
    final SimpleFeatureTypeBuilder ftBuilder = new SimpleFeatureTypeBuilder();
    ftBuilder.setName(gc2d.getName().toString());
    ftBuilder.setNamespaceURI("http://www.geotools.org/");

    // CRS
    ftBuilder.setCRS(gc2d.getCoordinateReferenceSystem2D());
    //		ftBuilder.setCRS(DefaultEngineeringCRS.GENERIC_2D);

    // TYPE is as follows the_geom | band
    ftBuilder.setDefaultGeometry("the_geom");
    ftBuilder.add("the_geom", geometryClass);
    if (!geometryClass.equals(Point.class)) {
      ftBuilder.add("value", Double.class);
    } else {

      // get sample type on bands
      final GridSampleDimension[] sampleDimensions = gc2d.getSampleDimensions();
      for (GridSampleDimension sd : sampleDimensions) {
        final SampleDimensionType sdType = sd.getSampleDimensionType();
        final int dataBuffType = TypeMap.getDataBufferType(sdType);

        // TODO I think this should be a public utility inside the FeatureUtilities class
        @SuppressWarnings("rawtypes")
        final Class bandClass;
        switch (dataBuffType) {
          case DataBuffer.TYPE_BYTE:
            bandClass = Byte.class;
            break;
          case DataBuffer.TYPE_DOUBLE:
            bandClass = Double.class;
            break;
          case DataBuffer.TYPE_FLOAT:
            bandClass = Float.class;
            break;
          case DataBuffer.TYPE_INT:
            bandClass = Integer.class;
            break;
          case DataBuffer.TYPE_SHORT:
          case DataBuffer.TYPE_USHORT:
            bandClass = Short.class;
            break;
          case DataBuffer.TYPE_UNDEFINED:
          default:
            return null;
        }
        ftBuilder.add(sd.getDescription().toString(), bandClass);
      }
    }
    return ftBuilder.buildFeatureType();
  }
 private void handleSampleDimensionType(SampleDimensionType sdType) {
   // old data dirs upgrading will have this empty
   if (sdType == null) {
     // pick the one with the largest domain and be done with it
     sdType = SampleDimensionType.REAL_64BITS;
   }
   final NumberRange<? extends Number> indicativeRange = TypeMap.getRange(sdType);
   setRange(indicativeRange);
 }
  /**
   * Creates a {@link GridCoverage} for the provided {@link PlanarImage} using the {@link
   * #raster2Model} that was provided for this coverage.
   *
   * <p>This method is vital when working with coverages that have a raster to model transformation
   * that is not a simple scale and translate.
   *
   * @param image contains the data for the coverage to create.
   * @param raster2Model is the {@link MathTransform} that maps from the raster space to the model
   *     space.
   * @return a {@link GridCoverage}
   * @throws IOException
   */
  protected final GridCoverage2D createCoverage(PlanarImage image, MathTransform raster2Model)
      throws IOException {

    // creating bands
    final SampleModel sm = image.getSampleModel();
    final ColorModel cm = image.getColorModel();
    final int numBands = sm.getNumBands();
    final GridSampleDimension[] bands = new GridSampleDimension[numBands];
    // setting bands names.

    Category noDataCategory = null;
    final Map<String, Double> properties = new HashMap<String, Double>();
    if (!Double.isNaN(noData)) {
      noDataCategory =
          new Category(
              Vocabulary.formatInternational(VocabularyKeys.NODATA),
              new Color[] {new Color(0, 0, 0, 0)},
              NumberRange.create(noData, noData),
              NumberRange.create(noData, noData));

      properties.put("GC_NODATA", new Double(noData));
    }

    Set<String> bandNames = new HashSet<String>();
    for (int i = 0; i < numBands; i++) {
      final ColorInterpretation colorInterpretation = TypeMap.getColorInterpretation(cm, i);
      if (colorInterpretation == null) throw new IOException("Unrecognized sample dimension type");
      Category[] categories = null;
      if (noDataCategory != null) {
        categories = new Category[] {noDataCategory};
      }
      String bandName = colorInterpretation.name();
      // make sure we create no duplicate band names
      if (colorInterpretation == ColorInterpretation.UNDEFINED || bandNames.contains(bandName)) {
        bandName = "Band" + (i + 1);
      }
      bands[i] = new GridSampleDimension(bandName, categories, null).geophysics(true);
    }
    // creating coverage
    if (raster2Model != null) {
      return coverageFactory.create(
          coverageName, image, crs, raster2Model, bands, null, properties);
    }
    return coverageFactory.create(
        coverageName, image, new GeneralEnvelope(originalEnvelope), bands, null, properties);
  }
  /**
   * Helper classes for creating {@link DimensionlessAxis} for the most common color models' bands.
   *
   * <p>Suypported colorspaces incluse RGBA, GRAY, GRAYA, HSV,HLS, LAB, LUV, IHS, CI_XYZ, CMY(K).
   * Notice that RGB is not handled here but through a wavelength axis.
   *
   * <p>This method returns null if an unsupported {@link ColorModel} is provided.
   *
   * @param raster a {@link RenderedImage} implementation from which to extract needed info, usually
   *     {@link ColorModel} and {@link SampleModel}.
   * @return a {@link DimensionlessAxis} or null if an unsupported {@link ColorModel} is provided.
   */
  public static DimensionlessAxis createFromRenderedImage(final RenderedImage raster) {
    if (raster == null) throw new IllegalArgumentException("Provided null input image");

    final ColorModel cm = raster.getColorModel();
    if (cm == null)
      throw new IllegalArgumentException("Provided input image with null color model");
    final SampleModel sm = raster.getSampleModel();
    if (sm == null)
      throw new IllegalArgumentException("Provided input image with null SampleModel");

    // get the color interpretation for the three bands
    final ColorInterpretation firstBandCI = TypeMap.getColorInterpretation(cm, 0);

    //		CMY - CMYK
    if (firstBandCI == ColorInterpretation.CYAN_BAND) {
      if (sm.getNumBands() == 3)
        return new DimensionlessAxis(
            Arrays.asList("CYAN", "MAGENTA", "YELLOW"),
            new NameImpl("CMY-AXIS"),
            new SimpleInternationalString("Axis for CMY bands"));
      else
        return new DimensionlessAxis(
            Arrays.asList("CYAN", "MAGENTA", "YELLOW", "BLACK"),
            new NameImpl("CMYK-AXIS"),
            new SimpleInternationalString("Axis for CMYK bands"));
    }

    // HSV
    if (firstBandCI == ColorInterpretation.HUE_BAND) {
      return new DimensionlessAxis(
          Arrays.asList("HUE", "SATURATION", "VALUE"),
          new NameImpl("HSV-AXIS"),
          new SimpleInternationalString("Axis for HSV bands"));
    }

    // RGBA
    if (firstBandCI == ColorInterpretation.RED_BAND) {
      return new DimensionlessAxis(
          Arrays.asList("RED", "GREEN", "BLUE", "ALPHA"),
          new NameImpl("RGBA-AXIS"),
          new SimpleInternationalString("Axis for RGBA bands"));
    }

    // PALETTE
    if (firstBandCI == ColorInterpretation.PALETTE_INDEX)
      return new DimensionlessAxis(
          Arrays.asList("PALETTE_INDEX"),
          new NameImpl("PALETTE_INDEX-AXIS"),
          new SimpleInternationalString("Axis for PALETTE INDEX bands"));

    // GRAY, GRAY+ALPHA
    if (firstBandCI == ColorInterpretation.GRAY_INDEX) {
      if (sm.getNumBands() == 2)
        return new DimensionlessAxis(
            Arrays.asList("GRAY", "ALPHA"),
            new NameImpl("GA-AXIS"),
            new SimpleInternationalString("Axis for GRAY-ALPHA bands"));
      else
        return new DimensionlessAxis(
            Arrays.asList("GRAY"),
            new NameImpl("GRAY-AXIS"),
            new SimpleInternationalString("Axis for GRAY bands"));
    }

    final ColorSpace cs = cm.getColorSpace();
    // IHS
    if (cs instanceof IHSColorSpace)
      return new DimensionlessAxis(
          Arrays.asList("INTENSITY", "HUE", "SATURATION"),
          new NameImpl("IHS-AXIS"),
          new SimpleInternationalString("Axis for IHS bands"));

    // YCbCr, LUV, LAB, HLS, IEXYZ
    switch (cs.getType()) {
      case ColorSpace.TYPE_YCbCr:
        return new DimensionlessAxis(
            Arrays.asList("LUMA", "CHROMA-A", "CHROMA-B"),
            new NameImpl("YCbCr-AXIS"),
            new SimpleInternationalString("Axis for YCbCr bands"));
      case ColorSpace.TYPE_Luv:
        return new DimensionlessAxis(
            Arrays.asList("LIGHTNESS", "U", "V"),
            new NameImpl("LUV-AXIS"),
            new SimpleInternationalString("Axis for LUV bands"));
      case ColorSpace.TYPE_Lab:
        return new DimensionlessAxis(
            Arrays.asList("LIGHTNESS", "A", "B"),
            new NameImpl("LAB-AXIS"),
            new SimpleInternationalString("Axis for LAB bands"));
      case ColorSpace.TYPE_HLS:
        return new DimensionlessAxis(
            Arrays.asList("HUE", "LIGHTNESS", "SATURATION"),
            new NameImpl("HLS-AXIS"),
            new SimpleInternationalString("Axis for HLS bands"));
      case ColorSpace.CS_CIEXYZ:
        return new DimensionlessAxis(
            Arrays.asList("X", "Y", "Z"),
            new NameImpl("XYZ-AXIS"),
            new SimpleInternationalString("Axis for XYZ bands"));

      default:
        return null;
    }
  }