/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-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.renderer.lite.gridcoverage2d;
import java.awt.Color;
import java.awt.image.IndexColorModel;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.math.BigInteger;
import java.util.AbstractList;
import org.geotools.geometry.GeneralDirectPosition;
import org.geotools.referencing.operation.matrix.Matrix1;
import org.geotools.referencing.piecewise.DefaultLinearPiecewiseTransform1DElement;
import org.geotools.referencing.piecewise.DefaultPiecewiseTransform1D;
import org.geotools.referencing.piecewise.PiecewiseTransform1DElement;
import org.geotools.renderer.i18n.ErrorKeys;
import org.geotools.renderer.i18n.Errors;
import org.geotools.resources.image.ColorUtilities;
import org.geotools.util.NumberRange;
import org.geotools.util.SimpleInternationalString;
import org.geotools.util.Utilities;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.operation.MathTransform1D;
import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.InternationalString;
/**
* @author Simone Giannecchini, GeoSolutions.
*
* @source $URL$
*/
public final class LinearColorMap extends AbstractList<LinearColorMapElement>
implements ColorMapTransform<LinearColorMapElement> {
public final static class LinearColorMapType {
/**
* Values are displayed using a gradual ramp of colors.
*/
public static final int TYPE_RAMP = 1;
/**
* Each value in the raster layer is to be displayed individually as
* single separate color.
*/
public static final int TYPE_VALUES = 3;
/**
* Vales are grouped into classes in order to display each class with a
* single separate color.
*/
public static final int TYPE_INTERVALS = 2;
/**
* Validates the provided color map type.
*
* @param linearColorMapType
* @return <code>false</code> if we cannot valid the provided type, <code>true</code> otherwise.
*/
static public boolean validateColorMapTye(final int linearColorMapType){
switch (linearColorMapType) {
case TYPE_RAMP:case TYPE_VALUES:case TYPE_INTERVALS:
return true;
default:
return false;
}
}
}
/**
* @uml.property name="colorModel"
*/
private IndexColorModel colorModel;
/**
* @uml.property name="standardElements"
* @uml.associationEnd multiplicity="(0 -1)"
*/
private final LinearColorMapElement[] standardElements;
/**
* @uml.property name="preFilteringElements"
* @uml.associationEnd multiplicity="(0 -1)"
*/
private final LinearColorMapElement[] preFilteringElements;
private final Color defaultColor;
private DefaultPiecewiseTransform1D<LinearColorMapElement> piecewise;
private DefaultPiecewiseTransform1D<LinearColorMapElement> preFilteringPiecewise;
private Color preFilteringColor;
/**
* @uml.property name="name"
*/
private InternationalString name;
private int hashCode=-1;
/**
* Constructor which creates a {@link LinearColorMap} without a
* {@link NoDataCategory}. Keep in mind that if the list has gaps, if you
* try to transform a value that falls into a gap you'll get a nice
* {@link TransformException}!
*
* @param categories
* the array of a categories to use for this
* {@link LinearColorMap}.
*/
public LinearColorMap(
final CharSequence name,
final LinearColorMapElement[] standardElements) {
this(name, standardElements, null);
}
public LinearColorMap(
final CharSequence name,
final LinearColorMapElement[] standardElements,
final Color defColor) {
this(name, standardElements, null, defColor);
}
public LinearColorMap(
final CharSequence name,
final LinearColorMapElement[] standardElements,
final LinearColorMapElement[] preFilteringElements,
final Color defaultColor) {
ColorMapUtilities.ensureNonNull("name", name);
ColorMapUtilities.ensureNonNull("standardElements", standardElements);
this.name=SimpleInternationalString.wrap(name);
///////////////////////////////////////////////////////////////////////
//
// Prefiltering transformation
//
////
//
// All the prefiltering transformations must share the same color, hence
// they must point to the same int
//
// /////////////////////////////////////////////////////////////////////
preliminarChecks(standardElements, preFilteringElements);
///////////////////////////////////////////////////////////////////////
//
// Checking that same int in output means same color
//
////
//
//
//
// /////////////////////////////////////////////////////////////////////
if(preFilteringElements!=null&&preFilteringElements.length>0)
{
this.preFilteringElements=(LinearColorMapElement[]) preFilteringElements.clone();
Color color=this.preFilteringElements[0].getColors()[0];
this.preFilteringColor=color;
}
else
this.preFilteringElements=null;
///////////////////////////////////////////////////////////////////////
//
// Standard transformation
//
// /////////////////////////////////////////////////////////////////////
this.standardElements=(LinearColorMapElement[]) standardElements.clone();
///////////////////////////////////////////////////////////////////////
//
// Default color to fill gaps, if provided.
//
// /////////////////////////////////////////////////////////////////////
this.defaultColor=defaultColor;
}
public LinearColorMap(String string,
LinearColorMapElement[] linearColorMapElements,
LinearColorMapElement[] linearColorMapElements2) {
this(string, linearColorMapElements, linearColorMapElements2, null);
}
/**
* Performing additional check on the provided domain elements in order to
* control that we don't have any strange overlap in the output values which
* could leave to strange color effect.
*
* @param domainElements
* to check.
* @param nodata
* category if we have any.
* @return the original {@link LinearColorMapElement} if nothing went
* wrong.
*/
private static void preliminarChecks(
final LinearColorMapElement[] domainElements,
final LinearColorMapElement[] domainElementsToPreserve) {
// /////////////////////////////////////////////////////////////////////
//
// Cycle on all the domain elements to preserve
//
// /////////////////////////////////////////////////////////////////////
ColorMapUtilities.checkPreservingElements(domainElementsToPreserve);
// /////////////////////////////////////////////////////////////////////
//
// Cycle on all the domain elements and compare them to all the others
//
// /////////////////////////////////////////////////////////////////////
final int num =
domainElementsToPreserve != null ?
domainElements.length + domainElementsToPreserve.length
: domainElements.length;
for (int i = 0; i < num; i++) {
final DefaultLinearPiecewiseTransform1DElement c0 =
i >= domainElements.length ?
(DefaultLinearPiecewiseTransform1DElement) domainElementsToPreserve[i- domainElements.length]:
(DefaultLinearPiecewiseTransform1DElement) domainElements[i];
final ColorMapTransformElement v0 = (ColorMapTransformElement) c0;
final NumberRange<? extends Number> outRange0 = c0.getOutputRange();
final Color[] colors0 = v0.getColors();
final int minimum0 = (int) outRange0.getMinimum();
final int maximum0 = (int) outRange0.getMaximum();
// ////////////////////////////////////////////////////////////////
//
// Check the c0 categories with all the others
//
// ////////////////////////////////////////////////////////////////
for (int j = 0; j < num; j++) {
// don't check a category with itself.
if (j == i)
continue;
// //
//
// We allow two LinearColorMapElement output ranges to overlap only if they
// map to a single value and they use the same color for it.
// Every other case is marked as an error either because it is
// an error or because it was too hard to support.
//
// //
final DefaultLinearPiecewiseTransform1DElement c1 =
j >= domainElements.length ? (
DefaultLinearPiecewiseTransform1DElement) domainElementsToPreserve[j- domainElements.length]:
(DefaultLinearPiecewiseTransform1DElement) domainElements[j];
final ColorMapTransformElement v1 = (ColorMapTransformElement) c1;
final NumberRange<? extends Number> outRange1 = c1.getOutputRange();
if (outRange1.intersects(outRange0)) {
// do they intersect?
if(!outRange0.intersects(outRange1))
continue;
// they intersect!!!
//check the values
final int minimum1 = (int) outRange1.getMinimum();
final int maximum1 = (int) outRange1.getMaximum();
final Color[] colors1 = v1.getColors();
if (minimum1==maximum0&&colors0[colors0.length-1].equals(colors1[0]))
continue;
if (minimum0==maximum1&&colors1[colors1.length-1].equals(colors0[0]))
continue;
throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, c0, c1));
// now check the colors
// if (colors1.length != colors0.length)
// throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, new Integer(colors0.length), new Integer(colors1.length)));
// if(!Arrays.equals(colors1, colors0))
// throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, new Integer(colors1.length), new Integer(colors1.length)));
// if (colors1.length != 1)
// throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, new Integer(colors1.length), new Integer(1)));
//
// if (!colors0[0].equals(colors1[0]))
// throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2, colors0[0], colors1[0]));
}
}
}
}
/**
* Returns a color model for this category list. This method builds up the color model from each category's colors (as returned by {@link #getColors} ).
* @param visibleBand The band to be made visible (usually 0). All other bands, if any will be ignored.
* @param numBands The number of bands for the color model (usually 1). The returned color model will renderer only the {@code visibleBand} and ignore the others, but the existence of all {@code numBands} will be at least tolerated. Supplemental bands, even invisible, are useful for processing with Java Advanced Imaging.
* @return The requested color model, suitable for {@link RenderedImage} objects with values in the <code> {@link #getRange} </code> range.
* @uml.property name="colorModel"
*/
public IndexColorModel getColorModel() {
initColorModel();
return colorModel;
}
private synchronized void initColorModel() {
if (colorModel == null) {
// /////////////////////////////////////////////////////////////////////
//
// STEP 1
//
/////
// First of all let's create the palette for the standard values.
// Afterward we look for the right values for the default color and
// for the preserving values color if present since these could
// reuse the same colors used for the standard values.
//
// /////////////////////////////////////////////////////////////////////
// //
//
// Computes the number of entries required for the color
// palette.
// Note that in case the output range has non inclusive edges we
// have already taken that into account when building the outMax
// and outMin values hence we do not have to do that again.
//
// //
BigInteger bits = new BigInteger("0");
final boolean preFilteringValuesPresent=preFilteringColor!=null;
int elementsCount = standardElements.length+(preFilteringValuesPresent?1:0);
int max=-1;
for (int i = 0; i < elementsCount; i++) {
final DefaultLinearPiecewiseTransform1DElement element =
i<standardElements.length?
(DefaultLinearPiecewiseTransform1DElement) standardElements[i]:
preFilteringElements[0];
final int elementMin=(int) element.getOutputMinimum();
final int elementMax=(int) element.getOutputMaximum();
for (int k = elementMin; k <= elementMax; k++)
bits = bits.setBit(k);
max = (int) Math.max(max, elementMax);
}
//zero based indexing
max++;
// /////////////////////////////////////////////////////////////////////
//
// Interpolate the colors in the color palette for standard values
//
// /////////////////////////////////////////////////////////////////////
int[] ARGB = new int[max];
int outMax=0,outMin=0;
for (int i = 0; i < elementsCount; i++) {
// I know that all the categories here are both
// ColorMapTransformElement and DefaultLinearPiecewiseTransform1DElement. The
// eventual NoDtaCategory as well.
final LinearColorMapElement element = i<standardElements.length? standardElements[i]:preFilteringElements[0];
// //
//
// Check if this category overlap with another one as far as
// the output value but with the same color. We already
// checked that they had the same color. If this happens we
// prevent the code from creating a new entry at the same
// place with the same color, no rocket science going on
// here!
//
// //
outMin = (int) element.getOutputMinimum();
outMax = (int) (element.getOutputMaximum());
//NOTE the second element is exclusive!!!
ColorUtilities.expand(element.getColors(), ARGB, outMin, outMax+1);
}
//create the prefiltering piecewise
this.preFilteringPiecewise=preFilteringElements==null?null:new DefaultPiecewiseTransform1D<LinearColorMapElement>(preFilteringElements);
// /////////////////////////////////////////////////////////////////////
//
// STEP 2
//
/////
// Now, if we do not have any default color we can proceed,
// otherwise we got some work to do. First of all, we need to check
// if the colors we are asked to use for the special cases are
// already in use. In such a case we don't reserve another index in
// the palette for them. Otherwise we have to add the new colors and
// recreate the ASRGB array.
//
// /////////////////////////////////////////////////////////////////////
final boolean lookForDefaultColor=defaultColor!=null;
boolean defaultColorFound=!lookForDefaultColor;
if(lookForDefaultColor){
////
//
// Search in the color map if we have the requested colors
//
////
int defaultColorIndex=-1;
for(int i=0;i<ARGB.length;i++)
{
if(lookForDefaultColor&&defaultColorIndex==-1&&bits.testBit(i)&&ARGB[i]==defaultColor.getRGB())
{
defaultColorIndex=i;
defaultColorFound=true;
break;
}
}
//now see what happened
if(defaultColorFound)
this.piecewise= new DefaultPiecewiseTransform1D<LinearColorMapElement>(this.standardElements,defaultColorIndex);
else
{
//check the bit vector for the first place available
int i=0;
for(;i<max;i++)
if(!bits.testBit(i))
break;
if(i==max){
max=i==max?max++:max;
bits=bits.setBit(i);
final int[] tempARGB = new int[max];
System.arraycopy(ARGB, 0, tempARGB, 0, ARGB.length);
tempARGB[tempARGB.length-1]=defaultColor.getRGB();
ARGB=tempARGB;
}
this.piecewise= new DefaultPiecewiseTransform1D<LinearColorMapElement>(this.standardElements,max);
}
}
else
{
this.piecewise= new DefaultPiecewiseTransform1D<LinearColorMapElement>(this.standardElements);
}
colorModel = new IndexColorModel(ColorUtilities
.getBitCount(max), max, ARGB, 0, ColorUtilities
.getTransferType(max), bits);
}
}
public SampleModel getSampleModel(int width, int height) {
// //
//
// force color model computation
//
// //
initColorModel();
// //
//
// create a suitable color model
// NOTE:
// IndexColorModel seems to badly choose its sample model. As of JDK
// 1.4-rc1, it construct a ComponentSampleModel, which is drawn very
// slowly to the screen. A much faster sample model is
// PixelInterleavedSampleModel, which is the sample model used by
// BufferedImage for TYPE_BYTE_INDEXED. We should check if this is fixed
// in future J2SE release.
//
// //
return new PixelInterleavedSampleModel(this.colorModel
.getTransferType(), width, height, 1, width, new int[1]);
}
// boolean canPiecewise() {
// checkOptimalJAIOps();
// return canPiecewise;
// }
//
// boolean canRescale() {
// checkOptimalJAIOps();
// return canRescale;
// }
//
// /**
// * Checks whether or not we can use the Rescale or Piecewise operation in
// * order to emulate the behavior of this {@link PiecewiseTransform1D}.
// */
// private void checkOptimalJAIOps() {
// synchronized (scales) {
// if (offsets != null)
// return;
//
// /*
// * STEP 4 - Check if the transcoding could be done with a JAI's
// * "Rescale" or "Piecewise" operations. The "Rescale" operation
// * requires a completely linear relationship between the source and
// * the destination sample values. The "Piecewise" operation is less
// * strict: piecewise breakpoints are very similar to categories, but
// * the transformation for all categories still have to be linear.
// */
// if (this.hasGaps() ) {
// // This is a shortcut that would lead us to avoid optimizations.
// // If we either have gaps in input or if the NoData set of
// // categories overlap the normal ones we avoid spending too much
// // in finding a way to since it might be counterproductive. At
// // latter time we could remove this simple checks and go a bit
// // deeper with the investigation.
// // Fallback on our "RasterClassifier".
// canPiecewise = canRescale = false;
// return;
// }
// final DomainElement1D[] elements = getDomainElements();
// final int numCategories = elements.length;
// final LinearColorMapElement tcList[] = new LinearColorMapElement[numCategories];
// System.arraycopy(elements, 0, tcList, 0, elements.length);
// Arrays.sort(tcList);
// float[] sourceBreakpoints = null;
// float[] targetBreakpoints = null;
// double expectedSource = Double.NaN;
// double expectedTarget = Double.NaN;
// int jbp = 0; // Break point index (vary with j)
// for (int j = 0; j < numCategories; j++) {
// final LinearColorMapElement element = tcList[j];
// final MathTransform1D transform = element.accessTransform();
// if (!(transform instanceof LinearTransform1D)) {
//
// // One category doesn't use a linear transformation. We
// // can't deal with that with "Rescale" or "Piecewise".
// // Fallback on our "RasterClassifier".
// canPiecewise = canRescale = false;
// break;
// }
// final LinearTransform1D lTransform = (LinearTransform1D) transform;
// double offset = lTransform.offset;
// double scale = lTransform.scale;
// if ((Double.isNaN(scale) || Double.isInfinite(scale))
// && !Double.isNaN(offset)) {
// // One category doesn't use a linear transformation. We
// // can't deal with that with "Rescale" or "Piecewise".
// // Fallback on our "RasterClassifier".
// canRescale = false;
// canPiecewise = false;
// break;
// } else if (Double.isNaN(scale) || Double.isInfinite(scale))
// scale = 0;
// // Allocates arrays the first time the loop is run up to this
// // point. Store scale and offset, and check if they still the
// // same.
// if (j == 0) {
// offsets = new double[1];
// sourceBreakpoints = new float[numCategories * 2];
// targetBreakpoints = new float[numCategories * 2];
// breakpoints[0] = new float[][] { sourceBreakpoints,
// targetBreakpoints };
// offsets[0] = offset;
// scales[0] = scale;
// }
// //@todo this is not always correct, especially when single valued classes falls in between
// if (offset != offsets[0] || scale != scales[0]) {
// canRescale = false;
// }
// // Compute breakpoints.
// final NumberRange range = (NumberRange) element
// .getRange();
// final double minimum = range.getMinimum(true);
// final double maximum = range.getMaximum(true);
// final float sourceMin = (float) minimum;
// final float sourceMax = (float) maximum;
// final float targetMin = (float) (minimum * scale + offset);
// final float targetMax = (float) (maximum * scale + offset);
// assert sourceMin <= sourceMax : range;
// if (!Double.isNaN(expectedSource)
// && ColorMapUtilities.compare(minimum, expectedSource) <= 0) {
// if (!Double.isNaN(expectedTarget)
// && ColorMapUtilities.compare(targetMin,
// expectedTarget) <= 0) {
// // This breakpoint is identical to the previous one. Do
// // not duplicate; overwrites the previous one since this
// // one is likely to be more accurate.
// jbp--;
// } else {
// // Found a discontinuity!!! The "piecewise" operation is
// // not really designed for such case. The behavior
// // between the last breakpoint and the current one may
// // not be what the user expected.
// assert sourceBreakpoints[jbp - 1] < sourceMin : expectedSource;
// canPiecewise = false;
//
// }
// } else if (j != 0) {
// // Found a gap between the last category and the current
// // one. The "piecewise" operation may not behave as the user
// // expected for sample values falling in this gap.
// assert !(expectedSource > sourceMin) : expectedSource;
// canPiecewise = false;
//
// }
// sourceBreakpoints[jbp] = sourceMin;
// sourceBreakpoints[jbp + 1] = sourceMax;
// targetBreakpoints[jbp] = targetMin;
// targetBreakpoints[jbp + 1] = targetMax;
// jbp += 2;
// expectedSource = range.getMaximum(false);
// expectedTarget = expectedSource * scale + offset;
//
// }
//
// breakpoints[0][0] = sourceBreakpoints = XArray.resize(
// sourceBreakpoints, jbp);
// breakpoints[0][1] = targetBreakpoints = XArray.resize(
// targetBreakpoints, jbp);
// assert XArray.isSorted(sourceBreakpoints);
// }
//
// }
//
// /**
// * @return
// * @uml.property name="breakpoints"
// */
// float[][][] getBreakpoints() {
// checkOptimalJAIOps();
// return (float[][][]) breakpoints.clone();
// }
//
// /**
// * @return
// * @uml.property name="offsets"
// */
// double[] getOffsets() {
// checkOptimalJAIOps();
// return (double[]) offsets.clone();
// }
//
// /**
// * @return
// * @uml.property name="scales"
// */
// double[] getScales() {
// checkOptimalJAIOps();
// return (double[]) scales.clone();
// }
public LinearColorMapElement get(int index) {
return standardElements[index];
}
public int size() {
return this.preFilteringElements.length+this.standardElements.length;
}
public double getDefaultValue() {
initColorModel();
return this.piecewise.getDefaultValue();
}
public boolean hasDefaultValue() {
initColorModel();
return this.piecewise.hasDefaultValue();
}
public NumberRange<?> getApproximateDomainRange() {
initColorModel();
return this.piecewise.getApproximateDomainRange();
}
public LinearColorMapElement findDomainElement(double sample) {
initColorModel();
final boolean prefiltering=this.preFilteringElements != null;
LinearColorMapElement retValue=null;
if(prefiltering)
retValue= preFilteringPiecewise.findDomainElement(sample);
if(retValue==null)
retValue= piecewise.findDomainElement(sample);
return retValue;
}
public LinearColorMapElement[] getDomainElements() {
return (LinearColorMapElement[]) this.standardElements.clone();
}
/**
* @return
* @uml.property name="name"
*/
public InternationalString getName() {
initColorModel();
return this.name;
}
public boolean hasGaps() {
initColorModel();
return this.piecewise.hasGaps();
}
public double derivative(double value) throws TransformException {
throw new UnsupportedOperationException(Errors.format(ErrorKeys.UNSUPPORTED_OPERATION_$1, "derivate"));
}
public double transform(double value) throws TransformException {
initColorModel();
PiecewiseTransform1DElement transform=(PiecewiseTransform1DElement) findDomainElement(value);
if(transform!=null)
return transform.transform(value);
return this.preFilteringPiecewise.transform(value);
}
public Matrix derivative(DirectPosition point)
throws MismatchedDimensionException, TransformException {
ColorMapUtilities.checkDimension(point);
return new Matrix1(derivative(point.getOrdinate(0)));
}
/*
* (non-Javadoc)
* @see org.opengis.referencing.operation.MathTransform#getSourceDimensions()
*/
public int getSourceDimensions() {
return 1;
}
/*
* (non-Javadoc)
* @see org.opengis.referencing.operation.MathTransform#getTargetDimensions()
*/
public int getTargetDimensions() {
return 1;
}
public MathTransform1D inverse() throws NoninvertibleTransformException {
throw new UnsupportedOperationException(Errors.format(ErrorKeys.UNSUPPORTED_OPERATION_$1, "transform"));
}
public boolean isIdentity() {
throw new UnsupportedOperationException(Errors.format(ErrorKeys.UNSUPPORTED_OPERATION_$1, "transform"));
}
public String toWKT() throws UnsupportedOperationException {
throw new UnsupportedOperationException(Errors.format(ErrorKeys.UNSUPPORTED_OPERATION_$1, "transform"));
}
public DirectPosition transform(DirectPosition ptSrc, DirectPosition ptDst)
throws MismatchedDimensionException, TransformException {
// /////////////////////////////////////////////////////////////////////
//
// input checks
//
// /////////////////////////////////////////////////////////////////////
ColorMapUtilities.ensureNonNull("ptSrc", ptSrc);
ColorMapUtilities.checkDimension(ptSrc);
if (ptDst == null) {
ptDst = new GeneralDirectPosition(1);
} else {
ColorMapUtilities.checkDimension(ptDst);
}
ptDst.setOrdinate(0, transform(ptSrc.getOrdinate(0)));
return ptDst;
}
public void transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff,
int numPts) throws TransformException {
throw new UnsupportedOperationException(Errors.format(ErrorKeys.UNSUPPORTED_OPERATION_$1, "transform"));
}
public void transform(float[] srcPts, int srcOff, float[] dstPts, int dstOff,
int numPts) throws TransformException {
throw new UnsupportedOperationException(Errors.format(ErrorKeys.UNSUPPORTED_OPERATION_$1, "transform"));
}
public void transform(float[] srcPts, int srcOff, double[] dstPts, int dstOff,
int numPts) throws TransformException {
throw new UnsupportedOperationException(Errors.format(ErrorKeys.UNSUPPORTED_OPERATION_$1, "transform"));
}
public void transform(double[] srcPts, int srcOff, float[] dstPts, int dstOff,
int numPts) throws TransformException {
throw new UnsupportedOperationException(Errors.format(ErrorKeys.UNSUPPORTED_OPERATION_$1, "transform"));
}
@Override
public boolean equals(Object o) {
if(this==o)
return true;
if(!(o instanceof LinearColorMap))
return false;
final LinearColorMap that=(LinearColorMap) o;
if(!Utilities.equals(name, that.name))
return false;
if(!Utilities.equals(defaultColor, that.defaultColor))
return false;
if(preFilteringColor!=that.preFilteringColor)
return false;
if(!Utilities.equals(preFilteringElements, that.preFilteringElements))
return false;
if(!Utilities.equals(standardElements, that.standardElements))
return false;
return piecewise.equals(that.piecewise);
}
@Override
public int hashCode() {
if(hashCode>=0)
return hashCode;
hashCode=37;
hashCode=Utilities.hash( name,hashCode);
hashCode=Utilities.hash( defaultColor,hashCode);
hashCode=Utilities.hash( preFilteringColor,hashCode);
hashCode=Utilities.hash( preFilteringElements,hashCode);
hashCode=Utilities.hash( standardElements,hashCode);
hashCode=Utilities.hash( piecewise,hashCode);
return hashCode;
}
}