Package org.geotools.gce.arcgrid

Source Code of org.geotools.gce.arcgrid.ArcGridReader

/*
*    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.arcgrid;

import it.geosolutions.imageio.plugins.arcgrid.AsciiGridsImageMetadata;
import it.geosolutions.imageio.plugins.arcgrid.spi.AsciiGridsImageReaderSpi;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.SampleModel;
import java.awt.image.renderable.ParameterBlock;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.FileCacheImageInputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import javax.measure.unit.Unit;
import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;

import org.geotools.coverage.Category;
import org.geotools.coverage.GridSampleDimension;
import org.geotools.coverage.TypeMap;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.data.DataSourceException;
import org.geotools.data.DataUtilities;
import org.geotools.data.PrjFileReader;
import org.geotools.factory.Hints;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.image.io.ImageIOExt;
import org.geotools.resources.i18n.Vocabulary;
import org.geotools.resources.i18n.VocabularyKeys;
import org.geotools.util.NumberRange;
import org.opengis.coverage.ColorInterpretation;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.geometry.Envelope;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValue;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import com.vividsolutions.jts.io.InStream;

/**
* This class can read an arc grid data source (ArcGrid or GRASS ASCII) and
* create a {@link GridCoverage2D} from the data.
*
* @author Daniele Romagnoli, GeoSolutions
* @author Simone Giannecchini, GeoSolutions
* @since 2.3.x
*
*
* @source $URL$
*/
public final class ArcGridReader extends AbstractGridCoverage2DReader implements
    GridCoverage2DReader {
  /** Logger. */
  private final static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.gce.arcgrid");

  /** Caches and ImageReaderSpi for an AsciiGridsImageReader. */
  private final static ImageReaderSpi readerSPI = new AsciiGridsImageReaderSpi();

  /** No data value for this dataset. */
  private double inNoData = Double.NaN;

    /**
   * Creates a new instance of an ArcGridReader basing the decision on whether
   * the file is compressed or not. I assume nothing about file extension.
   *
   * @param input
   *            Source object for which we want to build an ArcGridReader.
   * @throws DataSourceException
   */
  public ArcGridReader(Object input) throws DataSourceException {
    this(input, null);

  }

  /**
   * Creates a new instance of an ArcGridReader basing the decision on whether
   * the file is compressed or not. I assume nothing about file extension.
   *
   * @param input
   *            Source object for which we want to build an ArcGridReader.
   * @param hints
   *            Hints to be used by this reader throughout his life.
   * @throws DataSourceException
   */
  public ArcGridReader(Object input, final Hints hints)
      throws DataSourceException {
      super(input,hints);
    //
    // Checking input
    //
    coverageName = "AsciiGrid";
    try {

      //
      // Source management
      //
      checkSource(input,hints);

      //
      // CRS
      //
            final Object tempCRS = this.hints.get(Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM);
            if (tempCRS != null) {
                this.crs=(CoordinateReferenceSystem) tempCRS;
                LOGGER.log(Level.WARNING,"Using default coordinate reference system ");
            } else {      
                initCoordinateReferenceSystem();
            }

      //
      // Reader and metadata
      //
      // //
      //
      // Getting a reader for this format
      //
      // //
      final ImageReader reader = readerSPI.createReaderInstance();
      reader.setInput(inStream);


      //
      // Envelope and other metadata
      //
      parseMetadata(reader);

      //
      // Informations about multiple levels and such
      //
      getResolutionInfo(reader);

      // release the stream if we can.
      finalStreamPreparation();
    } catch (Exception e) {
      if (LOGGER.isLoggable(Level.SEVERE)){
          LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
      }
      throw new DataSourceException(e);
    }

  }

  /**
   * Close the {@link InStream} {@link ImageInputStream} if we open it up on
   * purpose to read header info for this {@link AbstractGridCoverage2DReader}.
   * If the stream cannot be closed, we just reset and mark it.
   *
   * @throws IOException
   */
  private void finalStreamPreparation() throws IOException {
    if (closeMe)
      inStream.close();
    else {
      inStream.reset();
      inStream.mark();
    }
  }

  /**
   * Checks the input provided to this {@link ArcGridReader} and sets all the
   * other objects and flags accordingly.
   *
   * @param input
   *            provided to this {@link ArcGridReader}.
   * @param hints
   *            Hints to be used by this reader throughout his life.
   *
   * @throws UnsupportedEncodingException
   * @throws DataSourceException
   * @throws IOException
   * @throws FileNotFoundException
   */
  private void checkSource(Object input, final Hints hints)
      throws UnsupportedEncodingException, DataSourceException,
      IOException, FileNotFoundException {
 
    closeMe = true;
    // //
    //
    // URL to File
    //
    // //
    // if it is a URL pointing to a File I convert it to a file,
    // otherwise, later on, I will try to get an inputstream out of it.
    if (input instanceof URL) {
      // URL that point to a file
      final URL sourceURL = ((URL) input);
      if (sourceURL.getProtocol().compareToIgnoreCase("file") == 0) {
        this.source = input = DataUtilities.urlToFile(sourceURL);

      }
    }

    // //
    //
    // File
    //
    // //
    if (input instanceof File) {
      final File sourceFile = (File) input;
      if (!sourceFile.exists() || sourceFile.isDirectory()
          || !sourceFile.canRead())
        throw new DataSourceException("Provided file does not exist or is a directory or is not readable!");
      this.coverageName = sourceFile.getName();
      final int dotIndex = coverageName.indexOf(".");
      gzipped = coverageName.toLowerCase().endsWith("gz");
      coverageName = (dotIndex == -1) ? coverageName : coverageName
          .substring(0, dotIndex);
      if(gzipped)
                            inStream = ImageIO.createImageInputStream(new GZIPInputStream(
                                new FileInputStream(sourceFile)))
      else{
          inStreamSPI= ImageIOExt.getImageInputStreamSPI(sourceFile);
          if (inStreamSPI == null)
                          throw new DataSourceException(
                                          "No input stream for the provided source");
          inStream = inStreamSPI.createInputStreamInstance(sourceFile, ImageIO.getUseCache(), ImageIO.getCacheDirectory());
      }
    } else
    // //
    //
    // URL
    //
    // //
    if (input instanceof URL) {
      final URL tempURL = ((URL) input);
      input = tempURL.openConnection().getInputStream();
      GZIPInputStream gzInStream = null;
      try {
        gzInStream = new GZIPInputStream((InputStream) input);
        gzipped = false;
      } catch (Exception e) {
        gzipped = false;
      }
      input = tempURL.openConnection().getInputStream();
      inStream = gzipped ? ImageIO.createImageInputStream(gzInStream)
          : ImageIO.createImageInputStream(tempURL.openConnection()
              .getInputStream());
    } else
    // //
    //
    // InputStream
    //
    // //
    if (input instanceof InputStream) {
      closeMe = false;
      if (ImageIO.getUseCache())
        inStream = new FileCacheImageInputStream((InputStream) input,
            null);
      else
        inStream = new MemoryCacheImageInputStream((InputStream) input);
      // let's mark it
      inStream.mark();
    } else
    // //
    //
    // ImageInputStream
    //
    // //
    if (input instanceof ImageInputStream) {
      closeMe = false;
      inStream = (ImageInputStream) input;
      inStream.mark();
    } else
      throw new IllegalArgumentException("Unsupported input type");

    if (inStream == null)
      throw new DataSourceException(
          "No input stream for the provided source");
  }

  /**
   * Gets resolution information about the coverage itself.
   *
   * @param reader
   *            an {@link ImageReader} to use for getting the resolution
   *            information.
   * @throws IOException
   * @throws TransformException
   */
  private void getResolutionInfo(ImageReader reader) throws IOException,
      TransformException {

    // //
    //
    // get the dimension of the hr image and build the model as well as
    // computing the resolution
    // //
    final Rectangle actualDim = new Rectangle(0, 0, reader.getWidth(0),reader.getHeight(0));
    originalGridRange = new GridEnvelope2D(actualDim);

    // ///
    //
    // setting the higher resolution avalaible for this coverage
    //
    // ///
    highestRes = getResolution(originalEnvelope, actualDim, crs);

  }

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

  /**
   * Reads a {@link GridCoverage2D} possibly matching as close as possible the
   * resolution computed by using the input params provided by using the
   * parameters for this {@link #read(GeneralParameterValue[])}.
   *
   * <p>
   * To have an idea about the possible read parameters take a look at
   * {@link AbstractGridFormat} class and {@link ArcGridFormat} class.
   *
   * @param params
   *            an array of {@link GeneralParameterValue} containing the
   *            parameters to control this read process.
   *
   * @return a {@link GridCoverage2D}.
   *
   * @see AbstractGridFormat
   * @see ArcGridFormat
   * @see org.opengis.coverage.grid.GridCoverageReader#read(org.opengis.parameter.GeneralParameterValue[])
   */
  @SuppressWarnings("unchecked")
  public GridCoverage2D read(GeneralParameterValue[] params)
      throws IllegalArgumentException, IOException {
    GeneralEnvelope readEnvelope = null;
    Rectangle requestedDim = null;
    OverviewPolicy overviewPolicy=null;
    if (params != null) {
      final int length = params.length;
      for (int i = 0; i < length; i++) {
        final ParameterValue param = (ParameterValue) params[i];
        final String name = param.getDescriptor().getName().getCode();
        if (name.equals(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName().toString())) {
          final GridGeometry2D gg = (GridGeometry2D) param.getValue();
          readEnvelope = new GeneralEnvelope((Envelope) gg.getEnvelope2D());
          requestedDim = gg.getGridRange2D().getBounds();
          continue;
        }
        if (name.equals(AbstractGridFormat.OVERVIEW_POLICY.getName().toString())) {
          overviewPolicy=(OverviewPolicy) param.getValue();
        }
      }
    }
    return createCoverage(readEnvelope, requestedDim, overviewPolicy);
  }

  /**
   * This method creates the GridCoverage2D from the underlying file.
   *
   * @param requestedDim
   * @param readEnvelope
   *
   *
   * @return a GridCoverage
   *
   * @throws java.io.IOException
   */
  private GridCoverage2D createCoverage(GeneralEnvelope requestedEnvelope,
      Rectangle requestedDim, OverviewPolicy overviewPolicy) throws IOException {

    if (!closeMe) {

      inStream.reset();
      inStream.mark();
    }
    // /////////////////////////////////////////////////////////////////////
    //
    // Doing an image read for reading the coverage.
    //
    // /////////////////////////////////////////////////////////////////////

    // //
    //
    // Setting subsampling factors with some checkings
    // 1) the subsampling factors cannot be zero
    // 2) the subsampling factors cannot be such that the w or h are zero
    //
    // //
    final ImageReadParam readP = new ImageReadParam();
    final Integer imageChoice;
    try {
      imageChoice = setReadParams(overviewPolicy, readP,
          requestedEnvelope, requestedDim);
    } catch (IOException e) {
      if (LOGGER.isLoggable(Level.SEVERE))
        LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
      return null;
    } catch (TransformException e) {
      if (LOGGER.isLoggable(Level.SEVERE))
        LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
      return null;
    }

    // //
    //
    // image and metadata
    //
    // //
    final ParameterBlock pbjImageRead = new ParameterBlock();
    // prepare input to handle possible parallelism between different
    // readers
    if (source instanceof File) {
      if (!gzipped){
          if(inStreamSPI!=null)
              pbjImageRead.add(inStreamSPI.createInputStreamInstance(source, ImageIO.getUseCache(), ImageIO.getCacheDirectory()    ));
          else
              pbjImageRead.add(ImageIO.createImageInputStream(source));
      }
      else
        pbjImageRead.add(ImageIO
            .createImageInputStream(new GZIPInputStream(
                new FileInputStream((File) source))));
    } else if (source instanceof ImageInputStream
        || source instanceof InputStream)
      pbjImageRead.add(inStream);
    else if (source instanceof URL) {
      if (gzipped)
        ImageIO.createImageInputStream(new GZIPInputStream(
            ((URL) source).openConnection().getInputStream()));
      else
        pbjImageRead.add(ImageIO.createImageInputStream(((URL) source)
            .openConnection().getInputStream()));

    }
    pbjImageRead.add(imageChoice);
    pbjImageRead.add(Boolean.FALSE);
    pbjImageRead.add(Boolean.FALSE);
    pbjImageRead.add(Boolean.FALSE);
    pbjImageRead.add(null);
    pbjImageRead.add(null);
    pbjImageRead.add(readP);
    pbjImageRead.add(readerSPI.createReaderInstance());
    final RenderedOp asciiCoverage = JAI.create("ImageRead", pbjImageRead,hints);

    // //
    //
    // Creating the coverage
    //
    // //
    try {

     
      // //
      //
      // Categories
      //
      //
      // //
      Unit<?> uom = null;
      final Category nan;
      if (Double.isNaN(inNoData)) {
        nan = new Category(Vocabulary
            .formatInternational(VocabularyKeys.NODATA), new Color(
            0, 0, 0, 0), 0);

      } else {
        nan = new Category(Vocabulary
            .formatInternational(VocabularyKeys.NODATA),
            new Color[] { new Color(0, 0, 0, 0) },
            NumberRange.create(0, 0),
            NumberRange.create(inNoData, inNoData));
 

      }


      //
      // Sample dimension
      //
            final SampleModel sm = asciiCoverage.getSampleModel();
            final ColorModel cm = asciiCoverage.getColorModel();
                  final ColorInterpretation colorInterpretation=TypeMap.getColorInterpretation(cm, 0);
                  if(colorInterpretation==null)
                         throw new IOException("Unrecognized sample dimension type");
                 
      final GridSampleDimension band = new GridSampleDimension(
          coverageName, new Category[] { nan }, uom).geophysics(true);
      final Map<String, Double> properties = new HashMap<String, Double>();
      properties.put("GC_NODATA", new Double(inNoData));

      //
      // Coverage
      //
      return coverageFactory.create(
              coverageName,
              asciiCoverage,
              originalEnvelope,
              new GridSampleDimension[] { band },
              null,
              properties);

    } catch (NoSuchElementException e) {
      if (LOGGER.isLoggable(Level.SEVERE))
        LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
      throw new DataSourceException(e);
    }
  }

  /**
   * This method is responsible for building up an envelope according to the
   * definition of the crs. It assumes that X coordinate on the ascii grid
   * itself maps to longitude and y coordinate maps to latitude.
   *
   * @param reader
   *            The {@link ImageReader} to parse.
   *
   * @throws MismatchedDimensionException
   */
  private void parseMetadata(ImageReader reader)
      throws Exception {

          // parse and set layout
          setLayout(reader);
         
                //
                // Getting metadata
                //
                final Object metadata = reader.getImageMetadata(0);
                if (!(metadata instanceof AsciiGridsImageMetadata)){
                        throw new DataSourceException(
                                        "Unexpected error! Metadata are not of the expected class.");
                }
           
                // casting the metadata
                final AsciiGridsImageMetadata gridMetadata = (AsciiGridsImageMetadata) metadata;
               
    // getting metadata
    final Node root = gridMetadata
        .getAsTree("it.geosolutions.imageio.plugins.arcgrid.AsciiGridsImageMetadata_1.0");

    // getting Grid Properties
    Node child = root.getFirstChild();
    NamedNodeMap attributes = child.getAttributes();
    final boolean grass = attributes.getNamedItem("GRASS").getNodeValue().equalsIgnoreCase("True");

    // getting Grid Properties
    child = child.getNextSibling();
    attributes = child.getAttributes();
    final int hrWidth = Integer.parseInt(attributes.getNamedItem("nColumns").getNodeValue());
    final int hrHeight = Integer.parseInt(attributes.getNamedItem("nRows").getNodeValue());
    originalGridRange = new GridEnvelope2D(new Rectangle(0, 0, hrWidth,hrHeight));
    final boolean pixelIsArea = AsciiGridsImageMetadata.RasterSpaceType.valueOf(attributes.getNamedItem("rasterSpaceType").getNodeValue()).equals(AsciiGridsImageMetadata.RasterSpaceType.PixelIsArea);
    if (!grass) {
        inNoData = Double.parseDouble(attributes.getNamedItem("noDataValue").getNodeValue());
    }

    // getting Envelope Properties
    child = child.getNextSibling();
    attributes = child.getAttributes();
    final double cellsizeX = Double.parseDouble(attributes.getNamedItem(
        "cellsizeX").getNodeValue());
    final double cellsizeY = Double.parseDouble(attributes.getNamedItem(
        "cellsizeY").getNodeValue());
    double xll = Double.parseDouble(attributes.getNamedItem("xll")
        .getNodeValue());
    double yll = Double.parseDouble(attributes.getNamedItem("yll")
        .getNodeValue());

    //
    // OGC specifications says that PixelIsArea map a pixel to the corner
    // of the grid while PixelIsPoint map a pixel to the centre of the grid.
    //
    if (!pixelIsArea) {
      final double correctionX = cellsizeX / 2d;
      final double correctionY = cellsizeY / 2d;
      xll -= correctionX;
      yll -= correctionY;
    }

    originalEnvelope = new GeneralEnvelope(new double[] { xll, yll },
        new double[] { xll + (hrWidth * cellsizeX),
            yll + (hrHeight * cellsizeY) });

    // setting the coordinate reference system for the envelope
    originalEnvelope.setCoordinateReferenceSystem(crs);

  }

    /**
   * Gets the coordinate system that will be associated to the
   * {@link GridCoverage}. The WGS84 coordinate system is used by default. It
   * is worth to point out that when reading from a stream which is not
   * connected to a file, like from an http connection (e.g. from a WCS) we
   * cannot rely on receiving a prj file too. In this case the exchange of
   * information about referencing should proceed the exchange of data thus I
   * rely on this and I ask the user who's invoking the read operation to
   * provide me a valid crs and envelope through read parameters.
   *
   * @throws FactoryException
   * @throws IOException
   * @throws FileNotFoundException
   */
  private void initCoordinateReferenceSystem() throws FileNotFoundException,
      IOException {

    // check to see if there is a projection file
    if (source instanceof File
        || (source instanceof URL && (((URL) source).getProtocol() == "file"))) {
      // getting name for the prj file
      final String sourceAsString;

      if (source instanceof File)
        sourceAsString = ((File) source).getAbsolutePath();
      else
        sourceAsString = ((URL) source).getFile();

      int index = sourceAsString.lastIndexOf(".");
      final StringBuffer prjFileName;
      if (index == -1)
        prjFileName = new StringBuffer(sourceAsString);
      else
        prjFileName = new StringBuffer(sourceAsString.substring(0,
            index));
      prjFileName.append(".prj");

      // does it exist?
      final File prjFile = new File(prjFileName.toString());
      if (prjFile.exists()) {
        // it exists then we have top read it
        PrjFileReader projReader = null;
        try {
          FileChannel channel = new FileInputStream(prjFile)
              .getChannel();
          projReader = new PrjFileReader(channel);
          crs = projReader.getCoordinateReferenceSystem();
        } catch (FileNotFoundException e) {
          // warn about the error but proceed, it is not fatal
          // we have at least the default crs to use
          LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
        } catch (IOException e) {
          // warn about the error but proceed, it is not fatal
          // we have at least the default crs to use
          LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
        } catch (FactoryException e) {
          // warn about the error but proceed, it is not fatal
          // we have at least the default crs to use
          LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
        } finally {
          if (projReader != null)
            try {
              projReader.close();
            } catch (IOException e) {
              // warn about the error but proceed, it is not fatal
              // we have at least the default crs to use
              LOGGER
                  .log(Level.SEVERE, e.getLocalizedMessage(),
                      e);
            }
        }
      }
    }
    if (crs == null) {
      crs = AbstractGridFormat.getDefaultCRS();
      LOGGER.info( "Unable to find crs, continuing with default CRS" );
    }
  }

  /**
   * Number of coverages for this reader is 1
   *
   * @return the number of coverages for this reader.
   */
  @Override
  public int getGridCoverageCount() {
    return 1;
  }
}
TOP

Related Classes of org.geotools.gce.arcgrid.ArcGridReader

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.