Issue #28 Implemented generic true color resource with new extension. Wrote new, more efficient, accurate, and scalable TileSet rendering/managing code to replace AbstractTileSet which VIIRS uses since it took over 10s to load a single frame of data with AbstractTileSet and around 20-30s to load a single true color frame of VIIRS. Put true color code in separate projects to encapsulate functionality. Resources need not depend on these projects, they just have to set the ImagingProvider in their ImagingCapability and true color resource can be loaded through a bundle file.

Change-Id: I624edb115b02611470e2e434e27759c45600111c

Former-commit-id: 7d75620c07c992ab74a8305a69ed87817f8f0639
This commit is contained in:
Max Schenkelberg 2012-08-13 12:20:05 -05:00
parent 603a9c90e4
commit 93e8619fa5
35 changed files with 2751 additions and 7 deletions

View file

@ -200,4 +200,16 @@
install-size="0"
version="0.0.0"/>
<plugin
id="com.raytheon.uf.viz.truecolor"
download-size="0"
install-size="0"
version="0.0.0"/>
<plugin
id="com.raytheon.uf.viz.truecolor.gl"
download-size="0"
install-size="0"
version="0.0.0"/>
</feature>

View file

@ -30,7 +30,8 @@ Require-Bundle: org.eclipse.ui,
com.raytheon.uf.common.dataplugin,
com.raytheon.uf.common.dataplugin.level,
com.raytheon.uf.common.dataquery;visibility:=reexport,
com.raytheon.uf.viz.application;bundle-version="1.0.0"
com.raytheon.uf.viz.application;bundle-version="1.0.0",
com.raytheon.uf.common.geospatial;bundle-version="1.12.1174"
Bundle-ActivationPolicy: lazy
Eclipse-BuddyPolicy: ext, registered, global
Eclipse-RegisterBuddy: org.apache.velocity, com.raytheon.edex.common, com.raytheon.uf.common.serialization, com.raytheon.uf.common.serialization.comm, com.raytheon.uf.common.status, com.raytheon.uf.common.dataplugin.level
@ -72,6 +73,7 @@ Export-Package: com.raytheon.uf.viz.core,
com.raytheon.uf.viz.core.status,
com.raytheon.uf.viz.core.style,
com.raytheon.uf.viz.core.style.level,
com.raytheon.uf.viz.core.tile,
com.raytheon.uf.viz.core.time,
com.raytheon.uf.viz.core.topo,
com.raytheon.uf.viz.core.velocity,
@ -87,7 +89,6 @@ Import-Package: com.raytheon.edex.colormap,
com.raytheon.uf.common.comm,
com.raytheon.uf.common.datastorage,
com.raytheon.uf.common.datastorage.records,
com.raytheon.uf.common.geospatial,
com.raytheon.uf.common.message,
com.raytheon.uf.common.message.response,
com.raytheon.uf.common.pointdata,

View file

@ -74,6 +74,11 @@ public class PixelCoverage {
new Coordinate(center.x - width / 2, center.y + height / 2));
}
public PixelCoverage(IMesh mesh) {
this(new Coordinate(0, 0), 0, 0);
this.mesh = mesh;
}
public Coordinate getLl() {
return ll;
}

View file

