Package org.geotools.gce.gtopo30

Source Code of org.geotools.gce.gtopo30.GTopo30Writer

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2003-2008, Open Source Geospatial Foundation (OSGeo)
*
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation;
*    version 2.1 of the License.
*
*    This library is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*    Lesser General Public License for more details.
*
*/
package org.geotools.gce.gtopo30;

import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.DataBuffer;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.media.jai.Histogram;
import javax.media.jai.ImageLayout;
import javax.media.jai.InterpolationBilinear;
import javax.media.jai.JAI;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.RenderedOp;

import org.geotools.coverage.Category;
import org.geotools.coverage.GridSampleDimension;
import org.geotools.coverage.grid.GeneralGridEnvelope;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridCoverageWriter;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.imageio.GeoToolsWriteParams;
import org.geotools.coverage.processing.operation.Resample;
import org.geotools.coverage.processing.operation.SelectSampleDimension;
import org.geotools.data.DataSourceException;
import org.geotools.data.DataUtilities;
import org.geotools.factory.Hints;
import org.geotools.image.io.ImageIOExt;
import org.geotools.parameter.Parameter;
import org.geotools.referencing.operation.matrix.XAffineTransform;
import org.geotools.resources.coverage.CoverageUtilities;
import org.geotools.util.NumberRange;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridCoverageWriter;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValueGroup;

import com.sun.media.jai.operator.ImageWriteDescriptor;

