Package org.geotools.gce.image

Source Code of org.geotools.gce.image.WorldImageWriter

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2002-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.image;

import java.awt.geom.AffineTransform;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.io.BufferedWriter;
import java.io.File;
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.Set;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import javax.media.jai.JAI;
import javax.media.jai.ParameterBlockJAI;

import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.io.AbstractGridCoverageWriter;
import org.geotools.data.DataSourceException;
import org.geotools.factory.Hints;
import org.geotools.image.ImageWorker;
import org.geotools.image.io.ImageIOExt;
import org.geotools.parameter.Parameter;
import org.geotools.referencing.operation.matrix.XAffineTransform;
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.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;

/**
* Writes a GridCoverage to a raster image file and an accompanying world file.
* The destination specified must point to the location of the raster file to
* write to, as this is how the format is determined. The directory that file is
* located in must also already exist.
*
* @author Simone Giannecchini, GeoSolutions
* @author rgould
* @author Alessio Fabiani, GeoSolutions
*
*
* @source $URL$
*/
public final class WorldImageWriter extends AbstractGridCoverageWriter
    implements GridCoverageWriter {
  /** format for this writer */
  private Format format = new WorldImageFormat();

  /**
   * Format chosen for this writer.
   *
   * The default format is png.
   */
  private String extension = "png";

  /**
   * Destination must be a File. The directory it resides in must already
   * exist. It must point to where the raster image is to be located. The
   * world image will be derived from there.
   *
   * @param destination
   */
  public WorldImageWriter(Object destination) {
    this(destination, null);
  }

  /**
   * Destination must be a File. The directory it resides in must already
   * exist. It must point to where the raster image is to be located. The
   * world image will be derived from there.
   *
   * @param destination
   */
  public WorldImageWriter(Object destination, Hints hints) {
    this.destination = destination;

    // convert everything into a file when possible
    // we have to separate the handling of a file from the handling of an
    // output stream due to the fact that the latter requires no world file.
    if (this.destination instanceof String) {
      destination = new File((String) destination);
    } else if (this.destination instanceof URL) {
      final URL url = ((URL) destination);
      if (url.getProtocol().equalsIgnoreCase("file")) {
        String auth = url.getAuthority();
        String path = url.getPath();
        if (auth != null && !auth.equals("")) {
          path = "//"+auth+path;
        }
     
        destination = new File(path);
      } else {
        throw new RuntimeException(
            "WorldImageWriter::write:It is not possible writing to an URL!");
      }
    }

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

  /**
   * (non-Javadoc)
   *
   * @see org.opengis.coverage.grid.GridCoverageWriter#getFormat()
   */
  public Format getFormat() {
    return format;
  }

  /**
   * Takes a GridCoverage and writes the image to the destination file. It
   * then reads the format of the file and writes an accompanying world file.
   * It will throw a FileFormatNotCompatibleWithGridCoverageException if
   * Destination is not a File (URL is a read-only format!).
   *
   * @param coverage
   *            the GridCoverage to write.
   * @param parameters
   *            no parameters are accepted. Currently ignored.
   *
   * @throws IllegalArgumentException
   *             DOCUMENT ME!
   * @throws IOException
   *             DOCUMENT ME!
   * @see org.opengis.coverage.grid.GridCoverageWriter#write(org.geotools.gc.GridCoverage,
   *      org.opengis.parameter.GeneralParameterValue[])
   */
  public void write(GridCoverage coverage, GeneralParameterValue[] parameters)
      throws IllegalArgumentException, IOException {
    final GridCoverage2D gc = (GridCoverage2D) coverage;
    // checking parameters
    // if provided we have to use them
    // specifically this is one of the way we can provide an output format
    if (parameters != null) {
      this.extension = ((Parameter) parameters[0]).stringValue();
    }
   
    // /////////////////////////////////////////////////////////////////////
    //
    // WorldFile and projection file.
    //
    // ////////////////////////////////////////////////////////////////////
    if (destination instanceof File) {
      // files destinations
      File imageFile = (File) destination;
      final String path = imageFile.getAbsolutePath();
      final int index = path.lastIndexOf(".");
      final String baseFile = index >= 0 ? path.substring(0, index)
          : path;

      // envelope and image
      final RenderedImage image = gc.getRenderedImage();

      // world file
      try {
        createWorldFile(coverage, image, baseFile,extension);
      } catch (TransformException e) {
        final IOException ex = new IOException();
        ex.initCause(e);
        throw ex;
      }

      // projection file
      createProjectionFile(baseFile, coverage
          .getCoordinateReferenceSystem());

    }

    // /////////////////////////////////////////////////////////////////////
    //
    // Encoding of the original coverage
    //
    // ////////////////////////////////////////////////////////////////////
    outStream = ImageIOExt.createImageOutputStream(gc.getRenderedImage(), destination);
    if (outStream == null)
      throw new IOException(
          "WorldImageWriter::write:No image output stream avalaible for the provided destination");
    this.encode(gc, outStream,extension);

  }

  /**
   * This method is responsible for creating a projection file using the WKT
   * representation of this coverage's coordinate reference system. We can
   * reuse this file in order to rebuild later the crs.
   *
   *
   * @param baseFile
   * @param coordinateReferenceSystem
   * @throws IOException
   */
  private static void createProjectionFile(final String baseFile,
      final CoordinateReferenceSystem coordinateReferenceSystem)
      throws IOException {
    final File prjFile = new File(new StringBuffer(baseFile).append(".prj")
        .toString());
    BufferedWriter out = new BufferedWriter(new FileWriter(prjFile));
    out.write(coordinateReferenceSystem.toWKT());
    out.close();

  }

  /**
   * This method is responsible fro creating a world file to georeference an
   * image given the image bounding box and the image geometry. The name of
   * the file is composed by the name of the image file with a proper
   * extension, depending on the format (see WorldImageFormat). The projection
   * is in the world file.
   *
   * @param gc
   *            Envelope of this image.
   * @param image
   *            Image to be used.
   * @param baseFile
   *            Basename and path for this image.
   * @throws IOException
   *             In case we cannot create the world file.
   * @throws TransformException
   * @throws TransformException
   */
  private static void createWorldFile(final GridCoverage gc, final RenderedImage image,
      final String baseFile, final String extension) throws IOException, TransformException {
    // /////////////////////////////////////////////////////////////////////
    //
    // CRS information
    //
    // ////////////////////////////////////////////////////////////////////
    final AffineTransform gridToWorld = (AffineTransform) gc.getGridGeometry().getGridToCRS();
    final boolean lonFirst = (XAffineTransform.getSwapXY(gridToWorld) != -1);

    // /////////////////////////////////////////////////////////////////////
    //
    // World File values
    // It is worthwhile to note that we have to keep into account the fact
    // that the axis could be swapped (LAT,lon) therefore when getting
    // xPixSize and yPixSize we need to look for it a the right place
    // inside the grid to world transform.
    //
    // ////////////////////////////////////////////////////////////////////
    final double xPixelSize = (lonFirst) ? gridToWorld.getScaleX()
        : gridToWorld.getShearY();
    final double rotation1 = (lonFirst) ? gridToWorld.getShearX()
        : gridToWorld.getScaleX();
    final double rotation2 = (lonFirst) ? gridToWorld.getShearY()
        : gridToWorld.getScaleY();
    final double yPixelSize = (lonFirst) ? gridToWorld.getScaleY()
        : gridToWorld.getShearX();
    final double xLoc = gridToWorld.getTranslateX();
    final double yLoc = gridToWorld.getTranslateY();

    // /////////////////////////////////////////////////////////////////////
    //
    // writing world file
    //
    // ////////////////////////////////////////////////////////////////////
    final StringBuffer buff = new StringBuffer(baseFile);
//     looking for another extension
    final Set ext=WorldImageFormat.getWorldExtension(extension);
    final Iterator it=ext.iterator();
    if(!it.hasNext())
      throw new DataSourceException("Unable to parse extension "+extension);
    buff.append((String)it.next());
    final File worldFile = new File(buff.toString());
    final PrintWriter out = new PrintWriter(new FileOutputStream(worldFile));
    out.println(xPixelSize);
    out.println(rotation1);
    out.println(rotation2);
    out.println(yPixelSize);
    out.println(xLoc);
    out.println(yLoc);
    out.flush();
    out.close();

  }

  /**
   * Encode the given coverage to the requsted output format.
   *
   * @param sourceCoverage
   *            the coverage to be encoded.s
   * @param outstream
   *            OutputStream
   * @throws IOException
   *
   * @throws IOException
   * @throws IllegalArgumentException
   *             DOCUMENT ME!
   */
  static private void encode(final GridCoverage2D sourceCoverage,
      final ImageOutputStream outstream, final String extension) throws IOException {

    // do we have a source coverage?
    if (sourceCoverage == null) {
      throw new IllegalArgumentException(
          "A coverage must be provided in order for write to succeed!");
    }

    /**
     * Getting the non geophysics view of this grid coverage. the
     * geophysiscs view usually comes with an index color model for 3 bands,
     * since sometimes I get some problems with JAI encoders I select only
     * the first band, which by the way is the only band we use.
     */
    RenderedImage image = (sourceCoverage).geophysics(false)
        .getRenderedImage();
    final ImageWorker worker = new ImageWorker(image);

    // /////////////////////////////////////////////////////////////////////
    //
    // With index color model we want just the first band
    //
    // /////////////////////////////////////////////////////////////////////
    if (image.getColorModel() instanceof IndexColorModel
        && (image.getSampleModel().getNumBands() > 1)) {
      worker.retainBands(1);
      image = worker.getRenderedImage();
    }

    /**
     * For the moment we do not work with DirectColorModel but instead we
     * switch to component color model which is really easier to handle even
     * if it much more memory expensive. Once we are in component color
     * model is really easy to go to Gif and similar.
     */
    if (image.getColorModel() instanceof DirectColorModel) {
      worker.forceComponentColorModel();
      image = worker.getRenderedImage();
    }

    /**
     * ADJUSTMENTS FOR VARIOUS FILE FORMATS
     */

    // ------------------------GIF-----------------------------------
    if (extension.compareToIgnoreCase("gif") == 0) {

      /**
       * IndexColorModel with more than 8 bits for sample might be a
       * problem because GIF allows only 8 bits based palette therefore I
       * prefere switching to component color model in order to handle
       * this properly. NOTE. The only transfert types avalaible for
       * IndexColorModel are byte and ushort.
       */
      if (image.getColorModel() instanceof IndexColorModel
          && (image.getSampleModel().getTransferType() != DataBuffer.TYPE_BYTE)) {
        worker.forceComponentColorModel();
        image = worker.getRenderedImage();
      }

      /**
       * component color model is not well digested by the gif encoder we
       * need to go to indecolor model somehow. This code for the moment
       * remove transparency, but I am confident I will find a way to add
       * that.
       */
      if (image.getColorModel() instanceof ComponentColorModel) {
        worker.forceIndexColorModelForGIF(true);
        image = worker.getRenderedImage();
      } else
      /**
       * IndexColorModel with full transparency support is not suitable
       * for gif images we need to go to bitmask loosing some
       * informations. we have only one full transparent color.
       */
      if (image.getColorModel() instanceof IndexColorModel) {
        worker.forceIndexColorModelForGIF(true);
        image = worker.getRenderedImage();
      }
    }
    // else
    // -----------------TIFF--------------------------------------

    // /**
    // * TIFF file format. We need just a couple of correction for this
    // * format. It seems that the encoder does not work fine with
    // * IndexColorModel therefore in such a case we need the reformat the
    // * input image to a ComponentColorModel.
    // */
    // if (extension.compareToIgnoreCase("tiff") == 0
    // || extension.compareToIgnoreCase("tif") == 0) {
    // // Are we dealing with IndexColorModel? If so we need to go back
    // // to ComponentColorModel
    // if (image.getColorModel() instanceof IndexColorModel) {
    // surrogateImage = ImageUtilities
    // .reformatColorModel2ComponentColorModel(surrogateImage);
    // }
    // }

    /**
     * write using JAI encoders
     */
    final ParameterBlockJAI pbjImageWrite = new ParameterBlockJAI(
        "ImageWrite");
    pbjImageWrite.addSource(image);
    pbjImageWrite.setParameter("Output", outstream);
    pbjImageWrite.setParameter("VerifyOutput", Boolean.FALSE);
    pbjImageWrite.setParameter("Format", extension);
    JAI.create("ImageWrite", pbjImageWrite);
    outstream.flush();
    outstream.close();

  }
}
TOP

Related Classes of org.geotools.gce.image.WorldImageWriter

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.