@ -19,7 +19,10 @@
**/
package com.raytheon.uf.viz.core.drawables.ext;
import java.util.Collection;
import com.raytheon.uf.viz.core.DrawableImage;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.drawables.ext.GraphicsExtension.IGraphicsExtensionInterface;
import com.raytheon.uf.viz.core.exception.VizException;
@ -43,6 +46,11 @@ import com.raytheon.uf.viz.core.exception.VizException;
public interface IImagingExtension extends IGraphicsExtensionInterface {
public static interface ImageProvider {
public Collection<DrawableImage> getImages(IGraphicsTarget target,
PaintProperties paintProps) throws VizException;
}
/**
* Draw the images passed in
*

View file

@ -23,6 +23,8 @@ import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import com.raytheon.uf.viz.core.drawables.ext.IImagingExtension.ImageProvider;
/**
* Capability for imaging features
*
@ -42,6 +44,8 @@ import javax.xml.bind.annotation.XmlAttribute;
@XmlAccessorType(XmlAccessType.NONE)
public class ImagingCapability extends AbstractCapability {
private ImageProvider provider;
@XmlAttribute
private float contrast = 1.0f;
@ -144,9 +148,25 @@ public class ImagingCapability extends AbstractCapability {
}
}
/**
* @return the provider
*/
public ImageProvider getProvider() {
return provider;
}
/**
* @param provider
* the provider to set
*/
public void setProvider(ImageProvider provider) {
this.provider = provider;
}
@Override
public AbstractCapability clone() {
ImagingCapability ic = new ImagingCapability();
ic.provider = provider;
ic.contrast = contrast;
ic.brightness = brightness;
ic.interpolationState = interpolationState;

View file

@ -0,0 +1,181 @@
/**
* 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.viz.core.tile;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import org.geotools.coverage.grid.GridGeometry2D;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.geometry.Envelope;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory;
/**
* Object that represents a single Tile. Contains a level and GridGeometry as
* well as a border which is used in {@link #intersects(Geometry)}
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 8, 2012 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public class Tile {
public final int tileLevel;
public final GridGeometry2D tileGeometry;
public final List<PreparedGeometry> tileBorder;
/**
* Create a Tile specifying a border for intersection checks
*
* @param tileLevel
* @param tileGeometry
* @param tileBorder
*/
public Tile(int tileLevel, GridGeometry2D tileGeometry, Geometry tileBorder) {
this.tileLevel = tileLevel;
this.tileGeometry = tileGeometry;
if (tileBorder != null) {
int num = tileBorder.getNumGeometries();
this.tileBorder = new ArrayList<PreparedGeometry>(num);
for (int n = 0; n < num; ++n) {
this.tileBorder.add(PreparedGeometryFactory.prepare(tileBorder
.getGeometryN(n)));
}
} else {
this.tileBorder = null;
}
}
/**
* Creates a Tile with no border. intersects calls will always return true
*
* @param tileLevel
* @param tileGeometry
*/
public Tile(int tileLevel, GridGeometry2D tileGeometry) {
this(tileLevel, tileGeometry, null);
}
public Rectangle getRectangle() {
GridEnvelope env = tileGeometry.getGridRange();
return new Rectangle(env.getLow(0), env.getLow(1), env.getSpan(0),
env.getSpan(1));
}
/**
* Checks if the geometry intersects the Tile. Tiles without a
* {@link #tileBorder} will always return true
*
* @param geometry
* @return
*/
public boolean intersects(Geometry geometry) {
if (tileBorder == null) {
return true;
}
for (PreparedGeometry border : tileBorder) {
if (border.intersects(geometry)) {
return true;
}
}
return false;
}
/**
* Checks to see if the x/y coordinate is contained by the Tile's CRS
* Envelope
*
* @param x
* @param y
* @return
*/
public boolean contains(double x, double y) {
Envelope env = tileGeometry.getEnvelope();
return env.getMinimum(0) <= x && env.getMaximum(0) >= x
&& env.getMinimum(1) <= y && env.getMaximum(1) >= y;
}
/**
* Checks to see if the Coordinate is contained by the Tile's CRS Envelope
*
* @param c
* @return
*/
public boolean contains(Coordinate c) {
return contains(c.x, c.y);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((tileGeometry == null) ? 0 : tileGeometry.hashCode());
result = prime * result + tileLevel;
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tile other = (Tile) obj;
if (tileGeometry == null) {
if (other.tileGeometry != null)
return false;
} else if (!tileGeometry.equals(other.tileGeometry))
return false;
if (tileLevel != other.tileLevel)
return false;
return true;
}
}

View file

@ -0,0 +1,415 @@
/**
* 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.viz.core.tile;
import java.util.ArrayList;
import java.util.List;
import org.geotools.coverage.grid.GeneralGridEnvelope;
import org.geotools.coverage.grid.GeneralGridGeometry;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.geometry.Envelope2D;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.referencing.operation.DefaultMathTransformFactory;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import com.raytheon.uf.common.geospatial.CRSCache;
import com.raytheon.uf.common.geospatial.util.WorldWrapCorrector;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
/**
* This object represents a single tile level. It does this by containing a 2
* dimensional array of {@link Tile} objects.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 8, 2012 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public class TileLevel {
/** Tile level's GridGeometry */
private GridGeometry2D levelGeometry;
/** GridGeometry tile level was created for */
private GeneralGridGeometry targetGeometry;
// Cached MathTransforms
private MathTransform crsToGrid;
private MathTransform gridToCRS;
private MathTransform tileCRSToTargetGrid;
/** Target grid to lat/lon for world wrap correcting */
private MathTransform targetGridToLatLon;
/** World wrap corrector, corrects Tile borders */
private WorldWrapCorrector corrector;
/** Level of this TileLevel */
private int tileLevel;
/** size for tiles in this level */
private int tileSize;
/** Pixel density of the tile level */
private double pixelDensity;
/** Tile array */
private Tile[][] tiles;
TileLevel(GridGeometry2D levelGeometry, GeneralGridGeometry targetGeometry,
int tileLevel, int tileSize) {
this.levelGeometry = levelGeometry;
this.targetGeometry = targetGeometry;
this.tileLevel = tileLevel;
this.tileSize = tileSize;
intialize(levelGeometry.getGridRange(), levelGeometry.getEnvelope());
}
private void intialize(GridEnvelope range, Envelope envelope) {
int width = range.getSpan(0);
int height = range.getSpan(1);
int fullTileDimX = width / tileSize;
int tileRemainderX = width % tileSize;
int fullTileDimY = height / tileSize;
int tileRemainderY = height % tileSize;
int totalTilesX = fullTileDimX;
if (tileRemainderX > 0) {
totalTilesX++;
}
int totalTilesY = fullTileDimY;
if (tileRemainderY > 0) {
totalTilesY++;
}
tiles = new Tile[totalTilesY][totalTilesX];
try {
gridToCRS = levelGeometry.getGridToCRS(PixelInCell.CELL_CORNER);
crsToGrid = gridToCRS.inverse();
DefaultMathTransformFactory factory = new DefaultMathTransformFactory();
tileCRSToTargetGrid = factory.createConcatenatedTransform(
CRSCache.getInstance().findMathTransform(
levelGeometry.getCoordinateReferenceSystem(),
targetGeometry.getCoordinateReferenceSystem()),
targetGeometry.getGridToCRS(PixelInCell.CELL_CORNER)
.inverse());
targetGridToLatLon = factory.createConcatenatedTransform(
targetGeometry.getGridToCRS(PixelInCell.CELL_CORNER),
CRSCache.getInstance().findMathTransform(
targetGeometry.getCoordinateReferenceSystem(),
DefaultGeographicCRS.WGS84));
corrector = new WorldWrapCorrector(targetGeometry);
// Calculate pixel density
// Grab the center x, 3/4 y of the map
double mapXCenter = targetGeometry.getGridRange().getSpan(0) * 0.5;
double mapYCenter = targetGeometry.getGridRange().getSpan(1) * 0.75;
double[] input = new double[] { mapXCenter, mapYCenter,
mapXCenter + 1, mapYCenter + 1 };
double[] output = new double[input.length];
tileCRSToTargetGrid.inverse().transform(input, 0, output, 0, 2);
levelGeometry.getGridToCRS(PixelInCell.CELL_CORNER).inverse()
.transform(output, 0, input, 0, 2);
pixelDensity = 1.0 / Math.abs(new Coordinate(input[0], input[1],
0.0).distance(new Coordinate(input[2], input[3], 0.0)));
} catch (Exception e) {
throw new RuntimeException(
"Cannot tranform tile CRS into target CRS", e);
}
}
/**
* Returns the pixel density for the tile level. This is the approximate
* number of target grid pixels a single tile grid pixel takes up
*
* @return
*/
public double getPixelDensity() {
return pixelDensity;
}
/**
* The number of tiles in the x direction (number of columns)
*
* @return
*/
public int getNumXTiles() {
return tiles[0].length;
}
/**
* The number of tiles in the y direction (number of rows)
*
* @return
*/
public int getNumYTiles() {
return tiles.length;
}
/**
* Level of this TileLevel
*
* @return
*/
public int getLevel() {
return tileLevel;
}
/**
* Get the Tile at the specified x/y index in the tile set
*
* @param x
* @param y
* @return
*/
public Tile getTile(int x, int y) {
Tile tile = tiles[y][x];
if (tile == null) {
synchronized (tiles) {
// Double check tile to see if another thread created it
tile = tiles[y][x];
if (tile == null) {
tiles[y][x] = tile = createTile(x, y);
}
}
}
return tile;
}
/**
* Gets the Tile for the specified tile grid location
*
* @param x
* @param y
* @return
*/
public Tile getTile(double x, double y) {
int xIdx = (int) x / tileSize;
int yIdx = (int) y / tileSize;
if (xIdx >= 0 && yIdx >= 0 && xIdx < getNumXTiles()
&& yIdx < getNumYTiles()) {
return tiles[yIdx][xIdx];
}
return null;
}
/**
* Transforms TileLevel crs x,y into tile level grid space x,y
*
* @param x
* @param y
* @return
*/
public double[] crsToGrid(double x, double y) throws TransformException {
double[] out = new double[2];
crsToGrid.transform(new double[] { x, y }, 0, out, 0, 1);
return out;
}
/**
* Creates a Tile for the specified x/y {@link #tiles} index
*
* @param x
* @param y
* @return
*/
private Tile createTile(int x, int y) {
GridEnvelope range = levelGeometry.getGridRange();
// Get grid range ranges and calculate grid range for the tile
int startX = range.getLow(0);
int startY = range.getLow(1);
int endX = range.getHigh(0) + 1;
int endY = range.getHigh(1) + 1;
int tileY = startY + y * tileSize;
int tileX = startX + x * tileSize;
int tileEndX = Math.min(endX, tileX + tileSize);
int tileEndY = Math.min(endY, tileY + tileSize);
// Convert grid range into crs envelope range
double[] in = new double[] { tileX, tileY, tileEndX, tileEndY };
double[] out = new double[in.length];
try {
gridToCRS.transform(in, 0, out, 0, 2);
} catch (TransformException e) {
throw new RuntimeException("Error getting tile envelope from grid",
e);
}
double envTileX = out[0];
double envTileY = out[1];
double envTileEndX = out[2];
double envTileEndY = out[3];
// Create tile GridGeometry
range = new GeneralGridEnvelope(new int[] { tileX, tileY }, new int[] {
tileEndX, tileEndY }, false);
GridGeometry2D tileGridGeom = new GridGeometry2D(range, new Envelope2D(
levelGeometry.getCoordinateReferenceSystem(), out[0], envTileY,
envTileEndX - envTileX, envTileEndY - envTileY));
// Calculate the border in target grid space for the Tile
Geometry border = null;
try {
double[] UL = new double[] { envTileX, envTileY };
double[] UR = new double[] { envTileEndX, envTileY };
double[] LR = new double[] { envTileEndX, envTileEndY };
double[] LL = new double[] { envTileX, envTileEndY };
// Create tile border based on pixel density tile level 0 should
// always have threshold of 1.0
border = createTileBorder(UL, UR, LR, LL,
Math.max(range.getSpan(0) / 4, 1),
Math.max(range.getSpan(1) / 4, 1),
tileLevel > 0 ? Math.max(pixelDensity, 1.0) : 1.0);
} catch (TransformException e) {
// Invalid geometry, don't add a border
}
// Create the Tile object
return new Tile(tileLevel, tileGridGeom, border);
}
/**
* Ensures all Tile objects are created for the level. This method can takes
* lots of time depending on grid resolution of level and {@link #tileSize}.
* Tiles are created dynamically as requested otherwise
*/
public void populateTiles() {
int totalTilesY = getNumYTiles();
int totalTilesX = getNumXTiles();
for (int y = 0; y < totalTilesY; ++y) {
for (int x = 0; x < totalTilesX; ++x) {
if (tiles[y][x] == null) {
tiles[y][x] = createTile(x, y);
}
}
}
}
private Geometry createTileBorder(double[] UL, double[] UR, double[] LR,
double[] LL, int maxHorDivisions, int maxVertDivisions,
double threshold) throws TransformException {
List<Coordinate> borderPoints = new ArrayList<Coordinate>(
maxVertDivisions * 2 + maxHorDivisions * 2);
double[] out = new double[2];
// UL to UR
tileCRSToTargetGrid.transform(UL, 0, out, 0, 1);
borderPoints.add(new Coordinate(out[0], out[1]));
calculateBorder(borderPoints, UL, null, UR, null, maxHorDivisions,
threshold);
// UR to LR
tileCRSToTargetGrid.transform(UR, 0, out, 0, 1);
borderPoints.add(new Coordinate(out[0], out[1]));
calculateBorder(borderPoints, UR, null, LR, null, maxVertDivisions, 1.0);
// LR to LL
tileCRSToTargetGrid.transform(LR, 0, out, 0, 1);
borderPoints.add(new Coordinate(out[0], out[1]));
calculateBorder(borderPoints, LR, null, LL, null, maxHorDivisions, 1.0);
// LL to UL
tileCRSToTargetGrid.transform(LL, 0, out, 0, 1);
borderPoints.add(new Coordinate(out[0], out[1]));
calculateBorder(borderPoints, LL, null, UL, null, maxVertDivisions, 1.0);
// Add start point to complete linear ring
tileCRSToTargetGrid.transform(UL, 0, out, 0, 1);
borderPoints.add(new Coordinate(out[0], out[1]));
// Create Geometry and world wrap correct (need to be in lat/lon)
return JTS.transform(corrector.correct(JTS.transform(TileSet.gf
.createPolygon(TileSet.gf.createLinearRing(borderPoints
.toArray(new Coordinate[borderPoints.size()])), null),
targetGridToLatLon)), targetGridToLatLon.inverse());
}
private int calculateBorder(List<Coordinate> borderList, double[] point1,
double[] transformedPoint1, double[] point3,
double[] transformedPoint3, double maxNumDivs, double threshold)
throws TransformException {
if (transformedPoint1 == null) {
transformedPoint1 = new double[point1.length];
tileCRSToTargetGrid.transform(point1, 0, transformedPoint1, 0, 1);
}
if (transformedPoint3 == null) {
transformedPoint3 = new double[point3.length];
tileCRSToTargetGrid.transform(point3, 0, transformedPoint3, 0, 1);
}
if (transformedPoint1 == null || transformedPoint3 == null) {
// if the image has some points outside the valid range of the
// screen then give up optimizing and assume the max number of
// points.
return (int) Math.ceil(maxNumDivs);
}
double[] point2 = { (point1[0] + point3[0]) / 2,
(point1[1] + point3[1]) / 2 };
double[] transformedPoint2 = new double[point2.length];
tileCRSToTargetGrid.transform(point2, 0, transformedPoint2, 0, 1);
double[] interp2 = { (transformedPoint1[0] + transformedPoint3[0]) / 2,
(transformedPoint1[1] + transformedPoint3[1]) / 2 };
double dX = transformedPoint2[0] - interp2[0];
double dY = transformedPoint2[1] - interp2[1];
double d = Math.hypot(dX, dY);
if (d < threshold || maxNumDivs < 1) {
return 1;
} else {
int nd1 = calculateBorder(borderList, point1, transformedPoint1,
point2, transformedPoint2, maxNumDivs / 2, threshold);
borderList.add(new Coordinate(transformedPoint2[0],
transformedPoint2[1]));
if (nd1 * 2 >= maxNumDivs) {
nd1 = (int) Math.ceil(maxNumDivs);
}
int nd2 = calculateBorder(borderList, point2, transformedPoint2,
point3, transformedPoint3, maxNumDivs / 2, threshold);
if (nd2 * 2 >= maxNumDivs) {
nd2 = (int) Math.ceil(maxNumDivs);
}
return (Math.max(nd1, nd2) * 2);
}
}
}

