org.integratedmodelling.engine.geospace.coverage.raster.RasterCoverage Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (C) 2007, 2015:
*
* - 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.DataBuffer;
import java.awt.image.WritableRaster;
import javax.media.jai.InterpolationNearest;
import javax.media.jai.RasterFactory;
import javax.media.jai.iterator.RandomIterFactory;
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.coverage.processing.Operations;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.integratedmodelling.api.modelling.IExtent;
import org.integratedmodelling.api.modelling.IObserver;
import org.integratedmodelling.api.modelling.IScale;
import org.integratedmodelling.api.modelling.IState;
import org.integratedmodelling.api.monitoring.IMonitor;
import org.integratedmodelling.api.space.IGridMask;
import org.integratedmodelling.common.utils.Escape;
import org.integratedmodelling.common.utils.MiscUtilities;
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.extents.SpaceExtent;
import org.integratedmodelling.engine.visualization.VisualizationFactory;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabUnsupportedOperationException;
import org.integratedmodelling.exceptions.KlabValidationException;
public class RasterCoverage extends AbstractRasterCoverage {
static GridCoverageFactory rasterFactory = new GridCoverageFactory();
public RasterCoverage(IState state) throws KlabException {
double[] data = VisualizationFactory.get().getStateDataAsNumbers(state);
IExtent space = state.getSpace();
if (data == null || space == null || !(space instanceof SpaceExtent)
|| ((SpaceExtent) space).getGrid() == null) {
throw new KlabValidationException("cannot create a coverage from a non-spatial state");
}
buildFromData(state.getObservable().getSemantics().getType().getLocalName(), ((SpaceExtent) space).getGrid(), data);
}
public RasterCoverage(IState state, Iterable locators) throws KlabException {
double[] data = VisualizationFactory.get().getStateDataAsNumbers(state, locators);
IExtent space = state.getSpace();
if (data == null || space == null || !(space instanceof SpaceExtent)
|| ((SpaceExtent) space).getGrid() == null) {
throw new KlabValidationException("cannot create a coverage from a non-spatial state");
}
buildFromData(state.getObservable().getSemantics().getType().getLocalName(), ((SpaceExtent) space).getGrid(), data);
}
private void buildFromData(String name, Grid extent, Object data) throws KlabException {
/*
* build a coverage
*
* TODO use a raster of the appropriate type - for now there is apparently a bug in geotools
* that makes it work only with float.
* */
WritableRaster raster = RasterFactory
.createBandedRaster(DataBuffer.TYPE_FLOAT, extent.getXCells(), extent.getYCells(), 1, null);
/*
* TODO raster should be pre-filled with a chosen nodata value
* TODO use activation layer
*/
IGridMask act = extent.requireActivationLayer(true);
if (data instanceof int[]) {
for (int i = 0; i < extent.getCellCount(); i++) {
float d = ((int[]) data)[i];
int[] xy = extent.getXYOffsets(i);
raster.setSample(xy[0], xy[1], 0, d);
}
} else if (data instanceof long[]) {
for (int i = 0; i < extent.getCellCount(); i++) {
int[] xy = extent.getXYOffsets(i);
raster.setSample(xy[0], xy[1], 0, ((long[]) data)[i]);
}
} else if (data instanceof float[]) {
for (int i = 0; i < extent.getCellCount(); i++) {
int[] xy = extent.getXYOffsets(i);
raster.setSample(xy[0], xy[1], 0, ((float[]) data)[i]);
}
} else if (data instanceof double[]) {
for (int i = 0; i < extent.getCellCount(); i++) {
int[] xy = extent.getXYOffsets(i);
raster.setSample(xy[0], xy[1], 0, (float) ((double[]) data)[i]);
}
} else {
throw new KlabValidationException("cannot create a raster coverage from a "
+ data.getClass());
}
this.coverage = rasterFactory.create(name, raster, extent.getEnvelope());
this.layerName = name;
this.dimension = coverage.getSampleDimension(0);
this.crs = coverage.getCoordinateReferenceSystem2D();
this.gridGeometry = coverage.getGridGeometry();
/* no data values */
noData = dimension.getNoDataValues();
xCellSize = coverage.getEnvelope2D().getWidth() / getXCells();
yCellSize = coverage.getEnvelope2D().getHeight() / getYCells();
this.boundingBox = new ReferencedEnvelope(coverage.getEnvelope2D().getMinX(), coverage
.getEnvelope2D().getMaxX(), coverage.getEnvelope2D().getMinY(), coverage.getEnvelope2D()
.getMaxY(), crs);
}
/**
* Produce a new raster coverage from a cell extent and a vector of values that follow the
* activation model in the extent. Used after external transformation of spatial data.
* @throws KlabException
*/
public RasterCoverage(String name, Grid extent, Object data) throws KlabException {
buildFromData(name, extent, data);
}
/**
* The resampling constructor. Will do its best to produce a new coverage that matches
* a new extent from a different one. Be careful with this one.
*
* TODO it should take an interpolation option as a parameter.
* FIXME the affine transform for the cropping does weird things and tilts the image on its
* side. Plus, reprojection doesn't retain values. Until I figure it out, stick to WCS please.
*
* @param cov
* @param extent
* @throws KlabException
*/
public RasterCoverage(RasterCoverage cov, Grid extent) throws KlabException {
this.sourceURL = cov.sourceURL;
this.dimension = cov.dimension;
this.boundingBox = extent.getEnvelope();
this.xCellSize = boundingBox.getWidth() / extent.getXCells();
this.yCellSize = boundingBox.getHeight() / extent.getYCells();
this.crs = extent.getCRS();
// here's the geometry we want and the crs for the derived coverage
this.gridGeometry = new GridGeometry2D(extent.getGridRange(), extent.getEnvelope());
/*
* subset first
*/
this.coverage = (GridCoverage2D) Operations.DEFAULT
.resample(cov.coverage, extent.getEnvelope(), new InterpolationNearest());
/*
* then resample
*/
this.coverage = (GridCoverage2D) Operations.DEFAULT
.resample(this.coverage, this.crs, this.gridGeometry, new InterpolationNearest());
// this.coverage.show();
}
public RasterCoverage(String sourceURL, GridCoverage2D coverage, GridSampleDimension dimension,
boolean isSingleBand) {
this.sourceURL = sourceURL;
/* add band fragment ONLY if there is more than one band */
if (!isSingleBand) {
this.sourceURL += "#" + Escape.forURL(dimension.toString());
}
this.coverage = coverage;
this.dimension = dimension;
this.crs = coverage.getCoordinateReferenceSystem2D();
this.gridGeometry = coverage.getGridGeometry();
/* no data values */
noData = dimension.getNoDataValues();
/* TODO see if we have to add the band info */
this.layerName = MiscUtilities.getURLBaseName(sourceURL).toLowerCase();
xCellSize = coverage.getEnvelope2D().getWidth() / getXCells();
yCellSize = coverage.getEnvelope2D().getHeight() / getYCells();
this.boundingBox = new ReferencedEnvelope(coverage.getEnvelope2D().getMinX(), coverage
.getEnvelope2D().getMaxX(), coverage.getEnvelope2D().getMinY(), coverage.getEnvelope2D()
.getMaxY(), crs);
}
public RasterCoverage(String name, GridCoverage2D raster) {
this.coverage = raster;
this.layerName = name;
this.dimension = raster.getSampleDimension(0);
// this.dimension = dimension;
this.crs = coverage.getCoordinateReferenceSystem2D();
this.gridGeometry = coverage.getGridGeometry();
/* no data values */
noData = dimension.getNoDataValues();
xCellSize = coverage.getEnvelope2D().getWidth() / getXCells();
yCellSize = coverage.getEnvelope2D().getHeight() / getYCells();
this.boundingBox = new ReferencedEnvelope(coverage.getEnvelope2D().getMinX(), coverage
.getEnvelope2D().getMaxX(), coverage.getEnvelope2D().getMinY(), coverage.getEnvelope2D()
.getMaxY(), crs);
// coverage.show();
}
// @Override
public void loadData() throws KlabException {
if (_loaded)
return;
/*
* get rid of old image if we had one
*/
if (image != null) {
image = null;
}
image = coverage.getRenderedImage();
itera = RandomIterFactory.create(image, null);
_loaded = true;
}
@Override
public ICoverage requireMatch(Area extent, IObserver observer, IMonitor monitor, boolean allowClassChange)
throws KlabException {
// System.out.println("Coverage " + getLayerName() + " requested to match " + extent);
if (!(extent instanceof Grid)) {
throw new KlabUnsupportedOperationException("cannot yet match a raster coverage to a non-raster extent");
}
Grid cext = (Grid) extent;
if (matchesExtent(cext)) {
return this;
}
/*
* This constructor clones our metadata into a new coverage and
* resamples our coverage into another that reflects our extent.
*/
RasterCoverage ret = new RasterCoverage(this, cext);
ret.setClassMappings(classMappings);
return ret;
}
public void setClassMappings(String[] classification) {
classMappings = classification;
}
}