All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.integratedmodelling.engine.geospace.coverage.raster.AbstractRasterCoverage Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (C) 2007, 2014:
 * 
 * - Ferdinando Villa  - integratedmodelling.org - any
 * other authors listed in @author annotations
 *
 * All rights reserved. This file is part of the k.LAB software suite, meant to enable
 * modular, collaborative, integrated development of interoperable data and model
 * components. For details, see http://integratedmodelling.org.
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms
 * of the Affero General Public License Version 3 or any later version.
 *
 * This program 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the Affero General Public License along with this
 * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite
 * 330, Boston, MA 02111-1307, USA. The license is also available at:
 * https://www.gnu.org/licenses/agpl.html
 *******************************************************************************/
package org.integratedmodelling.engine.geospace.coverage.raster;

import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.media.jai.iterator.RandomIter;

import org.geotools.coverage.GridSampleDimension;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.gce.geotiff.GeoTiffWriter;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.integratedmodelling.common.utils.NumberUtils;
import org.integratedmodelling.engine.geospace.Geospace;
import org.integratedmodelling.engine.geospace.coverage.ICoverage;
import org.integratedmodelling.engine.geospace.extents.Area;
import org.integratedmodelling.engine.geospace.extents.Grid;
import org.integratedmodelling.engine.geospace.gis.ThinklabVectorizer;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabIOException;
import org.integratedmodelling.exceptions.KlabUnsupportedOperationException;
import org.integratedmodelling.exceptions.KlabValidationException;
import org.opengis.geometry.BoundingBox;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

public abstract class AbstractRasterCoverage implements ICoverage {

    public static final String NODATA_PROPERTY = "raster.nodata";

    protected GridCoverage2D            coverage            = null;
    protected CoordinateReferenceSystem crs                 = null;
    // this one is only non-null if the layer was matched to a different extent; in that
    // case we
    // keep the original data bbox here so we can check if data are outside its bounds.
    protected ReferencedEnvelope        originalBoundingBox = null;
    protected ReferencedEnvelope        boundingBox         = null;
    protected GridGeometry2D            gridGeometry        = null;
    protected GridSampleDimension       dimension           = null;
    protected double[]                  noData              = null;
    protected String                    sourceURL;
    protected String                    layerName;
    protected double                    xCellSize;
    protected double                    yCellSize;
    protected RandomIter                itera;
    protected RenderedImage             image               = null;

    /*
     * allows lazy loading of raster image and JAI iterator
     */
    protected boolean _loaded    = false;
    // only for reporting
    protected boolean rasterized = false;

    public GridCoverage2D getCoverage() {
        return coverage;
    }
    
    public void setRasterized(boolean b) {
        this.rasterized = b;
    }

    public boolean isRasterized() {
        return this.rasterized;
    }
    
    /*
     * if this is not null, the raster encodes mappings to these values, with each raster
     * value mapping to classMappings[value - 1] and 0 representing no data.
     */
    String[] classMappings = null;

    static GridCoverageFactory rasterFactory = new GridCoverageFactory();

    public BoundingBox getBoundingBox() {
        return boundingBox;
    }

    @Override
    public ReferencedEnvelope getEnvelope() {
        return boundingBox;
    }

    // @Override
    public double[] getNodataValue() {
        return this.noData;
    }

    public void setNodataValue(double d) {
        this.noData = new double[] { d };
    }

    public void writeImage(File outfile, String format) throws KlabIOException {

        try {
            ImageIO.write(coverage.getRenderedImage(), "png", outfile);
        } catch (IOException e) {
            throw new KlabIOException(e);
        }
    }

    public int getXCells() {
        return getXRangeMax() - getXRangeOffset();
    }

    public int getYCells() {
        return getYRangeMax() - getYRangeOffset();
    }

    /**
     * Check coverage of point within the ORIGINAL data source. Only meaningful for WCS
     * coverages at the moment.
     * 
     * @param x
     * @param y
     * @return true if covered
     */
    public boolean isCovered(int x, int y) {

        if (this.originalBoundingBox == null)
            return true;

        double xx = boundingBox.getMinX() + (xCellSize * x) /*
                                                             * + (xCellSize/2.0)
                                                             */;
        double yy = boundingBox.getMinY() + (yCellSize * (getYCells() - y - 1)) /*
                                                                                 * +
                                                                                 * (yCellSize
                                                                                 * /2.0)
                                                                                 */;

        return originalBoundingBox.contains(xx, yy);
    }

    /**
     * Return the total number of cells in the coverage, including nodata ones.
     * 
     * @return total cells
     */
    public int getTotalCells() {
        return (getXRangeMax() - getXRangeOffset()) * (getYRangeMax() - getYRangeOffset());
    }

