/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.coverage.processing.operation;
import static java.lang.Math.round;
import jaitools.CollectionFactory;
import jaitools.media.jai.zonalstats.Result;
import jaitools.media.jai.zonalstats.ZonalStats;
import jaitools.media.jai.zonalstats.ZonalStatsDescriptor;
import jaitools.numeric.Range;
import jaitools.numeric.Statistic;
import jaitools.numeric.Range.Type;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.media.jai.ROI;
import javax.media.jai.ROIShape;
import junit.framework.TestCase;
import org.geotools.TestData;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.processing.AbstractProcessor;
import org.geotools.coverage.processing.OperationJAI;
import org.geotools.data.DataStore;
import org.geotools.data.FeatureSource;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.operation.transform.ProjectiveTransform;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.geometry.BoundingBox;
import org.opengis.metadata.spatial.PixelOrientation;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
public class ZonalStasTest extends TestCase {
final static double DELTA = 10E-4;
private final static Logger LOGGER = Logger.getLogger(ZonalStasTest.class.toString());
private class StatisticsTool {
/*
* external user params
*/
private Set<Statistic> statisticsSet;
private Integer[] bands;
private GridCoverage2D gridCoverage2D;
private List<SimpleFeature> featureList;
private List<Range<Double>> inclusionRanges;
private Range.Type rangesType = Type.EXCLUDE;
private boolean isLocal = false;
/*
* results
*/
private Map<String, Map<Statistic, List<Result>>> feature2StatisticsMap = new HashMap<String, Map<Statistic, List<Result>>>();
private StatisticsTool( Set<Statistic> statisticsSet, GridCoverage2D gridCoverage2D,
Integer[] bands, List<SimpleFeature> polygonsList, List<Range<Double>> inclusionRanges) {
this(statisticsSet, gridCoverage2D, bands, polygonsList, inclusionRanges, Range.Type.EXCLUDE, false);
}
private StatisticsTool( Set<Statistic> statisticsSet, GridCoverage2D gridCoverage2D,
Integer[] bands, List<SimpleFeature> polygonsList, List<Range<Double>> ranges,
final Range.Type rangesType, final boolean isLocal) {
this.statisticsSet = statisticsSet;
this.gridCoverage2D = gridCoverage2D;
this.bands = bands;
this.featureList = polygonsList;
this.inclusionRanges = ranges;
this.isLocal = isLocal;
this.rangesType = rangesType;
}
/**
* Run the requested analysis.
*
* <p>
* This is the moment in which the analysis takes place. This method
* is intended to give the user the possibility to choose the moment
* in which the workload is done.
* @throws Exception
*/
public void run() throws Exception {
processPolygonMode();
}
private void processPolygonMode() throws TransformException {
final AffineTransform gridToWorldTransformCorrected = new AffineTransform(
(AffineTransform) ((GridGeometry2D) gridCoverage2D.getGridGeometry())
.getGridToCRS2D(PixelOrientation.UPPER_LEFT));
final MathTransform worldToGridTransform;
try {
worldToGridTransform = ProjectiveTransform.create(gridToWorldTransformCorrected.createInverse());
} catch (NoninvertibleTransformException e) {
throw new IllegalArgumentException(e.getLocalizedMessage());
}
for (SimpleFeature feature : featureList ) {
final String fid = feature.getID();
final Geometry geometry = (Geometry) feature.getDefaultGeometry();
if (geometry instanceof Polygon || geometry instanceof MultiPolygon) {
final BoundingBox bbox = feature.getBounds();
final ReferencedEnvelope rEnvelope = new ReferencedEnvelope(bbox);
/*
* crop on region of interest
*/
final AbstractProcessor processor = AbstractProcessor.getInstance();
final ParameterValueGroup param = processor.getOperation("CoverageCrop").getParameters();
param.parameter("Source").setValue(gridCoverage2D);
param.parameter("Envelope").setValue(rEnvelope);
final GridCoverage2D cropped = (GridCoverage2D) processor.doOperation(param);
ROI roi = null;
final int numGeometries = geometry.getNumGeometries();
for( int i = 0; i < numGeometries; i++ ) {
Geometry geometryN = geometry.getGeometryN(i);
java.awt.Polygon awtPolygon = toAWTPolygon((Polygon) geometryN,
worldToGridTransform);
if (roi == null) {
roi = new ROIShape(awtPolygon);
} else {
ROI newRoi = new ROIShape(awtPolygon);
roi.add(newRoi);
}
}
final Statistic[] statistis = statisticsSet.toArray(new Statistic[statisticsSet.size()]);
final OperationJAI op = new OperationJAI("ZonalStats");
ParameterValueGroup params = op.getParameters();
params.parameter("dataImage").setValue(cropped);
params.parameter("stats").setValue(statistis);
params.parameter("bands").setValue(bands);
params.parameter("roi").setValue(roi);
params.parameter("ranges").setValue(inclusionRanges);
params.parameter("rangesType").setValue(rangesType);
params.parameter("rangeLocalStats").setValue(isLocal);
final GridCoverage2D coverage = (GridCoverage2D) op.doOperation(params, null);
final ZonalStats stats = (ZonalStats) coverage.getProperty(ZonalStatsDescriptor.ZONAL_STATS_PROPERTY);
final Map<Statistic, List<Result>> statsMap = new HashMap<Statistic, List<Result>>();
for( Statistic statistic : statistis ) {
final List<Range> inclRanges = CollectionFactory.list();
inclRanges.addAll(inclusionRanges);
List<Result> statsResult = stats.ranges(inclRanges).statistic(statistic).results();
statsMap.put(statistic, statsResult);
}
feature2StatisticsMap.put(fid, statsMap);
}
}
}
private java.awt.Polygon toAWTPolygon( final Polygon roiInput,
MathTransform worldToGridTransform ) throws TransformException {
final boolean isIdentity = worldToGridTransform.isIdentity();
final java.awt.Polygon retValue = new java.awt.Polygon();
final double coords[] = new double[2];
final LineString exteriorRing = roiInput.getExteriorRing();
final CoordinateSequence exteriorRingCS = exteriorRing.getCoordinateSequence();
final int numCoords = exteriorRingCS.size();
for( int i = 0; i < numCoords; i++ ) {
coords[0] = exteriorRingCS.getX(i);
coords[1] = exteriorRingCS.getY(i);
if (!isIdentity)
worldToGridTransform.transform(coords, 0, coords, 0, 1);
retValue.addPoint((int) round(coords[0] + 0.5d), (int) round(coords[1] + 0.5d));
}
return retValue;
}
/**
* Gets the performed statistics.
*
* @param fId the id of the feature used as region for the analysis.
* @return the {@link List} of results of the analysis for all the
* requested {@link Statistic} for the requested bands. Note
* that the result contains for every {@link Statistic} a result
* value for every band.
*/
public Map<Statistic, List<Result>> getStatistics( String fId ) {
return feature2StatisticsMap.get(fId);
}
}
@Before
public void setUp() throws Exception {
TestData.unzipFile(this, "test.zip");
}
@Test
// @Ignore
public void testPolygonZoneGlobalStats() throws Exception {
final File file = TestData.file(this,"test.tif");
final GeoTiffReader reader = new GeoTiffReader(file);
final GridCoverage2D coverage2D = (GridCoverage2D) reader.read(null);
final File fileshp = TestData.file(this,"testpolygon.shp");
final DataStore store = FileDataStoreFinder.getDataStore(fileshp.toURL());
FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = store.getFeatureSource(store.getNames().get(0));
FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = featureSource.getFeatures();
List<SimpleFeature> polygonList = new ArrayList<SimpleFeature>();
FeatureIterator<SimpleFeature> featureIterator = featureCollection.features();
while( featureIterator.hasNext() ) {
SimpleFeature feature = featureIterator.next();
polygonList.add(feature);
}
featureCollection.close(featureIterator);
// choose the stats
Set<Statistic> statsSet = new LinkedHashSet<Statistic>();
statsSet.add(Statistic.MIN);
statsSet.add(Statistic.MAX);
statsSet.add(Statistic.MEAN);
statsSet.add(Statistic.VARIANCE);
statsSet.add(Statistic.SDEV);
statsSet.add(Statistic.RANGE);
// select the bands to work on
Integer[] bands = new Integer[]{0};
List<Range<Double>> inclusionRanges = new ArrayList<Range<Double>>();
inclusionRanges.add(new Range<Double>(Double.valueOf(0),false, Double.valueOf(1300), true));
inclusionRanges.add(new Range<Double>(Double.valueOf(1370),true, Double.valueOf(1600), true));
// create the proper instance
StatisticsTool statisticsTool = new StatisticsTool(statsSet, coverage2D, bands, polygonList, inclusionRanges, Type.INCLUDE, false);
// do analysis
statisticsTool.run();
// get the results
String id = "testpolygon.1";
Map<Statistic, List<Result>> statistics = statisticsTool.getStatistics(id);
LOGGER.info(id + statistics.toString());
assertEquals(statistics.get(Statistic.RANGE).get(0).getValue().doubleValue(), 343.0, DELTA);
assertEquals(statistics.get(Statistic.SDEV).get(0).getValue().doubleValue(), 88.7358, DELTA);
assertEquals(statistics.get(Statistic.MIN).get(0).getValue().doubleValue(), 1255.0, DELTA);
assertEquals(statistics.get(Statistic.MEAN).get(0).getValue().doubleValue(), 1380.5423, DELTA);
assertEquals(statistics.get(Statistic.VARIANCE).get(0).getValue().doubleValue(), 7874.0598, DELTA);
assertEquals(statistics.get(Statistic.MAX).get(0).getValue().doubleValue(), 1598.0, DELTA);
id = "testpolygon.2";
statistics = statisticsTool.getStatistics(id);
LOGGER.info(id + statistics.toString());
assertEquals(statistics.get(Statistic.RANGE).get(0).getValue().doubleValue(), 216.0, DELTA);
assertEquals(statistics.get(Statistic.SDEV).get(0).getValue().doubleValue(), 36.7996, DELTA);
assertEquals(statistics.get(Statistic.MIN).get(0).getValue().doubleValue(), 1192.0, DELTA);
assertEquals(statistics.get(Statistic.MEAN).get(0).getValue().doubleValue(), 1248.3870, DELTA);
assertEquals(statistics.get(Statistic.VARIANCE).get(0).getValue().doubleValue(), 1354.2150, DELTA);
assertEquals(statistics.get(Statistic.MAX).get(0).getValue().doubleValue(), 1408.0, DELTA);
id = "testpolygon.3";
statistics = statisticsTool.getStatistics(id);
LOGGER.info(id + statistics.toString());
assertEquals(statistics.get(Statistic.RANGE).get(0).getValue().doubleValue(), 127.0000, DELTA);
assertEquals(statistics.get(Statistic.SDEV).get(0).getValue().doubleValue(), 30.9412, DELTA);
assertEquals(statistics.get(Statistic.MIN).get(0).getValue().doubleValue(), 1173.0, DELTA);
assertEquals(statistics.get(Statistic.MEAN).get(0).getValue().doubleValue(), 1266.3876, DELTA);
assertEquals(statistics.get(Statistic.VARIANCE).get(0).getValue().doubleValue(), 957.3594, DELTA);
assertEquals(statistics.get(Statistic.MAX).get(0).getValue().doubleValue(), 1300.0, DELTA);
reader.dispose();
}
@Test
// @Ignore
public void testPolygonZoneLocalStats() throws Exception {
final File file = TestData.file(this,"test.tif");
final GeoTiffReader reader = new GeoTiffReader(file);
final GridCoverage2D coverage2D = (GridCoverage2D) reader.read(null);
final File fileshp = TestData.file(this,"testpolygon.shp");
final DataStore store = FileDataStoreFinder.getDataStore(fileshp.toURL());
FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = store.getFeatureSource(store.getNames().get(0));
FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = featureSource.getFeatures();
List<SimpleFeature> polygonList = new ArrayList<SimpleFeature>();
FeatureIterator<SimpleFeature> featureIterator = featureCollection.features();
while( featureIterator.hasNext() ) {
SimpleFeature feature = featureIterator.next();
polygonList.add(feature);
}
featureCollection.close(featureIterator);
// choose the stats
Set<Statistic> statsSet = new LinkedHashSet<Statistic>();
statsSet.add(Statistic.MIN);
statsSet.add(Statistic.MAX);
statsSet.add(Statistic.MEAN);
statsSet.add(Statistic.VARIANCE);
statsSet.add(Statistic.SDEV);
statsSet.add(Statistic.RANGE);
// select the bands to work on
Integer[] bands = new Integer[]{0};
List<Range<Double>> inclusionRanges = new ArrayList<Range<Double>>();
inclusionRanges.add(new Range<Double>(Double.valueOf(0),false, Double.valueOf(1300), true));
inclusionRanges.add(new Range<Double>(Double.valueOf(1370),true, Double.valueOf(1600), true));
// create the proper instance
StatisticsTool statisticsTool = new StatisticsTool(statsSet, coverage2D, bands, polygonList, inclusionRanges, Range.Type.INCLUDE, true);
// do analysis
statisticsTool.run();
// get the results
String id = "testpolygon.1";
Map<Statistic, List<Result>> statistics = statisticsTool.getStatistics(id);
LOGGER.info(id + statistics.toString());
assertEquals(statistics.get(Statistic.RANGE).get(0).getValue().doubleValue(), 45.0, DELTA);
assertEquals(statistics.get(Statistic.RANGE).get(1).getValue().doubleValue(), 228.0, DELTA);
assertEquals(statistics.get(Statistic.SDEV).get(0).getValue().doubleValue(), 11.7972, DELTA);
assertEquals(statistics.get(Statistic.SDEV).get(1).getValue().doubleValue(), 63.7335, DELTA);
assertEquals(statistics.get(Statistic.MIN).get(0).getValue().doubleValue(), 1255.0, DELTA);
assertEquals(statistics.get(Statistic.MIN).get(1).getValue().doubleValue(), 1370.0, DELTA);
assertEquals(statistics.get(Statistic.MEAN).get(0).getValue().doubleValue(), 1283.1634, DELTA);
assertEquals(statistics.get(Statistic.MEAN).get(1).getValue().doubleValue(), 1433.8979, DELTA);
assertEquals(statistics.get(Statistic.VARIANCE).get(0).getValue().doubleValue(), 139.1754, DELTA);
assertEquals(statistics.get(Statistic.VARIANCE).get(1).getValue().doubleValue(), 4061.9665, DELTA);
assertEquals(statistics.get(Statistic.MAX).get(0).getValue().doubleValue(), 1300.0, DELTA);
assertEquals(statistics.get(Statistic.MAX).get(1).getValue().doubleValue(), 1598.0, DELTA);
reader.dispose();
}
}