/** * OrbisGIS is a java GIS application dedicated to research in GIScience. * OrbisGIS is developed by the GIS group of the DECIDE team of the * Lab-STICC CNRS laboratory, see <http://www.lab-sticc.fr/>. * * The GIS group of the DECIDE team is located at : * * Laboratoire Lab-STICC – CNRS UMR 6285 * Equipe DECIDE * UNIVERSITÉ DE BRETAGNE-SUD * Institut Universitaire de Technologie de Vannes * 8, Rue Montaigne - BP 561 56017 Vannes Cedex * * OrbisGIS is distributed under GPL 3 license. * * Copyright (C) 2007-2014 CNRS (IRSTV FR CNRS 2488) * Copyright (C) 2015-2017 CNRS (Lab-STICC UMR CNRS 6285) * * This file is part of OrbisGIS. * * OrbisGIS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * OrbisGIS 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with * OrbisGIS. If not, see <http://www.gnu.org/licenses/>. * * For more information, please consult: <http://www.orbisgis.org/> * or contact directly: * info_at_ orbisgis.org */ package org.orbisgis.coremap.renderer.se.parameter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.xml.bind.JAXBElement; import net.opengis.se._2_0.core.*; import org.orbisgis.coremap.renderer.se.AbstractSymbolizerNode; import org.orbisgis.coremap.renderer.se.SymbolizerNode; import org.orbisgis.coremap.renderer.se.parameter.real.RealParameter; import org.orbisgis.coremap.renderer.se.parameter.real.RealParameterContext; /** * Transformation of continuous values by a function defined on a number of nodes. * This is used to adjust the value distribution of an attribute to the desired distribution * of a continuous symbolization control variable (like size, width, color, etc...).</p> * <p><code>Interpolate</code> are defined by : * <ul><li>The interpolation mode</li> * <li>The list of interpolation points</li> * <li>The <code>RealParameter</code> used to retrieve the values to interpolate</li> * <li>The <code>FallbackType</code> value used when a data can't be processed</li> * @author Maxence Laurent * @param <ToType> One of RealParameter or ColorParameter * @param <FallbackType> extends ToType (the LiteralOne, please...) * @todo find a nice way to compute interpolation for RealParameter and ColorParameter * */ public abstract class Interpolate<ToType extends SeParameter, FallbackType extends ToType> extends AbstractSymbolizerNode implements SeParameter { private InterpolationMode mode; private RealParameter lookupValue; private FallbackType fallbackValue; private List<InterpolationPoint<ToType>> iPoints; /** * Supported interpolation modes. */ public enum InterpolationMode { LINEAR, COSINE, CUBIC } /** * The default constructor only instanciates an empty list of * <code>InterpolationPoint</code> and sets the interpolation mode to * {@link InterpolationMode#LINEAR}. */ protected Interpolate() { this.iPoints = new ArrayList<InterpolationPoint<ToType>>(); mode = InterpolationMode.LINEAR; } /** * Builds an <code>Interpolate</code> instance where the default value for * unprocessable cases is <code>fallbackValue</code>and the interpolation * mode is {@link InterpolationMode#LINEAR}. * @param fallbackValue */ public Interpolate(FallbackType fallbackValue) { this.fallbackValue = fallbackValue; this.iPoints = new ArrayList<InterpolationPoint<ToType>>(); mode = InterpolationMode.LINEAR; } /** * Retrieve the mode that is used to process the interpolation. * @return */ public InterpolationMode getMode() { return mode; } /** * Set the mode that must be used to process the interpolation. * @param mode */ public void setMode(InterpolationMode mode) { this.mode = mode; } /** * Retrieve the list of interpolation points. * @return */ protected List<InterpolationPoint<ToType>> getInterpolationPoints() { return iPoints; } /** * Set the default value to be returned if an input can't be processed. * @param fallbackValue */ public void setFallbackValue(FallbackType fallbackValue) { this.fallbackValue = fallbackValue; if(this.fallbackValue != null){ this.fallbackValue.setParent(this); } } /** * Retrieve the default value that is returned when an input can't be processed. * @return */ public FallbackType getFallbackValue() { return fallbackValue; } /** * Set the lookup value that will be used to retrieve the data to process. * @param lookupValue */ public void setLookupValue(RealParameter lookupValue) { this.lookupValue = lookupValue; if (this.lookupValue != null) { this.lookupValue.setContext(RealParameterContext.REAL_CONTEXT); this.lookupValue.setParent(this); } } /** * Get the lookup value that will be used to retrieve the data to process. * @return */ public RealParameter getLookupValue() { return lookupValue; } /** * Return the number of classes defined within the classification. According to this number (n), * available class value ID are [0;n] and ID for threshold are [0;n-1 * * @return number of defined class */ public int getNumInterpolationPoint() { return iPoints.size(); } /** * Add a new interpolation point. The new point is inserted at the right * place in the interpolation point list, according to its data * @param point */ public void addInterpolationPoint(InterpolationPoint<ToType> point) { iPoints.add(point); sortInterpolationPoint(); point.getValue().setParent(this); } /** * Get the ith <code>InterpolationPoint</code> in the list of interpolation * points. * @param i * @return The ith <code>InterpolationPoint</code> * @throws IndexOutOfBoundsException - if i is out of range * <code>(index < 0 || index >= size())</code> */ public InterpolationPoint<ToType> getInterpolationPoint(int i) { return iPoints.get(i); } /** * Set the <code>InterpolationMode</code> used to process the values. * @param mode one of the <code>Interpolate.InterpolationMode</code> values */ public void setInterpolationMode(InterpolationMode mode) { this.mode = mode; } /** * Get the <code>InterpolationMode</code> used to process the values. * @return */ public InterpolationMode getInterpolationMode() { return mode; } /** * Sort the interpolation points. */ private void sortInterpolationPoint() { Collections.sort(iPoints); } @Override public ParameterValueType getJAXBParameterValueType() { ParameterValueType p = new ParameterValueType(); p.getContent().add(this.getJAXBExpressionType()); return p; } @Override public JAXBElement<?> getJAXBExpressionType() { InterpolateType i = new InterpolateType(); if (fallbackValue != null) { i.setFallbackValue(fallbackValue.toString()); } if (lookupValue != null) { i.setLookupValue(lookupValue.getJAXBParameterValueType()); } if (mode != null) { i.setMode(ModeType.fromValue(mode.toString().toLowerCase())); } List<InterpolationPointType> ips = i.getInterpolationPoint(); for (InterpolationPoint<ToType> ip : iPoints) { InterpolationPointType ipt = new InterpolationPointType(); ipt.setValue(ip.getValue().getJAXBParameterValueType()); ipt.setData(ip.getData()); ips.add(ipt); } ObjectFactory of = new ObjectFactory(); return of.createInterpolate(i); } @Override public List<SymbolizerNode> getChildren() { List<SymbolizerNode> ls =new ArrayList<SymbolizerNode>(); ls.add(lookupValue); for(InterpolationPoint i : iPoints){ ls.add(i.getValue()); } return ls; } protected int getFirstIP(double data) { int i = -1; for (InterpolationPoint ip : iPoints) { if (ip.getData() > data) { return i; } i++; } return -1; } protected double cubicInterpolation(double d1, double d2, double x, double v1, double v2, double v3, double v4) { //double mu = (x - d1) / (d2 - d1); return 0.0; } protected double cosineInterpolation(double d1, double d2, double x, double v1, double v2) { double mu = (x - d1) / (d2 - d1); double mu2 = (1 - Math.cos(mu * Math.PI)) * 0.5; return v1 + mu2 * (v2 - v1); } protected double linearInterpolation(double d1, double d2, double x, double v1, double v2) { return v1 + (v2 - v1) * (x - d1) / (d2 - d1); } }