View file

@ -0,0 +1,258 @@
/**
* 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.viz.core.tile;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.keyvalue.MultiKey;
import org.geotools.coverage.grid.GeneralGridEnvelope;
import org.geotools.coverage.grid.GeneralGridGeometry;
import org.geotools.coverage.grid.GridGeometry2D;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.geometry.Envelope;
import com.raytheon.uf.viz.core.IExtent;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
/**
* A TileSet is an object that consists of multiple tile levels. Each tile level
* is represented by a {@link TileLevel} object. These objects are only accessed
* via static funtion
* {@link #getTileSet(GridGeometry2D, GeneralGridGeometry, int, int)} to ensure
* tile set sharing. When a TileSet is no longer needed, dispose must be called
* on it to make sure it is cleaned up
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 8, 2012 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public class TileSet {
/** Each tile level is LEVEL_FACTOR much smaller than the previous level */
private static final int LEVEL_FACTOR = 2;
static final GeometryFactory gf = new GeometryFactory();
/** Static cache for TileSets */
private static Map<MultiKey, TileSet> tileSetCache = new HashMap<MultiKey, TileSet>();
/**
* Creates a {@link TileSet} object with the specified tile size and levels
*
* @param tileSetGeometry
* @param targetGeometry
* @param levels
* @param tileSize
* @return
*/
public static TileSet getTileSet(GridGeometry2D tileSetGeometry,
GeneralGridGeometry targetGeometry, int levels, int tileSize) {
synchronized (TileSet.class) {
MultiKey key = new MultiKey(tileSetGeometry, targetGeometry,
levels, tileSize);
TileSet tileSet = tileSetCache.get(key);
if (tileSet == null) {
// No tileset for key, create one
tileSet = new TileSet(tileSetGeometry, targetGeometry, levels,
tileSize, key);
tileSetCache.put(key, tileSet);
}
++tileSet.refCount;
return tileSet;
}
}
/** GridGeometry tileSet is created for */
private GeneralGridGeometry targetGeometry;
/** {@link TileLevel} array, one per tile level */
private TileLevel[] tileLevels;
private int tileSize;
private int refCount = 0;
private MultiKey cacheKey;
private TileSet(GridGeometry2D tileSetGeometry,
GeneralGridGeometry targetGeometry, int levels, int tileSize,
MultiKey cacheKey) {
this.targetGeometry = targetGeometry;
this.tileSize = tileSize;
this.cacheKey = cacheKey;
initialize(tileSetGeometry, levels);
}
/**
* @param tileSetGeometry
* @param levels
*/
private void initialize(GridGeometry2D tileSetGeometry, int levels) {
Envelope envelope = tileSetGeometry.getEnvelope();
GridEnvelope range = tileSetGeometry.getGridRange();
int startX = range.getLow(0);
int startY = range.getLow(1);
int width = range.getSpan(0);
int height = range.getSpan(1);
tileLevels = new TileLevel[levels];
for (int i = 0; i < levels; ++i) {
if (i > 0) {
startX /= LEVEL_FACTOR;
startY /= LEVEL_FACTOR;
width /= LEVEL_FACTOR;
height /= LEVEL_FACTOR;
tileSetGeometry = new GridGeometry2D(new GeneralGridEnvelope(
new int[] { startX, startY },
new int[] { width, height }, false), envelope);
}
tileLevels[i] = new TileLevel(tileSetGeometry, targetGeometry, i,
tileSize);
}
// Fully populate lowest resolution tile level
tileLevels[tileLevels.length - 1].populateTiles();
}
/**
* Disposes of the TileSet, needs to be called when tile set is no longer
* needed
*/
public void dispose() {
synchronized (TileSet.class) {
--refCount;
if (refCount == 0) {
tileSetCache.remove(cacheKey);
}
}
}
/**
* Returns the {@link TileLevel} object for the specified level. Lower level
* = higher resolution
*
* @param level
* @return
*/
public TileLevel getTileLevel(int level) {
return tileLevels[level];
}
/**
* Returns all intersecting tiles in the level for the given extent. Lower
* tile level = higher resolution with 0 being the highest
*
* @param level
* level to get tiles at
* @param extent
* area requesting tiles at in targetGeometry space
* @return
*/
public Collection<Tile> getIntersectingTiles(int level, IExtent extent) {
// Start with level 0 and work way up to requested level
Geometry extentGeom = gf.createPolygon(
gf.createLinearRing(new Coordinate[] {
new Coordinate(extent.getMinX(), extent.getMinY()),
new Coordinate(extent.getMaxX(), extent.getMinY()),
new Coordinate(extent.getMaxX(), extent.getMaxY()),
new Coordinate(extent.getMinX(), extent.getMaxY()),
new Coordinate(extent.getMinX(), extent.getMinY()) }),
null);
TileLevel tileLevel = getTileLevel(tileLevels.length - 1);
return getIntersectingTiles(tileLevel, level, 0, 0,
tileLevel.getNumXTiles(), tileLevel.getNumYTiles(), extentGeom);
}
/**
* Returns intersecting tiles for the desired level. Recursive function that
* checks intersection by looking at previous tile level tiles
*
* @param tileLevel
* @param level
* @param startX
* @param startY
* @param endX
* @param endY
* @param extent
* @return
*/
private Collection<Tile> getIntersectingTiles(TileLevel tileLevel,
int level, int startX, int startY, int endX, int endY,
Geometry extent) {
TileLevel nextTileLevel = null;
if (tileLevel.getLevel() != level) {
// If we are not at desired level, get next tile level
nextTileLevel = getTileLevel(tileLevel.getLevel() - 1);
}
List<Tile> intersecting = new ArrayList<Tile>();
for (int y = startY; y < endY; ++y) {
for (int x = startX; x < endX; ++x) {
Tile tile = tileLevel.getTile(x, y);
if (tile.intersects(extent)) {
if (nextTileLevel != null) {
// nextTileLevel is not null meaning we are not at
// desired level. Calculate index into next tile level
// and call recursively
int nextStartX = x * LEVEL_FACTOR;
int nextStartY = y * LEVEL_FACTOR;
int nextEndX = Math.min(nextStartX + LEVEL_FACTOR,
nextTileLevel.getNumXTiles());
int nextEndY = Math.min(nextStartY + LEVEL_FACTOR,
nextTileLevel.getNumYTiles());
intersecting.addAll(getIntersectingTiles(nextTileLevel,
level, nextStartX, nextStartY, nextEndX,
nextEndY, extent));
} else if (tile.tileBorder != null) {
// This is desired tile level, add to list if we
// actually have a valid border that intersects
intersecting.add(tile);
}
}
}
}
return intersecting;
}
/**
* Gets the GridGeometry the tileSet is created for
*
* @return
*/
public GeneralGridGeometry getTargetGeometry() {
return targetGeometry;
}
}

