Hi,
I've made an optimized version of the recode function that uses a static
lookup table. It's working pretty nicely, indeed it's hard to tell apart
a recode table with over 200 rules with the same layer painted with
uniform color.
The scheme is simple, see the attached code.
Now... here is the ugly part.
What about the value the recode does not have a entry for?
What should the behavior be?
The current code evaluates colors, widths and sizes with defaults,
meaning that if I get no color, black is going to be used.
Is this the appropriate behavior? Uuhh... hard question.
If I make a recode table with 10 entries and the data has 20,
should I expect to see only the features with those 10 entries?
On one side, I think it would be handy.
On the other side, the recode table is not the same as rule filtering no?
One should probably filter the data if some features should not
be painted: rule filtering drives what should be painted, recode cares
about _how_ to paint it.
But now then... picture a recode table with 200 entries... out of
2000 possible values... anyone wants to write the massive chain
of ORs needed to extract only the part that we want? Uuugh...
The problem likely calls for some data manipulation (add an attribute
with a flag that says if the row is to be painted, or not), what
other alternatives are possible?
I guess a n-params "in" function that a database can turn into
a "in (x, y, z, ...)" could be a way to go. Opinions?
Cheers
Andrea
PS: I guess we may optimize in a similar way the categorize and
interpolate functions and get some nice speedups there too
--
-------------------------------------------------------
Ing. Andrea Aime
GeoSolutions S.A.S.
Tech lead
Via Poggio alle Viti 1187
55054 Massarosa (LU)
Italy
phone: +39 0584 962313
fax: +39 0584 962313
http://www.geo-solutions.it
http://geo-solutions.blogspot.com/
http://www.youtube.com/user/GeoSolutionsIT
http://www.linkedin.com/in/andreaaime
http://twitter.com/geowolf
-------------------------------------------------------
/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 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.filter.function;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.FilterAttributeExtractor;
import org.geotools.filter.capability.FunctionNameImpl;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.capability.FunctionName;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
/**
* This is an implemenation of the Recode function as defined by
* the OGC Symbology Encoding (SE) 1.1 specification.
* <p>
* The Recode function provides a lookup table facility (think HashTable)
* where both keys and values can be any {@code Expression}. The first
* parameter to the function specifies the source of the value to lookup,
* e.g. the name of a feature property as a {@code Literal}. The remaining
* parameters define the lookup table as key:value pairs. Thus there should
* be an odd number of parameters in total: the lookup value parameter plus
* the set of key value pairs.
* <p>
* Where the lookup involves {@code String} values, comparisons are done
* case-insensitively.
* <p>
* If the lookup value does not match any of the keys defined this function
* returns {@code null}.
*
* @author Johann Sorel (Geomatys)
* @author Michael Bedward
*
* @source $URL$
* @version $Id$
*/
public class RecodeFunction implements Function {
private static final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null);
private final List<Expression> parameters;
private boolean staticTable = true;
volatile private Map fastLookup = null;
private Class lastKeyType = null;
private Class lastContextType = null;
private final Literal fallback;
/**
* Make the instance of FunctionName available in a consistent spot.
*/
public static final FunctionName NAME = new FunctionNameImpl("Recode", "LookupValue", "Data 1",
"Value 1", "Data 2", "Value 2");
public RecodeFunction() {
this(new ArrayList<Expression>(), null);
}
public RecodeFunction(List<Expression> parameters, Literal fallback) {
this.parameters = parameters;
this.fallback = fallback;
// check inputs
if (parameters.size() % 2 != 1 && parameters.size() != 0) {
throw new IllegalArgumentException(
"There must be an equal number of lookup data and return values");
}
// see if the table is full of attribute independent expressions
FilterAttributeExtractor extractor = new FilterAttributeExtractor();
for (int i = 1; i < parameters.size(); i++) {
Expression expression = parameters.get(i);
if(expression != null) {
extractor.clear();
expression.accept(extractor, null);
if(!extractor.isConstantExpression()) {
staticTable = false;
break;
}
}
}
}
public String getName() {
return "Recode";
}
public FunctionName getFunctionName() {
return NAME;
}
public List<Expression> getParameters() {
return Collections.unmodifiableList(parameters);
}
public Object accept(ExpressionVisitor visitor, Object extraData) {
return visitor.visit(this, extraData);
}
public Object evaluate(Object object) {
return evaluate(object, Object.class);
}
public <T> T evaluate(Object object, Class<T> context) {
final Expression lookupExp = parameters.get(0);
final List<Expression> pairList = parameters.subList(1, parameters.size());
// fast lookup path
if(staticTable) {
Object lookup = lookupExp.evaluate(object);
if(lookup != null) {
if(fastLookup == null) {
synchronized (this) {
if(fastLookup == null) {
// build the fast lookup map
fastLookup = new HashMap();
lastKeyType = lookup.getClass();
lastContextType = context;
for (int i = 0; i < pairList.size(); i += 2) {
Object key = pairList.get(i).evaluate(object, lastKeyType);
Object value = pairList.get(i + 1).evaluate(object, context);
fastLookup.put(key, value);
}
}
}
}
if(fastLookup != null && lookup.getClass() == lastKeyType && context == lastContextType) {
T result = (T) fastLookup.get(lookup);
if(result == null) {
System.out.println("humm");
}
return result;
}
}
}
// dynamic evaluation path
for (int i = 0; i < pairList.size(); i += 2) {
Expression keyExpr = pairList.get(i);
Expression valueExpr = pairList.get(i + 1);
// we are going to test our propertyNameExpression against the keyExpression
// if they are equal we will return the valueExpression
//
org.opengis.filter.Filter compareFilter = ff.equal(lookupExp, keyExpr, false);
if (compareFilter.evaluate(object)) {
return valueExpr.evaluate(object, context);
}
}
return null;
}
public Literal getFallbackValue() {
return fallback;
}
}
------------------------------------------------------------------------------
All of the data generated in your IT infrastructure is seriously valuable.
Why? It contains a definitive record of application performance, security
threats, fraudulent activity, and more. Splunk takes this data and makes
sense of it. IT sense. And common sense.
http://p.sf.net/sfu/splunk-d2d-c2
_______________________________________________
Geotools-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/geotools-devel