/*
* 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.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.Expression;
import org.geotools.filter.ExpressionType;
import org.geotools.filter.FunctionExpression;
import org.geotools.filter.LiteralExpression;
import org.geotools.filter.capability.FunctionNameImpl;
import org.geotools.util.ProgressListener;
import org.opengis.feature.simple.SimpleFeature;
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");
/** function name **/
String name;
/** function params **/
List params = new ArrayList(2);
Literal fallback;
ProgressListener progress;
/** Creates a new instance of ClassificationFunction. Subclasses should call setName */
public ClassificationFunction() {
setName("ClassificationFunction");
params.add(0, null);
params.add(1, null);
this.expressionType = ExpressionType.FUNCTION;
}
public abstract int getArgCount();
/**
* @see org.opengis.filter.expression.Expression#accept(ExpressionVisitor, Object)
*/
public Object accept(ExpressionVisitor visitor, Object extraData) {
return visitor.visit(this, extraData);
}
//overriding evaluate(feature) to point at evaluate(object)
public Object evaluate(SimpleFeature feature) {
return evaluate((Object) feature);
}
public abstract Object evaluate(Object arg);
public void setFallbackValue(Literal fallback) {
this.fallback = fallback;
}
public Literal getFallbackValue() {
return fallback;
}
/**
* @deprecated please use getParameters
*/
public Expression[] getArgs() {
List parameters = getParameters();
return (Expression[]) parameters.toArray(new Expression[parameters.size()]);
}
/**
* @deprecated please use setParameters
*/
public void setArgs(Expression[] args) {
List parameters = new ArrayList();
for (int i = 0; i < args.length; i++) {
parameters.add(i, args[i]);
}
setParameters(parameters);
}
/**
* Gets the name of this function.
*
* @return the name of the function.
*
*/
public String getName() {
return name;
}
public FunctionName getFunctionName() {
return new FunctionNameImpl( getName(), getArgCount() );
}
/**
* Sets the name of the function.
*/
public void setName(String name) {
this.name = name;
}
/**
* Returns the function parameters (the contents are Expressions, usually attribute expression and literal expression).
*/
public List getParameters() {
return params;
}
/**
* Sets the function parameters.
*/
public void setParameters(List 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() {
LiteralExpression classes = (LiteralExpression) 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);
}
public Expression getExpression() {
return (Expression) getParameters().get(0);
}
public void setExpression(Expression e) {
getParameters().set(0, e);
}
/**
* Returns the implementation hints. The default implementation returns an empty map.
*/
public Map getImplementationHints() {
return Collections.EMPTY_MAP;
}
/**
* 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 = (new Long(Math.round(newVal)).intValue())/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;
}
}