View file

@ -0,0 +1,461 @@
/**
* 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.viz.core.tile;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.geotools.coverage.grid.GeneralGridGeometry;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import com.raytheon.uf.common.geospatial.CRSCache;
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.uf.viz.core.DrawableImage;
import com.raytheon.uf.viz.core.IExtent;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.IMesh;
import com.raytheon.uf.viz.core.drawables.IColormappedImage;
import com.raytheon.uf.viz.core.drawables.IImage;
import com.raytheon.uf.viz.core.drawables.IImage.Status;
import com.raytheon.uf.viz.core.drawables.IRenderable;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.jobs.JobPool;
import com.raytheon.uf.viz.core.rsc.capabilities.ImagingCapability;
import com.vividsolutions.jts.geom.Coordinate;
/**
* Renderable tile set class that creates a {@link TileSet} and renders images
* for tiles displayed using the {@link TileImageCreator} passed in at
* construction
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 8, 2012 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public class TileSetRenderable implements IRenderable {
public static interface TileImageCreator {
/**
* Create a complete DrawableImage for the given tile on the
* targetGeometry
*/
public DrawableImage createTileImage(IGraphicsTarget target, Tile tile,
GeneralGridGeometry targetGeometry) throws VizException;
}
private class TileImageCreatorTask implements Runnable {
private IGraphicsTarget target;
private Tile tile;
private TileImageCreatorTask(IGraphicsTarget target, Tile tile) {
this.target = target;
this.tile = tile;
}
@Override
public void run() {
try {
DrawableImage di = tileCreator.createTileImage(target, tile,
tileSet.getTargetGeometry());
if (di != null) {
di.getImage().stage();
}
addTileImage(tile, di);
} catch (VizException e) {
statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(),
e);
}
}
}
protected static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(TileSetRenderable.class);
/** Screen pixel to image pixel threshold at which we change levels */
protected static final double LEVEL_CHANGE_THRESHOLD = 2.0;
/** Job pool for tile creation */
protected static final JobPool tileCreationPool = new JobPool(
"Creating Image Tiles", 10, false);
/** Job map, should only have one job running per tile at a time */
protected Map<Tile, Runnable> jobMap = new ConcurrentHashMap<Tile, Runnable>();
/** Image map for tiles */
protected Map<Tile, DrawableImage> imageMap = new HashMap<Tile, DrawableImage>();
/** Full resolution tile set GridGeometry2D */
protected final GridGeometry2D tileSetGeometry;
protected final TileImageCreator tileCreator;
/** Desired size for each tile */
protected final int tileSize;
/** Number of tile levels to create */
protected final int tileLevels;
/** {@link TileSet} object, manages tiles */
protected TileSet tileSet;
/** Transform for tileset CRS to lat/lon */
protected final MathTransform localProjToLL;
/** Transform for lat/lon to tileset CRS */
protected final MathTransform llToLocalProj;
/** Stored imaging capability */
protected final ImagingCapability imaging;
/** Last painted tile level, used for interrogating proper level */
protected int lastPaintedLevel;
/** The ratio of target grid pixels / image pixel for each tile level */
protected double[] pixelWidth;
/**
* Constructs a tile set renderable, creators needs to call
* {@link #project(GeneralGridGeometry)} before the renderable can be used
*
* @param resource
* @param tileSetGeometry
* @param tileCreator
* @param tileLevels
* @param tileSize
*/
public TileSetRenderable(ImagingCapability imaging,
GridGeometry2D tileSetGeometry, TileImageCreator tileCreator,
int tileLevels, int tileSize) {
this.tileSetGeometry = tileSetGeometry;
this.tileCreator = tileCreator;
this.tileLevels = tileLevels;
this.tileSize = tileSize;
this.pixelWidth = new double[tileLevels];
this.imaging = imaging;
try {
// Set lat/lon math transforms for tile set
llToLocalProj = CRSCache.getInstance().findMathTransform(
DefaultGeographicCRS.WGS84,
tileSetGeometry.getCoordinateReferenceSystem());
localProjToLL = llToLocalProj.inverse();
} catch (Exception e) {
throw new IllegalArgumentException(
"Could not get tranform from tile crs to lat/lon", e);
}
}
/**
* Projects the tile set for use with the target geometry
*
* @param targetGeometry
*/
public synchronized void project(GeneralGridGeometry targetGeometry) {
// dispose the old TileSet
if (tileSet != null) {
tileSet.dispose();
}
// Create TileSet for new target geometry
tileSet = TileSet.getTileSet(tileSetGeometry, targetGeometry,
tileLevels, tileSize);
// Clear out meshes and create new ones cloning old ones
for (DrawableImage di : imageMap.values()) {
if (di != null) {
IMesh currentMesh = di.getCoverage().getMesh();
if (currentMesh != null) {
try {
di.getCoverage().setMesh(
currentMesh.clone(targetGeometry));
} catch (VizException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
}
currentMesh.dispose();
}
}
}
// Get the pixel densities for each tile level. This is approximately
// how many target grid pixels per image pixel there are for the level
for (int level = 0; level < tileLevels; ++level) {
TileLevel tileLevel = tileSet.getTileLevel(level);
pixelWidth[level] = tileLevel.getPixelDensity();
}
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.core.drawables.IRenderable#paint(com.raytheon.uf.
* viz.core.IGraphicsTarget,
* com.raytheon.uf.viz.core.drawables.PaintProperties)
*/
@Override
public void paint(IGraphicsTarget target, PaintProperties paintProps)
throws VizException {
Collection<DrawableImage> images = getImagesToRender(target, paintProps);
target.drawRasters(paintProps,
images.toArray(new DrawableImage[images.size()]));
}
/**
* Disposes the tile set and any data associated with it
*/
public synchronized void dispose() {
// Make sure any lingering jobs are canceled and joined on
for (Runnable job : jobMap.values()) {
tileCreationPool.cancel(job);
}
tileCreationPool.join();
// Dispose the tile set
if (tileSet != null) {
tileSet.dispose();
tileSet = null;
}
// Dispose of all the images for the tile set
for (DrawableImage image : imageMap.values()) {
if (image != null) {
image.dispose();
}
}
imageMap.clear();
}
/**
* Get the {@link DrawableImage} list to display for the given target and
* paint properties
*
* @param target
* @param paintProps
* @return
* @throws VizException
*/
public synchronized Collection<DrawableImage> getImagesToRender(
IGraphicsTarget target, PaintProperties paintProps)
throws VizException {
double screenToWorldRatio = paintProps.getCanvasBounds().width
/ paintProps.getView().getExtent().getWidth();
int usedTileLevel = tileLevels - 1;
/*
* pixelRatios[usedTileLevel] * screenToWorldRatio gives us
* canvasPixels/image pixel at the level, We should use the level if
* there are less than LEVEL_CHANGE_THRESHOLD canvas pixels per image
* pixel
*/
while ((pixelWidth[usedTileLevel] * screenToWorldRatio > LEVEL_CHANGE_THRESHOLD)
&& usedTileLevel > 0) {
usedTileLevel--;
}
lastPaintedLevel = usedTileLevel;
return getImagesWithinExtent(target, paintProps.getView().getExtent(),
usedTileLevel, 0);
}
/**
*
* @param target
* @param paintProps
* @param level
* @param depth
* @return
* @throws VizException
*/
protected List<DrawableImage> getImagesWithinExtent(IGraphicsTarget target,
IExtent extent, int level, int depth) throws VizException {
if (tileSet == null) {
// Early exit condition, disposed or haven't projected yet
return Collections.emptyList();
}
// Flag to determine if we should draw lower tile levels. This will be
// the case if we don't have all the images for all intersecting tiles
// at this level
boolean needDrawLower = false;
// Get the intersecting tiles for the level
Collection<Tile> intersecting = tileSet.getIntersectingTiles(level,
extent);
// These Tiles still need images created for them
List<Tile> tilesNeedingImage = new ArrayList<Tile>(intersecting.size());
List<DrawableImage> drawableImages = new ArrayList<DrawableImage>(
intersecting.size());
for (Tile tile : intersecting) {
// Flag to indicate if a tile needs an image created for it
boolean needsImage = false;
DrawableImage di = imageMap.get(tile);
if (di != null) {
IImage image = di.getImage();
if (image.getStatus() == Status.FAILED
|| image.getStatus() == Status.INVALID) {
// Image is invalid, re-request creation
needsImage = true;
} else {
image.setBrightness(imaging.getBrightness());
image.setContrast(imaging.getContrast());
image.setInterpolated(imaging.isInterpolationState());
if (image.getStatus() != Status.LOADED) {
needDrawLower = true;
}
drawableImages.add(di);
}
} else {
needsImage = true;
}
if (needsImage) {
tilesNeedingImage.add(tile);
needDrawLower = true;
}
}
if (depth == 0) {
// Only request images to be created if we are at the desired level
// i.e. the recursion depth is 0
if (tilesNeedingImage.isEmpty()) {
// All intersecting tiles are loaded for this level, cancel any
// jobs running for tiles we don't need anymore (may be case if
// zooming or panning)
for (Runnable job : jobMap.values()) {
tileCreationPool.cancel(job);
}
jobMap.clear();
} else {
// Create tiles needing images
createTileImages(target, tilesNeedingImage);
}
}
// Draw lower resolution data first
if (needDrawLower && (level + 1) < tileLevels) {
// put lower levels first in the list so they are drawn first.
List<DrawableImage> lowerImages = getImagesWithinExtent(target,
extent, level + 1, depth + 1);
lowerImages.addAll(drawableImages);
drawableImages = lowerImages;
}
return drawableImages;
}
/**
* Create tile images for the specified tiles
*
* @param target
* @param tilesToCreate
*/
protected void createTileImages(IGraphicsTarget target,
Collection<Tile> tilesToCreate) {
for (Tile tile : tilesToCreate) {
if (jobMap.get(tile) == null) {
// No job already running for tile, create and schedule one
TileImageCreatorTask job = new TileImageCreatorTask(target,
tile);
jobMap.put(tile, job);
tileCreationPool.schedule(job);
}
}
}
/**
* Adds a DrawableImage for the specified Tile. Disposes of any old image
*
* @param tile
* @param image
*/
public void addTileImage(Tile tile, DrawableImage image) {
DrawableImage oldImage = imageMap.put(tile, image);
if (oldImage != null) {
oldImage.dispose();
}
Runnable task = jobMap.remove(tile);
if (task != null) {
tileCreationPool.cancel(task);
}
}
/**
* Returns the raw image value from tile image that contains the lat/lon
* coordinate
*
* @param coordinate
* in lat/lon space
* @return
* @throws VizException
*/
public double interrogate(Coordinate coordinate) throws VizException {
try {
double[] local = new double[2];
llToLocalProj
.transform(new double[] { coordinate.x, coordinate.y }, 0,
local, 0, 1);
double localX = local[0];
double localY = local[1];
TileLevel level = tileSet.getTileLevel(lastPaintedLevel);
double[] grid = level.crsToGrid(localX, localY);
Tile tile = level.getTile(grid[0], grid[1]);
DrawableImage di = imageMap.get(tile);
if (di != null) {
IImage image = di.getImage();
if (image instanceof IColormappedImage) {
return ((IColormappedImage) image).getValue((int) grid[0]
% tileSize, (int) grid[1] % tileSize);
}
}
} catch (TransformException e) {
throw new VizException("Error interrogating ", e);
}
return Double.NaN;
}
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.raytheon.uf.viz.truecolor.gl</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,8 @@
#Mon Aug 06 13:10:16 CDT 2012
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6

View file

@ -0,0 +1,15 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: True Color GL
Bundle-SymbolicName: com.raytheon.uf.viz.truecolor.gl;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.raytheon.uf.viz.truecolor.gl.Activator
Bundle-Vendor: RAYTHEON
Require-Bundle: org.eclipse.core.runtime,
com.raytheon.uf.viz.core;bundle-version="1.12.1174",
com.raytheon.uf.viz.truecolor;bundle-version="1.0.0",
com.raytheon.viz.core.gl;bundle-version="1.12.1174",
javax.media.opengl;bundle-version="1.1.0",
com.raytheon.uf.common.util;bundle-version="1.12.1174"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy

View file

@ -0,0 +1,6 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
localization/,\
plugin.xml

View file

@ -0,0 +1,56 @@
#include <colorUtil>
#include <indexing>
uniform sampler2D rawTex;
uniform float naturalMin;
uniform float naturalMax;
uniform float cmapMin;
uniform float cmapMax;
uniform int isFloat;
uniform int band;
uniform sampler2D trueColorTexture;
uniform int height;
uniform int width;
float getIndex(sampler2D rawTex, float cmapMin, float cmapMax, float naturalMin, float naturalMax, int isFloat) {
vec4 textureValue = texture2D(rawTex, gl_TexCoord[0].st);
float naturalVal = textureValue.r;
if ( isFloat == 0 ) {
naturalVal = ((naturalVal * (naturalMax - naturalMin)) + naturalMin);
}
float index = findIndex(naturalVal, cmapMin, cmapMax);
if (index < 0.0 || index > 1.0) {
index = -1.0;
}
return index;
}
void main(void)
{
vec2 xy = gl_FragCoord.xy;
vec4 imageVal = texture2D(rawTex,gl_TexCoord[0].st);
vec4 curVal = texture2D(trueColorTexture, vec2((xy.x / float(width)), (xy.y / float(height))));
float r = curVal.r;
float g = curVal.g;
float b = curVal.b;
float a = curVal.a;
float index = getIndex(rawTex, cmapMin, cmapMax, naturalMin, naturalMax, isFloat);
if ( index < 0.0 ) {
index = a = 0.0;
} else {
a = 1.0;
}
if ( band == 0 && index > r ) {
r = index;
} else if ( band == 1 && index > g ) {
g = index;
} else if ( band == 2 && index > b ) {
b = index;
}
gl_FragColor = vec4(r,g,b,a);
}

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<?eclipse version="3.2"?>
<plugin>
<extension
point="com.raytheon.uf.viz.core.graphicsExtension">
<graphicsExtension
class="com.raytheon.uf.viz.truecolor.gl.extension.GLTrueColorImagingExtension">
</graphicsExtension>
</extension>
</plugin>

View file

@ -0,0 +1,30 @@
package com.raytheon.uf.viz.truecolor.gl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
private static BundleContext context;
static BundleContext getContext() {
return context;
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext bundleContext) throws Exception {
Activator.context = bundleContext;
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext bundleContext) throws Exception {
Activator.context = null;
}
}

View file

@ -0,0 +1,213 @@
/**
* 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.viz.truecolor.gl.extension;
import javax.media.opengl.GL;
import com.raytheon.uf.viz.core.DrawableImage;
import com.raytheon.uf.viz.core.IExtent;
import com.raytheon.uf.viz.core.PixelCoverage;
import com.raytheon.uf.viz.core.drawables.ColorMapParameters;
import com.raytheon.uf.viz.core.drawables.IImage;
import com.raytheon.uf.viz.core.drawables.ImagingSupport;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension;
import com.raytheon.uf.viz.truecolor.gl.image.GLTrueColorImage;
import com.raytheon.viz.core.gl.ext.GLOffscreenRenderingExtension;
import com.raytheon.viz.core.gl.glsl.AbstractGLSLImagingExtension;
import com.raytheon.viz.core.gl.glsl.GLShaderProgram;
import com.raytheon.viz.core.gl.images.AbstractGLImage;
import com.raytheon.viz.core.gl.images.GLColormappedImage;
/**
* GL implementation of the {@link ITrueColorImagingExtension}
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 6, 2012 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public class GLTrueColorImagingExtension extends AbstractGLSLImagingExtension
implements ITrueColorImagingExtension {
private AbstractGLImage writeToImage;
private Channel renderingChannel;
/*
* (non-Javadoc)
*
* @see com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension#
* initializeRaster(int[], com.raytheon.uf.viz.core.IExtent)
*/
@Override
public ITrueColorImage initializeRaster(int[] imageBounds,
IExtent imageExtent) throws VizException {
return new GLTrueColorImage(GLTrueColorImagingExtension.class,
imageBounds, imageExtent);
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.core.gl.ext.AbstractGLImagingExtension#getShaderProgramName
* ()
*/
@Override
public String getShaderProgramName() {
return "truecolor";
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.core.gl.ext.AbstractGLImagingExtension#preImageRender
* (com.raytheon.uf.viz.core.drawables.PaintProperties,
* com.raytheon.viz.core.gl.images.AbstractGLImage,
* com.raytheon.uf.viz.core.PixelCoverage)
*/
@Override
public Object preImageRender(PaintProperties paintProps,
AbstractGLImage image, PixelCoverage imageCoverage)
throws VizException {
if (image instanceof GLTrueColorImage) {
GLTrueColorImage trueColorImage = (GLTrueColorImage) image;
if (trueColorImage.isRepaint()) {
writeToImage = trueColorImage;
GLOffscreenRenderingExtension extension = target
.getExtension(GLOffscreenRenderingExtension.class);
try {
extension.renderOffscreen(trueColorImage,
trueColorImage.getImageExtent());
boolean allPainted = true;
for (Channel channel : Channel.values()) {
renderingChannel = channel;
DrawableImage[] imagesToDraw = trueColorImage
.getImages(channel);
if (imagesToDraw != null && imagesToDraw.length > 0) {
// Make sure images are staged before we mosaic them
ImagingSupport.prepareImages(target, imagesToDraw);
// Each image needs to draw separately due to gl
// issues when
// zoomed in very far, rendered parts near the
// corners don't
// show all the pixels for each image. Pushing and
// popping
// GL_TEXTURE_BIT before/after each render fixes
// this issue
for (DrawableImage di : imagesToDraw) {
allPainted &= drawRasters(paintProps, di);
}
// Need to set repaint based on if drawing
// completed.
trueColorImage.setRepaint(allPainted == false);
}
}
} finally {
extension.renderOnscreen();
}
renderingChannel = null;
writeToImage = null;
}
target.drawRasters(paintProps,
new DrawableImage(trueColorImage.getWrappedImage(),
imageCoverage));
// Don't actually render this image now since we just did it
return null;
} else {
GL gl = target.getGl();
// bind on GL_TEXTURE1 as 0 is channel image
writeToImage.bind(gl, GL.GL_TEXTURE1);
return image;
}
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.core.gl.ext.AbstractGLImagingExtension#postImageRender
* (com.raytheon.uf.viz.core.drawables.PaintProperties,
* com.raytheon.viz.core.gl.images.AbstractGLImage, java.lang.Object)
*/
@Override
public void postImageRender(PaintProperties paintProps,
AbstractGLImage image, Object data) throws VizException {
GL gl = target.getGl();
// Unbind the writeToImage from GL_TEXTURE1
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(writeToImage.getTextureStorageType(), 0);
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.core.gl.ext.AbstractGLImagingExtension#loadShaderData
* (com.raytheon.viz.core.gl.glsl.GLShaderProgram,
* com.raytheon.uf.viz.core.drawables.IImage,
* com.raytheon.uf.viz.core.drawables.PaintProperties)
*/
@Override
public void loadShaderData(GLShaderProgram program, IImage image,
PaintProperties paintProps) throws VizException {
if (image instanceof GLColormappedImage == false) {
throw new VizException(
"Can only render colormapped images in true color");
}
GLColormappedImage cmapImage = (GLColormappedImage) image;
ColorMapParameters colorMapParameters = cmapImage
.getColorMapParameters();
int textureType = cmapImage.getTextureType();
// Set the band image data
program.setUniform("rawTex", 0);
program.setUniform("naturalMin", colorMapParameters.getDataMin());
program.setUniform("naturalMax", colorMapParameters.getDataMax());
program.setUniform("cmapMin", colorMapParameters.getColorMapMin());
program.setUniform("cmapMax", colorMapParameters.getColorMapMax());
program.setUniform("isFloat", textureType == GL.GL_FLOAT
|| textureType == GL.GL_HALF_FLOAT_ARB ? 1 : 0);
// Set the composite image data
program.setUniform("trueColorTexture", 1);
program.setUniform("width", writeToImage.getWidth());
program.setUniform("height", writeToImage.getHeight());
// Set the band we are rendering to
program.setUniform("band", renderingChannel.ordinal());
}
}

View file

@ -0,0 +1,180 @@
/**
* 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.viz.truecolor.gl.image;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import com.raytheon.uf.viz.core.DrawableImage;
import com.raytheon.uf.viz.core.IExtent;
import com.raytheon.uf.viz.core.data.IRenderedImageCallback;
import com.raytheon.uf.viz.core.drawables.ext.IImagingExtension;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension.Channel;
import com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension.ITrueColorImage;
import com.raytheon.viz.core.gl.images.GLDelegateImage;
import com.raytheon.viz.core.gl.images.GLImage;
/**
* TODO Add Description
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 6, 2012 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public class GLTrueColorImage extends GLDelegateImage<GLImage> implements
ITrueColorImage {
private static class RGBCallback implements IRenderedImageCallback {
private int[] bounds;
private RGBCallback(int[] bounds) {
this.bounds = bounds;
}
@Override
public RenderedImage getImage() throws VizException {
return new BufferedImage(bounds[0], bounds[1],
BufferedImage.TYPE_INT_ARGB);
}
}
private boolean repaint = true;
private int[] bounds;
private IExtent imageExtent;
private Map<Channel, DrawableImage[]> channelMap = new HashMap<Channel, DrawableImage[]>();
/**
* @param extensionClass
*/
public GLTrueColorImage(Class<? extends IImagingExtension> extensionClass,
int[] bounds, IExtent imageExtent) {
super(new GLImage(new RGBCallback(bounds), IImagingExtension.class),
extensionClass);
this.bounds = bounds;
this.imageExtent = imageExtent;
}
/**
* @return the imageExtent
*/
public IExtent getImageExtent() {
return imageExtent;
}
/*
* (non-Javadoc)
*
* @see com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension.
* ITrueColorImage
* #setImages(com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension
* .Channel, com.raytheon.uf.viz.core.DrawableImage[])
*/
@Override
public void setImages(Channel channel, DrawableImage... images) {
DrawableImage[] prev = channelMap.put(channel, images);
if (prev != images) {
// Try to find equal array
if (prev != null && images != null && prev.length == images.length) {
for (int i = 0; i < images.length; ++i) {
if (images[i].getImage() != prev[i].getImage()) {
repaint = true;
break;
}
}
} else {
repaint = true;
}
}
}
/**
* Get the images for the specified channel
*
* @param channel
* @return
*/
public DrawableImage[] getImages(Channel channel) {
return channelMap.get(channel);
}
/**
* @return the repaint
*/
public boolean isRepaint() {
return repaint;
}
/**
* @param repaint
* the repaint to set
*/
public void setRepaint(boolean repaint) {
this.repaint = repaint;
}
/*
* (non-Javadoc)
*
* @see com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension.
* ITrueColorImage#setSize(int[])
*/
@Override
public void setSize(int[] bounds) {
if (Arrays.equals(bounds, this.bounds) == false) {
this.bounds = bounds;
image.dispose();
image = new GLImage(new RGBCallback(bounds),
IImagingExtension.class);
repaint = true;
}
}
/*
* (non-Javadoc)
*
* @see com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension.
* ITrueColorImage#setImageExtent(com.raytheon.uf.viz.core.IExtent)
*/
@Override
public void setImageExtent(IExtent extent) {
if (extent.equals(this.imageExtent) == false) {
this.imageExtent = extent;
repaint = true;
}
}
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.raytheon.uf.viz.truecolor</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,8 @@
#Mon Aug 06 11:45:00 CDT 2012
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6

View file

@ -0,0 +1,17 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Truecolor
Bundle-SymbolicName: com.raytheon.uf.viz.truecolor;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.raytheon.uf.viz.truecolor.Activator
Bundle-Vendor: RAYTHEON
Eclipse-RegisterBuddy: com.raytheon.uf.viz.core
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
com.raytheon.uf.viz.core;bundle-version="1.12.1174",
com.raytheon.viz.ui;bundle-version="1.12.1174",
com.raytheon.uf.common.time;bundle-version="1.12.1174",
com.raytheon.uf.common.geospatial;bundle-version="1.12.1174"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
Export-Package: com.raytheon.uf.viz.truecolor.extension

View file

@ -0,0 +1 @@
com.raytheon.uf.viz.truecolor.rsc.TrueColorResourceGroupData

View file

@ -0,0 +1,5 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
plugin.xml

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<?eclipse version="3.2"?>
<plugin>
<extension
point="com.raytheon.uf.viz.core.resource">
<resource
class="com.raytheon.uf.viz.truecolor.rsc.TrueColorResourceGroup"
name="True Color Imagery"
renderingOrderId="IMAGE_COUNTRY"
resourceType="PLAN_VIEW"/>
</extension>
</plugin>

View file

@ -0,0 +1,62 @@
package com.raytheon.uf.viz.truecolor;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
/**
* The activator class controls the plug-in life cycle
*/
public class Activator extends AbstractUIPlugin {
// The plug-in ID
public static final String PLUGIN_ID = "com.raytheon.uf.viz.truecolor"; //$NON-NLS-1$
public static final IUFStatusHandler statusHandler = UFStatus
.getHandler(Activator.class);
// The shared instance
private static Activator plugin;
/**
* The constructor
*/
public Activator() {
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext
* )
*/
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
* )
*/
public void stop(BundleContext context) throws Exception {
plugin = null;
super.stop(context);
}
/**
* Returns the shared instance
*
* @return the shared instance
*/
public static Activator getDefault() {
return plugin;
}
}

View file

@ -0,0 +1,75 @@
/**
* 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.viz.truecolor.extension;
import com.raytheon.uf.viz.core.DrawableImage;
import com.raytheon.uf.viz.core.IExtent;
import com.raytheon.uf.viz.core.drawables.IColormappedImage;
import com.raytheon.uf.viz.core.drawables.IImage;
import com.raytheon.uf.viz.core.drawables.ext.IImagingExtension;
import com.raytheon.uf.viz.core.exception.VizException;
/**
* Imaging extension for "true color" images. Where a set of
* {@link IColormappedImage} images are assigned to each band of an RGB channel
* image
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 6, 2012 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public interface ITrueColorImagingExtension extends IImagingExtension {
public static enum Channel {
RED, GREEN, BLUE;
}
public static interface ITrueColorImage extends IImage {
public void setImages(Channel channel, DrawableImage... images);
public void setSize(int[] bounds);
public void setImageExtent(IExtent extent);
}
/**
* Creates a true color image with the given imageBounds and imageExtent
*
* @param imageBounds
* @param imageExtent
* @param params
* @return
* @throws VizException
*/
public ITrueColorImage initializeRaster(int[] imageBounds,
IExtent imageExtent) throws VizException;
}

View file

@ -0,0 +1,250 @@
/**
* 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.viz.truecolor.rsc;
import java.util.Collection;
import org.eclipse.swt.graphics.Rectangle;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.viz.core.DrawableImage;
import com.raytheon.uf.viz.core.IExtent;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.PixelCoverage;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.drawables.ext.IImagingExtension.ImageProvider;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.IResourceDataChanged;
import com.raytheon.uf.viz.core.rsc.IResourceGroup;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.ResourceList;
import com.raytheon.uf.viz.core.rsc.capabilities.ColorMapCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.GroupNamingCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.ImagingCapability;
import com.raytheon.uf.viz.truecolor.Activator;
import com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension;
import com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension.Channel;
import com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension.ITrueColorImage;
import com.vividsolutions.jts.geom.Coordinate;
/**
* Resource group that operates on 3 resources, one assigned to each band of an
* RGB image. This resource checks for the {@link ImageProvider} on the
* {@link ImagingCapability}
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 6, 2012 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public class TrueColorResourceGroup extends
AbstractVizResource<TrueColorResourceGroupData, IDescriptor> implements
IResourceGroup, IResourceDataChanged {
private ITrueColorImage image;
private boolean timeAgnostic = true;
/**
* @param resourceData
* @param loadProperties
*/
public TrueColorResourceGroup(TrueColorResourceGroupData resourceData,
LoadProperties loadProperties) {
super(resourceData, loadProperties);
}
/*
* (non-Javadoc)
*
* @see com.raytheon.uf.viz.core.rsc.IResourceGroup#getResourceList()
*/
@Override
public ResourceList getResourceList() {
return resourceData.getResourceList();
}
/*
* (non-Javadoc)
*
* @see com.raytheon.uf.viz.core.rsc.AbstractVizResource#disposeInternal()
*/
@Override
protected void disposeInternal() {
resourceData.removeChangeListener(this);
for (ResourcePair rp : getResourceList()) {
rp.getResource().dispose();
}
image.dispose();
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.core.rsc.AbstractVizResource#paintInternal(com.raytheon
* .uf.viz.core.IGraphicsTarget,
* com.raytheon.uf.viz.core.drawables.PaintProperties)
*/
@Override
protected void paintInternal(IGraphicsTarget target,
PaintProperties paintProps) throws VizException {
Rectangle rect = paintProps.getCanvasBounds();
IExtent extent = paintProps.getView().getExtent();
image.setSize(new int[] { rect.width, rect.height });
image.setImageExtent(extent);
for (Channel c : Channel.values()) {
ResourcePair rp = resourceData.getResource(c);
if (rp != null) {
image.setImages(c, getImages(rp, target, paintProps));
}
}
Coordinate ul = new Coordinate(extent.getMinX(), extent.getMaxY());
Coordinate ur = new Coordinate(extent.getMaxX(), extent.getMaxY());
Coordinate lr = new Coordinate(extent.getMaxX(), extent.getMinY());
Coordinate ll = new Coordinate(extent.getMinX(), extent.getMinY());
target.drawRaster(image, new PixelCoverage(ul, ur, lr, ll), paintProps);
}
private DrawableImage[] getImages(ResourcePair rp, IGraphicsTarget target,
PaintProperties paintProps) throws VizException {
paintProps.setDataTime(paintProps.getFramesInfo().getTimeForResource(
rp.getResource()));
ImagingCapability imaging = rp.getResource().getCapability(
ImagingCapability.class);
Collection<DrawableImage> images = imaging.getProvider().getImages(
target, paintProps);
return images.toArray(new DrawableImage[images.size()]);
}
/*
* (non-Javadoc)
*
* @see com.raytheon.uf.viz.core.rsc.AbstractVizResource#isTimeAgnostic()
*/
@Override
public boolean isTimeAgnostic() {
return timeAgnostic;
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.core.rsc.AbstractVizResource#initInternal(com.raytheon
* .uf.viz.core.IGraphicsTarget)
*/
@Override
protected void initInternal(IGraphicsTarget target) throws VizException {
// We will name the composite
getCapability(GroupNamingCapability.class);
// Initialize them
for (ResourcePair rp : getResourceList()) {
AbstractVizResource<?, ?> resource = rp.getResource();
resource.init(target);
// Check resource for required capabilities
String error = null;
if (resource.hasCapability(ImagingCapability.class)) {
ImagingCapability imaging = resource
.getCapability(ImagingCapability.class);
if (imaging.getProvider() != null) {
if (resource.hasCapability(ColorMapCapability.class) == false) {
error = "does not have ColorMapCapability";
}
} else {
error = "does not have image provider set on the ImagingCapability";
}
} else {
error = "does not have the ImagingCapability";
}
if (error != null) {
Activator.statusHandler.handle(Priority.PROBLEM,
resourceData.getCompositeName(rp)
+ " resource in true color composite " + error);
resourceData.removeResource(rp);
}
}
ITrueColorImagingExtension ext = target
.getExtension(ITrueColorImagingExtension.class);
image = ext.initializeRaster(new int[] { 0, 0 }, null);
resourceData.addChangeListener(this);
resourceChanged(ChangeType.CAPABILITY,
getCapability(ImagingCapability.class));
timeAgnostic = true;
for (ResourcePair rp : getResourceList()) {
// If any resource is not time agnostic, neither are we
timeAgnostic &= rp.getResource().isTimeAgnostic();
}
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.core.rsc.AbstractVizResource#project(org.opengis.
* referencing.crs.CoordinateReferenceSystem)
*/
@Override
public void project(CoordinateReferenceSystem crs) throws VizException {
for (ResourcePair rp : getResourceList()) {
rp.getResource().project(crs);
}
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.core.rsc.IResourceDataChanged#resourceChanged(com
* .raytheon.uf.viz.core.rsc.IResourceDataChanged.ChangeType,
* java.lang.Object)
*/
@Override
public void resourceChanged(ChangeType type, Object object) {
if (type == ChangeType.CAPABILITY) {
if (object instanceof ImagingCapability) {
ImagingCapability imaging = (ImagingCapability) object;
image.setBrightness(imaging.getBrightness());
image.setContrast(imaging.getContrast());
image.setInterpolated(imaging.isInterpolationState());
}
}
}
}

View file

@ -0,0 +1,318 @@
/**
* 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.viz.truecolor.rsc;
import javax.xml.bind.annotation.XmlElement;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.AbstractNameGenerator;
import com.raytheon.uf.viz.core.rsc.AbstractResourceData;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.IResourceGroup;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.ResourceList;
import com.raytheon.uf.viz.core.rsc.ResourceProperties;
import com.raytheon.uf.viz.truecolor.extension.ITrueColorImagingExtension.Channel;
/**
* {@link TrueColorResourceGroup} resource data. Contains a red/blue/green
* channel resource and a name. Sub resources that .equal each other will be
* replaced with the first reference to save time
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 6, 2012 mschenke Initial creation
*
* </pre>
*
* @author mschenke
* @version 1.0
*/
public class TrueColorResourceGroupData extends AbstractResourceData implements
IResourceGroup {
private ResourceList resourceList;
@XmlElement
private String groupName;
@XmlElement
private AbstractResourceData redChannelResource;
@XmlElement
private AbstractResourceData greenChannelResource;
@XmlElement
private AbstractResourceData blueChannelResource;
public TrueColorResourceGroupData() {
nameGenerator = new AbstractNameGenerator() {
@Override
public String getName(AbstractVizResource<?, ?> resource) {
return groupName;
}
};
}
/*
* (non-Javadoc)
*
* @see com.raytheon.uf.viz.core.rsc.IResourceGroup#getResourceList()
*/
@Override
public ResourceList getResourceList() {
if (resourceList == null) {
resourceList = new ResourceList();
// Initialize the resource list, if any of the resources equal each
// other, replace with reference instead of copy to save memory
if (redChannelResource != null) {
addResource(redChannelResource);
}
if (greenChannelResource != null) {
if (greenChannelResource.equals(redChannelResource)) {
greenChannelResource = redChannelResource;
} else {
addResource(greenChannelResource);
}
}
if (blueChannelResource != null) {
if (blueChannelResource.equals(redChannelResource)) {
blueChannelResource = redChannelResource;
} else if (blueChannelResource.equals(greenChannelResource)) {
blueChannelResource = greenChannelResource;
} else {
addResource(blueChannelResource);
}
}
}
return resourceList;
}
private void addResource(AbstractResourceData resourceData) {
ResourcePair rp = new ResourcePair();
rp.setResourceData(resourceData);
rp.setLoadProperties(new LoadProperties());
rp.setProperties(new ResourceProperties());
resourceList.add(rp);
}
/**
* Removes a resource from the resource data
*
* @param rp
*/
public void removeResource(ResourcePair rp) {
resourceList.remove(rp);
if (rp.getResourceData() == redChannelResource) {
redChannelResource = null;
}
if (rp.getResourceData() == greenChannelResource) {
greenChannelResource = null;
}
if (rp.getResourceData() == blueChannelResource) {
blueChannelResource = null;
}
}
/**
* Get the composite name of the resource pair (Red, Red/Green, Blue, etc)
*
* @param rp
* @return
*/
public String getCompositeName(ResourcePair rp) {
String name = "";
if (rp.getResourceData() == redChannelResource) {
name += "Red";
}
if (rp.getResourceData() == greenChannelResource) {
if (name.isEmpty() == false) {
name += "/";
}
name += "Green";
}
if (rp.getResourceData() == blueChannelResource) {
if (name.isEmpty() == false) {
name += "/";
}
name += "Blue";
}
return name;
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.core.rsc.AbstractResourceData#construct(com.raytheon
* .uf.viz.core.rsc.LoadProperties,
* com.raytheon.uf.viz.core.drawables.IDescriptor)
*/
@Override
public AbstractVizResource<?, ?> construct(LoadProperties loadProperties,
IDescriptor descriptor) throws VizException {
return new TrueColorResourceGroup(this, loadProperties);
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.core.rsc.AbstractResourceData#update(java.lang.Object
* )
*/
@Override
public void update(Object updateData) {
// Nothing to update, updates will be handled by sub resources
}
/**
* Get the resource pair associated with the {@link Channel}
*
* @param channel
* @return
*/
public ResourcePair getResource(Channel channel) {
AbstractResourceData toCheckFor = null;
switch (channel) {
case RED:
toCheckFor = redChannelResource;
break;
case GREEN:
toCheckFor = greenChannelResource;
break;
case BLUE:
toCheckFor = blueChannelResource;
break;
}
for (ResourcePair rp : getResourceList()) {
if (rp.getResourceData() == toCheckFor) {
return rp;
}
}
return null;
}
/**
* @return the groupName
*/
public String getGroupName() {
return groupName;
}
/**
* @param groupName
* the groupName to set
*/
public void setGroupName(String groupName) {
this.groupName = groupName;
}
/**
* @return the redChannelResource
*/
public AbstractResourceData getRedChannelResource() {
return redChannelResource;
}
/**
* @param redChannelResource
* the redChannelResource to set
*/
public void setRedChannelResource(AbstractResourceData redChannelResource) {
this.redChannelResource = redChannelResource;
}
/**
* @return the greenChannelResource
*/
public AbstractResourceData getGreenChannelResource() {
return greenChannelResource;
}
/**
* @param greenChannelResource
* the greenChannelResource to set
*/
public void setGreenChannelResource(
AbstractResourceData greenChannelResource) {
this.greenChannelResource = greenChannelResource;
}
/**
* @return the blueChannelResource
*/
public AbstractResourceData getBlueChannelResource() {
return blueChannelResource;
}
/**
* @param blueChannelResource
* the blueChannelResource to set
*/
public void setBlueChannelResource(AbstractResourceData blueChannelResource) {
this.blueChannelResource = blueChannelResource;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TrueColorResourceGroupData other = (TrueColorResourceGroupData) obj;
if (blueChannelResource == null) {
if (other.blueChannelResource != null)
return false;
} else if (!blueChannelResource.equals(other.blueChannelResource))
return false;
if (greenChannelResource == null) {
if (other.greenChannelResource != null)
return false;
} else if (!greenChannelResource.equals(other.greenChannelResource))
return false;
if (groupName == null) {
if (other.groupName != null)
return false;
} else if (!groupName.equals(other.groupName))
return false;
if (redChannelResource == null) {
if (other.redChannelResource != null)
return false;
} else if (!redChannelResource.equals(other.redChannelResource))
return false;
return true;
}
}

View file

@ -36,7 +36,7 @@ void main(void) {
}
} else {
float naturalValue = ((rawValue * (naturalMax - naturalMin)) + naturalMin);
index = findIndex(naturalValue, cmapMin, cmapMax);
index = capIndex(findIndex(naturalValue, cmapMin, cmapMax));
}
// Lookup color in colorMap for index

View file

@ -96,9 +96,9 @@ float findFloatIndexLog(float rawValue, float cmapMin, float cmapMax, int mirror
}
/**
* Given a raw data value linearly determine the index(0-1) into cmapMin/cmapMax
* Given a raw data value linearly determine the index into cmapMin/cmapMax.
* Index is not capped and may be outsite of 0-1
*/
float findIndex(float rawValue, float cmapMin, float cmapMax) {
float index = ((rawValue - cmapMin) / abs(cmapMax-cmapMin));
return capIndex(index);
return ((rawValue - cmapMin) / abs(cmapMax-cmapMin));
}

View file

@ -74,6 +74,11 @@ public abstract class AbstractGLColorMapDataFormat {
*/
public abstract Number getValue(int x, int y, GLColorMapData data);
/**
* Get the number of bytes each pixel takes up
*
* @return
*/
public abstract int getBytesPerPixel();
/**

View file

@ -112,4 +112,8 @@ public class GLColorMapData {
public int[] getDimensions() {
return dimensions;
}
public int getBytesPerPixel() {
return dataFormat.getBytesPerPixel();
}
}

View file

@ -250,7 +250,7 @@ public class GLCMTextureData implements IImageCacheable {
public int getSize() {
if (data != null) {
int[] dimensions = data.getDimensions();
int totalSize = 1;
int totalSize = data.getBytesPerPixel();
for (int i = 0; i < dimensions.length; ++i) {
totalSize *= dimensions[i];
}