/* JAI-Ext - OpenSource Java Advanced Image Extensions Library * http://www.geo-solutions.it/ * Copyright 2014 GeoSolutions * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package it.geosolutions.jaiext.classifier; import it.geosolutions.jaiext.piecewise.DefaultLinearPiecewiseTransform1DElement; import it.geosolutions.jaiext.piecewise.MathTransformation; import it.geosolutions.jaiext.piecewise.PiecewiseTransform1DElement; import it.geosolutions.jaiext.piecewise.PiecewiseUtilities; import it.geosolutions.jaiext.range.Range; import java.awt.Color; import java.awt.image.IndexColorModel; import java.util.Arrays; /** * This {@link LinearColorMapElement} is a special implementation of both {@link PiecewiseTransform1DElement} and {@link ColorMapTransformElement} * which can be used to do various types of classifications on raster. Specifically the supported types of classifications are unique values, * classified and color ramps. * <p> * The supported types of classifications are {@link LinearColorMapType#TYPE_RAMP} , {@link LinearColorMapType#TYPE_VALUES} and * {@link LinearColorMapType#TYPE_INTERVALS} . * * @see LinearColorMap * @see LinearColorMap.LinearColorMapType * @author Simone Giannecchini, GeoSolutions * * @source $URL$ */ public class LinearColorMapElement extends DefaultLinearPiecewiseTransform1DElement implements PiecewiseTransform1DElement, ColorMapTransformElement { /** * UID */ private static final long serialVersionUID = 2216106857184603629L; /** * {@link Color} s associated to this {@link ColorMapTransformElement} . * * @uml.property name="colors" */ private Color[] colors; private int hashCode = -1; public static LinearColorMapElement create(final CharSequence name, final Color[] colors, final Range valueRange, final Range sampleRange) throws IllegalArgumentException { return new LinearColorMapElement(name, colors, valueRange, sampleRange); } public static LinearColorMapElement create(CharSequence name, final Color color, final Range inRange, final int outVal) throws IllegalArgumentException { return new ConstantColorMapElement(name, color, inRange, outVal); } /** * @see LinearColorMapElement#ClassificationCategory(CharSequence, Color[], NumberRange, NumberRange) */ public static LinearColorMapElement create(final CharSequence name, final Color color, final short value, final int sample) throws IllegalArgumentException { return new ConstantColorMapElement(name, color, value, sample); } /** * @see LinearColorMapElement#ClassificationCategory(CharSequence, Color[], NumberRange, NumberRange) */ public static LinearColorMapElement create(final CharSequence name, final Color color, final int value, final int sample) throws IllegalArgumentException { return new ConstantColorMapElement(name, color, value, sample); } /** * @see LinearColorMapElement#ClassificationCategory(CharSequence, Color[], NumberRange, NumberRange) */ public static LinearColorMapElement create(final CharSequence name, final Color color, final float value, final int sample) throws IllegalArgumentException { return new ConstantColorMapElement(name, color, value, sample); } /** * @see LinearColorMapElement#ClassificationCategory(CharSequence, Color[], NumberRange, NumberRange) */ public static LinearColorMapElement create(final CharSequence name, final Color color, final double value, final int sample) throws IllegalArgumentException { return new ConstantColorMapElement(name, color, value, sample); } /** * Constructor for a {@link LinearColorMapElement}. It allows users to build a category which is able to map values into integer sample values for * further rendering using and {@link IndexColorModel}. * * <strong>NOTE</strong> Due to the limitations of the {@link IndexColorModel} we can accept as valid ranges only those that fit between 0 -65535. * * @param name for this {@link DomainElement1D}. * @param colors to use when rendering values belonging to this {@link DomainElement1D} * @param valueRange the input range for this category. * @param sampleRange the sample range for this category. It will be used as indexes for the final color map. * @throws IllegalArgumentException in case the output range does not respect {@link IndexColorModel} limitations. */ LinearColorMapElement(final CharSequence name, final Color[] colors, final Range valueRange, final Range sampleRange) throws IllegalArgumentException { super(name, valueRange, checkSampleRange(sampleRange)); // //@todo check this test // final int inEquals = ColorMapUtilities.compare(getInputMaximum(), getInputMinimum()); // final int outEquals = ColorMapUtilities.compare(getOutputMaximum(), getOutputMinimum()); // if (inEquals == 0 && outEquals == 0) // this.type = LinearColorMap.LinearColorMapType.TYPE_VALUES; // else if (outEquals == 0) // this.type = LinearColorMap.LinearColorMapType.TYPE_INTERVALS; // else { // if (isIdentity()) // // this.type = LinearColorMap.LinearColorMapType.TYPE_VALUES; // else // this.type = LinearColorMap.LinearColorMapType.TYPE_RAMP; // } // ///////////////////////////////////////////////////////////////////// // // Initialise fields for visualization // // ///////////////////////////////////////////////////////////////////// this.colors = new Color[colors.length]; System.arraycopy(colors, 0, this.colors, 0, colors.length); } /** * This method is responsible for performing a few checks on the provided range in order to make sure we are talking about a valid range for * building an {@link IndexColorModel}. * * @param numberRange the range to use for mapping values to colors. * @return the input {@link NumberRange} if everything goes well. * @see IndexColorModel */ private static Range checkSampleRange(Range numberRange) { if (numberRange == null) throw new IllegalArgumentException(); final Class<?> elementClass = numberRange.getDataType().getClassValue(); if (!elementClass.equals(Integer.class) && !elementClass.equals(Byte.class) && !elementClass.equals(Short.class)) throw new IllegalArgumentException( "The following Range cannot be used for mapping value to colours"); if (numberRange.getMin().intValue() < 0 || numberRange.getMax().intValue() > 65535) throw new IndexOutOfBoundsException( "Input Range bounds are outside the available color representation"); return numberRange; } /* * (non-Javadoc) * * @see org.geotools.renderer.lite.gridcoverage2d.Category#equals(java.lang.Object) */ public boolean equals(final Object object) { if (this == object) return true; if (!(object instanceof LinearColorMapElement)) return false; final LinearColorMapElement that = (LinearColorMapElement) object; if (getEquivalenceClass() != that.getEquivalenceClass()) return false; if (Arrays.equals(this.getColors(), that.getColors())) return true; return super.equals(that); } /** * Returns the set of colors for this category. Change to the returned array will not affect this category. * * @see GridSampleDimension#getColorModel * @uml.property name="colors" */ public Color[] getColors() { return (Color[]) colors.clone(); } /** * Gives access to the internal {@link MathTransform1D}. * * @return the internal {@link MathTransform1D}. */ MathTransformation accessTransform() { return getTransform(); } /* * (non-Javadoc) * * @see org.geotools.referencing.piecewise.DefaultLinearPiecewiseTransform1DElement#toString() */ public String toString() { final StringBuilder buffer = new StringBuilder(super.toString()); buffer.append("\n").append("colors="); for (int i = 0; (colors != null) && i < colors.length; i++) { buffer.append(colors[i]); if (i + 1 < colors.length) buffer.append(","); } return buffer.toString(); } protected Class<?> getEquivalenceClass() { return LinearColorMapElement.class; } @Override public int hashCode() { if (hashCode >= 0) return hashCode; hashCode = 37; hashCode = PiecewiseUtilities.hash(colors, hashCode); hashCode = PiecewiseUtilities.hash(super.hashCode(), hashCode); return hashCode; } }