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

org.jgrasstools.gears.modules.r.summary.OmsZonalStats 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.gears.modules.r.summary;

import static java.lang.Math.abs;
import static java.lang.Math.ceil;
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
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_LICENSE;
import static org.jgrasstools.gears.libs.modules.JGTConstants.isNovalue;

import java.awt.geom.AffineTransform;
import java.text.MessageFormat;
import java.util.List;

import javax.media.jai.iterator.RandomIter;

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.Out;
import oms3.annotations.Status;

import org.geotools.coverage.grid.GridCoordinates2D;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.operation.matrix.XAffineTransform;
import org.jgrasstools.gears.libs.exceptions.ModelsIOException;
import org.jgrasstools.gears.libs.modules.JGTConstants;
import org.jgrasstools.gears.libs.modules.JGTModel;
import org.jgrasstools.gears.libs.monitor.IJGTProgressMonitor;
import org.jgrasstools.gears.utils.coverage.CoverageUtilities;
import org.jgrasstools.gears.utils.features.FeatureUtilities;
import org.jgrasstools.gears.utils.geometry.GeometryUtilities;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Polygon;

@Description("Calculate zonal stats.")
@Author(name = OMSHYDRO_AUTHORNAMES, contact = OMSHYDRO_AUTHORCONTACTS)
@Keywords("zonalstats")
@Label(JGTConstants.RASTERPROCESSING)
@Name("zonalstats")
@Status(Status.EXPERIMENTAL)
@License(OMSHYDRO_LICENSE)
public class OmsZonalStats extends JGTModel {

    @Description("The raster map to process.")
    @In
    public GridCoverage2D inRaster = null;

    @Description("The polygons map on which to do the stats.")
    @In
    public SimpleFeatureCollection inVector = null;

    @Description("Percentage of minimum active cells to have a valid stat.")
    @In
    public double pPercentageThres = 20.0;

    @Description("Total meanvalue (also produced by this module) for the calculation of the mean absolute deviation.")
    @In
    public Double pTotalMean = null;

    @Description("The input polygons with the added stats values.")
    @Out
    public SimpleFeatureCollection outVector;

