Package org.geotools.gce.gtopo30

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

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

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.SampleModel;
import java.awt.image.renderable.ParameterBlock;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteOrder;
import java.text.ParseException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.stream.ImageInputStream;
import javax.measure.unit.Unit;
import javax.measure.unit.UnitFormat;
import javax.media.jai.ImageLayout;
import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;

import org.geotools.coverage.Category;
import org.geotools.coverage.GridSampleDimension;
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.factory.Hints;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.referencing.factory.ReferencingFactoryContainer;
import org.geotools.referencing.operation.builder.GridToEnvelopeMapper;
import org.geotools.resources.i18n.Vocabulary;
import org.geotools.resources.i18n.VocabularyKeys;
import org.geotools.resources.image.ImageUtilities;
import org.geotools.util.NumberRange;
import org.opengis.coverage.grid.Format;
import org.opengis.geometry.Envelope;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.cs.CartesianCS;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransformFactory;
import org.opengis.referencing.operation.TransformException;

import com.sun.media.imageio.stream.RawImageInputStream;
import com.sun.media.imageioimpl.plugins.raw.RawImageReader;
import com.sun.media.imageioimpl.plugins.raw.RawImageReaderSpi;

/**
* This class provides a GridCoverageReader for the GTopo30Format.
*
* @author Simone Giannecchini
* @author jeichar
* @author mkraemer
*
*
* @source $URL$
*/
public final class GTopo30Reader extends AbstractGridCoverage2DReader implements GridCoverage2DReader {

  /** Logger. */
  private final static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.gce.gtopo30");
  /**
   * Cached {@link ImageIO} SPI for creating instances of
   * {@link RawImageReader}.
   */
  private final static RawImageReaderSpi imageIOSPI = new RawImageReaderSpi();

  private final static String dmext = ".dem";

  private final static String dhext = ".hdr";

  private final static String srext = ".src";

  private final static String shext = ".sch";

  private final static String stext = ".stx";

  private final static String prjext = ".prj";

  /** Dem data header URL */
  private final URL demURL;

  /** Dem statistics file URL */
  private final URL statsURL;

  /** Projection file. */
  private URL prjURL;

  /** The header for this GTOPO30 file. */
  private final GT30Header header;

  /** The file holding the statistics for this GTOPO30 file. */
  private final GT30Stats stats;

  /** The {@link URL} that points to the file to use. */
  private URL urlToUse;

  /** URL of the header file. */
  private final URL demHeaderURL;

  /**
   * GTopo30Reader constructor.
   *
   * @param source
   *            The source object (can be a File, an URL or a String
   *            representing a File or an URL).
   * @throws MalformedURLException
   *             if the URL does not correspond to one of the GTopo30 files
   * @throws IOException
   * @throws DataSourceException
   *             if the given url points to an unrecognized file
   * @throws IllegalArgumentException
   *             DOCUMENT ME!
   */
  public GTopo30Reader(final Object source) throws IOException {
    this(source, null);

  }

  /**
   * GTopo30Reader constructor.
   *
   * @param source
   *            The source object (can be a File, an URL or a String
   *            representing a File or an URL).
   * @throws MalformedURLException
   *             if the URL does not correspond to one of the GTopo30 files
   * @throws IOException
   * @throws DataSourceException
   *             if the given url points to an unrecognized file
   * @throws IllegalArgumentException
   *             DOCUMENT ME!
   */
  public GTopo30Reader(final Object source, final Hints hints)
      throws IOException {
      super(source, hints);

    if (source instanceof File) {
      urlToUse = ((File) source).toURI().toURL();
    } else if (source instanceof URL) {
      // we only allow files
      urlToUse = (URL) source;
    } else if (source instanceof String) {
      try {
        // is it a filename?
        urlToUse = new File((String) source).toURI().toURL();
      } catch (MalformedURLException e) {
        // is it a URL
        urlToUse = new URL((String) source);
      }
    } else {
      throw new IllegalArgumentException("Illegal input argument!");
    }
    this.source = source;
    coverageName = "gtopo30_coverage";
    // ///////////////////////////////////////////////////////////
    //
    // decoding source
    //
    // ///////////////////////////////////////////////////////////
    final String filename;

    filename = DataUtilities.urlToFile(urlToUse).getName();

    boolean recognized = false;
    boolean extUpperCase = false;

    if (filename.endsWith(dmext) || filename.endsWith(dhext)
        || filename.endsWith(srext) || filename.endsWith(shext)
        || filename.endsWith(stext) || filename.endsWith(prjext)) {
      recognized = true;
    } else {

      if (filename.endsWith(dmext.toUpperCase())
          || filename.endsWith(dhext.toUpperCase())
          || filename.endsWith(srext.toUpperCase())
          || filename.endsWith(shext.toUpperCase())
          || filename.endsWith(stext.toUpperCase())
          || filename.endsWith(prjext.toUpperCase())) {
        recognized = true;
        extUpperCase = true;
      }
    }

    if (!recognized) {
      throw new IOException(
          "Unrecognized file (file extension doesn't match)");
    }

    this.coverageName = filename.substring(0, filename.length() - 4);
    demURL = new URL(urlToUse, this.coverageName
        + (!extUpperCase ? dmext : dmext.toUpperCase()));
    prjURL = new URL(urlToUse, this.coverageName
        + (!extUpperCase ? prjext : prjext.toUpperCase()));
    demHeaderURL = new URL(urlToUse, this.coverageName
        + (!extUpperCase ? dhext : dhext.toUpperCase()));
    statsURL = new URL(urlToUse, this.coverageName
        + (!extUpperCase ? stext : stext.toUpperCase()));

    // ///////////////////////////////////////////////////////////
    //
    // Reading header and statistics
    //
    // ///////////////////////////////////////////////////////////
    header = new GT30Header(demHeaderURL);
    // get information from the header
    originalGridRange = new GridEnvelope2D(new Rectangle(0, 0, header.getNCols(), header.getNRows()));
    stats = new GT30Stats(this.statsURL);

    // ///////////////////////////////////////////////////////////
    //
    // Build the coordinate system and the envelope
    //
    // ///////////////////////////////////////////////////////////
                final Object tempCRS = this.hints.get(Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM);
                if (tempCRS != null) {
                    this.crs = (CoordinateReferenceSystem) tempCRS;
                    if(LOGGER.isLoggable(Level.WARNING))
                            LOGGER.log(Level.WARNING,"Using forced coordinate reference system ");
                } else
                    crs = initCRS();
    this.originalEnvelope = getBounds(crs);
    final GridToEnvelopeMapper geMapper= new  GridToEnvelopeMapper(originalGridRange,originalEnvelope);
    geMapper.setPixelAnchor(PixelInCell.CELL_CENTER);
    this.raster2Model=geMapper.createTransform();

    // /////////////////////////////////////////////////////////////////////
    //
    // Compute source Resolution
    //
    // /////////////////////////////////////////////////////////////////////
    highestRes = getResolution(originalEnvelope, new Rectangle(0, 0, header.getNCols(), header.getNRows()), crs);
    numOverviews = 0;
    overViewResolutions = null;
   
    //
    // ImageLayout
    //
                // Prepare temporary colorModel and sample model, needed to build the
                // RawImageInputStream
                final ColorModel cm = new ComponentColorModel(ColorSpace
                                .getInstance(ColorSpace.CS_GRAY), false, false,
                                Transparency.OPAQUE, DataBuffer.TYPE_SHORT);
                // building the final image layout
                final Dimension tileSize = ImageUtilities.toTileSize(new Dimension(originalGridRange.getSpan(0), originalGridRange.getSpan(1)));
                final SampleModel sm = cm .createCompatibleSampleModel(tileSize.width, tileSize.height);               

                ImageLayout il = new ImageLayout(0, 0, originalGridRange.getSpan(0),originalGridRange.getSpan(1));
                il.setTileGridXOffset(0).setTileGridYOffset(0).setTileWidth(tileSize.width).setTileHeight(tileSize.height);
                il.setColorModel(cm).setSampleModel(sm);
                setlayout(il);
  }

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

  /**
   * @see org.opengis.coverage.grid.GridCoverageReader#read(org.opengis.parameter.GeneralParameterValue[])
   */
  public GridCoverage2D read(
      final GeneralParameterValue[] params)
      throws java.lang.IllegalArgumentException, java.io.IOException {
    // /////////////////////////////////////////////////////////////////////
    //
    // do we have parameters to use for reading from the specified source
    //
    // /////////////////////////////////////////////////////////////////////
    GeneralEnvelope requestedEnvelope = null;
    Rectangle dim = null;
    OverviewPolicy overviewPolicy=null;
    if (params != null) {
      // /////////////////////////////////////////////////////////////////////
      //
      // Checking params
      //
      // /////////////////////////////////////////////////////////////////////
      if (params != null) {
        for (int i = 0; i < params.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();
            requestedEnvelope = new GeneralEnvelope((Envelope) gg
                .getEnvelope2D());
            dim = gg.getGridRange2D().getBounds();
            continue;
          }
          if (name.equals(AbstractGridFormat.OVERVIEW_POLICY
              .getName().toString())) {
            overviewPolicy=(OverviewPolicy) param.getValue();
            continue;
          }         
        }
      }
    }

    // /////////////////////////////////////////////////////////////////////
    //
    // Building the required coverage
    //
    // /////////////////////////////////////////////////////////////////////
    return getGridCoverage(requestedEnvelope, dim,overviewPolicy);
  }

  /**
   * Gets the bounding box of this datasource using the default speed of this
   * datasource as set by the implementer.
   *
   * @param lonFirst
   *
   * @return The bounding box of the datasource or null if unknown and too
   *         expensive for the method to calculate.
   *
   * @throws IOException
   *
   */
  private GeneralEnvelope getBounds(CoordinateReferenceSystem crs)
      throws IOException {
    GeneralEnvelope env = new GeneralEnvelope(new double[] { 0, 0 },
        new double[] { 0, 0 });

    // preparing data for the envelope
    final double xULC = header.getULXMap();
    final double yULC = header.getULYMap();
    final double xDim = header.getXDim();// dx
    final double yDim = header.getYDim();// dy
    final int imageWidth = header.getNCols();
    final int imageHeight = header.getNRows();
    final double longMin;
    final double latMax;
    final double longMax;
    final double latMin;

    longMin = xULC - xDim / 2.0;
    latMax = yULC + yDim / 2.0;
    longMax = longMin + imageWidth * xDim;
    latMin = latMax - imageHeight * yDim;

    // longitude
    env.setRange(0, longMin, longMax);
    // latitude
    env.setRange(1, latMin, latMax);

    env.setCoordinateReferenceSystem(crs);

    return env;
  }

  /**
   * Retrieves a grid coverage based on the DEM assoicated to this gtopo
   * coverage. The color palette is fixed and there is no possibility for the
   * final user to change it.
   *
   * @param dim
   * @param requestedEnvelope
   * @param overviewPolicy
   *
   * @return the GridCoverage object
   *
   * @throws DataSourceException
   *             if an error occurs
   */
  private GridCoverage2D getGridCoverage(GeneralEnvelope requestedEnvelope,
      Rectangle dim, OverviewPolicy overviewPolicy) throws IOException {
    int hrWidth = originalGridRange.getSpan(0);
    int hrHeight = originalGridRange.getSpan(1);

    // /////////////////////////////////////////////////////////////////////
    //
    // 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, dim);
    } catch (TransformException e) {
      throw new DataSourceException(e);
    }


    // /////////////////////////////////////////////////////////////////////
    //
    // Statistics
    //
    // /////////////////////////////////////////////////////////////////////
    final int max = stats.getMax();
    final int min = stats.getMin();

    // /////////////////////////////////////////////////////////////////////
    //
    // Preparing to load
    //
    // /////////////////////////////////////////////////////////////////////
    // trying to create a channel to the file to read
    final File file = DataUtilities.urlToFile(demURL);
    final ImageInputStream iis = ImageIO.createImageInputStream(file);
    if (header.getByteOrder().compareToIgnoreCase("M") == 0) {
      iis.setByteOrder(ByteOrder.BIG_ENDIAN);
    } else {
      iis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
    }

    // Prepare temporary colorModel and sample model, needed to build the
    // RawImageInputStream
    final ImageLayout layout = getImageLayout();
    final ImageTypeSpecifier its = new ImageTypeSpecifier(layout.getColorModel(null), layout.getSampleModel(null));
   
    // Finally, build the image input stream
    final RawImageInputStream raw = new RawImageInputStream(iis, its,
        new long[] { 0 }, new Dimension[] { new Dimension(hrWidth,
            hrHeight) });

    // building the final image layout
    final ImageLayout il = new ImageLayout(0, 0, hrWidth
        / readP.getSourceXSubsampling(), hrHeight
        / readP.getSourceYSubsampling(), 0, 0,
        layout.getTileWidth(null),
        layout.getTileHeight(null),
        layout.getSampleModel(null),
        layout.getColorModel(null));

    // First operator: read the image
    final RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT,
        il);
    final ParameterBlock pbjImageRead = new ParameterBlock();
    pbjImageRead.add(raw);
    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(imageIOSPI.createReaderInstance());
    RenderedOp image = JAI.create("ImageRead", pbjImageRead, hints);

    // sample dimension for this coverage
    final GridSampleDimension band = getSampleDimension(max, min);

    // setting metadata
    final Map<String,Double> metadata = new HashMap<String,Double>();
    metadata.put("maximum", Double.valueOf(stats.getMax()));
    metadata.put("minimum", Double.valueOf(stats.getMin()));
    metadata.put("mean", Double.valueOf(stats.getAverage()));
    metadata.put("std_dev", Double.valueOf(stats.getStdDev()));
    metadata.put("nodata", Double.valueOf(-9999.0));

    // /////////////////////////////////////////////////////////////////////
    //
    // Creating coverage
    //
    // /////////////////////////////////////////////////////////////////////
    // cleaning name
    String coverageName = (new File(this.coverageName)).getName();
    final int extension = coverageName.lastIndexOf(".");
    if (extension != -1) {
      String ext = coverageName.substring(extension + 1);

      if ((dmext.compareToIgnoreCase(ext) == 0)
          || (dhext.compareToIgnoreCase(ext) == 0)
          || (srext.compareToIgnoreCase(ext) == 0)
          || (shext.compareToIgnoreCase(ext) == 0)
          || (stext.compareToIgnoreCase(ext) == 0)) {
        coverageName = coverageName.substring(0, extension);
      }
    }

    // return the coverage
    return (GridCoverage2D) coverageFactory.create(coverageName, image,
        new GeneralEnvelope(originalEnvelope),
        new GridSampleDimension[] { band }, null, metadata);
  }

  /**
   * This method is responsible for the creation of the CRS for this GTOPO30.
   * The possible options are two, EPSG:4326 and POlar Stereographc. Inc ase
   * an error occurs the default CRS is chosen.
   *
   * @return CoordinateReferenceSystem a CRS for this coverage.
   * @throws IOException
   * @throws FactoryException
   */
  private CoordinateReferenceSystem initCRS() {
    BufferedReader reader = null;
    try {
      // getting a reader
      reader = new BufferedReader(new FileReader(DataUtilities.urlToFile(prjURL)));

      // reading the first line to see if I need to read it all
      final StringBuilder buffer = new StringBuilder(reader.readLine());

      if (buffer != null) {
        String line = buffer.toString().trim();

        if (!line.endsWith("POLAR") && !line.endsWith("GEOGRAPHIC")) {
          // in case I have a wkt string a need to read it all
          while ((line = reader.readLine()) != null)
            buffer.append(line);
        }
      }
      // closing the reader
      reader.close();
      // getting the content
      final String crsDescription = buffer.toString().trim();
      final DefaultGeographicCRS geoCRS = (DefaultGeographicCRS) CRS
          .decode("EPSG:4326", true);
      if (crsDescription != null) {
        if (crsDescription.endsWith("POLAR")) {
          // we need to build a polar stereographic crs based on wgs
          // 84. I am not so sure about the parameters I used. we
          // should check them again

          final CartesianCS cartCS = org.geotools.referencing.cs.DefaultCartesianCS.PROJECTED;
          final MathTransformFactory mtFactory = ReferencingFactoryFinder
              .getMathTransformFactory(null);
          final ParameterValueGroup parameters = mtFactory
              .getDefaultParameters("Polar_Stereographic");
          parameters.parameter("central_meridian").setValue(0.0);
          parameters.parameter("latitude_of_origin").setValue(-71.0);
          parameters.parameter("scale_factor").setValue(1);
          parameters.parameter("false_easting").setValue(0.0);
          parameters.parameter("false_northing").setValue(0.0);
          final ReferencingFactoryContainer factories =
              ReferencingFactoryContainer.instance(null);
          final Map<String,String> properties = Collections.singletonMap("name",
              "WGS 84 / Antartic Polar Stereographic");

          return factories.createProjectedCRS(properties, geoCRS,null, parameters, cartCS);
        }

        if (crsDescription.endsWith("GEOGRAPHIC")) {
          // in case I do not have a polar stereographic I build my
          // own CRS using either the supplied wkt
          // description or, in case none is supplied, a custom
          // Geographic WGS84 with lon, lat axes.
          return geoCRS;
        }
        return CRS.parseWKT(crsDescription);
      }
    } catch (IOException e) {
      // do nothing and return a default CRS but write down a message
      LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);

    } catch (FactoryException e) {
      // do nothing and return a default CRS but write down a message
      LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
    } finally {
      if (reader != null)
        try {
          // freeing
          reader.close();
        } catch (Exception e1) {
            if(LOGGER.isLoggable(Level.FINE))
                LOGGER.log(Level.FINE,e1.getLocalizedMessage(),e1);
        }
    }
                final CoordinateReferenceSystem crs = AbstractGridFormat.getDefaultCRS();
                LOGGER.info("PRJ file not found, proceeding with default crs");
                return crs;
  }

  /**
   * This method was implemented in order to reformat the input data to double
   * to introduce NaN as NoData instead of using -9999 since such a value for
   * NoData is automatically recognized from the SampleDimension code in order
   * to build the No Data category. This method has been used during tests in
   * order to try the creation of a GTOPO30 file from a floating point
   * coverage.
   *
   * @param max
   *            Max value in the input data
   * @param min
   *            Min Value in the input data
   *
   * @return the reformatted image.
   */


  /**
   * The purpose of this method is to build the sample dimensions for this
   * coverage.
   *
   * @param max
   *            Maximum value for this coverage.
   * @param min
   *            Minimum value for this coverage.
   *
   * @return The newly created sample dimensions.
   */
  private static GridSampleDimension getSampleDimension(final int max,
      final int min) {
    // Create the SampleDimension, with colors and byte transformation
    // needed for visualization
    UnitFormat unitFormat = UnitFormat.getInstance();
    Unit uom = null;

    try {
      // unit of measure is meter
      uom = (Unit) unitFormat.parseObject("m");
    } catch (ParseException ex1) {
      uom = null;
    }

   
    final Category nan =
        new Category(Vocabulary.format(VocabularyKeys.NODATA), new Color[] { new Color(0, 0, 0, 0) },
        NumberRange.create(0, 0), NumberRange.create((short) -9999,
            (short) -9999));
    final GridSampleDimension band = new GridSampleDimension(
        "digital-elevation", new Category[] { nan }, uom);

    return band.geophysics(true);
  }

}
TOP

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

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.