    /*
     * (non-Javadoc)
     * @see org.integratedmodelling.geospace.coverage.ICoverage#show()
     */
    public void show() {
        coverage.show();
    }

    /**
     * Gets the physical position in current coordinates that corresponds to the center of
     * the given pixel.
     * 
     * @param x
     * @param y
     * @return direct position
     */
    public DirectPosition2D getPosition(int x, int y) {

        double xx = boundingBox.getMinX() + (xCellSize * x) + (xCellSize / 2.0);
        double yy = boundingBox.getMinY() + (yCellSize * (getYCells() - y - 1)) + (yCellSize / 2.0);

        return new DirectPosition2D(xx, yy);
    }

    public double getDouble(int x, int y) {
        return itera.getSampleDouble(x, y, 0);
    }

    /**
     * Return the value at the given subdivision, either a double or whatever string our
     * value maps to if we're classifying.
     */
    @Override
    public Object getSubdivisionValue(int subdivisionOrder, Area extent) throws KlabValidationException {

        int[] xy = ((Grid) extent).getXYOffsets(subdivisionOrder);

        /**
         * force to NaN if outside of source data coverage. Geoserver does this wrong, so
         * we need to take over.
         */
//        if (!isCovered(xy[0], xy[1])) {
//            return Double.NaN;
//        }

        if (classMappings == null) {

            Double r = itera.getSampleDouble(xy[0], xy[1], 0);

            // the second condition is cheeky but will catch most of the nodata and
            // none of the good data
            if (Double.isNaN(r) || r < -1.0E35 || r > 1.0E35) {
                return Double.NaN;
            }

            if (this.noData != null) {
                for (double nd : noData) {
                    if (NumberUtils.equal(r, nd)) {
                        return Double.NaN;
                    }
                }
            }

            return r;
        }

        int index = itera.getSample(xy[0], xy[1], 0);
        return index == 0 ? null
                :   // the "nodata" of categories
                classMappings[index - 1];
    }

    public double getLatLowerBound() {
        return boundingBox.getMinY();
    }

    public double getLatUpperBound() {
        return boundingBox.getMaxY();
    }

    public double getLonLowerBound() {
        return boundingBox.getMinX();
    }

    public double getLonUpperBound() {
        return boundingBox.getMaxX();
    }

    public double getMaxDataValue() {
        return dimension.getMaximumValue();
    }

    public double getMinDataValue() {
        return dimension.getMinimumValue();
    }

    public double getNoDataValue() {
        // TODO check this is OK - it's clearly not
        return noData == null ? Double.NaN : noData[0];
    }

    @Override
    public String getCoordinateReferenceSystemCode() throws KlabException {
        return Geospace.getCRSIdentifier(crs, false);
    }

    @Override
    public String getSourceUrl() {
        return sourceURL;
    }

    public int getXRangeMax() {
        // todo use getEnvelope2D, then who knows
        // gridGeometry.getEnvelope2D().getMaximum(0);
        return gridGeometry.getGridRange2D().getHigh(0) + 1;
    }

    public int getXRangeOffset() {
        return gridGeometry.getGridRange().getLow(0);
    }

    public int getYRangeMax() {
        return gridGeometry.getGridRange().getHigh(1) + 1;
    }

    public int getYRangeOffset() {
        return gridGeometry.getGridRange().getLow(1);
    }

    @Override
    public String getLayerName() {
        return layerName;
    }

    public boolean matchesExtent(Grid extent) {

        return extent.getEnvelope().equals(boundingBox) && getXCells() == extent.getXCells()
                && getYCells() == extent.getYCells() && crs.equals(extent.getCRS());
    }

    /**
     * Vectorize into a vector coverage.
     * 
     * @param arealExtent
     * @return converted coverage
     * @throws KlabException
     */
    public ICoverage convertToVector(Grid arealExtent) throws KlabException {
        return ThinklabVectorizer.vectorize(this, arealExtent);
    }

    @Override
    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
        return crs;
    }

    @Override
    public Object write(File f) throws KlabException {

        if (!(f.toString().endsWith(".tif") || f.toString().endsWith(".tiff"))) {
            throw new KlabUnsupportedOperationException("raster coverage: only GeoTIFF format is supported for now");
        }

        GeoTiffWriter gtw;
        try {
            gtw = new GeoTiffWriter(f);
            gtw.write(coverage, null);
        } catch (IOException e) {
            throw new KlabIOException(e);
        }
        
        return null;
    }

    // @Override
    public void setName(String covId) {
        layerName = covId;
    }

    public boolean isLoaded() {
        return _loaded;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy