001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.feature.visitor;
017:
018: import java.util.ArrayList;
019: import java.util.Collections;
020: import java.util.List;
021:
022: import org.geotools.feature.Feature;
023: import org.opengis.filter.expression.Expression;
024:
025: /**
026: * Obtains the data needed for a Quantile operation (classification of features into classes of equal size).
027: *
028: * The result contains an array of lists with the expression values in each.
029: *
030: * @author Cory Horner, Refractions Research Inc.
031: *
032: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/feature/visitor/QuantileListVisitor.java $
033: */
034: public class QuantileListVisitor implements FeatureCalc {
035: private Expression expr;
036: private int count = 0;
037: private int bins;
038: private List items = new ArrayList();
039: private List[] bin;
040:
041: boolean visited = false;
042: int countNull = 0;
043: int countNaN = 0;
044:
045: public QuantileListVisitor(Expression expr, int bins) {
046: this .expr = expr;
047: this .bins = bins;
048: this .bin = new ArrayList[bins];
049: }
050:
051: public CalcResult getResult() {
052: if (bins == 0 || count == 0)
053: return null;
054:
055: // sort the list
056: Collections.sort(items);
057:
058: if (bins > count) { //resize
059: bins = count;
060: this .bin = new ArrayList[bins];
061: }
062:
063: // calculate number of items to put into each of the larger bins
064: int binPop = new Double(Math.ceil((double) count / bins))
065: .intValue();
066: // determine index of bin where the next bin has one less item
067: int lastBigBin = count % bins;
068: if (lastBigBin == 0)
069: lastBigBin = bins;
070: else
071: lastBigBin--;
072:
073: // put the items into their respective bins
074: int item = 0;
075: for (int binIndex = 0; binIndex < bins; binIndex++) {
076: bin[binIndex] = new ArrayList();
077: for (int binMember = 0; binMember < binPop; binMember++) {
078: bin[binIndex].add(items.get(item++));
079: }
080: if (lastBigBin == binIndex)
081: binPop--; // decrease the number of items in a bin for the next item
082: }
083: return new AbstractCalcResult() {
084: public Object getValue() {
085: return bin;
086: }
087: };
088: }
089:
090: public void visit(Feature feature) {
091: Object value = expr.evaluate(feature);
092:
093: if (value == null) {
094: countNull++; // increment the null count
095: return; // don't store this value
096: }
097:
098: if (value instanceof Double) {
099: double doubleVal = ((Double) value).doubleValue();
100: if (Double.isNaN(doubleVal) || Double.isInfinite(doubleVal)) {
101: countNaN++; // increment the NaN count
102: return; // don't store NaN value
103: }
104: }
105:
106: count++;
107: items.add(value);
108: }
109:
110: public void reset(int bins) {
111: this .bins = bins;
112: this .count = 0;
113: this .items = new ArrayList();
114: this .bin = new ArrayList[bins];
115: this .countNull = 0;
116: this .countNaN = 0;
117: }
118:
119: /**
120: * @return the number of features which returned a NaN
121: */
122: public int getNaNCount() {
123: return countNaN;
124: }
125:
126: /**
127: * @return the number of features which returned a null
128: */
129: public int getNullCount() {
130: return countNull;
131: }
132: }
|