/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2014, 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.process.spatialstatistics.core;
import java.util.logging.Logger;
import javax.media.jai.PlanarImage;
import javax.media.jai.iterator.RectIter;
import javax.media.jai.iterator.RectIterFactory;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.IllegalFilterException;
import org.geotools.util.logging.Logging;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Expression;
/**
* Statistics Visitor
*
* @author Minpa Lee, MangoSystem
*
* @source $URL$
*/
public class StatisticsVisitor {
protected static final Logger LOGGER = Logging.getLogger(StatisticsVisitor.class);
// FIRST, LAST, COUNT, SUM, MEAN, MIN, MAX, RANGE, STD, VAR, CoefficientOfVariance
private final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null);
private Expression expression;
private StatisticsStrategy strategy = null;
public StatisticsVisitor(StatisticsStrategy strategy) {
this.strategy = strategy;
}
public StatisticsVisitor(SimpleFeatureType schema, String propertyName)
throws IllegalFilterException {
init(schema, schema.getDescriptor(FeatureTypes.validateProperty(schema, propertyName)));
}
public StatisticsVisitor(SimpleFeatureType schema, int attrIndex) throws IllegalFilterException {
init(schema, schema.getDescriptor(attrIndex));
}
public StatisticsVisitor(Expression expression, StatisticsStrategy strategy)
throws IllegalFilterException {
this.expression = expression;
this.strategy = strategy;
}
private void init(SimpleFeatureType schema, AttributeDescriptor attributeType) {
expression = ff.property(attributeType.getLocalName());
strategy = createStrategy(attributeType.getType().getBinding());
}
private static StatisticsStrategy createStrategy(Class<?> type) {
if (type == Integer.class) {
return new IntegerStrategy();
} else if (type == Long.class) {
return new LongStrategy();
} else if (type == Float.class) {
return new FloatStrategy();
} else if (Number.class.isAssignableFrom(type)) {
return new DoubleStrategy();
} else if (String.class.isAssignableFrom(type)) {
return new StringStrategy();
}
return null;
}
public void setNoData(Number noData) {
if (strategy != null && noData != null)
strategy.setNoData(noData);
}
public void reset() {
if (strategy != null)
strategy.reset();
}
public void visit(GridCoverage2D inputCoverage, Integer bandIndex) {
reset();
PlanarImage inputImage = (PlanarImage) inputCoverage.getRenderedImage();
RectIter readIter = RectIterFactory.create(inputImage, inputImage.getBounds());
readIter.startLines();
while (!readIter.finishedLines()) {
readIter.startPixels();
while (!readIter.finishedPixels()) {
double sampleValue = readIter.getSampleDouble(bandIndex);
visit(Double.valueOf(sampleValue));
readIter.nextPixel();
}
readIter.nextLine();
}
}
public void visit(SimpleFeatureCollection features) {
reset();
SimpleFeatureIterator featureIter = features.features();
try {
while (featureIter.hasNext()) {
visit(featureIter.next());
}
} finally {
featureIter.close();
}
}
public void visit(SimpleFeature feature) {
visit(expression.evaluate(feature));
}
public void visit(Object value) {
if (strategy == null) {
strategy = createStrategy(value.getClass());
}
strategy.add(value);
}
public StatisticsVisitorResult getResult() {
return strategy == null ? new StatisticsVisitorResult() : strategy.getResult();
}
public interface StatisticsStrategy {
public void add(Object value);
public StatisticsVisitorResult getResult();
public void setNoData(Number noData);
public void reset();
}
public static class DoubleStrategy implements StatisticsStrategy {
Double noData = null;
int count = 0;
int invalidCount = 0;
Object firstValue = null;
Object lastValue = null;
double minVal = Double.MAX_VALUE;
double maxVal = Double.MIN_VALUE;
double sumOfVals = 0.0d;
double sumOfSqrs = 0.0d;
@Override
public void add(Object value) {
if (value == null) {
invalidCount++;
return;
}
// for BigDecimal, Double...
double curVal = Double.valueOf(value.toString());
if (Double.isNaN(curVal) || Double.isInfinite(curVal)) {
invalidCount++;
return;
} else if (noData != null && SSUtils.compareDouble(curVal, noData)) {
invalidCount++;
return;
}
if (firstValue == null) {
firstValue = value;
}
sumOfVals += curVal;
sumOfSqrs += curVal * curVal;
maxVal = Math.max(maxVal, curVal);
minVal = Math.min(minVal, curVal);
lastValue = value;
count++;
}
@Override
public StatisticsVisitorResult getResult() {
StatisticsVisitorResult sr = new StatisticsVisitorResult();
sr.setFirstValue(firstValue);
sr.setLastValue(lastValue);
sr.setCount(count);
sr.setMinimum(minVal);
sr.setMaximum(maxVal);
sr.setSum(sumOfVals);
sr.setNoData(noData);
if (count > 0) {
// Population Standard Deviation
double variance = (sumOfSqrs - Math.pow(sumOfVals, 2.0) / count) / count;
sr.setVariance(variance);
}
return sr;
}
@Override
public void reset() {
count = invalidCount = 0;
firstValue = lastValue = null;
minVal = Double.MAX_VALUE;
maxVal = Double.MIN_VALUE;
sumOfVals = sumOfSqrs = 0.0d;
}
@Override
public void setNoData(Number noData) {
this.noData = new Double(noData.doubleValue());
}
}
public static class FloatStrategy implements StatisticsStrategy {
Float noData = null;
int count = 0;
int invalidCount = 0;
Object firstValue = null;
Object lastValue = null;
float minVal = Float.MAX_VALUE;
float maxVal = Float.MIN_VALUE;
double sumOfVals = 0.0d;
double sumOfSqrs = 0.0d;
@Override
public void add(Object value) {
if (value == null) {
invalidCount++;
return;
}
float curVal = ((Float) value).floatValue();
if (Float.isNaN(curVal) || Float.isInfinite(curVal)) {
invalidCount++;
return;
} else if (noData != null && SSUtils.compareFloat(curVal, noData)) {
invalidCount++;
return;
}
if (firstValue == null) {
firstValue = value;
}
sumOfVals += curVal;
sumOfSqrs += curVal * curVal;
maxVal = Math.max(maxVal, curVal);
minVal = Math.min(minVal, curVal);
lastValue = value;
count++;
}
@Override
public StatisticsVisitorResult getResult() {
StatisticsVisitorResult sr = new StatisticsVisitorResult();
sr.setFirstValue(firstValue);
sr.setLastValue(lastValue);
sr.setCount(count);
sr.setMinimum(minVal);
sr.setMaximum(maxVal);
sr.setSum(sumOfVals);
sr.setNoData(noData);
if (count > 0) {
// Population Standard Deviation
double variance = (sumOfSqrs - Math.pow(sumOfVals, 2.0) / count) / count;
sr.setVariance(variance);
}
return sr;
}
@Override
public void reset() {
count = invalidCount = 0;
firstValue = lastValue = null;
minVal = Float.MAX_VALUE;
maxVal = Float.MIN_VALUE;
sumOfVals = sumOfSqrs = 0.0d;
}
@Override
public void setNoData(Number noData) {
this.noData = new Float(noData.floatValue());
}
}
public static class LongStrategy implements StatisticsStrategy {
Long noData = null;
int count = 0;
int invalidCount = 0;
Object firstValue = null;
Object lastValue = null;
long minVal = Long.MAX_VALUE;
long maxVal = Long.MIN_VALUE;
double sumOfVals = 0.0d;
double sumOfSqrs = 0.0d;
@Override
public void add(Object value) {
if (value == null) {
invalidCount++;
return;
}
long curVal = ((Long) value).longValue();
if (noData != null && curVal == noData) {
invalidCount++;
return;
}
if (firstValue == null) {
firstValue = value;
}
sumOfVals += curVal;
sumOfSqrs += curVal * curVal;
maxVal = Math.max(maxVal, curVal);
minVal = Math.min(minVal, curVal);
lastValue = value;
count++;
}
@Override
public StatisticsVisitorResult getResult() {
StatisticsVisitorResult sr = new StatisticsVisitorResult();
sr.setFirstValue(firstValue);
sr.setLastValue(lastValue);
sr.setCount(count);
sr.setMinimum(minVal);
sr.setMaximum(maxVal);
sr.setSum(sumOfVals);
sr.setNoData(noData);
if (count > 0) {
// Population Standard Deviation
double variance = (sumOfSqrs - Math.pow(sumOfVals, 2.0) / count) / count;
sr.setVariance(variance);
}
return sr;
}
@Override
public void reset() {
count = invalidCount = 0;
firstValue = lastValue = null;
minVal = Long.MAX_VALUE;
maxVal = Long.MIN_VALUE;
sumOfVals = sumOfSqrs = 0.0d;
}
@Override
public void setNoData(Number noData) {
this.noData = new Long(noData.longValue());
}
}
public static class IntegerStrategy implements StatisticsStrategy {
Integer noData = null;
int count = 0;
int invalidCount = 0;
Object firstValue = null;
Object lastValue = null;
int minVal = Integer.MAX_VALUE;
int maxVal = Integer.MIN_VALUE;
double sumOfVals = 0.0d;
double sumOfSqrs = 0.0d;
@Override
public void add(Object value) {
if (value == null) {
invalidCount++;
return;
}
int curVal = ((Integer) value).intValue();
if (noData != null && curVal == noData) {
invalidCount++;
return;
}
if (firstValue == null) {
firstValue = value;
}
sumOfVals += curVal;
sumOfSqrs += curVal * curVal;
maxVal = Math.max(maxVal, curVal);
minVal = Math.min(minVal, curVal);
lastValue = value;
count++;
}
@Override
public StatisticsVisitorResult getResult() {
StatisticsVisitorResult sr = new StatisticsVisitorResult();
sr.setFirstValue(firstValue);
sr.setLastValue(lastValue);
sr.setCount(count);
sr.setMinimum(minVal);
sr.setMaximum(maxVal);
sr.setSum(sumOfVals);
sr.setNoData(noData);
if (count > 0) {
// Population Standard Deviation
double variance = (sumOfSqrs - Math.pow(sumOfVals, 2.0) / count) / count;
sr.setVariance(variance);
}
return sr;
}
@Override
public void reset() {
count = invalidCount = 0;
firstValue = lastValue = null;
minVal = Integer.MAX_VALUE;
maxVal = Integer.MIN_VALUE;
sumOfVals = sumOfSqrs = 0.0d;
}
@Override
public void setNoData(Number noData) {
this.noData = new Integer(noData.intValue());
}
}
public static class StringStrategy implements StatisticsStrategy {
Object noData = null;
int count = 0;
int invalidCount = 0;
Object firstValue = null;
Object lastValue = null;
@Override
public void add(Object value) {
if (value == null) {
invalidCount++;
return;
}
if (noData != null && noData.equals(value)) {
invalidCount++;
return;
}
if (firstValue == null) {
firstValue = value;
}
lastValue = value;
count++;
}
@Override
public StatisticsVisitorResult getResult() {
StatisticsVisitorResult sr = new StatisticsVisitorResult();
sr.setFirstValue(firstValue);
sr.setLastValue(lastValue);
sr.setCount(count);
sr.setNoData(noData);
return sr;
}
@Override
public void reset() {
count = invalidCount = 0;
firstValue = lastValue = null;
}
@Override
public void setNoData(Number noData) {
this.noData = noData;
}
}
}