org.jgrasstools.lesto.modules.raster.Las2BivariateRasterMosaic Maven / Gradle / Ivy
The newest version!
/*
* 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 .
*/
package org.jgrasstools.lesto.modules.raster;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Math.round;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSHYDRO_AUTHORCONTACTS;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSHYDRO_AUTHORNAMES;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSHYDRO_DRAFT;
import static org.jgrasstools.gears.i18n.GearsMessages.OMSHYDRO_LICENSE;
import java.awt.Point;
import java.awt.image.WritableRaster;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.media.jai.iterator.WritableRandomIter;
import oms3.annotations.Author;
import oms3.annotations.Description;
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.Status;
import oms3.annotations.UI;
import oms3.annotations.Unit;
import org.apache.commons.math3.linear.DecompositionSolver;
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.linear.RRQRDecomposition;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.linear.RealVector;
import org.apache.commons.math3.linear.SingularMatrixException;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.jgrasstools.gears.io.las.ALasDataManager;
import org.jgrasstools.gears.io.las.core.LasRecord;
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.libs.modules.ThreadedRunnable;
import org.jgrasstools.gears.modules.r.imagemosaic.OmsImageMosaicCreator;
import org.jgrasstools.gears.utils.RegionMap;
import org.jgrasstools.gears.utils.colors.ColorTables;
import org.jgrasstools.gears.utils.colors.RasterStyleUtilities;
import org.jgrasstools.gears.utils.coverage.CoverageUtilities;
import org.jgrasstools.gears.utils.files.FileUtilities;
import org.jgrasstools.gears.utils.geometry.GeometryUtilities;
import org.jgrasstools.gears.utils.math.NumericsUtilities;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Polygon;
@Description("Module that converts a las data into a set of mosaic rasters using a bivariate function.")
@Author(name = OMSHYDRO_AUTHORNAMES, contact = OMSHYDRO_AUTHORCONTACTS)
@Keywords("las, lidar, raster, bivariate, mosaic")
@Label(JGTConstants.LESTO + "/raster")
@Name("lasfolder2bivariaterastermosaic")
@Status(OMSHYDRO_DRAFT)
@License(OMSHYDRO_LICENSE)
@SuppressWarnings("nls")
public class Las2BivariateRasterMosaic extends JGTModel {
@Description("Las files folder main index file path.")
@UI(JGTConstants.FILEIN_UI_HINT)
@In
public String inLas = null;
@Description("A region of interest. If not supplied the whole dataset is used.")
@UI(JGTConstants.FILEIN_UI_HINT)
@In
public String inRoi;
@Description("The tilesize of the subrasters.")
@Unit("m")
@In
public double pTilesize = 5000;
@Description("New resolution used for the tiles.")
@Unit("m")
@In
public double pRes = 1;
@Description("Buffer of influence for points interpolation in number of cells.")
@In
public int pBuffer = 2;
@Description("The impulse to use (if empty everything is used).")
@In
public Integer pImpulse = 1;
@Description("Number of threads to use.")
@In
public int pThreads = 1;
@Description("If true, intensity is used instead of elevation.")
@In
public boolean doIntensity = false;
@Description("Minimum number of points to consider the resulting cell valid.")
@In
public int pMinpoints = 6;
@Description("The output folder.")
@UI(JGTConstants.FILEOUT_UI_HINT)
@In
public String outFolder = null;
private static final double NOINTENSITY = -9999.0;
private volatile double minValue = Double.POSITIVE_INFINITY;
private volatile double maxValue = Double.NEGATIVE_INFINITY;
private CoordinateReferenceSystem crs;
@SuppressWarnings("rawtypes")
@Execute
public void process() throws Exception {
checkNull(inLas, outFolder);
final File outFolderFile = new File(outFolder);
try (ALasDataManager lasData = ALasDataManager.getDataManager(new File(inLas), null, 0, null)) {
lasData.open();
if (pImpulse != null) {
lasData.setImpulsesConstraint(new double[]{pImpulse});
}
ReferencedEnvelope roiEnvelope = lasData.getOverallEnvelope();
crs = roiEnvelope.getCoordinateReferenceSystem();
if (crs == null) {
if (inRoi == null) {
throw new ModelsIllegalargumentException("The lasfolder file needs to have a prj definition.", this);
}
}
if (inRoi != null) {
SimpleFeatureCollection inRoiFC = getVector(inRoi);
roiEnvelope = inRoiFC.getBounds();
}
double overallW = roiEnvelope.getMinX();
double overallE = roiEnvelope.getMaxX();
double overallS = roiEnvelope.getMinY();
double overallN = roiEnvelope.getMaxY();
double[] xBins = NumericsUtilities.range2Bins(overallW, overallE, pTilesize, true);
double[] yBins = NumericsUtilities.range2Bins(overallS, overallN, pTilesize, true);
int tilesCols = xBins.length - 1;
int tilesRows = yBins.length - 1;
pm.message("Splitting into tiles: " + tilesCols + " x " + tilesRows);
pm.beginTask("Interpolating tiles...", tilesCols * tilesRows);
ThreadedRunnable< ? > runnable = null;
if (pThreads > 1) {
runnable = new ThreadedRunnable(pThreads, pm);
}
for( int x = 0; x < xBins.length - 1; x++ ) {
for( int y = 0; y < yBins.length - 1; y++ ) {
final double w = xBins[x];
final double e = xBins[x + 1];
final double s = yBins[y];
final double n = yBins[y + 1];
final int _x = x;
final int _y = y;
if (runnable != null) {
runnable.executeRunnable(new Runnable(){
public void run() {
try {
processTile(outFolderFile, crs, lasData, _x, _y, w, e, s, n);
} catch (Exception ex) {
ex.printStackTrace();
}
pm.worked(1);
}
});
} else {
try {
processTile(outFolderFile, crs, lasData, _x, _y, w, e, s, n);
} catch (Exception ex) {
ex.printStackTrace();
}
pm.worked(1);
}
}
}
if (runnable != null) {
runnable.waitAndClose();
}
pm.done();
}
OmsImageMosaicCreator im = new OmsImageMosaicCreator();
im.inFolder = outFolder;
im.process();
String name = outFolderFile.getName();
String style = RasterStyleUtilities
.createStyleForColortable(ColorTables.extrainbow.name(), minValue, maxValue, null, 1.0);
File styleFile = new File(outFolderFile, name + ".sld");
FileUtilities.writeFile(style, styleFile);
}
@SuppressWarnings({"unchecked"})
private void processTile( File outFolderFile, CoordinateReferenceSystem crs, ALasDataManager lasData, int x, int y, double w,
double e, double s, double n ) throws Exception {
StringBuilder sb = new StringBuilder();
sb.append("tile_");
sb.append(x);
sb.append("_");
sb.append(y);
final String tileName = sb.toString();
sb.append(".tiff");
File outTileFile = new File(outFolderFile, sb.toString());
if (outTileFile.exists()) {
pm.errorMessage("Not overwriting existing tile: " + outTileFile.getName());
return;
}
final int cols = (int) round((e - w) / pRes);
final int rows = (int) round((n - s) / pRes);
double xRes = (e - w) / cols;
double yRes = (n - s) / rows;
RegionMap regionMap = CoverageUtilities.makeRegionParamsMap(n, s, w, e, xRes, yRes, cols, rows);
WritableRaster outWR = CoverageUtilities.createDoubleWritableRaster(cols, rows, null, null, JGTConstants.doubleNovalue);
GridCoverage2D outputCoverage = CoverageUtilities.buildCoverage("data", outWR, regionMap, crs);
// pm.message("Reading laspoints for: " + tileName);
Envelope env = new Envelope(w, e, s, n);
/*
* enlarge by buffer value, to solve border issues
*
* -> outside points are read to aid interpolation at borders
* -> this forces to keep two gridgeometries active, the
* resulting coverage + the buffered one
*/
double deltaX = xRes * pBuffer;
double deltaY = yRes * pBuffer;
env.expandBy(deltaX, deltaY);
Polygon roiPolygon = GeometryUtilities.createPolygonFromEnvelope(env);
List tileLasPoints = lasData.getPointsInGeometry(roiPolygon, true);
if (tileLasPoints.size() > 100) {
final GridGeometry2D gridGeometry = outputCoverage.getGridGeometry();
int newCols = cols + 2 * pBuffer;
int newRows = rows + 2 * pBuffer;
GridGeometry2D bufferedGridGeometry = CoverageUtilities.gridGeometryFromRegionValues(env.getMaxY(), env.getMinY(),
env.getMaxX(), env.getMinX(), newCols, newRows, crs);
ArrayList[][] lasMatrix = new ArrayList[newCols][newRows];
for( int c = 0; c < newCols; c++ ) {
for( int r = 0; r < newRows; r++ ) {
ArrayList item = new ArrayList();
lasMatrix[c][r] = item;
}
}
// Splitting las into cells
final Point point = new Point();
for( LasRecord dot : tileLasPoints ) {
if (doIntensity) {
if (dot.intensity == NOINTENSITY) {
continue;
}
minValue = min(dot.intensity, minValue);
maxValue = max(dot.intensity, maxValue);
} else {
minValue = min(dot.z, minValue);
maxValue = max(dot.z, maxValue);
}
CoverageUtilities.colRowFromCoordinate(new Coordinate(dot.x, dot.y), bufferedGridGeometry, point);
lasMatrix[point.x][point.y].add(dot);
}
WritableRandomIter outWIter = null;
try {
outWIter = CoverageUtilities.getWritableRandomIterator(outWR);
for( int c = pBuffer; c < newCols - pBuffer; c++ ) {
if (c % 100 == 0) {
StringBuilder sb1 = new StringBuilder();
sb1.append(tileName);
sb1.append(": ");
sb1.append(c);
sb1.append(" of ");
sb1.append(newCols);
pm.message(sb1.toString());
}
for( int r = pBuffer; r < newRows - pBuffer; r++ ) {
Coordinate coordinate = CoverageUtilities.coordinateFromColRow(c, r, bufferedGridGeometry);
List currentPoints = new ArrayList();
for( int tmpC = c - pBuffer; tmpC <= c + pBuffer; tmpC++ ) {
for( int tmpR = r - pBuffer; tmpR <= r + pBuffer; tmpR++ ) {
currentPoints.addAll(lasMatrix[tmpC][tmpR]);
}
}
int size = currentPoints.size();
if (size >= pMinpoints) {
// need at least as many samples as parameters
try {
double[] parameters = calculateParameters(currentPoints);
double interpolatedValue = getInterpolatedValue(parameters, coordinate.x, coordinate.y);
// limit by min/max
if (interpolatedValue < minValue) {
interpolatedValue = minValue;
}
if (interpolatedValue > maxValue) {
interpolatedValue = maxValue;
}
CoverageUtilities.colRowFromCoordinate(coordinate, gridGeometry, point);
outWIter.setSample(point.x, point.y, 0, interpolatedValue);
} catch (SingularMatrixException ex) {
// we ignore the singular matrix leaving the cell undefined
}
}
}
}
} finally {
if (outWIter != null)
outWIter.done();
}
dumpRaster(outputCoverage, outTileFile.getAbsolutePath());
} else {
pm.message(tileName + " ignored because of no points there.");
}
}
private double[] calculateParameters( final List pointsInGeometry ) {
int pointsNum = pointsInGeometry.size();
final double[][] xyMatrix = new double[pointsNum][6];
final double[] valueArray = new double[pointsNum];
for( int i = 0; i < pointsNum; i++ ) {
LasRecord dot = pointsInGeometry.get(i);
xyMatrix[i][0] = dot.x * dot.x; // x^2
xyMatrix[i][1] = dot.y * dot.y; // y^2
xyMatrix[i][2] = dot.x * dot.y; // xy
xyMatrix[i][3] = dot.x; // x
xyMatrix[i][4] = dot.y; // y
xyMatrix[i][5] = 1;
if (doIntensity) {
valueArray[i] = dot.intensity;
} else {
valueArray[i] = dot.z;
}
}
RealMatrix A = MatrixUtils.createRealMatrix(xyMatrix);
RealVector z = MatrixUtils.createRealVector(valueArray);
DecompositionSolver solver = new RRQRDecomposition(A).getSolver();
RealVector solution = solver.solve(z);
// start values for a, b, c, d, e, f, all set to 0.0
final double[] parameters = solution.toArray();
return parameters;
}
private double getInterpolatedValue( double[] parameters, double x, double y ) {
double z = parameters[0] * x * x + //
parameters[1] * y * y + //
parameters[2] * x * y + //
parameters[3] * x + //
parameters[4] * y + //
parameters[5];
return z;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy