/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2004-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.
*
* reated on October 27, 2004, 11:27 AM
*/
package org.geotools.filter.function;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.DefaultExpression;
import org.geotools.filter.FunctionExpression;
import org.opengis.util.ProgressListener;
import org.opengis.filter.capability.FunctionName;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.filter.expression.Literal;
/**
* Parent for classifiers which break a feature collection into the specified number of classes.
*
* @author James Macgill
* @author Cory Horner, Refractions Research Inc.
*
*
* @source $URL$
*/
public abstract class ClassificationFunction extends DefaultExpression implements FunctionExpression {
protected static final java.util.logging.Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.filter.function");
FunctionName name;
/** function params **/
List<org.opengis.filter.expression.Expression> params = new ArrayList<org.opengis.filter.expression.Expression>(2);
Literal fallback;
ProgressListener progress;
public ClassificationFunction(FunctionName name) {
this.name = name;
}
/**
* @see org.opengis.filter.expression.Expression#accept(ExpressionVisitor, Object)
*/
public Object accept(ExpressionVisitor visitor, Object extraData) {
return visitor.visit(this, extraData);
}
public abstract Object evaluate(Object arg);
public void setFallbackValue(Literal fallback) {
this.fallback = fallback;
}
public Literal getFallbackValue() {
return fallback;
}
/**
* Gets the name of this function.
*
* @return the name of the function.
*
*/
public String getName() {
return name.getName();
}
public FunctionName getFunctionName() {
return name;
}
/**
* Returns the function parameters (the contents are Expressions, usually attribute expression and literal expression).
*/
public List<org.opengis.filter.expression.Expression> getParameters() {
return params;
}
/**
* Sets the function parameters.
*/
public void setParameters(List<org.opengis.filter.expression.Expression> params) {
this.params = params;
}
public ProgressListener getProgressListener() {
return progress;
}
public void setProgressListener(ProgressListener progress) {
this.progress = progress;
}
/**
* @deprecated use getClasses()
*/
public int getNumberOfClasses() {
return getClasses();
}
public int getClasses() {
Literal classes = (Literal) getParameters().get(1);
return ((Integer) classes.evaluate(null, Integer.class)).intValue();
}
/**
* @deprecated use setClasses()
*/
public void setNumberOfClasses(int classes) {
setClasses(classes);
}
public void setClasses(int classes) {
org.opengis.filter.FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
Literal expression = ff.literal(classes);
getParameters().set(1, expression);
}
/**
* Returns the implementation hints. The default implementation returns an empty map.
*/
public Map<RenderingHints.Key, ?> getImplementationHints() {
return Collections.emptyMap();
}
/**
* Determines the number of decimal places to truncate the interval at.
*
* @param slotWidth
*/
protected int decimalPlaces(double slotWidth) {
String str = Double.toString(slotWidth);
if (str.indexOf(".") > -1) {
while (str.endsWith("0")) {
str = str.substring(0, str.length() - 1);
}
}
int intPart = new Double(Math.floor(slotWidth)).intValue();
double decPart = slotWidth - intPart;
int intPoints = Integer.toString(intPart).length();
int decPoints = str.length() - intPoints;
if (str.indexOf(".") > -1) {
decPoints--;
}
if (decPart == 0) {
decPoints = 0;
}
//if there are dec points, show at least one
if (intPart == 0) { //if int part is 0, be very specific
if (decPoints > 6) {
return 5;
} else if (decPoints > 0) {
return decPoints;
} else {
return 1;
}
} else if (decPoints == 0) { //if there are no dec points, don't show any
return 0;
} else { //aim for a number of digits (not including '.') up to a reasonable limit (5)
int chars = intPoints + decPoints;
if (chars < 6) {
return decPoints;
} else if (intPoints > 4) {
return 1;
} else {
return 5 - intPoints;
}
}
}
/**
* Truncates a double to a certain number of decimals places. Note:
* truncation at zero decimal places will still show up as x.0, since we're
* using the double type.
*
* @param value
* number to round-off
* @param decimalPlaces
* number of decimal places to leave
* @return the rounded value
*/
protected double round(double value, int decimalPlaces) {
double divisor = Math.pow(10, decimalPlaces);
double newVal = value * divisor;
newVal = Math.round(newVal) / divisor;
return newVal;
}
/**
* Corrects a round off operation by incrementing or decrementing the
* decimal place (preferably the smallest one). This should usually be used
* to adjust the bounds to include a value. Example: 0.31-->0.44 where 0.44
* is the maximum value and end of the range. We could just make the ,
* round(0.31, 1)=0.3; round(0.44 max value = 0.49
*
* @param value
* @param decimalPlaces
* @param up
*/
protected double fixRound(double value, int decimalPlaces, boolean up) {
double divisor = Math.pow(10, decimalPlaces);
double newVal = value * divisor;
if (up) newVal++; //+0.001 (for 3 dec places)
else newVal--; //-0.001
newVal = newVal/divisor;
return newVal;
}
}