/**
* Class used for encoding a {@link GridCoverage2D} into a GTOPO30 file.
*
*
* @author Simone Giannecchini
* @author jeichar
* @author mkraemer
*
*
* @source $URL$
*/
final public class GTopo30Writer extends AbstractGridCoverageWriter implements
    GridCoverageWriter {

  /** Logger. */
  private final static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.gce.gtopo30");

  static {
    // register new JAI operation
    NoDataReplacerOpImage.register(JAI.getDefaultInstance());
  }

  /** Cached factory for a {@link SelectSampleDimension} operation. */
  private final static SelectSampleDimension sdFactory = new SelectSampleDimension();





  /** Cached factory for {@link Resample} operation. */
  private final static Resample resampleFactory = new Resample();

  /**
   * Standard width for the GIF image.
   */
  private final static int GIF_WIDTH = 640;

  /** Standard height for the GIF image. */
  private final static int GIF_HEIGHT = 480;

  /**
   * Creates a {@link GTopo30Writer}.
   *
   * @param dest
   *            The destination object can be a {@link File} (a directory
   *            actually), an {@link URL} to a directory, or a String
   *            representing a directory or an {@link URL} to a directory.
   * @throws DataSourceException
   */
  public GTopo30Writer(final Object dest) throws DataSourceException {
    this(dest, null);
  }

  /**
   * Creates a {@link GTopo30Writer}.
   *
   * @param dest
   *            The destination object can be a File (a directory actually),
   *            an URL to a directory, or a String representing a directory or
   *            an URL to a directory.
   * @throws DataSourceException
   */
  public GTopo30Writer(final Object dest, final Hints hints)
      throws DataSourceException {

    // /////////////////////////////////////////////////////////////////////
    //
    // Initial checks
    //
    // /////////////////////////////////////////////////////////////////////
    if (dest == null) {
      throw new NullPointerException("The provided destination is null.");
    }

    destination = dest;
    final File temp;
    final URL url;

    // we only accept a directory as a path
    if (dest instanceof String) {
      temp = new File((String) dest);

      // if it exists and it is not a directory that 's not good
      if ((temp.exists() && !temp.isDirectory()) || !temp.exists()) {
        destination = null; // we cannot write
      } else if (!temp.exists()) {
        // well let's create it!
        if (!temp.mkdir()) {
          destination = null;
        } else {
          destination = temp.getAbsolutePath();
        }
      }
    } else if (dest instanceof File) {
      temp = (File) dest;

      if (temp.exists() && !temp.isDirectory()) {
        this.destination = null;
      } else if (!temp.exists()) {
        // let's create it
        if (temp.mkdir()) {
          destination = temp.getAbsolutePath();
        } else {
          destination = null;
        }
      }
    } else if (dest instanceof URL) {
      url = (URL) dest;

      if (url.getProtocol().compareToIgnoreCase("file") != 0) {
        destination = null;
      }

      temp = DataUtilities.urlToFile(url);

      if (temp.exists() && !temp.isDirectory()) {
        destination = null;
      } else if (!temp.exists()) {
        // let's create it
        if (temp.mkdir()) {
          destination = temp.getAbsolutePath();
        } else {
          destination = null;
        }
      }
    } else if (dest instanceof ImageOutputStream) {
      this.destination = dest;
    } else {
      throw new IllegalArgumentException(
          "The provided destination is of an incorrect type.");

    }

    // /////////////////////////////////////////////////////////////////////
    //
    // managing hints
    //
    // /////////////////////////////////////////////////////////////////////
    // //
    //
    // managing hints
    //
    // //
    if (this.hints == null)
      this.hints= new Hints()
    if (hints != null) {
      // prevent the use from reordering axes
      this.hints.add(hints);
    }
  }

  /**
   * @see org.opengis.coverage.grid.GridCoverageWriter#getFormat()
   */
    public Format getFormat() {
    return new GTopo30Format();
  }

  /**
   * @see org.opengis.coverage.grid.GridCoverageWriter#write(org.opengis.coverage.grid.GridCoverage,
   *      org.opengis.parameter.GeneralParameterValue[])
   */
  public void write(final GridCoverage coverage,
      final GeneralParameterValue[] params)
      throws java.lang.IllegalArgumentException, java.io.IOException {

    // /////////////////////////////////////////////////////////////////////
    //
    // Checking input params
    //
    // /////////////////////////////////////////////////////////////////////
    if (coverage == null)
      throw new NullPointerException(
          "The provided source coverage is null");
    // the source GridCoverage2D
    GridCoverage2D gc2D = (GridCoverage2D) coverage;

    // /////////////////////////////////////////////////////////////////////
    //
    // Checking writing params
    //
    // /////////////////////////////////////////////////////////////////////
    GeoToolsWriteParams gtParams = null;
    if (params != null) {

      if (params != null) {
        Parameter<?> param;
        final int length = params.length;
        for (int i = 0; i < length; i++) {
          param = (Parameter<?>) params[i];
          if (param.getDescriptor().getName().getCode().equals(
              AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName()
                  .toString())) {
            gtParams = (GeoToolsWriteParams) param.getValue();
          }
        }
      }
    }
    if (gtParams == null)
      gtParams = new GTopo30WriteParams();
    // compression
    final boolean compressed = gtParams.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT;
    // write band
    int[] writeBands = gtParams.getSourceBands();
    int writeBand = CoverageUtilities.getVisibleBand(gc2D
        .getRenderedImage());
    if ((writeBands == null || writeBands.length == 0 || writeBands.length > 1)
        && (writeBand < 0 || writeBand > gc2D.getNumSampleDimensions()))
      throw new IllegalArgumentException(
          "You need to supply a valid index for deciding which band to write.");
    if (!((writeBands == null || writeBands.length == 0 || writeBands.length > 1)))
      writeBand = writeBands[0];

    // destination file name
    String fileName = gc2D.getName().toString();

    // destination XXX HAS to be a dir
    if (compressed) {
      destination = new ZipOutputStream(new BufferedOutputStream(
          new FileOutputStream(new File((File) destination,
              new StringBuffer(fileName).append(".zip")
                  .toString()))));
    }

    // /////////////////////////////////////////////////////////////////////
    //
    // STEP 1
    //
    // We might need to do a band select in order to cope with the GTOPO30
    // limitation.
    //
    // /////////////////////////////////////////////////////////////////////
    final ParameterValueGroup pvg = sdFactory.getParameters();
    pvg.parameter("Source").setValue(gc2D);
    pvg.parameter("SampleDimensions").setValue(new int[]{writeBand});
    pvg.parameter("VisibleSampleDimension").setValue(writeBand);
    gc2D = (GridCoverage2D) sdFactory.doOperation(pvg, hints);

    // /////////////////////////////////////////////////////////////////////
    //
    // STEP 2
    //
    // We might need to reformat the selected band for this coverage in
    // order to cope with the GTOPO30 limitation.
    //
    // /////////////////////////////////////////////////////////////////////
    final PlanarImage reFormattedData2Short = reFormatCoverageImage(gc2D,
        DataBuffer.TYPE_SHORT);

    // /////////////////////////////////////////////////////////////////////
    //
    // STEP 2
    //
    // Start with writing things out.
    //
    // /////////////////////////////////////////////////////////////////////
    // //
    //
    // write DEM
    //
    // //
    this.writeDEM(reFormattedData2Short, fileName, destination);

    // //
    //
    // write statistics
    //
    // //
    this.writeStats(reFormattedData2Short, fileName, destination, gc2D);
    // we won't use this image anymore let's release the resources.

    // //
    //
    // write world file
    //
    // //
    this.writeWorldFile(gc2D, fileName, destination);

    // //
    //
    // write projection
    //
    // //
    this.writePRJ(gc2D, fileName, destination);

    // //
    //
    // write HDR
    //
    // //
    this.writeHDR(gc2D, fileName, destination);

    // //
    //
    // write gif
    //
    // //
    this.writeGIF(gc2D, fileName, destination);

    // //
    //
    // write src
    //
    // //
    this.writeSRC(gc2D, fileName, destination);

    if (compressed) {

      ((ZipOutputStream) destination).close();
    }
  }

  /**
   * Reformats the input single banded planar image to having
   * {@link DataBuffer#TYPE_SHORT} sample type as requested by the GTOPO30
   * format.
   *
   * @param gc2D
   *            source {@link GridCoverage2D} from which to take the input
   *            {@link PlanarImage}.
   *
   * @return the reformated {@link PlanarImage}.
   */
  private PlanarImage reFormatCoverageImage(final GridCoverage2D gc2D,
      int writeBand) {
    // internal image
    PlanarImage image = (PlanarImage) gc2D.getRenderedImage();
    // sample dimension type
    final int origDataType = image.getSampleModel().getDataType();
    // short?
    if (DataBuffer.TYPE_SHORT == origDataType) {
      return image;
    }

    final GridSampleDimension visibleSD = ((GridSampleDimension) gc2D
        .getSampleDimension(0)).geophysics(true);

    // getting categories
    final List oldCategories = visibleSD.getCategories();

    // removing old nodata category
    // candidate
    Category candidate = null;
    NumberRange candidateRange = null;
    final Iterator it = oldCategories.iterator();

    while (it.hasNext()) {
      candidate = (Category) it.next();

      // removing candidate for NaN
      if (candidate.getName().toString().equalsIgnoreCase("no data")) {
        candidateRange = candidate.getRange();

        break;
      }
    }

    // new no data category
    final double oldNoData = candidateRange.getMinimum();
    final ParameterBlockJAI pbjM = new ParameterBlockJAI(
        "org.geotools.gce.gtopo30.NoDataReplacer");
    pbjM.addSource(image);
    pbjM.setParameter("oldNoData", oldNoData);
    image = JAI.create("org.geotools.gce.gtopo30.NoDataReplacer", pbjM,
        hints);
    return image;
  }

  /**
   * Writing down the header file for the gtopo30 format:
   *
   * @param coverage
   *            The GridCoverage to write
   * @param file
   *            The destination object (can be a File or ZipOutputStream)
   *
   * @throws IOException
   *             If the file could not be written
   */
  private void writeHDR(final GridCoverage2D gc, final String name,
      final Object dest) throws IOException {

    // final GeneralEnvelope envelope = (GeneralEnvelope) gc.getEnvelope();
    final double noData = -9999.0;

      // checking the directions of the axes.
      // we need to understand how the axes of this gridcoverage are
      // specified
  /*
   * Note from Martin: I suggest to replace all the above lines by the commented code below.
   * The change need to be tested in order to make sure that I didn't made a mistake in the
   * mathematic. Note that 'lonFirst' totally vanish.
   */
      final AffineTransform gridToWorld = (AffineTransform) gc
          .getGridGeometry().getGridToCRS2D();
      boolean lonFirst = (XAffineTransform.getSwapXY(gridToWorld) != -1);
 
      final double geospatialDx = Math.abs((lonFirst) ? gridToWorld
          .getScaleX() : gridToWorld.getShearY());
      final double geospatialDy = Math.abs((lonFirst) ? gridToWorld
          .getScaleY() : gridToWorld.getShearX());
 
      // getting corner coordinates of the left upper corner
      final double xUpperLeft = lonFirst ? gridToWorld.getTranslateX()
          : gridToWorld.getTranslateY();
      final double yUpperLeft = lonFirst ? gridToWorld.getTranslateY()
          : gridToWorld.getTranslateX();
  /*
        final AffineTransform worldToGrid = (AffineTransform) gc
            .getGridGeometry().getGridToCoordinateSystem().inverse();
 
        final double geospatialDx = 1 / XAffineTransform.getScaleX0(worldToGrid);
        final double geospatialDy = 1 / XAffineTransform.getScaleY0(worldToGrid);
 
        // getting corner coordinates of the left upper corner
        final double xUpperLeft = -worldToGrid.getTranslateX() * geospatialDx;
        final double yUpperLeft = -worldToGrid.getTranslateY() * geospatialDy;
  */
 
      // calculating the physical resolution over x and y.
      final int geometryWidth = gc.getGridGeometry().getGridRange().getSpan(0);
      final int geometryHeight = gc.getGridGeometry().getGridRange().getSpan(1);
 
      if (dest instanceof File) {
 
        final PrintWriter out = new PrintWriter(
            new BufferedOutputStream(new FileOutputStream(new File(
                (File) dest, new StringBuffer(name).append(
                    ".HDR").toString()))));
 
        // output header and assign header fields
        out.print("BYTEORDER");
        out.print(" ");
        out.println("M");
 
        out.print("LAYOUT");
        out.print(" ");
        out.println("BIL");
 
        out.print("NROWS");
        out.print(" ");
        out.println(geometryHeight);
 
        out.print("NCOLS");
        out.print(" ");
        out.println(geometryWidth);
 
        out.print("NBANDS");
        out.print(" ");
        out.println("1");
 
        out.print("NBITS");
        out.print(" ");
        out.println("16");
 
        out.print("BANDROWBYTES");
        out.print(" ");
        out.println(geometryWidth * 2);
 
        out.print("TOTALROWBYTES");
        out.print(" ");
        out.println(geometryWidth * 2);
 
        out.print("BANDGAPBYTES");
        out.print(" ");
        out.println(0);
 
        out.print("NODATA");
        out.print(" ");
        out.println((int) noData);
 
        out.print("ULXMAP");
        out.print(" ");
        out.println(xUpperLeft);
 
        out.print("ULYMAP");
        out.print(" ");
        out.println(yUpperLeft);
 
        out.print("XDIM");
        out.print(" ");
        out.println(geospatialDx);
 
        out.print("YDIM");
        out.print(" ");
        out.println(geospatialDy);
        out.flush();
        out.close();
      } else {
        final ZipOutputStream outZ = (ZipOutputStream) dest;
        final ZipEntry e = new ZipEntry(gc.getName().toString()
            + ".HDR");
        outZ.putNextEntry(e);
 
        // writing world file
        outZ.write("BYTEORDER".getBytes());
        outZ.write(" ".getBytes());
        outZ.write("M".getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("LAYoutZ".getBytes());
        outZ.write(" ".getBytes());
        outZ.write("BIL".getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("NROWS".getBytes());
        outZ.write(" ".getBytes());
        outZ.write(Integer.toString(geometryHeight).getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("NCOLS".getBytes());
        outZ.write(" ".getBytes());
        outZ.write(Integer.toString(geometryWidth).getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("NBANDS".getBytes());
        outZ.write(" ".getBytes());
        outZ.write("1".getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("NBITS".getBytes());
        outZ.write(" ".getBytes());
        outZ.write("16".getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("BANDROWBYTES".getBytes());
        outZ.write(" ".getBytes());
        outZ.write(Integer.toString(geometryWidth * 2).getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("TOTALROWBYTES".getBytes());
        outZ.write(" ".getBytes());
        outZ.write(Integer.toString(geometryWidth * 2).getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("BANDGAPBYTES".getBytes());
        outZ.write(" ".getBytes());
        outZ.write("0".getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("NODATA".getBytes());
        outZ.write(" ".getBytes());
        outZ.write(Integer.toString((int) noData).getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("ULXMAP".getBytes());
        outZ.write(" ".getBytes());
        outZ.write(Double.toString(xUpperLeft + (geospatialDx / 2))
            .getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("ULYMAP".getBytes());
        outZ.write(" ".getBytes());
        outZ.write(Double.toString(yUpperLeft - (geospatialDy / 2))
            .getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("XDIM".getBytes());
        outZ.write(" ".getBytes());
        outZ.write(Double.toString(geospatialDx).getBytes());
        outZ.write("\n".getBytes());
 
        outZ.write("YDIM".getBytes());
        outZ.write(" ".getBytes());
        outZ.write(Double.toString(geospatialDy).toString().getBytes());
        outZ.write("\n".getBytes());
 
        outZ.closeEntry();
 
        ((ZipOutputStream) dest).closeEntry();
      }
  }

  /**
   * Writes the source file (.SRC). The default byte order is BIG_ENDIAN.
   *
   * @param gc
   *            The GridCoverage to write
   * @param file
   *            The destination object (can be a File or ZipOutputStream)
   * @param dest
   *
   * @throws FileNotFoundException
   *             If the destination file could not be found
   * @throws IOException
   *             If the file could not be written
   */
  private void writeSRC(GridCoverage2D gc, final String name, Object dest)
      throws FileNotFoundException, IOException {

    // /////////////////////////////////////////////////////////////////////
    // TODO @task @todo
    // Here I am making the assumption that the non geophysiscs view is 8
    // bit but it can also be 16. I should do something more general like a
    // clamp plus a format but for the moment this is enough.
    //
    // We need also to get the one visible band
    //
    // /////////////////////////////////////////////////////////////////////
    gc = gc.geophysics(false);
    ImageOutputStream out = null;

    RenderedImage image = gc.getRenderedImage();
    if (dest instanceof File) {
      final File file = new File((File) dest, new StringBuffer(name).append(".SRC").toString());
            out = ImageIOExt.createImageOutputStream(image, file);
    } else {
      final ZipOutputStream outZ = (ZipOutputStream) dest;
      final ZipEntry e = new ZipEntry(gc.getName().toString() + ".SRC");
      outZ.putNextEntry(e);

      out = ImageIOExt.createImageOutputStream(image, outZ);
    }

    // setting byte order
    out.setByteOrder(java.nio.ByteOrder.BIG_ENDIAN);

    // /////////////////////////////////////////////////////////////////////
    //
    // Prepare to write
    //
    // /////////////////////////////////////////////////////////////////////
    image = untileImage(image);

    final ParameterBlockJAI pbj = new ParameterBlockJAI("imagewrite");
    pbj.addSource(image);
    pbj.setParameter("Format", "raw");
    pbj.setParameter("Output", out);
    final RenderedOp wOp = JAI.create("ImageWrite", pbj);

    // /////////////////////////////////////////////////////////////////////
    //
    // Dispose things
    //
    // /////////////////////////////////////////////////////////////////////
    final Object o = wOp
        .getProperty(ImageWriteDescriptor.PROPERTY_NAME_IMAGE_WRITER);
    if (o instanceof ImageWriter)
      ((ImageWriter) o).dispose();

    if (!(dest instanceof File)) {
      ((ZipOutputStream) dest).closeEntry();
    }
    out.flush();
    out.close();

  }

  /**
   * Writing a gif file as an overview for this GTopo30.
   *
   * @param gc
   *            The GridCoverage to write
   * @param file
   *            The destination object (can be a File or ZipOutputStream)
   * @param dest
   *
   * @throws IOException
   *             If the file could not be written
   */
  private void writeGIF(final GridCoverage2D gc, final String name,
      Object dest) throws IOException {
    ImageOutputStream out = null;

        // rescaling to a smaller resolution in order to save space on storage
        final GridCoverage2D gc1 = rescaleCoverage(gc);

        // get the non-geophysiscs view
        final GridCoverage2D gc2 = gc1.geophysics(false);

        // get the underlying image
        final RenderedImage image = gc2.getRenderedImage();

        // get the image out stream
    if (dest instanceof File) {
      // writing gif image
      final File file = new File((File) dest,
          new StringBuffer(name).append(".GIF").toString());
            out = ImageIOExt.createImageOutputStream(image, file);
    } else {
      final ZipOutputStream outZ = (ZipOutputStream) dest;
      final ZipEntry e = new ZipEntry(gc.getName().toString() + ".GIF");
      outZ.putNextEntry(e);

      out = ImageIOExt.createImageOutputStream(image, outZ);
    }

    // write it down as a gif
    final ParameterBlockJAI pbj = new ParameterBlockJAI("ImageWrite");
    pbj.addSource(image);
    pbj.setParameter("Output", out);
    pbj.setParameter("Format", "gif");
    JAI.create("ImageWrite", pbj, new RenderingHints(JAI.KEY_TILE_CACHE,
        null));

    if (dest instanceof File) {
      out.close();
    } else {
      out.flush();
      ((ZipOutputStream) dest).closeEntry();
    }

    // disposing the old unused coverages
    gc1.dispose(false);
  }

  /**
   * Purpose of this method is rescaling the original coverage in order to
   * create an overview for the shaded relief gif image to be put inside the
   * gtopo30 set of files.
   *
   * @param gc
   *            the original {@link GridCoverage2D}.
   *
   * @return the rescaled {@link GridCoverage2D}.
   */
  private GridCoverage2D rescaleCoverage(GridCoverage2D gc) {
    final RenderedImage image = gc.getRenderedImage();
    final int width = image.getWidth();
    final int height = image.getHeight();

    if ((height < GIF_HEIGHT) && (width < GIF_WIDTH)) {
      return gc;
    }

    // new grid range
    final GeneralGridEnvelope newGridrange = new GeneralGridEnvelope(new int[] {0, 0 }, new int[] { GIF_WIDTH, GIF_HEIGHT });
    final GridGeometry2D newGridGeometry = new GridGeometry2D(newGridrange,gc.getEnvelope());

    // resample this coverage
    final ParameterValueGroup pvg= resampleFactory.getParameters();
    pvg.parameter("Source").setValue(gc);
    pvg.parameter("GridGeometry").setValue(newGridGeometry);
    pvg.parameter("InterpolationType").setValue(new InterpolationBilinear());
    return (GridCoverage2D) resampleFactory.doOperation(pvg, hints);

  }

  /**
   * Write a projection file (.PRJ) using wkt
   *
   * @param gc
   *            The GridCoverage to write
   * @param file
   *            The destination object (can be a File or ZipOutputStream)
   * @param dest
   *
   * @throws IOException
   *             If the file could not be written
   */
  private void writePRJ(final GridCoverage2D gc, String name, Object dest)
      throws IOException {
    if (dest instanceof File) {
      // create the file
      final BufferedWriter fileWriter = new BufferedWriter(
          new FileWriter(new File((File) dest, new StringBuffer(name)
              .append(".PRJ").toString())));

      // write information on crs
      fileWriter.write(gc.getCoordinateReferenceSystem().toWKT());
      fileWriter.close();
    } else {
      final ZipOutputStream out = (ZipOutputStream) dest;
      final ZipEntry e = new ZipEntry(new StringBuffer(gc.getName()
          .toString()).append(".PRJ").toString());
      out.putNextEntry(e);
      out.write(gc.getCoordinateReferenceSystem().toWKT().getBytes());
      out.closeEntry();
    }
  }

  /**
   * Writes the stats file (.STX).
   *
   * @param image
   *            The GridCoverage to write
   * @param file
   *            The destination object (can be a File or ZipOutputStream)
   * @param gc
   *            DOCUMENT ME!
   *
   * @throws IOException
   *             If the file could not be written
   */
  private void writeStats(final PlanarImage image, String name, Object dest,
      final GridCoverage2D gc) throws IOException {
    ParameterBlock pb = new ParameterBlock();
    // /////////////////////////////////////////////////////////////////////
    //
    // we need to evaluate stats first using jai
    //
    // /////////////////////////////////////////////////////////////////////
    final double[] Max = new double[] { Short.MAX_VALUE };//we should encode more than the maximum short anyway
    final double[] Min = new double[] { Short.MIN_VALUE };//we should avoid No Data values TODO

    // histogram
    pb.addSource(image);
    pb.add(null); // no roi
    pb.add(1);
    pb.add(1);
    pb.add(new int[] { (int) (Max[0] - Min[0] + 1) });
    pb.add(Min);
    pb.add(Max);
    pb.add(1);

    // /////////////////////////////////////////////////////////////////////
    //
    // Create the histogram
    //
    // /////////////////////////////////////////////////////////////////////
    final PlanarImage histogramImage = JAI.create("histogram", pb,
        new RenderingHints(JAI.KEY_TILE_CACHE, null));
    final Histogram hist = (Histogram) histogramImage
        .getProperty("histogram");
    pb.removeParameters();
    pb.removeSources();

    // /////////////////////////////////////////////////////////////////////
    //
    // Write things
    //
    // /////////////////////////////////////////////////////////////////////
    if (dest instanceof File) {
      // files destinations
      // write statistics
      if (dest instanceof File) {
        dest = new File((File) dest, new StringBuffer(name).append(
            ".STX").toString());
      }
      // writing world file
      final PrintWriter p = new PrintWriter(new FileOutputStream(
          ((File) dest)));
      p.print(1);
      p.print(" ");
      p.print((int) Min[0]);
      p.print(" ");
      p.print((int) Max[0]);
      p.print(" ");
      p.print(hist.getMean()[0]);
      p.print(" ");
      p.print(hist.getStandardDeviation()[0]);
      p.close();
    } else {
      final ZipOutputStream outZ = (ZipOutputStream) dest;
      final ZipEntry e = new ZipEntry(name + ".STX");
      outZ.putNextEntry(e);

      // writing world file
      outZ.write("1".getBytes());
      outZ.write(" ".getBytes());
      outZ.write(new Integer((int) Min[0]).toString().getBytes());
      outZ.write(" ".getBytes());
      outZ.write(new Integer((int) Max[0]).toString().getBytes());
      outZ.write(" ".getBytes());
      outZ.write(new Double(hist.getMean()[0]).toString().getBytes());
      outZ.write(" ".getBytes());
      outZ.write(new Double(hist.getStandardDeviation()[0]).toString()
          .getBytes());
      ((ZipOutputStream) dest).closeEntry();
    }

    histogramImage.dispose();
  }

  /**
   * Writes the world file (.DMW)
   *
   * @param gc
   *            The GridCoverage to write
   * @param worldFile
   *            The destination world file (can be a file or a
   *            ZipOutputStream)
   *
   * @throws IOException
   *             if the file could not be written
   */
  private void writeWorldFile(final GridCoverage2D gc, String name,
      Object dest) throws IOException {
    // final RenderedImage image = (PlanarImage) gc.getRenderedImage();

    /**
     * It is worth to point out that here I build the values using the axes
     * order specified in the CRs which can be either LAT,LON or LON,LAT.
     * This is important since we ned to know how to assocaite the
     * underlying raster dimensions with the envelope which is in CRS
     * values.
     */

    // /////////////////////////////////////////////////////////////////////
    //
    // trying to understand the direction of the first axis in order to
    // understand how to associate the value to the crs.
    //
    // /////////////////////////////////////////////////////////////////////
    final AffineTransform gridToWorld = (AffineTransform) gc
        .getGridGeometry().getGridToCRS2D();
    boolean lonFirst = (XAffineTransform.getSwapXY(gridToWorld) != -1);

    // /////////////////////////////////////////////////////////////////////
    //
    // associate value to crs
    //
    // /////////////////////////////////////////////////////////////////////
    final double xPixelSize = (lonFirst) ? gridToWorld.getScaleX()
        : gridToWorld.getShearY();
    final double rotation1 = (lonFirst) ? gridToWorld.getShearX()
        : gridToWorld.getScaleY();
    final double rotation2 = (lonFirst) ? gridToWorld.getShearY()
        : gridToWorld.getScaleX();
    final double yPixelSize = (lonFirst) ? gridToWorld.getScaleY()
        : gridToWorld.getShearX();
    final double xLoc = lonFirst ? gridToWorld.getTranslateX()
        : gridToWorld.getTranslateY();
    final double yLoc = lonFirst ? gridToWorld.getTranslateY()
        : gridToWorld.getTranslateX();
    if (dest instanceof File) {

      dest = new File((File) dest, new StringBuffer(name).append(
          ".DMW").toString());
      // writing world file
      final PrintWriter out = new PrintWriter(new FileOutputStream(
          (File) dest));
      out.println(xPixelSize);
      out.println(rotation1);
      out.println(rotation2);
      out.println(yPixelSize);
      out.println(xLoc);
      out.println(yLoc);
      out.close();
    } else {
      final ZipOutputStream outZ = (ZipOutputStream) dest;
      final ZipEntry e = new ZipEntry(gc.getName().toString()
          + ".DMW");
      outZ.putNextEntry(e);

      // writing world file
      outZ.write(Double.toString(xPixelSize).getBytes());
      outZ.write("\n".getBytes());
      outZ.write(Double.toString(rotation1).toString().getBytes());
      outZ.write("\n".getBytes());
      outZ.write(Double.toString(rotation2).toString().getBytes());
      outZ.write("\n".getBytes());
      outZ.write(Double.toString(xPixelSize).toString().getBytes());
      outZ.write("\n".getBytes());
      outZ.write(Double.toString(yPixelSize).toString().getBytes());
      outZ.write("\n".getBytes());
      outZ.write(Double.toString(xLoc).toString().getBytes());
      outZ.write("\n".getBytes());
      outZ.write(Double.toString(yLoc).toString().getBytes());
      outZ.write("\n".getBytes());
      ((ZipOutputStream) dest).closeEntry();
    }
  }

  /**
   * Writes the digital elevation model file (.DEM). The default byte order is
   * BIG_ENDIAN.
   *
   * @param image
   *            The GridCoverage object to write
   * @param name
   *            DOCUMENT ME!
   * @param dest
   *            The destination object (can be a File or a ZipOutputStream)
   *
   * @throws FileNotFoundException
   *             If the destination file could not be found
   * @throws IOException
   *             If the file could not be written
   */
  private void writeDEM(PlanarImage image, final String name, Object dest)
      throws FileNotFoundException, IOException {
    ImageOutputStream out;

    if (dest instanceof File) {
      // write statistics
      dest = new File((File) dest, new StringBuffer(name).append(
            ".DEM").toString());
      out = ImageIOExt.createImageOutputStream(image, (File) dest);
    } else {
      final ZipOutputStream outZ = (ZipOutputStream) dest;
      final ZipEntry e = new ZipEntry(name + ".DEM");
      outZ.putNextEntry(e);
      out = ImageIOExt.createImageOutputStream(image, outZ);
    }

    out.setByteOrder(java.nio.ByteOrder.BIG_ENDIAN);

    // untile the image in case it is tiled
    // otherwise we could add tiles which are unexistant in the original
    // data
    // generating failures when reading back the data again.
    image = untileImage(image);

    // requesting an imageio writer
    // setting tile parameters in order to tile the image on the disk
    final ParameterBlockJAI pbjW = new ParameterBlockJAI("ImageWrite");
    pbjW.addSource(image);
    pbjW.setParameter("Format", "raw");
    pbjW.setParameter("Output", out);
    JAI.create("ImageWrite", pbjW);

    if (dest instanceof File) {
      out.flush();
      out.close();
    } else {
      ((ZipOutputStream) dest).closeEntry();
    }
  }

  /**
   * This method has the objective of untiling the final image to write on
   * disk since we do not want to have tiles added to the file on disk causing
   * failures when reading it back into memory.
   *
   * @param image
   *            Image to untile.
   *
   * @return Untiled image.
   */
  private PlanarImage untileImage(RenderedImage image) {
    final ParameterBlockJAI pbj = new ParameterBlockJAI("format");
    pbj.addSource(image);
    pbj.setParameter("dataType", image.getSampleModel().getTransferType());

    final ImageLayout layout = new ImageLayout(image);
    layout.unsetTileLayout();
    layout.setTileGridXOffset(0);
    layout.setTileGridYOffset(0);
    layout.setTileHeight(image.getHeight());
    layout.setTileWidth(image.getWidth());
    layout.setValid(ImageLayout.TILE_GRID_X_OFFSET_MASK
        | ImageLayout.TILE_GRID_Y_OFFSET_MASK
        | ImageLayout.TILE_HEIGHT_MASK | ImageLayout.TILE_WIDTH_MASK);

    final RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT,
        layout);
    // avoid caching this image

    return JAI.create("format", pbj, hints);
  }

}
TOP

Related Classes of org.geotools.gce.gtopo30.GTopo30Writer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.