/*
* This file is part of JGrasstools (http://www.jgrasstools.org)
* (C) HydroloGIS - www.hydrologis.com
*
* JGrasstools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jgrasstools.gears.modules.r.interpolation2d;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_AUTHORCONTACTS;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_AUTHORNAMES;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_DOCUMENTATION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_F_CAT_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_IN_GRID_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_IN_MASK_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_IN_VECTOR_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_KEYWORDS;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_LABEL;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_LICENSE;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_NAME;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_OUT_RASTER_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_P_BUFFER_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_P_MAX_THREADS_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_P_MODE_DESCRIPTION;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSSURFACEINTERPOLATOR_STATUS;
import static org.jgrasstools.gears.libs.modules.JGTConstants.isNovalue;
import static org.jgrasstools.gears.libs.modules.Variables.IDW;
import static org.jgrasstools.gears.libs.modules.Variables.TPS;
import java.awt.image.WritableRaster;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.media.jai.iterator.RandomIter;
import javax.media.jai.iterator.RandomIterFactory;
import javax.media.jai.iterator.WritableRandomIter;
import org.geotools.coverage.grid.GridCoordinates2D;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.jgrasstools.gears.libs.exceptions.ModelsIllegalargumentException;
import org.jgrasstools.gears.libs.modules.JGTConstants;
import org.jgrasstools.gears.libs.modules.JGTModel;
import org.jgrasstools.gears.modules.r.interpolation2d.core.IDWInterpolator;
import org.jgrasstools.gears.modules.r.interpolation2d.core.ISurfaceInterpolator;
import org.jgrasstools.gears.modules.r.interpolation2d.core.TPSInterpolator;
import org.jgrasstools.gears.utils.RegionMap;
import org.jgrasstools.gears.utils.coverage.CoverageUtilities;
import org.jgrasstools.gears.utils.geometry.EGeometryType;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.operation.TransformException;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.index.strtree.STRtree;
import oms3.annotations.Author;
import oms3.annotations.Description;
import oms3.annotations.Documentation;
import oms3.annotations.Execute;
import oms3.annotations.In;
import oms3.annotations.Keywords;
import oms3.annotations.Label;
import oms3.annotations.License;
import oms3.annotations.Name;
import oms3.annotations.Out;
import oms3.annotations.Status;
import oms3.annotations.UI;
import oms3.annotations.Unit;
@Description(OMSSURFACEINTERPOLATOR_DESCRIPTION)
@Documentation(OMSSURFACEINTERPOLATOR_DOCUMENTATION)
@Author(name = OMSSURFACEINTERPOLATOR_AUTHORNAMES, contact = OMSSURFACEINTERPOLATOR_AUTHORCONTACTS)
@Keywords(OMSSURFACEINTERPOLATOR_KEYWORDS)
@Label(OMSSURFACEINTERPOLATOR_LABEL)
@Name(OMSSURFACEINTERPOLATOR_NAME)
@Status(OMSSURFACEINTERPOLATOR_STATUS)
@License(OMSSURFACEINTERPOLATOR_LICENSE)
public class OmsSurfaceInterpolator extends JGTModel {
@Description(OMSSURFACEINTERPOLATOR_IN_VECTOR_DESCRIPTION)
@In
public SimpleFeatureCollection inVector;
@Description(OMSSURFACEINTERPOLATOR_IN_GRID_DESCRIPTION)
@In
public GridCoverage2D inGrid = null;
@Description(OMSSURFACEINTERPOLATOR_IN_MASK_DESCRIPTION)
@In
public GridCoverage2D inMask = null;
@Description(OMSSURFACEINTERPOLATOR_F_CAT_DESCRIPTION)
@In
public String fCat;
@Description(OMSSURFACEINTERPOLATOR_P_MODE_DESCRIPTION)
@UI("combo:" + TPS + "," + IDW)
@In
public String pMode = TPS;
@Description(OMSSURFACEINTERPOLATOR_P_BUFFER_DESCRIPTION)
@Unit("m")
@In
public double pBuffer = 4.0;
@Description(OMSSURFACEINTERPOLATOR_P_MAX_THREADS_DESCRIPTION)
@In
public int pMaxThreads = 1;
@Description(OMSSURFACEINTERPOLATOR_OUT_RASTER_DESCRIPTION)
@Out
public GridCoverage2D outRaster = null;
private ISurfaceInterpolator interpolator;
private STRtree coordinatesSpatialTree;
private GridGeometry2D gridGeometry;
@Execute
public void process() throws Exception {
checkNull(inGrid);
gridGeometry = inGrid.getGridGeometry();
RegionMap regionMap = CoverageUtilities.getRegionParamsFromGridCoverage(inGrid);
final int cols = regionMap.getCols();
int rows = regionMap.getRows();
coordinatesSpatialTree = new STRtree();
if (inVector != null) {
checkNull(fCat);
GeometryDescriptor geometryDescriptor = inVector.getSchema().getGeometryDescriptor();
if (!EGeometryType.isPoint(geometryDescriptor)) {
throw new ModelsIllegalargumentException("The geometry has to be a point geometry.", this, pm);
}
SimpleFeatureIterator featureIterator = inVector.features();
Coordinate[] coordinates = new Coordinate[inVector.size()];
int index = 0;
pm.beginTask("Indexing control points...", coordinates.length);
while( featureIterator.hasNext() ) {
SimpleFeature feature = featureIterator.next();
Geometry geometry = (Geometry) feature.getDefaultGeometry();
coordinates[index] = geometry.getCoordinate();
double value = ((Number) feature.getAttribute(fCat)).doubleValue();
coordinates[index].z = value;
Envelope env = new Envelope(coordinates[index]);
coordinatesSpatialTree.insert(env, coordinates[index]);
pm.worked(1);
}
pm.done();
pm.message("Indexed control points: " + coordinates.length);
} else {
// create it from grid
pm.beginTask("Indexing control points...", cols);
RandomIter inIter = CoverageUtilities.getRandomIterator(inGrid);
int count = 0;
for( int c = 0; c < cols; c++ ) {
for( int r = 0; r < rows; r++ ) {
double value = inIter.getSampleDouble(c, r, 0);
if (!JGTConstants.isNovalue(value)) {
Coordinate coordinate = CoverageUtilities.coordinateFromColRow(c, r, gridGeometry);
coordinate.z = value;
Envelope env = new Envelope(coordinate);
coordinatesSpatialTree.insert(env, coordinate);
count++;
}
}
pm.worked(1);
}
pm.done();
pm.message("Indexed control points (from input grid): " + count);
}
coordinatesSpatialTree.build();
if (pMode.equals(IDW)) {
interpolator = new IDWInterpolator(pBuffer);
} else {
interpolator = new TPSInterpolator(pBuffer);
}
WritableRaster interpolatedWR = CoverageUtilities.createDoubleWritableRaster(cols, rows, null, null,
JGTConstants.doubleNovalue);
final WritableRandomIter interpolatedIter = RandomIterFactory.createWritable(interpolatedWR, null);
boolean doMultiThread = pMaxThreads > 1;
ExecutorService fixedThreadPool = null;
if (doMultiThread)
fixedThreadPool = Executors.newFixedThreadPool(pMaxThreads);
pm.beginTask("Performing interpolation...", rows);
final double[] eval = new double[1];
for( int r = 0; r < rows; r++ ) {
final int row = r;
if (doMultiThread) {
Runnable runner = new Runnable(){
public void run() {
processing(cols, coordinatesSpatialTree, interpolatedIter, eval, row);
}
};
fixedThreadPool.execute(runner);
} else {
processing(cols, coordinatesSpatialTree, interpolatedIter, eval, row);
}
}
if (doMultiThread) {
try {
fixedThreadPool.shutdown();
fixedThreadPool.awaitTermination(30, TimeUnit.DAYS);
fixedThreadPool.shutdownNow();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
pm.done();
outRaster = CoverageUtilities.buildCoverage("interpolatedraster", interpolatedWR, regionMap,
inGrid.getCoordinateReferenceSystem());
}
private void processing( final int cols, final STRtree tree, final WritableRandomIter interpolatedIter, final double[] eval,
final int row ) {
try {
for( int c = 0; c < cols; c++ ) {
final DirectPosition gridToWorld = gridGeometry.gridToWorld(new GridCoordinates2D(c, row));
// System.out.println(row + "/" + c);
boolean doProcess = true;
if (inMask != null) {
inMask.evaluate(gridToWorld, eval);
if (isNovalue(eval[0])) {
doProcess = false;
}
}
if (doProcess) {
final Coordinate currentCoord = new Coordinate();
final double[] coord = gridToWorld.getCoordinate();
currentCoord.x = coord[0];
currentCoord.y = coord[1];
final Envelope env = new Envelope(currentCoord.x - pBuffer, currentCoord.x + pBuffer, currentCoord.y
- pBuffer, currentCoord.y + pBuffer);
@SuppressWarnings("unchecked")
final List<Coordinate> result = tree.query(env);
// System.out.println(row + "/" + c + " = " + result.size());
// we need at least 3 points
if (result.size() < 4) {
continue;
}
final double value = interpolator.getValue(result.toArray(new Coordinate[0]), currentCoord);
synchronized (interpolatedIter) {
interpolatedIter.setSample(c, row, 0, value);
}
}
}
pm.worked(1);
} catch (TransformException e) {
e.printStackTrace();
}
}
}