Package org.geotools.feature.visitor

Source Code of org.geotools.feature.visitor.NearestVisitor$NearestAccumulator

/*
*    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.feature.visitor;

import java.util.Date;

import org.geotools.factory.CommonFactoryFinder;
import org.geotools.util.Converters;
import org.opengis.feature.type.PropertyDescriptor;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.Expression;

import com.vividsolutions.jts.geom.Geometry;

/**
* Finds the nearest value to the provided one in the attribute domain.
*
* @author Andrea Aime - GeoSolutions
* @author Ilkka Rinne / Spatineo Inc for the Finnish Meteorological Institute
*
* @param <T>
*/
public class NearestVisitor implements FeatureCalc {
    private Expression expr;

    private Class attributeClass;

    private NearestAccumulator accumulator;

    boolean visited = false;

    double shortestDistance = Double.MAX_VALUE;

    private Object valueToMatch;
   
    private Object nearest;

    /**
     * Creates a NearestVisitor instance for the given attribute and a value to match.
     *
     * @param expression
     * @param valueToMatch The target value to match
     */
    public NearestVisitor(Expression expression, Object valueToMatch) {
        this.expr = expression;
        this.valueToMatch = valueToMatch;
    }

    /**
     * Visitor function, which looks at each feature and finds the value of the attribute given
     * attribute nearest to the given comparison value.
     *
     * @param feature the feature to be visited
     */
    @SuppressWarnings("unchecked")
    public void visit(org.opengis.feature.Feature feature) {
        // bail out immediately if we have already found an exact match
        if (visited) {
            return;
        }
       
        if(attributeClass == null) {
            PropertyDescriptor desc = (PropertyDescriptor) expr.evaluate(feature.getType());
            attributeClass = desc.getType().getBinding();
            if(accumulator == null) {
                accumulator = getAccumulator(attributeClass);
            }
        }

        // extract the value
        Object attribValue = expr.evaluate(feature);
        if (attribValue == null) {
            return;
        } else {
            visited |= accumulator.visit(attribValue);
        }
    }

    private NearestAccumulator getAccumulator(Class attributeClass) {
        if(Number.class.isAssignableFrom(attributeClass)) {
            Double convertedTarget = Converters.convert(valueToMatch, Double.class);
            return new NumberAccumulator(convertedTarget);
        } else if(Date.class.isAssignableFrom(attributeClass)) {
            Date convertedTarget = Converters.convert(valueToMatch, Date.class);
            return new DateAccumulator(convertedTarget);
        } else if(Geometry.class.isAssignableFrom(attributeClass)) {
            Geometry convertedTarget = Converters.convert(valueToMatch, Geometry.class);
            return new GeometryAccumulator(convertedTarget);
        } else if(Comparable.class.isAssignableFrom(attributeClass)) {
            Comparable convertedTarget = (Comparable) Converters.convert(valueToMatch, attributeClass);
            return new ComparableAccumulator(convertedTarget);
        }
        // TODO: we should probably create a custom one for strings, there are various
        // string distance algorithms described on the net
       
        throw new IllegalArgumentException("Don't know how to compute nearest for target class " + attributeClass);
    }

    public void reset() {
        visited = false;
        accumulator = null;
        attributeClass = null;
        nearest = null;
    }
   
    public void setValue(Object nearest) {
        this.nearest = nearest;
        this.visited = true;
    }

    public void setValue(Object maxBelow, Object minAbove) {
        if(maxBelow == null) {
            this.nearest = minAbove;
        } else if(minAbove == null) {
            this.nearest = maxBelow;
        } else {
            NearestAccumulator accumulator = getAccumulator(maxBelow.getClass());
            accumulator.visit(maxBelow);
            accumulator.visit(minAbove);
            nearest = accumulator.getNearest();
        }
    }

    /**
     * Returns the match after {@link #visit}.
     *
     * @return
     * @throws IllegalStateException
     */
    public Object getNearestMatch() throws IllegalStateException {
        if(nearest == null) {
            if(accumulator != null) {
                this.nearest = accumulator.getNearest();
            }
        }
       
        return nearest;
    }

    @Override
    public CalcResult getResult() {
        return new AbstractCalcResult() {
            @Override
            public Object getValue() {
                return NearestVisitor.this.getNearestMatch();
            }
        };
    }

    /**
     * Expression used to access collection content.
     *
     * @return expr used to access collection
     */
    public Expression getExpression() {
        return expr;
    }
   
    /**
     * Provided value to match against.
     *
     * @return value to match against.
     */
    public Object getValueToMatch() {
        return valueToMatch;
    }

    static interface NearestAccumulator<T> {
        public boolean visit(T value);

        public T getNearest();
    }

    static class ComparableAccumulator implements NearestAccumulator<Comparable> {

        private Comparable minAbove;

        private Comparable maxBelow;

        private Comparable targetValue;

        public ComparableAccumulator(Comparable targetValue) {
            this.targetValue = targetValue;
        }

        @Override
        public boolean visit(Comparable value) {
            // compare to find the two values that are right below, and right above, the target
            // number
            int aboveBelow = value.compareTo(targetValue);
            boolean exact = false;
            if (aboveBelow == 0) {
                // equality, bail out now
                minAbove = maxBelow = value;
                exact = true;
            } else if (aboveBelow > 0) {
                if (minAbove == null || minAbove.compareTo(value) > 0) {
                    minAbove = value;
                }
            } else if (aboveBelow < 0) {
                if (maxBelow == null || maxBelow.compareTo(value) < 0) {
                    maxBelow = value;
                }
            }

            return exact;
        }

        public Comparable getNearest() {
            if(maxBelow == null) {
                return minAbove;
            } else if(minAbove == null) {
                return maxBelow;
            } else {
                // No real guarantee this will return the closest, but this is the best we can do
                // not knowing anything else about the target class
                int diffAbove = Math.abs(targetValue.compareTo(minAbove));
                int diffBelow = Math.abs(targetValue.compareTo(maxBelow));
   
                if (diffAbove < diffBelow) {
                    return minAbove;
                } else {
                    return maxBelow;
                }
            }
        }

    }

    class NumberAccumulator implements NearestAccumulator<Number> {

        double targetValue;

        double difference = Double.MAX_VALUE;

        Number nearest;

        public NumberAccumulator(Number targetValue) {
            this.targetValue = targetValue.doubleValue();
        }

        @Override
        public boolean visit(Number value) {
            double v = value.doubleValue();
            double d = Math.abs(v - targetValue);
            if (d < difference) {
                difference = d;
                nearest = value;
            }

            return d == 0;

        }

        @Override
        public Number getNearest() {
            return nearest;
        }

    }

    class DateAccumulator implements NearestAccumulator<Date> {

        long targetValue;

        long difference = Long.MAX_VALUE;

        Date nearest;

        public DateAccumulator(Date targetValue) {
            this.targetValue = targetValue.getTime();
        }

        @Override
        public boolean visit(Date value) {
            long v = value.getTime();
            long d = Math.abs(v - targetValue);
            if (d < difference) {
                difference = d;
                nearest = value;
            }
           
            return d == 0;
        }

        @Override
        public Date getNearest() {
            return nearest;
        }

    }

    class GeometryAccumulator implements NearestAccumulator<Geometry> {

        Geometry targetValue;

        double distance = Double.MAX_VALUE;

        Geometry nearest;

        public GeometryAccumulator(Geometry targetValue) {
            this.targetValue = targetValue;
        }

        @Override
        public boolean visit(Geometry value) {
            double d = targetValue.distance(value);
            if (d < distance) {
                distance = d;
                nearest = value;
            }
            return d == 0;
        }

        @Override
        public Geometry getNearest() {
            return nearest;
        }

    }

}
TOP

Related Classes of org.geotools.feature.visitor.NearestVisitor$NearestAccumulator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.