    /**
     * The array holding:
     * 
    *
  • totalMean
  • *
  • userTotalMean
  • *
  • totalActiveCells
  • *
* if {@link #pTotalMean} is != null. */ double[] tm_usertm_tactivecells = new double[3]; public static final String MIN = "min"; public static final String MAX = "max"; public static final String AVG = "avg"; public static final String VAR = "var"; public static final String SDEV = "sdev"; public static final String AVGABSDEV = "avgabsdev"; public static final String ACTCELLS = "actcells"; public static final String INVCELLS = "invcells"; @Execute public void process() throws Exception { checkNull(inVector, inRaster); boolean hasUserTotalMean = false; if (pTotalMean != null) { hasUserTotalMean = true; tm_usertm_tactivecells[1] = pTotalMean; } ReferencedEnvelope bounds = inVector.getBounds(); CoordinateReferenceSystem crs = bounds.getCoordinateReferenceSystem(); SimpleFeatureBuilder featureBuilder = createFeatureBuilder(crs, hasUserTotalMean); outVector = new DefaultFeatureCollection(); List geometriesList = FeatureUtilities.featureCollectionToGeometriesList(inVector, true, null); // pm.message("" + readEnvelope); GridGeometry2D gridGeometry = inRaster.getGridGeometry(); RandomIter readIter = CoverageUtilities.getRandomIterator(inRaster); pm.beginTask("Processing polygons...", geometriesList.size()); for( Geometry geometry : geometriesList ) { double[] polygonStats = polygonStats(geometry, gridGeometry, readIter, hasUserTotalMean, tm_usertm_tactivecells, pPercentageThres, pm); if (polygonStats == null) { continue; } Object[] values; if (!hasUserTotalMean) { values = new Object[]{geometry, // polygonStats[0], // polygonStats[1], // polygonStats[2], // polygonStats[3], // polygonStats[4], // (int) polygonStats[5], // (int) polygonStats[6] // }; } else { values = new Object[]{geometry, // polygonStats[0], // polygonStats[1], // polygonStats[2], // polygonStats[3], // polygonStats[4], // polygonStats[5], // (int) polygonStats[6], // (int) polygonStats[7] // }; } featureBuilder.addAll(values); SimpleFeature feature = featureBuilder.buildFeature(null); ((DefaultFeatureCollection) outVector).add(feature); pm.worked(1); } pm.done(); if (!hasUserTotalMean) { tm_usertm_tactivecells[0] = tm_usertm_tactivecells[0] / tm_usertm_tactivecells[2]; pm.message("Total mean: " + tm_usertm_tactivecells[0]); } } /** * @param geometry * @param gridGeometry * @param inIter * @param tm_utm_tac the array holding: *
    *
  • totalMean
  • *
  • userTotalMean
  • *
  • totalActiveCells
  • *
* @param percentageThres * @param monitor an optional monitor. If null an exception * is thrown in case of cusps, else an errormessage is given, but * processing will go on ignoring the geometry. * @return * @throws Exception */ public static double[] polygonStats( Geometry geometry, GridGeometry2D gridGeometry, RandomIter inIter, boolean hasUserTotalMean, double[] tm_utm_tac, double percentageThres, IJGTProgressMonitor monitor ) throws Exception { GeometryFactory gf = GeometryUtilities.gf(); GridEnvelope2D gridRange = gridGeometry.getGridRange2D(); int rows = gridRange.height; int cols = gridRange.width; int startX = gridRange.x; int startY = gridRange.y; AffineTransform gridToCRS = (AffineTransform) gridGeometry.getGridToCRS(); double xRes = XAffineTransform.getScaleX0(gridToCRS); double yRes = XAffineTransform.getScaleY0(gridToCRS); final double delta = xRes / 4.0; Envelope env = geometry.getEnvelopeInternal(); env.expandBy(xRes, yRes); double geometryUpperBound = env.getMaxY(); double envArea = env.getWidth() * env.getHeight(); int maxCells = (int) ceil(envArea / (xRes * yRes)); int activeCellCount = 0; int passiveCellCount = 0; double min = Double.POSITIVE_INFINITY; double max = Double.NEGATIVE_INFINITY; MathTransform gridToCRS2 = gridGeometry.getGridToCRS(); double[] values = new double[maxCells]; LineString previousLine = null; final Coordinate worldWestCoord = new Coordinate(); final Coordinate worldEastCoord = new Coordinate(); for( int r = startY; r < startY + rows; r++ ) { // do scan line to fill the polygon Coordinate gridWestCoord = new Coordinate(startX, r); JTS.transform(gridWestCoord, worldWestCoord, gridToCRS2); if (geometryUpperBound < worldWestCoord.y) { continue; } Coordinate gridEastCoord = new Coordinate(startX + cols - 1, r); JTS.transform(gridEastCoord, worldEastCoord, gridToCRS2); LineString line = gf.createLineString(new Coordinate[]{worldWestCoord, worldEastCoord}); if (previousLine != null && previousLine.equals(line)) { previousLine = line; continue; } if (geometry.intersects(line)) { Geometry internalLines = geometry.intersection(line); int lineNums = internalLines.getNumGeometries(); for( int l = 0; l < lineNums; l++ ) { Coordinate[] coords = internalLines.getGeometryN(l).getCoordinates(); if (coords.length == 2) { for( int j = 0; j < coords.length; j = j + 2 ) { Coordinate startC = new Coordinate(coords[j].x + delta, coords[j].y); Coordinate endC = new Coordinate(coords[j + 1].x - delta, coords[j + 1].y); DirectPosition2D startDP; DirectPosition2D endDP; if (startC.x < endC.x) { startDP = new DirectPosition2D(startC.x, startC.x); endDP = new DirectPosition2D(endC.x, endC.x); } else { startDP = new DirectPosition2D(endC.x, endC.x); endDP = new DirectPosition2D(startC.x, startC.x); } GridCoordinates2D startGridCoord = gridGeometry.worldToGrid(startDP); GridCoordinates2D endGridCoord = gridGeometry.worldToGrid(endDP); /* * the part in between has to be filled */ for( int k = startGridCoord.x; k <= endGridCoord.x; k++ ) { double v = inIter.getSampleDouble(k, r, 0); if (isNovalue(v)) { passiveCellCount++; continue; } min = Math.min(min, v); max = Math.max(max, v); values[activeCellCount] = v; activeCellCount++; if (!hasUserTotalMean) { tm_utm_tac[0] = tm_utm_tac[0] + v; tm_utm_tac[2] = tm_utm_tac[2] + 1; } } } } else { if (coords.length == 1) { String message = MessageFormat.format("Found a cusp in: {0}/{1}", coords[0].x, coords[0].y); if (monitor != null) { monitor.errorMessage(message); } else { throw new ModelsIOException(message, "ZonalStats"); } } else { throw new ModelsIOException(MessageFormat.format( "Found intersection with more than 2 points in: {0}/{1}", coords[0].x, coords[0].y), "ZonalStats"); } } } } previousLine = line; } int all = activeCellCount + passiveCellCount; double ratio = 100.0 * activeCellCount / all; if (ratio < percentageThres) { return null; } double mean = mean(values, activeCellCount); double sdev = standardDeviation(values, mean, activeCellCount); double var = variance(values, mean, activeCellCount); double[] result; if (hasUserTotalMean) { double meanAbsoluteDeviation = meanAbsoluteDeviation(values, activeCellCount, tm_utm_tac[1]); result = new double[]{min, max, mean, var, sdev, meanAbsoluteDeviation, activeCellCount, passiveCellCount}; } else { result = new double[]{min, max, mean, var, sdev, activeCellCount, passiveCellCount}; } return result; } public static SimpleFeatureBuilder createFeatureBuilder( CoordinateReferenceSystem crs, boolean hasUserTotalMean ) { SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); b.setName("stats"); b.setCRS(crs); b.add("the_geom", Polygon.class); b.add(MIN, Double.class); b.add(MAX, Double.class); b.add(AVG, Double.class); b.add(VAR, Double.class); b.add(SDEV, Double.class); if (hasUserTotalMean) b.add(AVGABSDEV, Double.class); b.add(ACTCELLS, Integer.class); b.add(INVCELLS, Integer.class); SimpleFeatureType type = b.buildFeatureType(); return new SimpleFeatureBuilder(type); } private static double meanAbsoluteDeviation( double[] values, int count, double userTotalMean ) { double mean = 0; for( int i = 0; i < count; i++ ) { mean = mean + abs(values[i] - userTotalMean); } return mean / count; } private static double mean( double[] values, int count ) { double mean = 0; for( int i = 0; i < count; i++ ) { mean += values[i]; } return mean / count; } private static double standardDeviation( double[] values, double mean, int count ) { double sd = variance(values, mean, count); sd = sqrt(sd); return sd; } private static double variance( double[] values, double mean, int count ) { double variance = 0; for( int i = 0; i < count; i++ ) { variance = variance + pow(values[i] - mean, 2.0); } variance = variance / (count); return variance; } protected void processCell( int readCol, int readRow, int writeCol, int writeRow, int readCols, int readRows, int writeCols, int writeRows ) { // not used in this case } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy