From 2dbe54cb4ac72470a051d33280767b8343a60f43 Mon Sep 17 00:00:00 2001 From: Max Schenkelberg Date: Mon, 11 Nov 2013 11:09:00 -0600 Subject: [PATCH] Issue #2492 Added getDataUnit to IColormappedImage and implementing classes. Deprecated dataUnit methods in ColorMapParameters. Fixed colormap slider to work with data and colormap units differing. Change-Id: Ic82f45447113425a08909be4c5229502882db022 Former-commit-id: 752c9574b4198f914573999dc9fe0cd4f70b9cd0 [formerly 2f794486e3ec1b9b2b8e1a928287233d8643fed8] [formerly e58a5df04b8cbb88a5dbfe413c06a3e7eb53fcaa] [formerly 752c9574b4198f914573999dc9fe0cd4f70b9cd0 [formerly 2f794486e3ec1b9b2b8e1a928287233d8643fed8] [formerly e58a5df04b8cbb88a5dbfe413c06a3e7eb53fcaa] [formerly bd903f6641380d73560c8083af5fb3c04c18c784 [formerly e58a5df04b8cbb88a5dbfe413c06a3e7eb53fcaa [formerly 58ccc575973ea9a1778659e17daff8577747dec5]]]] Former-commit-id: bd903f6641380d73560c8083af5fb3c04c18c784 Former-commit-id: ad9578db6fa9b431d8d7c6b7c9dc6664a4a5d702 [formerly 3f3b244b0dbc40afae295d294be866fd14dbbbfd] [formerly f684ed2d6192fd370d5f9876f88c4494c40afb31 [formerly c85532a180cb9152c21b798e8d811399d8663b08]] Former-commit-id: 13938eba0ef04bbdaad219d1622f03eac1794f4a [formerly 60616f7ef04b75b3229d1e77e1480be83c20fbae] Former-commit-id: 7383be61c1760f4a3d1bb924e4a4a1171829022c --- .../viz/core/drawables/IColormappedImage.java | 11 +- .../ext/colormap/ColormappedImage.java | 31 +- .../graphics/ext/KmlColormappedImage.java | 16 + .../export/graphics/ext/KmlMosaicImage.java | 12 + .../META-INF/MANIFEST.MF | 6 +- .../objects/DispatchingColormappedImage.java | 12 + .../GLTrueColorImagingExtension.java | 3 +- .../localization/glsl/include/mapping.glsl | 148 ++++-- .../gl/ext/imaging/GLDataMappingFactory.java | 4 + .../imaging/GLDefaultImagingExtension.java | 3 +- .../viz/core/gl/glsl/GLSLStructFactory.java | 2 +- .../gl/images/AbstractGLColormappedImage.java | 3 +- .../core/gl/images/GLColormappedImage.java | 9 + .../images/GLOffscreenColormappedImage.java | 7 + .../gl/internal/ext/mosaic/GLMosaicImage.java | 12 + .../viz/ui/dialogs/ColorMapSliderComp.java | 494 ++++++++---------- .../uf/common/colormap/LogConverter.java | 103 ++++ .../uf/common/colormap/image/Colormapper.java | 134 +++-- .../colormap/prefs/ColorMapParameters.java | 27 + 19 files changed, 610 insertions(+), 427 deletions(-) create mode 100644 edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/LogConverter.java diff --git a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/IColormappedImage.java b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/IColormappedImage.java index 65337306dd..afe9e1a04a 100644 --- a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/IColormappedImage.java +++ b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/IColormappedImage.java @@ -19,8 +19,9 @@ **/ package com.raytheon.uf.viz.core.drawables; -import com.raytheon.uf.common.colormap.prefs.ColorMapParameters; +import javax.measure.unit.Unit; +import com.raytheon.uf.common.colormap.prefs.ColorMapParameters; /** * Describes a Colormapped Image @@ -64,4 +65,12 @@ public interface IColormappedImage extends IImage { */ public abstract double getValue(int x, int y); + /** + * Get the unit associated with the data in the image. Values returned from + * {@link #getValue(int, int)} will be in this unit + * + * @return + */ + public abstract Unit getDataUnit(); + } \ No newline at end of file diff --git a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/ext/colormap/ColormappedImage.java b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/ext/colormap/ColormappedImage.java index 709fb95c68..8544c7a73e 100644 --- a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/ext/colormap/ColormappedImage.java +++ b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/drawables/ext/colormap/ColormappedImage.java @@ -21,6 +21,8 @@ package com.raytheon.uf.viz.core.drawables.ext.colormap; import java.awt.image.RenderedImage; +import javax.measure.unit.Unit; + import com.raytheon.uf.common.colormap.image.ColorMapData; import com.raytheon.uf.common.colormap.image.Colormapper; import com.raytheon.uf.common.colormap.prefs.ColorMapParameters; @@ -59,6 +61,8 @@ public class ColormappedImage implements IColormappedImage, private ColorMapParameters parameters; + private Unit dataUnit; + public ColormappedImage(IGraphicsTarget target, IColorMapDataRetrievalCallback callback, ColorMapParameters parameters) { @@ -103,9 +107,7 @@ public class ColormappedImage implements IColormappedImage, */ @Override public void dispose() { - if (image != null) { - image.dispose(); - } + image.dispose(); } /* @@ -209,11 +211,15 @@ public class ColormappedImage implements IColormappedImage, */ @Override public RenderedImage getImage() throws VizException { - if (parameters == null || parameters.getColorMap() == null) { - return null; + RenderedImage image = null; + if (parameters != null && parameters.getColorMap() != null) { + ColorMapData colorMapData = callback.getColorMapData(); + if (colorMapData != null) { + this.dataUnit = colorMapData.getDataUnit(); + image = Colormapper.colorMap(colorMapData, parameters); + } } - ColorMapData colorMapData = callback.getColorMapData(); - return Colormapper.colorMap(colorMapData, parameters); + return image; } /* @@ -237,4 +243,15 @@ public class ColormappedImage implements IColormappedImage, image.stage(); } + /* + * (non-Javadoc) + * + * @see com.raytheon.uf.viz.core.drawables.IColormappedImage#getDataUnit() + */ + @Override + public Unit getDataUnit() { + return dataUnit == null ? getColorMapParameters().getDataUnit() + : dataUnit; + } + } diff --git a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlColormappedImage.java b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlColormappedImage.java index 91a1be4184..47d320c949 100644 --- a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlColormappedImage.java +++ b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlColormappedImage.java @@ -24,6 +24,8 @@ import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; +import javax.measure.unit.Unit; + import org.geotools.coverage.grid.GridGeometry2D; import com.raytheon.uf.common.colormap.image.ColorMapData; @@ -64,6 +66,8 @@ public class KmlColormappedImage extends KmlImage implements IColormappedImage { private ColorMapParameters colorMapParameters; + private Unit dataUnit; + public KmlColormappedImage(IColorMapDataRetrievalCallback dataCallback, ColorMapParameters colorMapParameters) { this.dataCallback = dataCallback; @@ -72,6 +76,7 @@ public class KmlColormappedImage extends KmlImage implements IColormappedImage { public DataSource getData(GridGeometry2D geometry) throws VizException { ColorMapData data = dataCallback.getColorMapData(); + this.dataUnit = data.getDataUnit(); switch (data.getDataType()) { case FLOAT: return new FloatBufferWrapper(((FloatBuffer) data.getBuffer()), @@ -122,4 +127,15 @@ public class KmlColormappedImage extends KmlImage implements IColormappedImage { return 0; } + /* + * (non-Javadoc) + * + * @see com.raytheon.uf.viz.core.drawables.IColormappedImage#getDataUnit() + */ + @Override + public Unit getDataUnit() { + return dataUnit == null ? getColorMapParameters().getDataUnit() + : dataUnit; + } + } diff --git a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlMosaicImage.java b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlMosaicImage.java index 62e01dc168..28dd547f24 100644 --- a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlMosaicImage.java +++ b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/ext/KmlMosaicImage.java @@ -21,6 +21,8 @@ package com.raytheon.uf.viz.kml.export.graphics.ext; import java.util.Comparator; +import javax.measure.unit.Unit; + import com.raytheon.uf.common.colormap.prefs.ColorMapParameters; import com.raytheon.uf.viz.core.DrawableImage; import com.raytheon.uf.viz.core.IExtent; @@ -146,4 +148,14 @@ class KmlMosaicImage implements IMosaicImage { this.imageExtent = imageExtent; } + /* + * (non-Javadoc) + * + * @see com.raytheon.uf.viz.core.drawables.IColormappedImage#getDataUnit() + */ + @Override + public Unit getDataUnit() { + return getColorMapParameters().getColorMapUnit(); + } + } \ No newline at end of file diff --git a/cave/com.raytheon.uf.viz.remote.graphics/META-INF/MANIFEST.MF b/cave/com.raytheon.uf.viz.remote.graphics/META-INF/MANIFEST.MF index 98e79b34d5..0ce0fb6dde 100644 --- a/cave/com.raytheon.uf.viz.remote.graphics/META-INF/MANIFEST.MF +++ b/cave/com.raytheon.uf.viz.remote.graphics/META-INF/MANIFEST.MF @@ -10,12 +10,12 @@ Eclipse-BuddyPolicy: dependent Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, com.raytheon.uf.viz.core;bundle-version="1.12.1174", - org.geotools;bundle-version="2.6.4", com.raytheon.uf.common.colormap;bundle-version="1.12.1174", com.raytheon.viz.ui;bundle-version="1.12.1174", - javax.vecmath;bundle-version="1.3.1", com.raytheon.uf.common.util;bundle-version="1.12.1174", - com.raytheon.uf.common.geospatial;bundle-version="1.12.1174" + com.raytheon.uf.common.geospatial;bundle-version="1.12.1174", + javax.measure;bundle-version="1.0.0", + javax.vecmath;bundle-version="1.3.1" Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy Export-Package: com.raytheon.uf.viz.remote.graphics, diff --git a/cave/com.raytheon.uf.viz.remote.graphics/src/com/raytheon/uf/viz/remote/graphics/objects/DispatchingColormappedImage.java b/cave/com.raytheon.uf.viz.remote.graphics/src/com/raytheon/uf/viz/remote/graphics/objects/DispatchingColormappedImage.java index 7c1c8efbdd..94fedfccf6 100644 --- a/cave/com.raytheon.uf.viz.remote.graphics/src/com/raytheon/uf/viz/remote/graphics/objects/DispatchingColormappedImage.java +++ b/cave/com.raytheon.uf.viz.remote.graphics/src/com/raytheon/uf/viz/remote/graphics/objects/DispatchingColormappedImage.java @@ -19,6 +19,8 @@ **/ package com.raytheon.uf.viz.remote.graphics.objects; +import javax.measure.unit.Unit; + import com.raytheon.uf.common.colormap.IColorMap; import com.raytheon.uf.common.colormap.prefs.ColorMapParameters; import com.raytheon.uf.common.colormap.prefs.IColorMapParametersListener; @@ -150,4 +152,14 @@ public class DispatchingColormappedImage extends } } + /* + * (non-Javadoc) + * + * @see com.raytheon.uf.viz.core.drawables.IColormappedImage#getDataUnit() + */ + @Override + public Unit getDataUnit() { + return wrappedObject.getDataUnit(); + } + } diff --git a/cave/com.raytheon.uf.viz.truecolor.gl/src/com/raytheon/uf/viz/truecolor/gl/extension/GLTrueColorImagingExtension.java b/cave/com.raytheon.uf.viz.truecolor.gl/src/com/raytheon/uf/viz/truecolor/gl/extension/GLTrueColorImagingExtension.java index a2dfb6b56f..acae687de9 100644 --- a/cave/com.raytheon.uf.viz.truecolor.gl/src/com/raytheon/uf/viz/truecolor/gl/extension/GLTrueColorImagingExtension.java +++ b/cave/com.raytheon.uf.viz.truecolor.gl/src/com/raytheon/uf/viz/truecolor/gl/extension/GLTrueColorImagingExtension.java @@ -52,7 +52,8 @@ import com.raytheon.viz.core.gl.images.AbstractGLImage; * * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Aug 6, 2012 mschenke Initial creation + * Aug 6, 2012 mschenke Initial creation + * Nov 4, 2013 2492 mschenke Reworked to use GLSL Data mapping * * * diff --git a/cave/com.raytheon.viz.core.gl/localization/glsl/include/mapping.glsl b/cave/com.raytheon.viz.core.gl/localization/glsl/include/mapping.glsl index 8ded421d66..4951fa1307 100644 --- a/cave/com.raytheon.viz.core.gl/localization/glsl/include/mapping.glsl +++ b/cave/com.raytheon.viz.core.gl/localization/glsl/include/mapping.glsl @@ -161,69 +161,111 @@ float getLinearIndex(float cmapValue, float cmapMin, float cmapMax) { return capIndex(index); } +/** + * Converts a colormap value to a log index + */ +float valueToLogIndex(float value, float rangeMin, float rangeMax) { + // Account for 0 min index + if (rangeMin == 0) { + rangeMin = 0.0000001; + if (rangeMax < 0) { + rangeMin = -rangeMin; + } + } + + int reverse = 0; + if ((value < rangeMin && rangeMin > 0) + || (value > rangeMin && rangeMin < 0)) { + reverse = 1; + } + + value = abs(value); + rangeMin = abs(rangeMin); + rangeMax = abs(rangeMax); + + // Check uncomputable index value, everything between this range is 0, + // rangeMin->rangeMax 0 -> 1, -rangeMin->-rangeMax 0 -> -1 + if (value <= rangeMin && value >= -rangeMin) { + return 0; + } + + double index = (log(value) - log(rangeMin)) + / (log(rangeMax) - log(rangeMin)); + if (reverse != 0) { + index = -index; + } + + return index; +} + /** * This function logarithmically finds the index for the cmapValue into * cmapMin/cmapMax (capped at 0-1). */ float getLogIndex(float cmapValue, float cmapMin, float cmapMax, int mirror) { + int inverted = 0; + float rangeMin = abs(cmapMin); + float rangeMax = abs(cmapMax); + float rangeValue = abs(cmapValue); + if (rangeMin > rangeMax) { + // Inverted colormapping range (cmapMax is closest to 0) + inverted = 1; + float tmp = rangeMin; + rangeMin = rangeMax; + rangeMax = tmp; + } + float index = 0.0; - // is this strictly negative, strictly positive or neg to pos scaling? - if (cmapMin >= 0.0 && cmapMax >= 0.0 && mirror != 1) { - if (cmapValue < cmapMin) { - index = 0.0; - } else { - // simple calculation - index = ((log(cmapValue) - log(cmapMin)) - / abs(log(cmapMax) - log(cmapMin))); + // Flag if min/max values are on opposite sides of zero + int minMaxOpposite = 0; + if ((cmapMin < 0 && cmapMax > 0) || (cmapMin > 0 && cmapMax < 0)) { + minMaxOpposite = 1; + } + + if (mirror != 0 || minMaxOpposite != 0) { + if (cmapMax < 0) { + // Invert colormapping if negative range was given + cmapValue = -cmapValue; } - } else if (cmapMin <= 0.0 && cmapMax <= 0.0 && mirror != 1) { - index = ((log(cmapValue) - log(cmapMax)) - / abs(log(cmapMin) - log(cmapMax))); - } else { - // special case, neg to pos: - float colorMapMin = cmapMin; - float colorMapMax = cmapMax; - float zeroVal = max(colorMapMax, abs(colorMapMin)) * 0.0001; - if (mirror == 1 && (colorMapMin > 0.0 || colorMapMax < 0.0)) { - if (colorMapMax < 0.0) { - colorMapMax = -cmapMax; - cmapValue = -cmapValue; - zeroVal = -colorMapMin; - } else { - zeroVal = cmapMin; - } - colorMapMin = -cmapMax; + // Log scaling is happening on both sides of zero, need to compute + // our zero index value + float zeroVal = rangeMin; + if (minMaxOpposite == 1) { + // Min/Max are on opposite sides of zero, compute a zero value + zeroVal = max(rangeMin, rangeMax) * 0.0001; } - float leftZero = 0.0; - float rightZero = 0.0; + + float negCmapMax = rangeMin; + float posCmapMax = rangeMax; + if (mirror != 0) { + negCmapMax = posCmapMax = rangeMax; + } + + // Compute log zero val and log neg/pos max vals float absLogZeroVal = abs(log(zeroVal)); - - rightZero = absLogZeroVal + log(colorMapMax); - - float cmapMax2 = abs(colorMapMin); - - leftZero = absLogZeroVal + log(cmapMax2); - - float zeroIndex = leftZero / (leftZero + rightZero); - - // figure out index for texture val - float absTextureColor = abs(cmapValue); - if (absTextureColor <= zeroVal) { - index = zeroIndex; - } else if (cmapValue > 0.0) { - // positive texture color value, find index from 0 to - // cmapMax: - float logTexColor = absLogZeroVal + log(cmapValue); - - float texIndex = logTexColor / rightZero; - index = (zeroIndex + ((1.0 - zeroIndex) * texIndex)); + float logNegCmapMax = absLogZeroVal + log(negCmapMax); + float logPosCmapMax = absLogZeroVal + log(posCmapMax); + // Calculate index which zeroVal is at based on neg max and pos max + float zeroValIndex = logNegCmapMax / (logNegCmapMax + logPosCmapMax); + if (cmapValue > 0) { + index = valueToLogIndex(rangeValue, zeroVal, posCmapMax); + index = zeroValIndex + (1 - zeroValIndex) * index; } else { - // negative texture color value, find index from 0 to - // cmapMax: - float logTexColor = absLogZeroVal + log(absTextureColor); - - float texIndex = logTexColor / leftZero; - index = (zeroIndex - (zeroIndex * texIndex)); + index = valueToLogIndex(rangeValue, zeroVal, negCmapMax); + index = zeroValIndex - zeroValIndex * index; + } + if (inverted != 0) { + index = 1.0 - index; + } + } else { + // Simple case, just use log converter to get index + index = valueToLogIndex(rangeValue, rangeMin, rangeMax); + if (inverted == 1) { + index = 1.0 - index; + } + if (cmapMin > 0 && cmapValue < rangeMin + || (cmapMin < 0 && cmapValue > -rangeMin)) { + index = -index; } } return capIndex(index); diff --git a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/ext/imaging/GLDataMappingFactory.java b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/ext/imaging/GLDataMappingFactory.java index 9fdbad5dd3..f867700f2b 100644 --- a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/ext/imaging/GLDataMappingFactory.java +++ b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/ext/imaging/GLDataMappingFactory.java @@ -256,6 +256,10 @@ public class GLDataMappingFactory { float currDelta = (float) (currEndValue - dataMapping[0]); for (int i = 2; i < dataMapping.length; ++i) { double nextValue = dataMapping[i]; + // Deltas are compared in float space because it + // minimizes the precision errors and the mapping will + // occur in floats in GLSL so no need for the extra + // precision float nextDelta = (float) ((nextValue - currEndValue) / (i - currEndIndex)); if (nextDelta == currDelta) { // Remove linear entries diff --git a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/ext/imaging/GLDefaultImagingExtension.java b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/ext/imaging/GLDefaultImagingExtension.java index 5f81878958..20a502a7eb 100644 --- a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/ext/imaging/GLDefaultImagingExtension.java +++ b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/ext/imaging/GLDefaultImagingExtension.java @@ -27,7 +27,8 @@ import com.raytheon.viz.core.gl.glsl.GLShaderProgram; import com.raytheon.viz.core.gl.images.AbstractGLImage; /** - * TODO Add Description + * Default GL imaging extension. Renders RGB images in GL applying alpha, + * brightness, and contrast settings. * *
  * 
diff --git a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/glsl/GLSLStructFactory.java b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/glsl/GLSLStructFactory.java
index e8fd320933..757e84544d 100644
--- a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/glsl/GLSLStructFactory.java
+++ b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/glsl/GLSLStructFactory.java
@@ -23,7 +23,7 @@ import com.raytheon.uf.common.colormap.prefs.ColorMapParameters;
 import com.raytheon.viz.core.gl.dataformat.AbstractGLColorMapDataFormat;
 
 /**
- * Factory for creating GLSL struct mappings
+ * Factory for creating API defined GLSL structs in a {@link GLShaderProgram}.
  * 
  * 
  * 
diff --git a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/AbstractGLColormappedImage.java b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/AbstractGLColormappedImage.java
index 8f093e27f2..8ab9210a16 100644
--- a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/AbstractGLColormappedImage.java
+++ b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/AbstractGLColormappedImage.java
@@ -39,7 +39,8 @@ import com.sun.opengl.util.texture.TextureCoords;
  * 
  * Date         Ticket#    Engineer    Description
  * ------------ ---------- ----------- --------------------------
- * Oct 16, 2013       2333 mschenke    Initial creation
+ * Oct 16, 2013 2333       mschenke    Initial creation
+ * Nov  4, 2013 2492       mschenke    Reworked to use GLSL Data mapping
  * 
  * 
* diff --git a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/GLColormappedImage.java b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/GLColormappedImage.java index 98811a23db..5c4643c67c 100644 --- a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/GLColormappedImage.java +++ b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/GLColormappedImage.java @@ -19,6 +19,7 @@ **/ package com.raytheon.viz.core.gl.images; +import javax.measure.unit.Unit; import javax.media.opengl.GL; import com.raytheon.uf.common.colormap.image.ColorMapData.ColorMapDataType; @@ -102,4 +103,12 @@ public class GLColormappedImage extends AbstractGLColormappedImage { super.usaAsFrameBuffer(); } + @Override + public Unit getDataUnit() { + if (data != null && data.getDataUnit() != null) { + return data.getDataUnit(); + } + return getColorMapParameters().getDataUnit(); + } + } diff --git a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/GLOffscreenColormappedImage.java b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/GLOffscreenColormappedImage.java index 181ee5275a..c802cc94a3 100644 --- a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/GLOffscreenColormappedImage.java +++ b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/images/GLOffscreenColormappedImage.java @@ -19,6 +19,8 @@ **/ package com.raytheon.viz.core.gl.images; +import javax.measure.unit.Unit; + import com.raytheon.uf.common.colormap.prefs.ColorMapParameters; import com.raytheon.uf.viz.core.drawables.ext.IImagingExtension; import com.raytheon.viz.core.gl.dataformat.GLColorMapData; @@ -66,4 +68,9 @@ public class GLOffscreenColormappedImage extends AbstractGLColormappedImage { return Double.NaN; } + @Override + public Unit getDataUnit() { + return getColorMapParameters().getColorMapUnit(); + } + } diff --git a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/internal/ext/mosaic/GLMosaicImage.java b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/internal/ext/mosaic/GLMosaicImage.java index 08adb03de7..b09ff41055 100644 --- a/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/internal/ext/mosaic/GLMosaicImage.java +++ b/cave/com.raytheon.viz.core.gl/src/com/raytheon/viz/core/gl/internal/ext/mosaic/GLMosaicImage.java @@ -19,6 +19,8 @@ **/ package com.raytheon.viz.core.gl.internal.ext.mosaic; +import javax.measure.unit.Unit; + import com.raytheon.uf.common.colormap.prefs.ColorMapParameters; import com.raytheon.uf.viz.core.DrawableImage; import com.raytheon.uf.viz.core.IExtent; @@ -172,4 +174,14 @@ public class GLMosaicImage extends GLDelegateImage this.image = wrappedImage; } + /* + * (non-Javadoc) + * + * @see com.raytheon.uf.viz.core.drawables.IColormappedImage#getDataUnit() + */ + @Override + public Unit getDataUnit() { + return image.getDataUnit(); + } + } diff --git a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/dialogs/ColorMapSliderComp.java b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/dialogs/ColorMapSliderComp.java index 1120a0a891..8ba78dbc9f 100644 --- a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/dialogs/ColorMapSliderComp.java +++ b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/dialogs/ColorMapSliderComp.java @@ -21,12 +21,12 @@ package com.raytheon.viz.ui.dialogs; import java.text.DecimalFormat; -import javax.measure.converter.ConversionException; import javax.measure.converter.UnitConverter; +import javax.measure.unit.Unit; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; @@ -36,11 +36,9 @@ import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Scale; import org.eclipse.swt.widgets.Text; +import com.raytheon.uf.common.colormap.image.Colormapper; import com.raytheon.uf.common.colormap.prefs.ColorMapParameters; -import com.raytheon.uf.common.status.IUFStatusHandler; -import com.raytheon.uf.common.status.UFStatus; -import com.raytheon.uf.common.status.UFStatus.Priority; -import com.raytheon.viz.ui.dialogs.colordialog.ColorUtil; +import com.raytheon.uf.common.colormap.prefs.DataMappingPreferences.DataMappingEntry; /** * Composite for slider bars for ColorMapParameters @@ -51,7 +49,9 @@ import com.raytheon.viz.ui.dialogs.colordialog.ColorUtil; * * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Jan 3, 2012 mschenke Initial creation + * Jan 3, 2012 mschenke Initial creation + * Nov 8, 2013 2492 mschenke Rewritten to work with colormap + * units different from data units * *
* @@ -61,8 +61,13 @@ import com.raytheon.viz.ui.dialogs.colordialog.ColorUtil; public class ColorMapSliderComp extends Composite { - private static final transient IUFStatusHandler statusHandler = UFStatus - .getHandler(ColorMapSliderComp.class); + private static final String NaN_STRING = "NO DATA"; + + private static final int SLIDER_MIN = 0; + + private static final int SLIDER_MAX = 255; + + private static final int SLIDER_INC = 1; private ColorMapParameters cmap; @@ -74,25 +79,25 @@ public class ColorMapSliderComp extends Composite { private Text maxValueText; - private String[] sliderText; + private final float cmapAbsoluteMin; - private float cmapMin; + private final float cmapAbsoluteMax; - private float cmapMax; + private final float origCmapMin; - private float cmapWidth; + private final float origCmapMax; - private float cmapIncrement; + private float currentCmapMin; - private DecimalFormat format = null; + private float currentCmapMax; - private float currentMin; + private final DecimalFormat format; - private float currentMax; + private final boolean dataInverted; - private float origCmapMin; + private UnitConverter displayToColorMap; - private float origCmapMax; + private UnitConverter colorMapToDisplay; /** * @param parent @@ -101,8 +106,46 @@ public class ColorMapSliderComp extends Composite { public ColorMapSliderComp(Composite parent, ColorMapParameters cmap) { super(parent, SWT.NONE); this.cmap = cmap; - this.origCmapMin = cmap.getColorMapMin(); - this.origCmapMax = cmap.getColorMapMax(); + this.currentCmapMin = this.origCmapMin = cmap.getColorMapMin(); + this.currentCmapMax = this.origCmapMax = cmap.getColorMapMax(); + this.displayToColorMap = cmap.getDisplayToColorMapConverter(); + this.colorMapToDisplay = cmap.getColorMapToDisplayConverter(); + if (displayToColorMap == null) { + displayToColorMap = Unit.ONE.getConverterTo(Unit.ONE); + } + if (colorMapToDisplay == null) { + colorMapToDisplay = Unit.ONE.getConverterTo(Unit.ONE); + } + float cmapAbsoluteMin, cmapAbsoluteMax; + if (cmap.getDataMapping() != null) { + cmapAbsoluteMin = cmap.getColorMapMin(); + cmapAbsoluteMax = cmap.getColorMapMax(); + } else { + UnitConverter dataToColorMap = cmap.getDataToColorMapConverter(); + cmapAbsoluteMin = cmap.getDataMin(); + cmapAbsoluteMax = cmap.getDataMax(); + if (dataToColorMap != null) { + cmapAbsoluteMin = (float) dataToColorMap.convert(cmap + .getDataMin()); + cmapAbsoluteMax = (float) dataToColorMap.convert(cmap + .getDataMax()); + } + } + + this.cmapAbsoluteMin = cmapAbsoluteMin; + this.cmapAbsoluteMax = cmapAbsoluteMax; + + boolean dataInverted = false; + if ((cmapAbsoluteMin > cmapAbsoluteMax && cmap.getDataMin() < cmap + .getDataMax()) + || (cmapAbsoluteMin < cmapAbsoluteMax && cmap.getDataMin() > cmap + .getDataMax())) { + dataInverted = true; + } + + this.dataInverted = dataInverted; + this.format = getTextFormat(); + initializeComponents(); } @@ -115,16 +158,15 @@ public class ColorMapSliderComp extends Composite { * */ private void initializeComponents() { - buildColorMapData(); setLayout(new GridLayout(3, false)); Label maxLabel = new Label(this, SWT.None); maxLabel.setText("Max:"); maxSlider = new Scale(this, SWT.HORIZONTAL); - maxSlider.setMaximum(255); - maxSlider.setMinimum(0); - maxSlider.setIncrement(1); + maxSlider.setMaximum(SLIDER_MAX); + maxSlider.setMinimum(SLIDER_MIN); + maxSlider.setIncrement(SLIDER_INC); maxSlider.setSelection(maxSlider.getMaximum()); GridData layoutData = new GridData(SWT.FILL, SWT.DEFAULT, true, false); layoutData.minimumWidth = 250; @@ -142,9 +184,9 @@ public class ColorMapSliderComp extends Composite { minLabel.setText("Min:"); minSlider = new Scale(this, SWT.HORIZONTAL); - minSlider.setMaximum(255); - minSlider.setMinimum(0); - minSlider.setIncrement(1); + minSlider.setMaximum(SLIDER_MAX); + minSlider.setMinimum(SLIDER_MIN); + minSlider.setIncrement(SLIDER_INC); minSlider.setSelection(minSlider.getMinimum()); layoutData = new GridData(SWT.FILL, SWT.DEFAULT, true, false); layoutData.minimumWidth = 250; @@ -156,286 +198,186 @@ public class ColorMapSliderComp extends Composite { minValueText.setLayoutData(labelLayoutData); maxSlider.addSelectionListener(new SelectionAdapter() { - @Override public void widgetSelected(SelectionEvent e) { - if (maxSlider.getSelection() <= minSlider.getSelection()) { - maxSlider.setSelection(minSlider.getSelection() + 1); - } - maxValueText.setText(selectionToText(maxSlider.getSelection())); - changeMax(maxSlider.getSelection()); + setColorMapMax(selectionToColorMapValue(maxSlider + .getSelection())); } - }); minSlider.addSelectionListener(new SelectionAdapter() { - @Override public void widgetSelected(SelectionEvent e) { - if (minSlider.getSelection() >= maxSlider.getSelection()) { - minSlider.setSelection(maxSlider.getSelection() - 1); - } - minValueText.setText(selectionToText(minSlider.getSelection())); - changeMin(minSlider.getSelection()); + setColorMapMin(selectionToColorMapValue(minSlider + .getSelection())); } - }); - maxValueText.addKeyListener(new KeyListener() { - + maxValueText.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.character == SWT.CR) { - maxTextChanged(); + setColorMapMax(textToColorMapValue(maxValueText.getText() + .trim())); } } - - @Override - public void keyReleased(KeyEvent e) { - // do nothing - } - }); - minValueText.addKeyListener(new KeyListener() { - + minValueText.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.character == SWT.CR) { - minTextChanged(); + setColorMapMin(textToColorMapValue(minValueText.getText() + .trim())); } } - - @Override - public void keyReleased(KeyEvent e) { - // do nothing - } - }); - // set initial values - currentMax = cmap.getColorMapMax(); - currentMin = cmap.getColorMapMin(); - maxSlider.setSelection(cmapToSelection(currentMax)); - minSlider.setSelection(cmapToSelection(currentMin)); - setMaxText(); - setMinText(); + setColorMapMin(currentCmapMin); + setColorMapMax(currentCmapMax); } - private void setMaxText() { - maxValueText.setText(cmapToText(currentMax)); - } - - private void setMinText() { - minValueText.setText(cmapToText(currentMin)); - } - - private void setColorMapMax(float f) { - if (currentMax != f) { - currentMax = f; - cmap.setColorMapMax(f, true); + /** + * Converts a slider selection index to a colormap value + * + * @param selection + * @return + */ + private float selectionToColorMapValue(int selection) { + double indexValue = Colormapper.getLinearIndex(selection, SLIDER_MIN, + SLIDER_MAX); + if (dataInverted) { + indexValue = 1 - indexValue; } + double colorMapValue = cmapAbsoluteMin + + (cmapAbsoluteMax - cmapAbsoluteMin) * indexValue; + return (float) colorMapValue; } - private void setColorMapMin(float f) { - if (currentMin != f) { - currentMin = f; - cmap.setColorMapMin(f, true); + /** + * Converts a colormap value to a slider selection index + * + * @param colorMapValue + * @return + */ + private int colorMapValueToSelection(float colorMapValue) { + double indexValue = Colormapper.getLinearIndex(colorMapValue, + cmapAbsoluteMin, cmapAbsoluteMax); + if (dataInverted) { + indexValue = 1 - indexValue; } + return (int) (SLIDER_MIN + (SLIDER_MAX - SLIDER_MIN) * indexValue); } - private void minTextChanged() { - String text = minValueText.getText().trim().split(" ")[0]; - try { - float f = Float.valueOf(text); - UnitConverter unitConv = cmap.getImageToDisplayConverter(); - if (unitConv != null) { - f = (float) unitConv.inverse().convert(f); - } - if (f >= currentMax) { - setMinText(); - statusHandler.handle(Priority.ERROR, - "Minimum of colormap range cannot exceed the maximum."); - } else if (cmapMin >= f) { - setColorMapMin(cmapMin); - minSlider.setSelection(0); - setMinText(); - } else { - setColorMapMin(f); - minSlider.setSelection(cmapToSelection(f)); - } - } catch (NumberFormatException ex) { - statusHandler.handle(Priority.ERROR, - "Minimum of colormap range cannot be parsed: " + text); - setMinText(); - } catch (ConversionException ex) { - statusHandler.handle(Priority.ERROR, "Unit converter error.", ex); - setMinText(); - } - } - - private void maxTextChanged() { - String text = maxValueText.getText().trim().split(" ")[0]; - try { - float f = Float.valueOf(text); - UnitConverter unitConv = cmap.getImageToDisplayConverter(); - if (unitConv != null) { - f = (float) unitConv.inverse().convert(f); - } - if (currentMin >= f) { - statusHandler - .handle(Priority.ERROR, - "Maximum of colormap range cannot be below the minimum."); - setMaxText(); - } else if (f >= cmapMax) { - setColorMapMax(cmapMax); - maxSlider.setSelection(255); - setMaxText(); - } else { - setColorMapMax(f); - maxSlider.setSelection(cmapToSelection(f)); - } - } catch (NumberFormatException ex) { - statusHandler.handle(Priority.ERROR, - "Maximum of colormap range cannot be parsed: " + text); - setMaxText(); - } catch (ConversionException ex) { - statusHandler.handle(Priority.ERROR, "Unit converter error.", ex); - setMaxText(); - } - - } - - private void changeMax(int position) { - // slider min and max is based on the color map, so position is the new - // color map max - currentMax = selectionToCmap(position); - cmap.setColorMapMax(currentMax, true); - } - - private void changeMin(int position) { - // slider min and max is based on the color map, so position is the new - // color map min - currentMin = selectionToCmap(position); - cmap.setColorMapMin(currentMin, true); - } - - private String cmapToText(double value) { - UnitConverter unitConv = cmap.getImageToDisplayConverter(); - String textStr = ""; - - if (unitConv != null) { - value = unitConv.convert(value); - - if (((Double) value).isNaN()) { - textStr = "NO DATA"; + /** + * Converts a text string to a colormap value + * + * @param text + * @return + */ + private float textToColorMapValue(String text) { + if (cmap.getDataMapping() != null && text.isEmpty() == false) { + // First check for data mapping entries + for (DataMappingEntry entry : cmap.getDataMapping().getEntries()) { + if (entry.getLabel() != null && text.equals(entry.getLabel())) { + return entry.getPixelValue().floatValue(); + } } } - - String txt; - if (textStr.length() == 0) { - txt = format.format(value); + if (NaN_STRING.equals(text)) { + // If special NaN String, try to find first NaN value + for (int i = SLIDER_MIN; i < SLIDER_MAX; i += SLIDER_INC) { + float colorMapValue = selectionToColorMapValue(i); + if (Double.isNaN(colorMapToDisplay.convert(colorMapValue))) { + return colorMapValue; + } + } } else { - txt = textStr; - } - - return txt; - } - - private String selectionToText(int selection) { - String rval = "ERR"; - - if (selection > -1 && selection < sliderText.length) { - // exact match into sliderText array - rval = sliderText[selection]; - } else { - statusHandler.handle(Priority.CRITICAL, "index " + selection - + " out of range, max " + (sliderText.length - 1)); - } - return rval; - } - - private float selectionToCmap(int selection) { - float percentOffset = selection / 255.0f; - float value = percentOffset * cmapWidth + cmapMin; - return value; - } - - private int cmapToSelection(float value) { - int selection = (int) ((value - cmapMin) * 255.0f / cmapWidth); - return selection; - } - - // modified from logic in ColorBar.java - private void buildColorMapData() { - sliderText = new String[256]; - cmapWidth = cmap.getDataMax() - cmap.getDataMin(); - cmapIncrement = cmapWidth / ColorUtil.MAX_VALUE; - cmapMin = cmap.getDataMin(); - cmapMax = cmap.getDataMax(); - float start = cmap.getDataMin(); - String units = ""; - - UnitConverter unitConv = cmap.getImageToDisplayConverter(); - - Double lastVal = Double.NaN; - - // TODO: Handle piecewise pixel converts to show ranges (for radar) - for (int i = 0; i < sliderText.length; ++i) { - double value = start; - - // handle precision errors - if (value > cmapMax) { - // if the difference is .1 the increment between steps assume - // that cmapMax is ok - if ((value - cmapMax) < (.1 * cmapIncrement)) { - value = cmapMax; - } + // Attempt to parse and convert + try { + float displayValue = Float.parseFloat(text); + return (float) displayToColorMap.convert(displayValue); + } catch (Throwable t) { + // Ignore, NaN will be returned and text will be reverted } + } + return Float.NaN; + } - String textStr = ""; - - if (cmap.isLogarithmic()) { - // TODO: Handle case where min/max go from neg to pos - if (cmap.getColorMapMax() >= 0 && cmap.getColorMapMin() >= 0) { - double index = (i) / ColorUtil.MAX_VALUE; - value = Math.pow(Math.E, - (Math.log(cmap.getColorMapMin()) + (index * (Math - .log(cmap.getColorMapMax()) - Math.log(cmap - .getColorMapMin()))))); - } - if (format == null) { - format = new DecimalFormat("0.000"); - } - } - - if (unitConv != null) { - value = unitConv.convert(value); - - /* - * Check if the last value is non a number. - */ - if (lastVal.isNaN()) { - // If value is not a number then set the text to - // "NO DATA". - if (((Double) value).isNaN()) { - textStr = "NO DATA"; - } - lastVal = value; - } else { - // If value is not a number then prepend ">" - // to the value. - if (((Double) value).isNaN()) { - textStr = "> " + lastVal; - } else { - lastVal = value; + /** + * Converts a colormap value into a text display string + * + * @param colorMapValue + * @return + */ + private String colorMapValueToText(float colorMapValue) { + String text = null; + if (cmap.getDataMapping() != null) { + text = cmap.getDataMapping().getLabelValueForDataValue( + colorMapValue); + } + if (text == null || text.trim().isEmpty()) { + float displayValue = (float) colorMapToDisplay + .convert(colorMapValue); + if (Float.isNaN(displayValue) == false) { + text = format.format(displayValue); + } else { + text = NaN_STRING; + int selection = colorMapValueToSelection(colorMapValue); + for (int i = selection; i >= SLIDER_MIN; i -= SLIDER_INC) { + displayValue = (float) colorMapToDisplay + .convert(selectionToColorMapValue(i)); + if (Float.isNaN(displayValue) == false) { + text = "> " + format.format(displayValue); + break; } } } + } + return text; + } + + /** + * Sets the colormap min value, updates the text and slider + * + * @param colorMapMin + */ + private void setColorMapMin(float colorMapMin) { + if (Float.isNaN(colorMapMin) == false) { + currentCmapMin = colorMapMin; + } + minSlider.setSelection(colorMapValueToSelection(currentCmapMin)); + minValueText.setText(colorMapValueToText(currentCmapMin)); + + cmap.setColorMapMin(currentCmapMin, true); + } + + /** + * Sets the colormap max value, updates the text and slider + * + * @param colorMapMax + */ + private void setColorMapMax(float colorMapMax) { + if (Float.isNaN(colorMapMax) == false) { + currentCmapMax = colorMapMax; + } + maxSlider.setSelection(colorMapValueToSelection(currentCmapMax)); + maxValueText.setText(colorMapValueToText(currentCmapMax)); + + cmap.setColorMapMax(currentCmapMax, true); + } + + private DecimalFormat getTextFormat() { + if (cmap.isLogarithmic() == false) { + for (int i = SLIDER_MIN; i < SLIDER_MAX; ++i) { + double cmapValue = selectionToColorMapValue(i); + double displayValue = colorMapToDisplay.convert(cmapValue); + if (Double.isNaN(displayValue)) { + continue; + } - if (format == null && new Double(value).isNaN() == false) { int zeros = 0; - String val = "" + value; + String val = "" + displayValue; char[] vals = val.substring(val.indexOf(".") + 1).toCharArray(); for (int j = 0; j < vals.length; ++j) { if (vals[j] == '0') { @@ -451,28 +393,10 @@ public class ColorMapSliderComp extends Composite { for (int j = 0; j < zeros; ++j) { f += "0"; } - format = new DecimalFormat(f); + return new DecimalFormat(f); } - - String txt; - - /* - * If textStr doesn't have any text then set txt to the value in the - * value variable. - */ - if (textStr.length() == 0) { - txt = format.format(value); - } else { - txt = textStr; - } - - if (units != null && units.length() != 0) { - txt += " " + units; - } - - sliderText[i] = txt; - start += cmapIncrement; } + return new DecimalFormat("0.000"); } } diff --git a/edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/LogConverter.java b/edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/LogConverter.java new file mode 100644 index 0000000000..f6a88b3bfa --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/LogConverter.java @@ -0,0 +1,103 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.common.colormap; + +/** + * Converter that can do simple log scaling given a start/end range on either + * side of zero. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Nov 7, 2013  2492      mschenke     Initial creation
+ * 
+ * 
+ * + * @author mschenke + * @version 1.0 + */ + +public class LogConverter { + + private static double EFFECTIVE_ZERO = Double.MIN_VALUE; + + public static double valueToIndex(double value, double rangeMin, + double rangeMax) { + // Account for 0 min index + if (rangeMin == 0) { + rangeMin = EFFECTIVE_ZERO; + if (rangeMax < 0) { + rangeMin = -rangeMin; + } + } + + boolean reverse = false; + if ((value < rangeMin && rangeMin > 0) + || (value > rangeMin && rangeMin < 0)) { + reverse = true; + } + + value = Math.abs(value); + rangeMin = Math.abs(rangeMin); + rangeMax = Math.abs(rangeMax); + + // Check uncomputable index value, everything between this range is 0, + // rangeMin->rangeMax 0 -> 1, -rangeMin->-rangeMax 0 -> -1 + if (value <= rangeMin && value >= -rangeMin) { + return 0; + } + + double index = (Math.log(value) - Math.log(rangeMin)) + / (Math.log(rangeMax) - Math.log(rangeMin)); + if (reverse) { + index = -index; + } + + return index; + } + + public static double indexToValue(double index, double rangeMin, + double rangeMax) { + // Account for 0 min index + if (rangeMin == 0) { + rangeMin = EFFECTIVE_ZERO; + if (rangeMax < 0) { + rangeMin = -rangeMin; + } + } + + boolean reverse = index < 0; + + index = Math.abs(index); + rangeMin = Math.abs(rangeMin); + rangeMax = Math.abs(rangeMax); + + double value = Math.exp(Math.log(rangeMin) + + (index * (Math.log(rangeMax) - Math.log(rangeMin)))); + if (reverse) { + value = -value; + } + return value; + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/image/Colormapper.java b/edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/image/Colormapper.java index d225d7317a..d6c4251945 100644 --- a/edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/image/Colormapper.java +++ b/edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/image/Colormapper.java @@ -40,6 +40,7 @@ import javax.measure.unit.Unit; import com.raytheon.uf.common.colormap.Color; import com.raytheon.uf.common.colormap.IColorMap; +import com.raytheon.uf.common.colormap.LogConverter; import com.raytheon.uf.common.colormap.image.ColorMapData.ColorMapDataType; import com.raytheon.uf.common.colormap.prefs.ColorMapParameters; @@ -158,27 +159,6 @@ public class Colormapper { blue, alpha); } - /** - * Sets an index value into the indexArray for use in a Raster with - * IndexColorModel set - * - * @param dataArray - * @param idx - * @param idxValue - */ - public static void setIndexValue(Object dataArray, int idx, int idxValue) { - if (dataArray instanceof byte[]) { - ((byte[]) dataArray)[idx] = (byte) idxValue; - } else if (dataArray instanceof short[]) { - ((short[]) dataArray)[idx] = (short) idxValue; - } else if (dataArray instanceof int[]) { - ((int[]) dataArray)[idx] = idxValue; - } else { - throw new IllegalArgumentException("Unsupported dataArray type: " - + (dataArray != null ? dataArray.getClass() : null)); - } - } - /** * Returns the double representation of the data value for the Buffer at the * given index @@ -250,64 +230,70 @@ public class Colormapper { */ public static double getLogIndex(double cmapValue, double cmapMin, double cmapMax, boolean mirror) { + boolean inverted = false; + double rangeMin = Math.abs(cmapMin); + double rangeMax = Math.abs(cmapMax); + double rangeValue = Math.abs(cmapValue); + if (rangeMin > rangeMax) { + // Inverted colormapping range (cmapMax is closest to 0) + inverted = true; + double tmp = rangeMin; + rangeMin = rangeMax; + rangeMax = tmp; + } + double index = 0.0; - // is this strictly negative, strictly positive or neg to pos scaling? - if (cmapMin >= 0.0 && cmapMax >= 0.0 && !mirror) { - if (cmapValue < cmapMin) { - index = 0.0; - } else { - // simple calculation - index = ((Math.log(cmapValue) - Math.log(cmapMin)) / Math - .abs(Math.log(cmapMax) - Math.log(cmapMin))); + // Flag if min/max values are on opposite sides of zero + boolean minMaxOpposite = (cmapMin < 0 && cmapMax > 0) + || (cmapMin > 0 && cmapMax < 0); + + if (mirror || minMaxOpposite) { + if (cmapMax < 0) { + // Invert colormapping if negative range was given + cmapValue = -cmapValue; } - } else if (cmapMin <= 0.0 && cmapMax <= 0.0 && !mirror) { - index = ((Math.log(cmapValue) - Math.log(cmapMax)) / Math.abs(Math - .log(cmapMin) - Math.log(cmapMax))); - } else { - // special case, neg to pos: - double colorMapMin = cmapMin; - double colorMapMax = cmapMax; - double zeroVal = Math.max(colorMapMax, Math.abs(colorMapMin)) * 0.0001; - if (mirror && (colorMapMin > 0.0 || colorMapMax < 0.0)) { - if (colorMapMax < 0.0) { - colorMapMax = -cmapMax; - cmapValue = -cmapValue; - zeroVal = -colorMapMin; - } else { - zeroVal = cmapMin; - } - colorMapMin = -cmapMax; + // Log scaling is happening on both sides of zero, need to compute + // our zero index value + double zeroVal = rangeMin; + if (minMaxOpposite) { + // Min/Max are on opposite sides of zero, compute a zero value + zeroVal = Math.max(rangeMin, rangeMax) * 0.0001; } - double leftZero = 0.0; - double rightZero = 0.0; + + double negCmapMax = rangeMin; + double posCmapMax = rangeMax; + if (mirror) { + negCmapMax = posCmapMax = rangeMax; + } + + // Compute log zero val and log neg/pos max vals double absLogZeroVal = Math.abs(Math.log(zeroVal)); - - rightZero = absLogZeroVal + Math.log(colorMapMax); - - double cmapMax2 = Math.abs(colorMapMin); - - leftZero = absLogZeroVal + Math.log(cmapMax2); - - double zeroIndex = leftZero / (leftZero + rightZero); - - // figure out index for texture val - double absTextureColor = Math.abs(cmapValue); - if (absTextureColor <= zeroVal) { - index = zeroIndex; - } else if (cmapValue > 0.0) { - // positive texture color value, find index from 0 to - // cmapMax: - double logTexColor = absLogZeroVal + Math.log(cmapValue); - - double texIndex = logTexColor / rightZero; - index = (zeroIndex + ((1.0 - zeroIndex) * texIndex)); + double logNegCmapMax = absLogZeroVal + Math.log(negCmapMax); + double logPosCmapMax = absLogZeroVal + Math.log(posCmapMax); + // Calculate index which zeroVal is at based on neg max and pos max + double zeroValIndex = logNegCmapMax + / (logNegCmapMax + logPosCmapMax); + if (cmapValue > 0) { + index = LogConverter.valueToIndex(rangeValue, zeroVal, + posCmapMax); + index = zeroValIndex + (1 - zeroValIndex) * index; } else { - // negative texture color value, find index from 0 to - // cmapMax: - double logTexColor = absLogZeroVal + Math.log(absTextureColor); - - double texIndex = logTexColor / leftZero; - index = (zeroIndex - (zeroIndex * texIndex)); + index = LogConverter.valueToIndex(rangeValue, zeroVal, + negCmapMax); + index = zeroValIndex - zeroValIndex * index; + } + if (inverted) { + index = 1.0 - index; + } + } else { + // Simple case, just use log converter to get index + index = LogConverter.valueToIndex(rangeValue, rangeMin, rangeMax); + if (inverted) { + index = 1.0 - index; + } + if (cmapMin > 0 && cmapValue < rangeMin + || (cmapMin < 0 && cmapValue > -rangeMin)) { + index = -index; } } return index; diff --git a/edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/prefs/ColorMapParameters.java b/edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/prefs/ColorMapParameters.java index b20d9a1518..56e39d911b 100644 --- a/edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/prefs/ColorMapParameters.java +++ b/edexOsgi/com.raytheon.uf.common.colormap/src/com/raytheon/uf/common/colormap/prefs/ColorMapParameters.java @@ -133,6 +133,7 @@ public class ColorMapParameters { protected Unit colorMapUnit; /** Units of the data values to colormap */ + @Deprecated protected Unit dataUnit; /** The maximum value used to apply the colormap */ @@ -142,9 +143,11 @@ public class ColorMapParameters { protected float colorMapMin; /** The maximum (usually theoretical) value of the data */ + @Deprecated protected float dataMax; /** The minimum (usually theoretical) value of the data */ + @Deprecated protected float dataMin; /** The intervals upon which to apply labeling to the color bar */ @@ -162,15 +165,19 @@ public class ColorMapParameters { protected String colorMapName; /** The converter that converts data values to {@link #displayUnit} * */ + @Deprecated protected UnitConverter dataToDisplayConverter; /** The converter that converts display values to {@link #dataUnit} * */ + @Deprecated protected UnitConverter displayToDataConverter; /** The converter that converts data values to {@link #colorMapUnit} */ + @Deprecated protected UnitConverter dataToColorMapConverter; /** The converter that converts color map unit values to {@link #dataUnit} */ + @Deprecated protected UnitConverter colorMapToDataConverter; /** The converter that converts color map unit values to {@link #displayUnit} */ @@ -491,32 +498,40 @@ public class ColorMapParameters { } /** + * @deprecated data max is not important for general colormapping use * @return the dataMax */ + @Deprecated public float getDataMax() { return dataMax; } /** + * @deprecated data max is not important for general colormapping use * @param dataMax * the dataMax to set */ + @Deprecated public void setDataMax(float dataMax) { this.dataMax = dataMax; notifyListener(); } /** + * @deprecated data min is not important for general colormapping use * @return the dataMin */ + @Deprecated public float getDataMin() { return dataMin; } /** + * @deprecated data min is not important for general colormapping use * @param dataMin * the dataMin to set */ + @Deprecated public void setDataMin(float dataMin) { this.dataMin = dataMin; notifyListener(); @@ -563,8 +578,10 @@ public class ColorMapParameters { /** * Returns the unit data values to be colormapped are in * + * @deprecated data unit is not important for general colormapping use * @return the dataUnit */ + @Deprecated public Unit getDataUnit() { return dataUnit; } @@ -572,9 +589,11 @@ public class ColorMapParameters { /** * Sets the unit data values to be colormapped are in * + * @deprecated data unit is not important for general colormapping use * @param dataUnit * the dataUnit to set */ + @Deprecated public void setDataUnit(Unit dataUnit) { this.dataUnit = dataUnit; @@ -595,8 +614,10 @@ public class ColorMapParameters { * Returns the {@link UnitConverter} from {@link #dataUnit} to * {@link #displayUnit} * + * @deprecated data unit is not important for general colormapping use * @return the dataToDisplayConverter */ + @Deprecated public UnitConverter getDataToDisplayConverter() { if (dataToDisplayConverter == null) { dataToDisplayConverter = constructConverter(dataUnit, displayUnit); @@ -611,8 +632,10 @@ public class ColorMapParameters { * Returns the {@link UnitConverter} from {@link #displayUnit} to * {@link #dataUnit} * + * @deprecated data unit is not important for general colormapping use * @return the displayToDataConverter */ + @Deprecated public UnitConverter getDisplayToDataConverter() { if (displayToDataConverter == null) { displayToDataConverter = constructConverter(displayUnit, dataUnit); @@ -667,8 +690,10 @@ public class ColorMapParameters { * Returns a {@link UnitConverter} converting {@link #dataUnit} values to * the {@link #colorMapUnit} if compatible or null otherwise * + * @deprecated data unit is not important for general colormapping use * @return */ + @Deprecated public UnitConverter getDataToColorMapConverter() { if (dataToColorMapConverter == null) { dataToColorMapConverter = constructConverter(dataUnit, colorMapUnit); @@ -773,8 +798,10 @@ public class ColorMapParameters { * Returns a {@link UnitConverter} converting {@link #colorMapUnit} values * to the {@link #dataUnit} if compatible or null otherwise * + * @deprecated data unit is not important for general colormapping use * @return */ + @Deprecated public UnitConverter getColorMapToDataConverter() { if (colorMapToDataConverter == null) { colorMapToDataConverter = constructConverter(colorMapUnit, dataUnit);