iter = resourcesToInstantiate.iterator();
+ while (iter.hasNext()) {
+ if (iter.next() == rp) {
+ iter.remove();
+ }
+ }
}
try {
diff --git a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/rsc/capabilities/MultiChannelCapability.java b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/rsc/capabilities/MultiChannelCapability.java
deleted file mode 100644
index 259b6d6799..0000000000
--- a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/rsc/capabilities/MultiChannelCapability.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/**
- * 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.rsc.capabilities;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.adapters.XmlAdapter;
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-import com.raytheon.uf.viz.core.drawables.ColorMapParameters;
-import com.raytheon.uf.viz.core.drawables.ext.colormap.IMultiChannelImageExtension.Channel;
-import com.raytheon.uf.viz.core.drawables.ext.colormap.IMultiChannelImageExtension.ChannelData;
-
-/**
- * Capability for multi channel imagery
- *
- *
- *
- * SOFTWARE HISTORY
- *
- * Date Ticket# Engineer Description
- * ------------ ---------- ----------- --------------------------
- * Dec 20, 2011 mschenke Initial creation
- *
- *
- *
- * @author mschenke
- * @version 1.0
- */
-@XmlAccessorType(XmlAccessType.NONE)
-public class MultiChannelCapability extends AbstractCapability {
-
- @XmlAccessorType(XmlAccessType.NONE)
- public static class ChannelSerializable {
-
- @XmlAttribute
- private String name;
-
- @XmlAttribute
- private Channel channel;
-
- @XmlAttribute
- private boolean invert;
-
- @XmlElement
- private float dataMin;
-
- @XmlElement
- private float dataMax;
-
- @XmlElement
- private float cmapMin;
-
- @XmlElement
- private float cmapMax;
-
- public ChannelSerializable() {
-
- }
-
- public ChannelSerializable(Channel channel, ChannelData channelData) {
- this.channel = channel;
- this.name = channelData.name;
- this.invert = channelData.invert;
- ColorMapParameters params = channelData.parameters;
- this.dataMin = params.getDataMin();
- this.dataMax = params.getDataMax();
- this.cmapMin = params.getColorMapMin();
- this.cmapMax = params.getColorMapMax();
- }
-
- /**
- * @return the name
- */
- public String getName() {
- return name;
- }
-
- /**
- * @param name
- * the name to set
- */
- public void setName(String name) {
- this.name = name;
- }
-
- /**
- * @return the channel
- */
- public Channel getChannel() {
- return channel;
- }
-
- /**
- * @param channel
- * the channel to set
- */
- public void setChannel(Channel channel) {
- this.channel = channel;
- }
-
- /**
- * @return the dataMin
- */
- public float getDataMin() {
- return dataMin;
- }
-
- /**
- * @param dataMin
- * the dataMin to set
- */
- public void setDataMin(float dataMin) {
- this.dataMin = dataMin;
- }
-
- /**
- * @return the dataMax
- */
- public float getDataMax() {
- return dataMax;
- }
-
- /**
- * @param dataMax
- * the dataMax to set
- */
- public void setDataMax(float dataMax) {
- this.dataMax = dataMax;
- }
-
- /**
- * @return the cmapMin
- */
- public float getCmapMin() {
- return cmapMin;
- }
-
- /**
- * @param cmapMin
- * the cmapMin to set
- */
- public void setCmapMin(float cmapMin) {
- this.cmapMin = cmapMin;
- }
-
- /**
- * @return the cmapMax
- */
- public float getCmapMax() {
- return cmapMax;
- }
-
- /**
- * @param cmapMax
- * the cmapMax to set
- */
- public void setCmapMax(float cmapMax) {
- this.cmapMax = cmapMax;
- }
-
- /**
- * @return the invert
- */
- public boolean isInvert() {
- return invert;
- }
-
- /**
- * @param invert
- * the invert to set
- */
- public void setInvert(boolean invert) {
- this.invert = invert;
- }
-
- }
-
- public static class Marshaller extends
- XmlAdapter> {
-
- /*
- * (non-Javadoc)
- *
- * @see
- * javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang
- * .Object)
- */
- @Override
- public HashMap unmarshal(ChannelSerializable[] v)
- throws Exception {
- HashMap channelMap = new HashMap();
- for (ChannelSerializable cs : v) {
- ColorMapParameters params = new ColorMapParameters();
- params.setDataMin(cs.dataMin);
- params.setDataMax(cs.dataMax);
- params.setColorMapMin(cs.cmapMin);
- params.setColorMapMax(cs.cmapMax);
- channelMap.put(cs.channel, new ChannelData(cs.name, params,
- cs.invert));
- }
- return channelMap;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object
- * )
- */
- @Override
- public ChannelSerializable[] marshal(HashMap v)
- throws Exception {
- ChannelSerializable[] serializable = new ChannelSerializable[v
- .size()];
- int i = 0;
- for (Entry entry : v.entrySet()) {
- ChannelSerializable cs = new ChannelSerializable(
- entry.getKey(), entry.getValue());
- serializable[i++] = cs;
- }
- return serializable;
- }
-
- }
-
- @XmlJavaTypeAdapter(value = Marshaller.class)
- private HashMap channelMap = new HashMap();
-
- private String[] names;
-
- /**
- * @return the names
- */
- public String[] getNames() {
- return names;
- }
-
- /**
- * @param names
- * the names to set
- */
- public void setNames(String[] names) {
- this.names = names;
- }
-
- /**
- * @return the channelMap
- */
- public Map getChannelMap() {
- return channelMap;
- }
-
- /**
- * @param channelMap
- * the channelMap to set
- */
- public void setChannelMap(HashMap channelMap) {
- if (channelMap == null) {
- channelMap = new HashMap();
- }
- this.channelMap = channelMap;
- capabilityChanged();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.raytheon.uf.viz.core.rsc.capabilities.AbstractCapability#
- * capabilityChanged()
- */
- @Override
- public void capabilityChanged() {
- super.capabilityChanged();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.raytheon.uf.viz.core.rsc.capabilities.AbstractCapability#clone()
- */
- @Override
- public AbstractCapability clone() {
- MultiChannelCapability cap = new MultiChannelCapability();
- cap.channelMap = new HashMap(channelMap);
- return cap;
- }
-
-}
diff --git a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/tile/Tile.java b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/tile/Tile.java
new file mode 100644
index 0000000000..bdf90b372f
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/tile/Tile.java
@@ -0,0 +1,195 @@
+/**
+ * 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)}
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 8, 2012 mschenke Initial creation
+ *
+ *
+ *
+ * @author mschenke
+ * @version 1.0
+ */
+
+public class Tile {
+
+ public final int tileLevel;
+
+ public final GridGeometry2D tileGeometry;
+
+ public final List 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(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 crsContains(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 crsContains(Coordinate c) {
+ return crsContains(c.x, c.y);
+ }
+
+ /**
+ * Checks to see if the x/y coordinate is contained by the Tile's grid
+ * envelope
+ *
+ * @param gridX
+ * @param gridY
+ * @return
+ */
+ public boolean gridContains(int gridX, int gridY) {
+ GridEnvelope ge = tileGeometry.getGridRange();
+ return ge.getLow(0) <= gridX && ge.getHigh(0) >= gridX
+ && ge.getLow(1) <= gridY && ge.getHigh(1) >= gridY;
+ }
+
+ /*
+ * (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;
+ }
+
+}
diff --git a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/tile/TileLevel.java b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/tile/TileLevel.java
new file mode 100644
index 0000000000..a6cf76a71c
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/tile/TileLevel.java
@@ -0,0 +1,418 @@
+/**
+ * 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.
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 8, 2012 mschenke Initial creation
+ *
+ *
+ *
+ * @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) {
+ double xIdx = x / tileSize;
+ double yIdx = y / tileSize;
+ if (xIdx >= 0 && yIdx >= 0 && xIdx < getNumXTiles()
+ && yIdx < getNumYTiles()) {
+ Tile tile = getTile((int) xIdx, (int) yIdx);
+ if (tile.gridContains((int) x, (int) y)) {
+ return tile;
+ }
+ }
+ 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 borderPoints = new ArrayList(
+ 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 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);
+ }
+ }
+}
diff --git a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/tile/TileSetRenderable.java b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/tile/TileSetRenderable.java
new file mode 100644
index 0000000000..5dc7b02223
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/tile/TileSetRenderable.java
@@ -0,0 +1,462 @@
+/**
+ * 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
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 8, 2012 mschenke Initial creation
+ *
+ *
+ *
+ * @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 jobMap = new ConcurrentHashMap();
+
+ /** Image map for tiles */
+ protected Map imageMap = new HashMap();
+
+ /** 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 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 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 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 intersecting = tileSet.getIntersectingTiles(level,
+ extent);
+
+ // These Tiles still need images created for them
+ List tilesNeedingImage = new ArrayList(intersecting.size());
+ List drawableImages = new ArrayList(
+ 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 {
+ target.setNeedsRefresh(true);
+ // 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 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 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;
+ }
+}
diff --git a/cave/com.raytheon.uf.viz.d2d.ui/src/com/raytheon/uf/viz/d2d/ui/map/actions/ClearAction.java b/cave/com.raytheon.uf.viz.d2d.ui/src/com/raytheon/uf/viz/d2d/ui/map/actions/ClearAction.java
index 5df6c34b9f..37aa7319b3 100644
--- a/cave/com.raytheon.uf.viz.d2d.ui/src/com/raytheon/uf/viz/d2d/ui/map/actions/ClearAction.java
+++ b/cave/com.raytheon.uf.viz.d2d.ui/src/com/raytheon/uf/viz/d2d/ui/map/actions/ClearAction.java
@@ -32,10 +32,12 @@ import com.raytheon.uf.viz.core.IDisplayPane;
import com.raytheon.uf.viz.core.IDisplayPaneContainer;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.globals.VizGlobalsManager;
+import com.raytheon.uf.viz.core.maps.display.VizMapEditor;
import com.raytheon.uf.viz.core.status.StatusConstants;
import com.raytheon.uf.viz.d2d.ui.Activator;
import com.raytheon.viz.ui.EditorUtil;
import com.raytheon.viz.ui.HistoryList;
+import com.raytheon.viz.ui.editor.AbstractEditor;
import com.raytheon.viz.ui.editor.IMultiPaneEditor;
/**
@@ -60,7 +62,8 @@ import com.raytheon.viz.ui.editor.IMultiPaneEditor;
* @version 1
*/
public class ClearAction extends AbstractHandler {
- private static final transient IUFStatusHandler statusHandler = UFStatus.getHandler(ClearAction.class);
+ private static final transient IUFStatusHandler statusHandler = UFStatus
+ .getHandler(ClearAction.class);
@Override
public Object execute(ExecutionEvent arg0) throws ExecutionException {
@@ -69,15 +72,14 @@ public class ClearAction extends AbstractHandler {
new NewMapEditor().execute(null);
return null;
}
-
- if (!(part instanceof IDisplayPaneContainer)) {
- return null;
- }
-
try {
- HistoryList.getInstance().refreshLatestBundle();
- clear((IDisplayPaneContainer) part);
- HistoryList.getInstance().addBundle();
+ if (part instanceof IDisplayPaneContainer) {
+ HistoryList.getInstance().refreshLatestBundle();
+ clear(part);
+ HistoryList.getInstance().addBundle();
+ } else {
+ clear(part);
+ }
} catch (VizException e) {
throw new ExecutionException("Error during clear", e);
}
@@ -85,26 +87,19 @@ public class ClearAction extends AbstractHandler {
return null;
}
- /**
- * Clears the map
- *
- * The map is cleared with either the provided default-procedure.xml or a
- * default world map, if no default-bundle.xml is available.
- *
- * @param target
- * the graphics target
- * @return the map descriptor prepared
- * @throws VizException
- */
- public static void clear(IDisplayPaneContainer container)
- throws VizException {
- if (container instanceof IMultiPaneEditor) {
- ((IMultiPaneEditor) container).clear();
+ public static void clear(IEditorPart part) throws VizException {
+ if (part instanceof VizMapEditor) {
+ ((VizMapEditor) part).clear();
+ } else if (part instanceof AbstractEditor) {
+ // AbstractEditor asks the user if they are sure if we pass in
+ // save=true, if the user clicked clear than they must be sure so
+ // pass in save=false.
+ part.getSite().getPage().closeEditor(part, false);
} else {
- for (IDisplayPane displayPane : container.getDisplayPanes()) {
- displayPane.clear();
- }
+ // Give other editors a chance to save.
+ part.getSite().getPage().closeEditor(part, true);
}
+
if (EditorUtil.getActiveEditor() == null) {
try {
new NewMapEditor().execute(null);
diff --git a/cave/com.raytheon.uf.viz.derivparam.python/src/com/raytheon/uf/viz/derivparam/python/MasterDerivScript.java b/cave/com.raytheon.uf.viz.derivparam.python/src/com/raytheon/uf/viz/derivparam/python/MasterDerivScript.java
index 5cc4fd004a..f98f52cad2 100644
--- a/cave/com.raytheon.uf.viz.derivparam.python/src/com/raytheon/uf/viz/derivparam/python/MasterDerivScript.java
+++ b/cave/com.raytheon.uf.viz.derivparam.python/src/com/raytheon/uf/viz/derivparam/python/MasterDerivScript.java
@@ -385,11 +385,12 @@ public class MasterDerivScript extends PythonInterpreter {
private void setDataRecordArg(String argName, IDataRecord argValue)
throws JepException {
boolean reshape = true;
+ long[] sizes = argValue.getSizes();
if (argValue instanceof FloatDataRecord) {
FloatDataRecord record = (FloatDataRecord) argValue;
- if (record.getDimension() == 2) {
- jep.setNumeric(argName, record.getFloatData(),
- (int) record.getSizes()[0], (int) record.getSizes()[1]);
+ if (sizes.length == 2) {
+ jep.setNumeric(argName, record.getFloatData(), (int) sizes[0],
+ (int) sizes[1]);
reshape = false;
} else {
evaluateArgument(argName, record.getFloatData());
@@ -401,18 +402,18 @@ public class MasterDerivScript extends PythonInterpreter {
jep.eval("del globals()['numpy']");
} else if (argValue instanceof IntegerDataRecord) {
IntegerDataRecord record = (IntegerDataRecord) argValue;
- if (record.getDimension() == 2) {
- jep.setNumeric(argName, record.getIntData(),
- (int) record.getSizes()[0], (int) record.getSizes()[1]);
+ if (sizes.length == 2) {
+ jep.setNumeric(argName, record.getIntData(), (int) sizes[0],
+ (int) sizes[1]);
reshape = false;
} else {
evaluateArgument(argName, record.getIntData());
}
} else if (argValue instanceof ByteDataRecord) {
ByteDataRecord record = (ByteDataRecord) argValue;
- if (record.getDimension() == 2) {
- jep.setNumeric(argName, record.getByteData(),
- (int) record.getSizes()[0], (int) record.getSizes()[1]);
+ if (sizes.length == 2) {
+ jep.setNumeric(argName, record.getByteData(), (int) sizes[0],
+ (int) sizes[1]);
reshape = false;
} else {
evaluateArgument(argName, record.getByteData());
diff --git a/cave/com.raytheon.uf.viz.derivparam/src/com/raytheon/uf/viz/derivparam/inv/AbstractInventory.java b/cave/com.raytheon.uf.viz.derivparam/src/com/raytheon/uf/viz/derivparam/inv/AbstractInventory.java
index 7046d034fa..c5079e13d2 100644
--- a/cave/com.raytheon.uf.viz.derivparam/src/com/raytheon/uf/viz/derivparam/inv/AbstractInventory.java
+++ b/cave/com.raytheon.uf.viz.derivparam/src/com/raytheon/uf/viz/derivparam/inv/AbstractInventory.java
@@ -171,11 +171,11 @@ public abstract class AbstractInventory implements DerivParamUpdateListener {
protected Map> sourceAliases = new HashMap>();
- private List allSources;
+ protected List allSources;
- private List allParameters;
+ protected List allParameters;
- private List allLevels;
+ protected List allLevels;
/**
* A call to this method assigns the passed grid tree to the original grid
diff --git a/cave/com.raytheon.uf.viz.kml.export.feature/.project b/cave/com.raytheon.uf.viz.kml.export.feature/.project
new file mode 100644
index 0000000000..31141696dc
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export.feature/.project
@@ -0,0 +1,11 @@
+
+
+ com.raytheon.uf.viz.kml.export.feature
+
+
+
+
+
+
+
+
diff --git a/cave/com.raytheon.uf.viz.kml.export.feature/build.properties b/cave/com.raytheon.uf.viz.kml.export.feature/build.properties
new file mode 100644
index 0000000000..64f93a9f0b
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export.feature/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/cave/com.raytheon.uf.viz.kml.export.feature/feature.xml b/cave/com.raytheon.uf.viz.kml.export.feature/feature.xml
new file mode 100644
index 0000000000..2a68be49e2
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export.feature/feature.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ [Enter Feature Description here.]
+
+
+
+ [Enter Copyright Description here.]
+
+
+
+ [Enter License Description here.]
+
+
+
+
+
+
+
diff --git a/cave/com.raytheon.uf.viz.kml.export/.classpath b/cave/com.raytheon.uf.viz.kml.export/.classpath
new file mode 100644
index 0000000000..ad32c83a78
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/cave/com.raytheon.uf.viz.kml.export/.project b/cave/com.raytheon.uf.viz.kml.export/.project
new file mode 100644
index 0000000000..ac1ac33ee2
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/.project
@@ -0,0 +1,28 @@
+
+
+ com.raytheon.uf.viz.kml.export
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/cave/com.raytheon.uf.viz.kml.export/.settings/org.eclipse.jdt.core.prefs b/cave/com.raytheon.uf.viz.kml.export/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..9a05f5f33c
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Wed May 30 15:50:31 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
diff --git a/cave/com.raytheon.uf.viz.kml.export/META-INF/MANIFEST.MF b/cave/com.raytheon.uf.viz.kml.export/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..f5ee20d888
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/META-INF/MANIFEST.MF
@@ -0,0 +1,27 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: KML Export
+Bundle-SymbolicName: com.raytheon.uf.viz.kml.export;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: com.raytheon.uf.viz.kml.export.Activator
+Eclipse-RegisterBuddy: com.raytheon.uf.common.serialization
+Bundle-Vendor: RAYTHEON
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ com.raytheon.uf.common.serialization;bundle-version="1.12.1174",
+ org.geotools;bundle-version="2.6.4",
+ com.raytheon.uf.viz.core;bundle-version="1.12.1174",
+ com.raytheon.uf.viz.core.rsc;bundle-version="1.0.0",
+ com.raytheon.uf.common.geospatial;bundle-version="1.12.1174",
+ com.raytheon.uf.common.colormap;bundle-version="1.12.1174",
+ com.raytheon.uf.common.time;bundle-version="1.12.1174",
+ com.raytheon.viz.ui;bundle-version="1.12.1174",
+ com.raytheon.uf.viz.core.maps;bundle-version="1.12.1174",
+ de.micromata.opengis.kml;bundle-version="1.0.0",
+ com.raytheon.viz.pointdata;bundle-version="1.12.1174",
+ com.raytheon.viz.radar;bundle-version="1.12.1174",
+ com.raytheon.uf.common.dataplugin.radar;bundle-version="1.0.0",
+ javax.measure;bundle-version="1.0.0",
+ javax.vecmath;bundle-version="1.3.1"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
diff --git a/cave/com.raytheon.uf.viz.kml.export/build.properties b/cave/com.raytheon.uf.viz.kml.export/build.properties
new file mode 100644
index 0000000000..e9863e281e
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
diff --git a/cave/com.raytheon.uf.viz.kml.export/plugin.xml b/cave/com.raytheon.uf.viz.kml.export/plugin.xml
new file mode 100644
index 0000000000..be0787b52b
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/plugin.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/Activator.java b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/Activator.java
new file mode 100644
index 0000000000..565c20c8ce
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/Activator.java
@@ -0,0 +1,50 @@
+package com.raytheon.uf.viz.kml.export;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * 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.kml.export"; //$NON-NLS-1$
+
+ // 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;
+ }
+
+}
diff --git a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportDialog.java b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportDialog.java
new file mode 100644
index 0000000000..da710ea2ad
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportDialog.java
@@ -0,0 +1,606 @@
+/**
+ * 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.kml.export;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowData;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+
+import com.raytheon.uf.viz.core.drawables.ResourcePair;
+import com.raytheon.uf.viz.kml.export.KmlExportOptions.KmlExportTimeMode;
+import com.raytheon.viz.ui.dialogs.CaveSWTDialog;
+
+/**
+ * Allow user to select options for export and starts the KmlExportJob.
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 5, 2012 bsteffen Initial creation
+ *
+ *
+ *
+ * @author bsteffen
+ * @version 1.0
+ */
+
+public class KmlExportDialog extends CaveSWTDialog {
+
+ private final KmlExportOptions options;
+
+ private Text locationText;
+
+ private Tree productTree;
+
+ private Button exportHiddenButton;
+
+ private Button exportMapsButton;
+
+ private Button shadeEarthButton;
+
+ private Button setTimesButton;
+
+ private Button timeSpanButton;
+
+ private Button timeStampButton;
+
+ private Button fillPlotsButton;
+
+ private Button selectedFramesButton;
+
+ private Button currentFramesButton;
+
+ private Button allFramesButton;
+
+ private Text framesFromText;
+
+ private Text framesToText;
+
+ public KmlExportDialog(Shell shell, KmlExportOptions options) {
+ super(shell, SWT.RESIZE | SWT.DIALOG_TRIM);
+ this.setText("Export KML");
+ this.options = options;
+ }
+
+ @Override
+ protected void initializeComponents(Shell shell) {
+
+ Composite leftComposite = new Composite(shell, SWT.NONE);
+ leftComposite.setLayoutData(new GridData(SWT.NONE, SWT.FILL, false,
+ true));
+ RowLayout layout = new RowLayout(SWT.VERTICAL);
+ layout.fill = true;
+ leftComposite.setLayout(layout);
+
+ Group locationGroup = new Group(leftComposite, SWT.NONE);
+ initializeLocationGroup(locationGroup);
+
+ Group framesGroup = new Group(leftComposite, SWT.NONE);
+ initializeFramesGroup(framesGroup);
+
+ Group optionsGroup = new Group(leftComposite, SWT.NONE);
+ initializeOptionsGroup(optionsGroup);
+
+ // Group timeOptionsGroup = new Group(leftComposite, SWT.NONE);
+ // initializeTimeOptionsGroup(timeOptionsGroup);
+
+ Group productsGroup = new Group(shell, SWT.NONE);
+ GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ gridData.widthHint = 250;
+ productsGroup.setLayoutData(gridData);
+ initializeProductsGroup(productsGroup);
+
+ Composite buttonComposite = new Composite(shell, SWT.NONE);
+ gridData = new GridData(SWT.FILL, SWT.NONE, true, false, 2, 1);
+ gridData.horizontalAlignment = SWT.CENTER;
+ buttonComposite.setLayoutData(gridData);
+ initializeButtons(buttonComposite);
+
+ populateProductTree();
+ if (productTree.getItemCount() == 0) {
+ // for the intial load change this option so there is something to
+ // export.
+ exportMapsButton.setSelection(true);
+ populateProductTree();
+ }
+
+ shell.pack();
+ shell.setMinimumSize(shell.getSize());
+ }
+
+ @Override
+ protected Layout constructShellLayout() {
+ GridLayout mainLayout = new GridLayout(2, false);
+ mainLayout.marginHeight = 3;
+ mainLayout.marginWidth = 3;
+ return mainLayout;
+ }
+
+ protected void initializeLocationGroup(Group group) {
+ group.setLayout(new GridLayout(2, false));
+ group.setText("Export Location");
+ locationText = new Text(group, SWT.BORDER);
+ GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ gridData.widthHint = 250;
+ locationText.setLayoutData(gridData);
+ locationText.setText(options.getKmzFileLocation().getAbsolutePath());
+ Button button = new Button(group, SWT.PUSH);
+ button.setText("Browse ...");
+ button.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent event) {
+ selectDestinationFile();
+ }
+ });
+ }
+
+ protected void initializeFramesGroup(Group group) {
+ group.setLayout(new GridLayout(5, false));
+ group.setText("Export Location");
+
+ allFramesButton = new Button(group, SWT.RADIO);
+ allFramesButton.setText("All Frames");
+ GridData gridData = new GridData();
+ gridData.horizontalSpan = 5;
+ allFramesButton.setLayoutData(gridData);
+ allFramesButton.setSelection(true);
+
+ currentFramesButton = new Button(group, SWT.RADIO);
+ currentFramesButton.setText("Current Frame");
+ gridData = new GridData();
+ gridData.horizontalSpan = 5;
+ currentFramesButton.setLayoutData(gridData);
+
+ selectedFramesButton = new Button(group, SWT.RADIO);
+ selectedFramesButton.setText("Frames");
+
+ new Label(group, SWT.NONE).setText("from:");
+ framesFromText = new Text(group, SWT.BORDER);
+ gridData = new GridData();
+ gridData.widthHint = 24;
+ framesFromText.setLayoutData(gridData);
+ framesFromText.setEnabled(false);
+ framesFromText.setText("1");
+ new Label(group, SWT.NONE).setText("to:");
+ framesToText = new Text(group, SWT.BORDER);
+ gridData = new GridData();
+ gridData.widthHint = 24;
+ framesToText.setLayoutData(gridData);
+ framesToText.setEnabled(false);
+ int numFrames = 1;
+ for (KmlPane pane : options.getPanes()) {
+ int frames = pane.getDisplay().getDescriptor().getFramesInfo()
+ .getFrameCount();
+ numFrames = Math.max(frames, numFrames);
+ }
+ framesToText.setText(Integer.toString(numFrames));
+ selectedFramesButton.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ framesToText.setEnabled(selectedFramesButton.getSelection());
+ framesFromText.setEnabled(selectedFramesButton.getSelection());
+ }
+
+ });
+ }
+
+ protected void initializeOptionsGroup(Group group) {
+ group.setText("Other Options");
+ group.setLayout(new RowLayout(SWT.VERTICAL));
+ exportHiddenButton = new Button(group, SWT.CHECK);
+ exportHiddenButton.setText("Export Hidden");
+ exportHiddenButton.setSelection(true);
+ exportHiddenButton
+ .setToolTipText("Include hidden products in the selection of products to export.");
+ exportHiddenButton.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ populateProductTree();
+ }
+ });
+ exportMapsButton = new Button(group, SWT.CHECK);
+ exportMapsButton.setText("Export Maps");
+ exportMapsButton.setSelection(false);
+ exportMapsButton
+ .setToolTipText("Include maps in the selection of products to export.");
+ exportMapsButton.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ populateProductTree();
+ }
+ });
+ shadeEarthButton = new Button(group, SWT.CHECK);
+ shadeEarthButton.setText("Shade Earth");
+ shadeEarthButton.setSelection(options.isShadeEarth());
+ shadeEarthButton
+ .setToolTipText("Hides the Google Earth surface imagery.");
+ fillPlotsButton = new Button(group, SWT.CHECK);
+ fillPlotsButton.setText("Show Background Tiles");
+ fillPlotsButton.setSelection(options.isFillPlotBackground());
+ fillPlotsButton
+ .setToolTipText("Displays an opaque background tile behind point observations");
+ }
+
+ protected void initializeTimeOptionsGroup(Group group) {
+ group.setText("Time Options");
+ group.setLayout(new RowLayout(SWT.VERTICAL));
+ setTimesButton = new Button(group, SWT.CHECK);
+ setTimesButton.setText("Set KML Time");
+ setTimesButton
+ .setSelection(options.getTimeMode() != KmlExportTimeMode.NONE);
+ setTimesButton
+ .setToolTipText("Causes Google Earth to display a time slider.");
+ Composite timeComposite = new Composite(group, SWT.NONE);
+ RowLayout layout = new RowLayout(SWT.VERTICAL);
+ layout.marginLeft = 20;
+ timeComposite.setLayout(layout);
+ timeSpanButton = new Button(timeComposite, SWT.RADIO);
+ timeSpanButton.setText("Time Span");
+ timeSpanButton
+ .setSelection(options.getTimeMode() != KmlExportTimeMode.TIME_STAMP);
+ timeSpanButton.setEnabled(setTimesButton.getSelection());
+ timeSpanButton
+ .setToolTipText("Allow products to be visible over a time range.");
+ timeStampButton = new Button(timeComposite, SWT.RADIO);
+ timeStampButton.setText("Time Stamp");
+ timeStampButton
+ .setSelection(options.getTimeMode() == KmlExportTimeMode.TIME_STAMP);
+ timeStampButton.setEnabled(setTimesButton.getSelection());
+ timeStampButton
+ .setToolTipText("Makes each product visible only at its exact valid time.");
+ setTimesButton.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ timeSpanButton.setEnabled(setTimesButton.getSelection());
+ timeStampButton.setEnabled(setTimesButton.getSelection());
+ }
+
+ });
+ }
+
+ protected void initializeProductsGroup(Group group) {
+ group.setText("Products");
+ group.setLayout(new FillLayout());
+ productTree = new Tree(group, SWT.CHECK);
+ productTree.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (e.detail == SWT.CHECK) {
+ treeItemChecked((TreeItem) e.item);
+ }
+ super.widgetSelected(e);
+ }
+
+ });
+ }
+
+ protected void initializeButtons(Composite comp) {
+ comp.setLayout(new RowLayout(SWT.HORIZONTAL));
+ Button okButton = new Button(comp, SWT.PUSH);
+ okButton.setText("OK");
+ okButton.setLayoutData(new RowData(100, SWT.DEFAULT));
+ okButton.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ okPressed();
+ }
+
+ });
+
+ Button cancelButton = new Button(comp, SWT.PUSH);
+ cancelButton.setText("Cancel");
+ cancelButton.setLayoutData(new RowData(100, SWT.DEFAULT));
+ cancelButton.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ close();
+ }
+
+ });
+ }
+
+ protected void populateProductTree() {
+ boolean exportMaps = exportMapsButton.getSelection();
+ boolean exportHidden = exportHiddenButton.getSelection();
+ if (options.isSinglePane()) {
+ List rscList = options.getSinglPane().getResources(
+ exportMaps, exportHidden);
+ populateProductSubTree(rscList, null);
+ } else {
+ int index = 0;
+ for (KmlPane pane : options.getPanes()) {
+ List rscList = pane.getResources(exportMaps,
+ exportHidden);
+ TreeItem item = null;
+ if (index < productTree.getItemCount()) {
+ item = productTree.getItem(index);
+ }
+ if (!rscList.isEmpty()) {
+ if (item == null || item.getData() != pane) {
+ item = new TreeItem(productTree, SWT.NONE, index);
+ item.setText("Pane " + (index + 1));
+ item.setData(pane);
+ populateProductSubTree(rscList, item);
+ item.setExpanded(true);
+ } else {
+ populateProductSubTree(rscList, item);
+ }
+ index += 1;
+ } else {
+ if (item != null && item.getData() == pane) {
+ item.dispose();
+ }
+ }
+ }
+ }
+ }
+
+ private void populateProductSubTree(List rscList,
+ final TreeItem parent) {
+ TreeItem[] items = parent != null ? parent.getItems() : productTree
+ .getItems();
+ int itemIndex = 0;
+ int rscIndex = 0;
+ while (itemIndex < items.length || rscIndex < rscList.size()) {
+ TreeItem item = null;
+ if (itemIndex < items.length) {
+ item = items[itemIndex];
+ }
+ ResourcePair pair = null;
+ if (rscIndex < rscList.size()) {
+ pair = rscList.get(rscIndex);
+ }
+ if (item != null && item.getData() == pair) {
+ itemIndex += 1;
+ rscIndex += 1;
+ } else if (item != null && !rscList.contains(item.getData())) {
+ item.dispose();
+ itemIndex += 1;
+ } else {
+ if (parent != null) {
+ item = new TreeItem(parent, SWT.NONE, rscIndex);
+ } else {
+ item = new TreeItem(productTree, SWT.NONE, rscIndex);
+ }
+ String name = pair.getResource().getName();
+ if (name == null) {
+ name = pair.getResource().getClass().getSimpleName();
+ }
+ item.setText(name);
+ item.setData(pair);
+ item.setChecked(true);
+ treeItemChecked(item);
+ rscIndex += 1;
+ }
+ }
+ }
+
+ private void treeItemChecked(TreeItem item) {
+ for (TreeItem ti : item.getItems()) {
+ ti.setChecked(item.getChecked());
+ }
+ TreeItem parent = item.getParentItem();
+ while (parent != null) {
+ parent.setChecked(true);
+ for (TreeItem ti : parent.getItems()) {
+ if (!ti.getChecked()) {
+ parent.setChecked(false);
+ break;
+ }
+ }
+ parent = parent.getParentItem();
+ }
+ }
+
+ protected void selectDestinationFile() {
+ FileDialog fileDialog = new FileDialog(this.shell, SWT.SAVE);
+ File file = new File(locationText.getText());
+ fileDialog.setFileName(file.getName());
+ if (file.getParentFile() != null && file.getParentFile().isDirectory()) {
+ fileDialog.setFilterPath(file.getParent());
+ }
+ fileDialog.setFilterExtensions(new String[] { ".kmz" });
+ fileDialog.open();
+
+ String filterPath = fileDialog.getFilterPath();
+ String selectedFile = fileDialog.getFileName();
+ /*
+ * Ensure that the user has entered a name for the file.
+ */
+ if (selectedFile.equalsIgnoreCase("")) {
+ return;
+ }
+
+ if (!filterPath.endsWith("/")) {
+ filterPath += "/";
+ }
+ String destinationFile = filterPath + selectedFile;
+ this.locationText.setText(destinationFile);
+ this.locationText.setToolTipText(destinationFile);
+ }
+
+ protected void okPressed() {
+ if (allFramesButton.getSelection()) {
+ options.setFirstFrameIndex(Integer.MIN_VALUE);
+ options.setLastFrameIndex(Integer.MAX_VALUE);
+ } else if (currentFramesButton.getSelection()) {
+ for (KmlPane pane : options.getPanes()) {
+ int frame = pane.getDisplay().getDescriptor().getFramesInfo()
+ .getFrameIndex();
+ frame = Math.max(0, frame);
+ options.setFirstFrameIndex(frame);
+ options.setLastFrameIndex(frame + 1);
+ }
+ } else {
+ try {
+ int from = Integer.parseInt(framesFromText.getText()) - 1;
+ options.setFirstFrameIndex(from);
+ } catch (NumberFormatException e) {
+ MessageBox mb = new MessageBox(getShell(), SWT.ICON_ERROR
+ | SWT.OK);
+ mb.setText("Invalid Number");
+ mb.setMessage(framesFromText.getText()
+ + " is not a valid number, please enter a valid number for the starting frame.");
+ mb.open();
+ return;
+ }
+ try {
+ int to = Integer.parseInt(framesToText.getText());
+ options.setLastFrameIndex(to);
+ } catch (NumberFormatException e) {
+ MessageBox mb = new MessageBox(getShell(), SWT.ICON_ERROR
+ | SWT.OK);
+ mb.setText("Invalid Number");
+ mb.setMessage(framesFromText.getText()
+ + " is not a valid number, please enter a valid number for the ending frame.");
+ mb.open();
+ return;
+ }
+ }
+ options.setKmzFileLocation(new File(locationText.getText()));
+ options.setShadeEarth(shadeEarthButton.getSelection());
+ // if (!setTimesButton.getSelection()) {
+ // options.setTimeMode(KmlExportTimeMode.NONE);
+ // } else if (timeSpanButton.getSelection()) {
+ // options.setTimeMode(KmlExportTimeMode.TIME_SPAN);
+ // } else if (timeStampButton.getSelection()) {
+ // options.setTimeMode(KmlExportTimeMode.TIME_STAMP);
+ //
+ // }
+ options.setFillPlotBackground(fillPlotsButton.getSelection());
+ if (options.isSinglePane()) {
+ List resourcesToExport = new ArrayList();
+ for (TreeItem ti : productTree.getItems()) {
+ if (ti.getChecked()) {
+ resourcesToExport.add((ResourcePair) ti.getData());
+ }
+ }
+ options.getSinglPane().setResourcesToExport(resourcesToExport);
+ } else {
+ for (TreeItem paneitem : productTree.getItems()) {
+ KmlPane pane = (KmlPane) paneitem.getData();
+ List resourcesToExport = new ArrayList();
+ for (TreeItem ti : paneitem.getItems()) {
+ if (ti.getChecked()) {
+ resourcesToExport.add((ResourcePair) ti.getData());
+ }
+ }
+ pane.setResourcesToExport(resourcesToExport);
+ }
+ }
+ if (!validate()) {
+ // clear the current selection
+ for (KmlPane pane : options.getPanes()) {
+ pane.setResourcesToExport(null);
+ }
+ return;
+ }
+ new KmlExportJob(options).schedule();
+ close();
+ }
+
+ protected boolean validate() {
+ boolean products = false;
+ for (KmlPane pane : options.getPanes()) {
+ if (pane.getResourcesToExport() != null
+ && !pane.getResourcesToExport().isEmpty()) {
+ products = true;
+ break;
+ }
+ }
+ if (!products) {
+ MessageBox mb = new MessageBox(getShell(), SWT.ICON_ERROR | SWT.OK);
+ mb.setText("No Products");
+ mb.setMessage("No products are selected.");
+ mb.open();
+ return false;
+ }
+ if (options.getFirstFrameIndex() > options.getLastFrameIndex()
+ || options.getLastFrameIndex() < 0) {
+ MessageBox mb = new MessageBox(getShell(), SWT.ICON_ERROR | SWT.OK);
+ mb.setText("Invalid Range");
+ mb.setMessage("The frame range you entered is invalid, please enter a valid range");
+ mb.open();
+ return false;
+ }
+ File file = options.getKmzFileLocation();
+ if (!file.getParentFile().exists()) {
+ MessageBox mb = new MessageBox(getShell(), SWT.ICON_QUESTION
+ | SWT.YES | SWT.NO);
+ mb.setText("Create Directory");
+ mb.setMessage("The directory " + file.getParent()
+ + " does not exist, would you like to create it.");
+ int result = mb.open();
+ if (result == SWT.YES) {
+ if (!file.getParentFile().mkdirs()) {
+ mb = new MessageBox(getShell(), SWT.ICON_ERROR | SWT.OK);
+ mb.setText("Error Creating Directory");
+ mb.setMessage("An unspecified error has occured creating the directory, please select a new file location.");
+ mb.open();
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ if (file.exists()) {
+ MessageBox mb = new MessageBox(getShell(), SWT.ICON_WARNING
+ | SWT.YES | SWT.NO);
+ mb.setText("Overwrite file");
+ mb.setMessage("The specified file already exist. Would you like to overwrite it?");
+ int result = mb.open();
+ if (result == SWT.NO) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportHandler.java b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportHandler.java
new file mode 100644
index 0000000000..087bcceca8
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportHandler.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.viz.kml.export;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+import com.raytheon.uf.viz.core.IDisplayPane;
+import com.raytheon.uf.viz.core.IDisplayPaneContainer;
+import com.raytheon.uf.viz.core.drawables.AbstractRenderableDisplay;
+import com.raytheon.uf.viz.core.maps.display.MapRenderableDisplay;
+import com.raytheon.uf.viz.kml.export.KmlExportOptions.KmlExportTimeMode;
+import com.raytheon.viz.ui.EditorUtil;
+
+/**
+ *
+ * Handler for events from the KML Export menu item.
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 1, 2012 bsteffen Initial creation
+ *
+ *
+ *
+ * @author bsteffen
+ * @version 1.0
+ */
+public class KmlExportHandler extends AbstractHandler {
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ new KmlExportDialog(HandlerUtil.getActiveShell(event),
+ getDefaultOptions()).open();
+ return null;
+ }
+
+ protected static KmlExportOptions getDefaultOptions() {
+ KmlExportOptions options = new KmlExportOptions();
+ options.setFirstFrameIndex(Integer.MIN_VALUE);
+ options.setLastFrameIndex(Integer.MAX_VALUE);
+ options.setPreserveVisibility(true);
+ options.setShadeEarth(false);
+ options.setTimeMode(KmlExportTimeMode.TIME_SPAN);
+ options.setKmzFileLocation(new File(System.getProperty("user.home"),
+ "caveExport.kmz"));
+ options.setPlotIconScale(3.5);
+ options.setFillPlotBackground(false);
+ options.setPaintSleepMillis(10);
+ options.setMaxRefreshSeconds(60);
+ IDisplayPaneContainer container = EditorUtil.getActiveVizContainer();
+ List panes = new ArrayList();
+ for (IDisplayPane pane : container.getDisplayPanes()) {
+ AbstractRenderableDisplay display = (AbstractRenderableDisplay) pane
+ .getRenderableDisplay();
+ panes.add(new KmlPane(display, pane.getBounds()));
+ }
+ options.setPanes(panes);
+ return options;
+ }
+
+ @Override
+ public void setEnabled(Object evaluationContext) {
+ IDisplayPaneContainer container = EditorUtil.getActiveVizContainer();
+ if (container == null) {
+ super.setBaseEnabled(false);
+ return;
+ }
+ IDisplayPane pane = container.getActiveDisplayPane();
+ if (pane == null) {
+ super.setBaseEnabled(false);
+ return;
+ }
+ super.setBaseEnabled(pane.getRenderableDisplay() instanceof MapRenderableDisplay);
+ }
+
+}
diff --git a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportJob.java b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportJob.java
new file mode 100644
index 0000000000..74a10c515a
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportJob.java
@@ -0,0 +1,676 @@
+/**
+ * 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.kml.export;
+
+import java.awt.Graphics;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.graphics.RGB;
+import org.geotools.geometry.DirectPosition2D;
+import org.geotools.referencing.GeodeticCalculator;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+
+import com.raytheon.uf.common.colormap.Color;
+import com.raytheon.uf.common.colormap.IColorMap;
+import com.raytheon.uf.common.geospatial.MapUtil;
+import com.raytheon.uf.common.geospatial.TransformFactory;
+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.common.time.DataTime;
+import com.raytheon.uf.viz.core.IExtent;
+import com.raytheon.uf.viz.core.drawables.AbstractRenderableDisplay;
+import com.raytheon.uf.viz.core.drawables.ColorMapParameters;
+import com.raytheon.uf.viz.core.drawables.ColorMapParameters.LabelEntry;
+import com.raytheon.uf.viz.core.drawables.IDescriptor;
+import com.raytheon.uf.viz.core.drawables.IDescriptor.FramesInfo;
+import com.raytheon.uf.viz.core.drawables.PaintProperties;
+import com.raytheon.uf.viz.core.drawables.PaintStatus;
+import com.raytheon.uf.viz.core.drawables.ResourcePair;
+import com.raytheon.uf.viz.core.exception.VizException;
+import com.raytheon.uf.viz.core.jobs.JobPool;
+import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
+import com.raytheon.uf.viz.core.rsc.IResourceGroup;
+import com.raytheon.uf.viz.core.rsc.ResourceList;
+import com.raytheon.uf.viz.core.rsc.capabilities.BlendableCapability;
+import com.raytheon.uf.viz.core.rsc.capabilities.BlendedCapability;
+import com.raytheon.uf.viz.core.rsc.capabilities.ColorMapCapability;
+import com.raytheon.uf.viz.kml.export.graphics.KmlGraphicsFactoryAdapter;
+import com.raytheon.uf.viz.kml.export.graphics.KmlGraphicsTarget;
+import com.raytheon.uf.viz.kml.export.io.KmlOutputManager;
+import com.raytheon.uf.viz.kml.export.io.KmlRootOutputManager;
+
+import de.micromata.opengis.kml.v_2_2_0.AbstractObject;
+import de.micromata.opengis.kml.v_2_2_0.Document;
+import de.micromata.opengis.kml.v_2_2_0.Feature;
+import de.micromata.opengis.kml.v_2_2_0.Folder;
+import de.micromata.opengis.kml.v_2_2_0.LinearRing;
+import de.micromata.opengis.kml.v_2_2_0.LookAt;
+import de.micromata.opengis.kml.v_2_2_0.MultiGeometry;
+import de.micromata.opengis.kml.v_2_2_0.Placemark;
+import de.micromata.opengis.kml.v_2_2_0.PolyStyle;
+import de.micromata.opengis.kml.v_2_2_0.ScreenOverlay;
+import de.micromata.opengis.kml.v_2_2_0.Style;
+import de.micromata.opengis.kml.v_2_2_0.TimePrimitive;
+import de.micromata.opengis.kml.v_2_2_0.TimeSpan;
+import de.micromata.opengis.kml.v_2_2_0.TimeStamp;
+import de.micromata.opengis.kml.v_2_2_0.Units;
+import de.micromata.opengis.kml.v_2_2_0.Vec2;
+
+/**
+ * The main Job for exporting KML in a background thread
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 6, 2012 bsteffen Initial creation
+ *
+ *
+ *
+ * @author bsteffen
+ * @version 1.0
+ */
+
+public class KmlExportJob extends Job {
+ private static final transient IUFStatusHandler statusHandler = UFStatus
+ .getHandler(KmlExportJob.class);
+
+ private static final SimpleDateFormat KML_TIME_FORMAT = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+
+ private final KmlExportOptions options;
+
+ private final JobPool backgroundPool = new JobPool("Exporting KML", 4,
+ true, Job.INTERACTIVE);
+
+ public KmlExportJob(KmlExportOptions options) {
+ super("Generating Kml");
+ setUser(true);
+ this.options = options;
+ KML_TIME_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ SubMonitor smonitor = SubMonitor
+ .convert(monitor, "Generating KML", 800);
+
+ try {
+ copyPanes(smonitor.newChild(20, SubMonitor.SUPPRESS_NONE));
+
+ initPanes(smonitor.newChild(80, SubMonitor.SUPPRESS_NONE));
+
+ KmlRootOutputManager out = new KmlRootOutputManager(
+ options.getKmzFileLocation());
+
+ exportPanes(smonitor.newChild(500, SubMonitor.SUPPRESS_NONE), out);
+ joinBackground(smonitor.newChild(150, SubMonitor.SUPPRESS_NONE));
+ // Do not dispose until all background processes are done
+ for (KmlPane pane : options.getPanes()) {
+ pane.getDisplay().dispose();
+ }
+ recursiveInvisibility(out.getContainer(), true);
+ out.close();
+ smonitor.worked(50);
+ } catch (IOException e) {
+ statusHandler.handle(Priority.PROBLEM, "Error writing KML", e);
+ }
+ smonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Copy each pane and remove any panes that aren't being exported.
+ *
+ * @param monitor
+ */
+ private void copyPanes(IProgressMonitor monitor) {
+ // Keep this code as fast as possible, if the user runs kml export in
+ // the background and modifies the main display it will affect kml if
+ // copy is not done.
+ List panes = options.getPanes();
+ monitor.beginTask("Copying Displays", panes.size());
+ Iterator paneIt = panes.iterator();
+ while (paneIt.hasNext()) {
+ KmlPane pane = paneIt.next();
+ List exports = pane.getResourcesToExport();
+ if (exports == null || exports.isEmpty()) {
+ paneIt.remove();
+ } else {
+ try {
+ AbstractRenderableDisplay display = pane.getDisplay();
+ // copy the current time before clone
+ FramesInfo fi = display.getDescriptor().getFramesInfo();
+ if (fi.getFrameTimes() != null) {
+ int index = fi.getFrameIndex();
+ if (index > options.getFirstFrameIndex()
+ && index < options.getLastFrameIndex()) {
+ pane.setDisplayedTime(fi.getFrameTimes()[fi
+ .getFrameIndex()]);
+ }
+ }
+ display = display.cloneDisplay();
+ pane.setDisplay(display);
+ KmlGraphicsFactoryAdapter graphicsAdapter = new KmlGraphicsFactoryAdapter(
+ display.getView().getExtent(), pane.getBounds());
+ display.setGraphicsAdapter(graphicsAdapter);
+ KmlGraphicsTarget target = graphicsAdapter.constructTarget(
+ null, 0.0f, 0.0f);
+ target.setBackgroundColor(display.getBackgroundColor());
+ display.setup(target);
+ pane.setTarget(target);
+ } catch (VizException e) {
+ statusHandler.handle(Priority.PROBLEM,
+ e.getLocalizedMessage(), e);
+ paneIt.remove();
+ }
+ }
+ monitor.worked(1);
+ }
+ monitor.done();
+ }
+
+ private void initPanes(IProgressMonitor monitor) {
+ List panes = options.getPanes();
+ monitor.beginTask("Initializing Displays", panes.size());
+ Iterator paneIt = panes.iterator();
+ while (paneIt.hasNext()) {
+ KmlPane pane = paneIt.next();
+ try {
+ AbstractRenderableDisplay display = pane.getDisplay();
+
+ IDescriptor descriptor = display.getDescriptor();
+ descriptor.setRenderableDisplay(display);
+ descriptor.getResourceList().instantiateResources(descriptor,
+ true);
+ for (ResourcePair rp : descriptor.getResourceList()) {
+ rp.getResource().init(pane.getTarget());
+ monitor.worked(1);
+ }
+ } catch (VizException e) {
+ statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(),
+ e);
+ paneIt.remove();
+ }
+ monitor.worked(1);
+ }
+ monitor.done();
+ }
+
+ private void exportPanes(IProgressMonitor monitor, KmlOutputManager out)
+ throws IOException {
+ List panes = options.getPanes();
+ SubMonitor smonitor = SubMonitor.convert(monitor, "Exporting Displays",
+ panes.size() * 100);
+ int paneNumber = 1;
+ for (KmlPane pane : options.getPanes()) {
+ KmlOutputManager displayOut = out;
+ if (!options.isSinglePane()) {
+ displayOut = out.createFolder("Pane " + (paneNumber++));
+ }
+ AbstractRenderableDisplay display = pane.getDisplay();
+ setView(pane, displayOut);
+ if (options.isShadeEarth()) {
+ shadeEarth(displayOut, display.getBackgroundColor());
+ }
+ IDescriptor descriptor = display.getDescriptor();
+ List exports = new ArrayList();
+ for (ResourcePair rp : descriptor.getResourceList()) {
+ if (pane.getResourcesToExport().contains(rp)) {
+ exports.add(rp);
+ } else {
+ rp.getProperties().setVisible(false);
+ }
+ }
+ exportResources(smonitor.newChild(100, SubMonitor.SUPPRESS_NONE),
+ displayOut, exports, pane);
+ if (smonitor.isCanceled()) {
+ break;
+ }
+ }
+ smonitor.done();
+ }
+
+ private void exportResources(IProgressMonitor monitor,
+ KmlOutputManager out, List exports, KmlPane pane)
+ throws IOException {
+ SubMonitor smonitor = SubMonitor.convert(monitor, "Exporting Products",
+ exports.size() * 100);
+ KmlGraphicsTarget target = pane.getTarget();
+ AbstractRenderableDisplay display = pane.getDisplay();
+ IDescriptor descriptor = display.getDescriptor();
+ List visibility = new ArrayList();
+ for (ResourcePair rp : exports) {
+ visibility.add(rp.getProperties().isVisible());
+ rp.getProperties().setVisible(false);
+ }
+ for (int c = 0; c < exports.size(); c++) {
+ ResourcePair rp = exports.get(c);
+ AbstractVizResource, ?> rsc = rp.getResource();
+ rp.getProperties().setVisible(true);
+ String name = rp.getResource().getName();
+ if (name == null) {
+ name = rp.getResource().getClass().getSimpleName();
+ }
+ KmlOutputManager resourceOut = out.createFolder(name.trim());
+ SubMonitor rscmonitor = smonitor.newChild(100,
+ SubMonitor.SUPPRESS_NONE);
+ if (rsc.hasCapability(BlendableCapability.class)) {
+ ResourceList list = rsc
+ .getCapability(BlendableCapability.class)
+ .getResourceList();
+ exportResources(rscmonitor, resourceOut, list, pane);
+ } else {
+ int startIndex = options.getFirstFrameIndex();
+ startIndex = Math.max(startIndex, 0);
+ int lastIndex = options.getLastFrameIndex();
+ lastIndex = Math.min(lastIndex, descriptor.getNumberOfFrames());
+ rscmonitor.beginTask("Saving " + rsc.getName(), lastIndex
+ - startIndex);
+ addColorMap(resourceOut, display.getBackgroundColor(), rsc);
+ DataTime[] times = descriptor.getFramesInfo().getTimeMap()
+ .get(rsc);
+ if ((times == null || times.length == 0)
+ && rsc instanceof IResourceGroup) {
+ ResourceList list = ((IResourceGroup) rsc)
+ .getResourceList();
+ for (ResourcePair pair : list) {
+ times = descriptor.getFramesInfo().getTimeMap()
+ .get(pair.getResource());
+ if (times != null && times.length > 0) {
+ break;
+ }
+ }
+ }
+ List pastFrames = new ArrayList();
+ for (int i = startIndex; i < lastIndex; i += 1) {
+ descriptor.setFramesInfo(new FramesInfo(i));
+ KmlOutputManager timeOut = resourceOut;
+ if (rsc.isTimeAgnostic()
+ && (times == null || times.length == 0)) {
+ i = lastIndex - 1;
+ } else {
+ if (i < 0 || times == null || i >= times.length) {
+ rscmonitor.worked(1);
+ continue;
+ }
+ DataTime time = times[i];
+ if (time == null || pastFrames.contains(time)) {
+ rscmonitor.worked(1);
+ continue;
+ }
+ timeOut = resourceOut.createFolder(time
+ .getLegendString());
+ timeOut.getContainer().setTimePrimitive(
+ getTimePrimitive(times, i));
+ pastFrames.add(time);
+ }
+ PaintProperties paintProps = new PaintProperties(1.0f,
+ (float) display.getZoom(), display.getView(),
+ pane.getBounds(), false, descriptor.getFramesInfo());
+ paintResource(rscmonitor, timeOut, display, target, rsc,
+ paintProps);
+ rscmonitor.worked(1);
+ if (rscmonitor.isCanceled()) {
+ return;
+ }
+ }
+ }
+ rp.getProperties().setVisible(false);
+ if (options.isPreserveVisibility() && !visibility.get(c)) {
+ resourceOut.getContainer().setVisibility(false);
+ }
+ rscmonitor.done();
+ }
+ }
+
+ private void setView(KmlPane pane, KmlOutputManager out) {
+ IExtent extent = pane.getDisplay().getView().getExtent();
+ try {
+ DirectPosition2D center = new DirectPosition2D(
+ extent.getCenter()[0], extent.getCenter()[1]);
+ DirectPosition2D corner = new DirectPosition2D(extent.getMaxX(),
+ extent.getMinX());
+ MathTransform gridToLatLon = TransformFactory.gridToLatLon(pane
+ .getDisplay().getDescriptor().getGridGeometry(),
+ PixelInCell.CELL_CENTER);
+ gridToLatLon.transform(center, center);
+ gridToLatLon.transform(corner, corner);
+
+ GeodeticCalculator gc = new GeodeticCalculator();
+ gc.setStartingGeographicPoint(MapUtil.correctLon(center.x),
+ center.y);
+ gc.setDestinationGeographicPoint(MapUtil.correctLon(corner.x),
+ corner.y);
+ LookAt lookAt = out.getContainer().createAndSetLookAt();
+ lookAt.setLongitude(center.x);
+ lookAt.setLatitude(center.y);
+ lookAt.setRange(gc.getOrthodromicDistance());
+ if (pane.getDisplayedTime() != null) {
+ DataTime time = pane.getDisplayedTime();
+ TimeStamp ts = new TimeStamp();
+ ts.setWhen(KML_TIME_FORMAT.format(new Date(time.getMatchValid())));
+ // At the time of this writing the current api doesn't allow
+ // setting time primitive for AbstractView
+ lookAt.setAbstractViewObjectExtension(Arrays
+ .asList((AbstractObject) ts));
+ }
+ } catch (TransformException e) {
+ statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e);
+ } catch (FactoryException e) {
+ statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e);
+ }
+ }
+
+ /**
+ * Given a list of times for a resource and the index of the current time,
+ * generate a KML TimePrimitive for a resource. When the time mode is SPAN
+ * the times in the array are used to calculate a span such that the spans
+ * for every time create a continuos time line.
+ *
+ * @param times
+ * @param index
+ * @return
+ */
+ private TimePrimitive getTimePrimitive(DataTime[] times, int index) {
+ long validTime = times[index].getMatchValid();
+ switch (options.getTimeMode()) {
+ case TIME_STAMP: {
+ TimeStamp ts = new TimeStamp();
+ ts.setWhen(KML_TIME_FORMAT.format(new Date(validTime)));
+ return ts;
+ }
+ case TIME_SPAN: {
+ long prevValid = 0;
+ long nextValid = 0;
+ for (DataTime t : times) {
+ if (t == null) {
+ continue;
+ }
+ long valid = t.getMatchValid();
+ if (valid < validTime) {
+ if (prevValid == 0 || prevValid < valid) {
+ prevValid = valid;
+ }
+ } else if (valid > validTime) {
+ if (nextValid == 0 || nextValid > valid) {
+ nextValid = valid;
+ }
+ }
+ }
+ long prevDist = 0;
+ long nextDist = 0;
+ if (prevValid != 0) {
+ nextDist = prevDist = (validTime - prevValid) / 2;
+ }
+ if (nextValid != 0) {
+ nextDist = (nextValid - validTime) / 2;
+ if (prevDist == 0) {
+ prevDist = nextDist;
+ }
+ }
+ TimeSpan span = new TimeSpan();
+ span.setBegin(KML_TIME_FORMAT
+ .format(new Date(validTime - prevDist)));
+ span.setEnd(KML_TIME_FORMAT.format(new Date(validTime + nextDist)));
+ return span;
+ }
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * KML reference documentation from google clearly states that a feature is
+ * visible only if all of it's ancestors are also visible. Google
+ * Earth(tested on version 6.2) ignores this and displays everything as
+ * visible unless that item is specifically set to invisible even when
+ * ancestors are invisible. This function makes google earth work properly
+ * by finding invisible features and making all their children invisible.
+ *
+ * @param feature
+ * @param parentVisibility
+ */
+ private void recursiveInvisibility(Feature feature, boolean parentVisibility) {
+ if (!parentVisibility) {
+ feature.setVisibility(false);
+ }
+ List features = null;
+ if (feature instanceof Folder) {
+ features = ((Folder) feature).getFeature();
+ } else if (feature instanceof Document) {
+ features = ((Document) feature).getFeature();
+ }
+ if (features == null) {
+ return;
+ }
+ for (Feature f : features) {
+ if (f == null) {
+ continue;
+ }
+ recursiveInvisibility(f,
+ !Boolean.FALSE.equals(feature.isVisibility()));
+ }
+ }
+
+ private void paintResource(IProgressMonitor monitor, KmlOutputManager out,
+ AbstractRenderableDisplay display, KmlGraphicsTarget target,
+ AbstractVizResource, ?> resource, PaintProperties paintProps) {
+ target.setNeedsRefresh(true);
+ long startTime = System.currentTimeMillis();
+ while (target.isNeedsRefresh()
+ || resource.getPaintStatus() != PaintStatus.PAINTED) {
+ if (target.isNeedsRefresh()) {
+ target.beginFrame(paintProps.getView(), false);
+ try {
+ display.paint(target, paintProps);
+ } catch (VizException e) {
+ statusHandler.handle(Priority.PROBLEM,
+ e.getLocalizedMessage(), e);
+ return;
+ }
+ target.endFrame();
+ }
+ if (System.currentTimeMillis() - startTime > options
+ .getMaxRefreshSeconds() * 1000) {
+ statusHandler.handle(Priority.PROBLEM, resource.getName()
+ + " took more than " + options.getMaxRefreshSeconds()
+ + " seconds to paint, KML may be incomplete.");
+ break;
+ }
+ try {
+ Thread.sleep(options.getPaintSleepMillis());
+ } catch (InterruptedException e) {
+ }
+ if (monitor.isCanceled()) {
+ break;
+ }
+ }
+ List generators = new ArrayList(
+ target.getGenerators());
+ for (KmlFeatureGenerator generator : generators) {
+ generator
+ .setGridGeometry(display.getDescriptor().getGridGeometry());
+ generator.setBackgroundColor(display.getBackgroundColor());
+ generator.setOptions(options);
+ }
+ backgroundPool.schedule(new GenerateRunnable(generators, out));
+ }
+
+ private void addColorMap(KmlOutputManager out, RGB backcolor,
+ AbstractVizResource, ?> rsc) throws IOException {
+ ColorMapParameters parameters = null;
+ if (rsc.hasCapability(ColorMapCapability.class)) {
+ ColorMapCapability cap = rsc
+ .getCapability(ColorMapCapability.class);
+ parameters = cap.getColorMapParameters();
+ } else {
+ return;
+ }
+ double xAnchor = 0;
+ if (rsc.hasCapability(BlendedCapability.class)) {
+ BlendedCapability cap = rsc.getCapability(BlendedCapability.class);
+ xAnchor = Math.min(1, cap.getResourceIndex());
+ }
+ IColorMap colorMap = parameters.getColorMap();
+ BufferedImage bi = new BufferedImage(colorMap.getSize() * 2, 25,
+ BufferedImage.TYPE_INT_RGB);
+ Graphics graphics = bi.getGraphics();
+ graphics.setColor(new java.awt.Color(backcolor.red, backcolor.green,
+ backcolor.blue));
+ graphics.fillRect(0, 0, bi.getWidth(), 25);
+ int x = 0;
+ for (Color color : colorMap.getColors()) {
+ graphics.setColor(new java.awt.Color(color.getRed(), color
+ .getGreen(), color.getBlue(), color.getAlpha()));
+ graphics.drawLine(x, 0, x, 25);
+ x += 1;
+ graphics.drawLine(x, 0, x, 25);
+ x += 1;
+ }
+ for (LabelEntry label : parameters.getLabels()) {
+ if (label.getText().isEmpty()) {
+ continue;
+ }
+ Rectangle2D bounds = graphics.getFontMetrics().getStringBounds(
+ label.getText(), graphics);
+ int centerX = (int) (bi.getWidth() * label.getLocation());
+ int leftX = (int) (centerX - bounds.getWidth() / 2);
+ if (leftX < 0) {
+ leftX = 0;
+ } else if (leftX + bounds.getWidth() > bi.getWidth()) {
+ leftX = (int) (bi.getWidth() - bounds.getWidth());
+ }
+ graphics.setColor(java.awt.Color.BLACK);
+ graphics.fillRect(leftX - 1, 2, (int) bounds.getWidth() + 2,
+ (int) bounds.getHeight() + 2);
+ graphics.setColor(java.awt.Color.WHITE);
+ graphics.drawString(label.getText(), leftX,
+ (int) bounds.getHeight() + 1);
+ }
+ graphics.dispose();
+ ScreenOverlay overlay = new ScreenOverlay();
+ overlay.setName("ColorMap");
+ Vec2 overlayxy = overlay.createAndSetOverlayXY();
+ overlayxy.withX(xAnchor).withXunits(Units.FRACTION);
+ overlayxy.withY(1).withYunits(Units.FRACTION);
+ Vec2 screenxy = overlay.createAndSetScreenXY();
+ screenxy.withX(xAnchor).withXunits(Units.FRACTION);
+ screenxy.withY(1).withYunits(Units.FRACTION);
+ overlay.createAndSetIcon().setHref(
+ out.addImage(bi, "colormap" + xAnchor + ".png"));
+ out.addFeature(overlay);
+ }
+
+ private void shadeEarth(KmlOutputManager out, RGB color) {
+ Placemark placemark = new Placemark();
+ placemark.setName("Background Color");
+ Style style = new Style();
+ style.createAndSetIconStyle().setScale(0.0);
+ PolyStyle polyStyle = style.createAndSetPolyStyle();
+ polyStyle.setFill(true);
+ polyStyle.setOutline(false);
+ polyStyle.setColor(KmlFeatureGenerator.toColorStr(1.0, color));
+ placemark.setStyleUrl(out.getStyleUrl(style));
+ // Google earth seems to do a weird things with one big polygon when you
+ // zoom way out, specifically there is lots of flickering and it misses
+ // big pieces towards the back of the sphere, lots of smaller polygons
+ // helps avoid the missing hunks but I still see a lot of flickering.
+ MultiGeometry multi = placemark.createAndSetMultiGeometry();
+ for (int i = -180; i < 180; i += 10) {
+ for (int j = -90; j < 90; j += 10) {
+ LinearRing ring = multi.createAndAddPolygon()
+ .createAndSetOuterBoundaryIs().createAndSetLinearRing();
+ ring.addToCoordinates(i, j);
+ ring.addToCoordinates(i, j + 10);
+ ring.addToCoordinates(i + 10, j + 10);
+ ring.addToCoordinates(i + 10, j);
+ ring.addToCoordinates(i, j);
+ }
+
+ }
+ out.addFeature(placemark);
+ }
+
+ private void joinBackground(IProgressMonitor monitor) {
+ // some tasks(like radar mosaic) can take a very long time to finish the
+ // background task, so this waits for those to finish and makes an
+ // attempt to let the user know how it is going.
+ int remaining = backgroundPool.getWorkRemaining();
+ monitor.beginTask("Finalizing KML", remaining);
+ while (remaining > 0) {
+ try {
+ Thread.sleep(300);
+ } catch (InterruptedException e) {
+ }
+ int r = backgroundPool.getWorkRemaining();
+ monitor.worked(remaining - r);
+ remaining = r;
+ if (monitor.isCanceled()) {
+ monitor.subTask("Canceling");
+ backgroundPool.cancel();
+ break;
+ }
+ }
+ backgroundPool.join();
+ }
+
+ private static class GenerateRunnable implements Runnable {
+
+ private final List generators;
+
+ private final KmlOutputManager outputManager;
+
+ public GenerateRunnable(List generators,
+ KmlOutputManager outputManager) {
+ this.generators = generators;
+ this.outputManager = outputManager;
+ }
+
+ @Override
+ public void run() {
+ for (KmlFeatureGenerator generator : generators) {
+ generator.addFeature(outputManager);
+ }
+ }
+
+ }
+
+}
diff --git a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportOptions.java b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportOptions.java
new file mode 100644
index 0000000000..e1f98cc73c
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlExportOptions.java
@@ -0,0 +1,172 @@
+/**
+ * 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.kml.export;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Contains any options which can be configured for KML export.
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 4, 2012 bsteffen Initial creation
+ *
+ *
+ *
+ * @author bsteffen
+ * @version 1.0
+ */
+
+public class KmlExportOptions {
+
+ public enum KmlExportTimeMode {
+ NONE, TIME_STAMP, TIME_SPAN;
+ }
+
+ private File kmzFileLocation;
+
+ private int firstFrameIndex;
+
+ private int lastFrameIndex;
+
+ private boolean shadeEarth;
+
+ // Google Earth requires a fairly large plot scale to make plots look nice
+ // but World Wind prefers a smaller scale.
+ private double plotIconScale;
+
+ private boolean fillPlotBackground;
+
+ private boolean preserveVisibility;
+
+ private KmlExportTimeMode timeMode;
+
+ private List panes;
+
+ private int paintSleepMillis;
+
+ private int maxRefreshSeconds;
+
+ public File getKmzFileLocation() {
+ return kmzFileLocation;
+ }
+
+ public void setKmzFileLocation(File kmzFileLocation) {
+ this.kmzFileLocation = kmzFileLocation;
+ }
+
+ public int getFirstFrameIndex() {
+ return firstFrameIndex;
+ }
+
+ public void setFirstFrameIndex(int firstFrameIndex) {
+ this.firstFrameIndex = firstFrameIndex;
+ }
+
+ public int getLastFrameIndex() {
+ return lastFrameIndex;
+ }
+
+ public void setLastFrameIndex(int lastFrameIndex) {
+ this.lastFrameIndex = lastFrameIndex;
+ }
+
+ public boolean isShadeEarth() {
+ return shadeEarth;
+ }
+
+ public void setShadeEarth(boolean shadeEarth) {
+ this.shadeEarth = shadeEarth;
+ }
+
+ public boolean isFillPlotBackground() {
+ return fillPlotBackground;
+ }
+
+ public void setFillPlotBackground(boolean fillPlotBackground) {
+ this.fillPlotBackground = fillPlotBackground;
+ }
+
+ public boolean isPreserveVisibility() {
+ return preserveVisibility;
+ }
+
+ public void setPreserveVisibility(boolean preserveVisibility) {
+ this.preserveVisibility = preserveVisibility;
+ }
+
+ public KmlExportTimeMode getTimeMode() {
+ return timeMode;
+ }
+
+ public void setTimeMode(KmlExportTimeMode timeMode) {
+ this.timeMode = timeMode;
+ }
+
+ public List getPanes() {
+ return panes;
+ }
+
+ public void setPanes(List panes) {
+ this.panes = panes;
+ }
+
+ public boolean isSinglePane() {
+ return panes != null && panes.size() == 1;
+ }
+
+ public KmlPane getSinglPane() {
+ if (isSinglePane()) {
+ return panes.get(0);
+ } else {
+ return null;
+ }
+ }
+
+ public int getPaintSleepMillis() {
+ return paintSleepMillis;
+ }
+
+ public void setPaintSleepMillis(int paintSleepMillis) {
+ this.paintSleepMillis = paintSleepMillis;
+ }
+
+ public int getMaxRefreshSeconds() {
+ return maxRefreshSeconds;
+ }
+
+ public void setMaxRefreshSeconds(int maxRefreshSeconds) {
+ this.maxRefreshSeconds = maxRefreshSeconds;
+ }
+
+ public double getPlotIconScale() {
+ return plotIconScale;
+ }
+
+ public void setPlotIconScale(double plotIconScale) {
+ this.plotIconScale = plotIconScale;
+ }
+
+}
diff --git a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlFeatureGenerator.java b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlFeatureGenerator.java
new file mode 100644
index 0000000000..5bdb52ba34
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlFeatureGenerator.java
@@ -0,0 +1,111 @@
+/**
+ * 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.kml.export;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.graphics.RGB;
+import org.geotools.coverage.grid.GeneralGridGeometry;
+import org.geotools.coverage.grid.GridGeometry2D;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+
+import com.raytheon.uf.common.geospatial.TransformFactory;
+import com.raytheon.uf.viz.kml.export.io.KmlOutputManager;
+
+import de.micromata.opengis.kml.v_2_2_0.Coordinate;
+
+/**
+ * Anything that can be drawn on the screen can also be used to create a KML
+ * feature, this class provides some basic utility functions as well as an
+ * interface for classes that generate KML.
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 14, 2012 bsteffen Initial creation
+ *
+ *
+ *
+ * @author bsteffen
+ * @version 1.0
+ */
+
+public abstract class KmlFeatureGenerator {
+
+ protected GridGeometry2D gridGeometry;
+
+ protected MathTransform gridToLatLon;
+
+ protected RGB backgroundColor;
+
+ protected KmlExportOptions options;
+
+ public void setGridGeometry(GeneralGridGeometry gridGeometry) {
+ this.gridGeometry = GridGeometry2D.wrap(gridGeometry);
+ }
+
+ public void setBackgroundColor(RGB backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ }
+
+ public void setOptions(KmlExportOptions options) {
+ this.options = options;
+ }
+
+ public Coordinate transformToLatLon(double gridX, double gridY)
+ throws TransformException, FactoryException {
+ return transformToLatLon(new double[] { gridX, gridY });
+ }
+
+ public Coordinate transformToLatLon(double[] gridPixel)
+ throws TransformException, FactoryException {
+ if (gridToLatLon == null) {
+ gridToLatLon = TransformFactory.gridToLatLon(gridGeometry,
+ PixelInCell.CELL_CENTER);
+ }
+ double[] out = new double[2];
+ gridToLatLon.transform(gridPixel, 0, out, 0, 1);
+ return new Coordinate(out[0], out[1]);
+ }
+
+ public List transformToLatLon(List gridPixels)
+ throws TransformException, FactoryException {
+ List result = new ArrayList();
+ for (double[] gridPixel : gridPixels) {
+ result.add(transformToLatLon(gridPixel));
+ }
+ return result;
+ }
+
+ public abstract void addFeature(KmlOutputManager outputManager);
+
+ public static String toColorStr(double alpha, RGB rgb) {
+ return String.format("%02x%02x%02x%02x", (int) (alpha * 255), rgb.blue,
+ rgb.green, rgb.red);
+ }
+
+}
diff --git a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlPane.java b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlPane.java
new file mode 100644
index 0000000000..a889a562d3
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/KmlPane.java
@@ -0,0 +1,123 @@
+/**
+ * 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.kml.export;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.graphics.Rectangle;
+
+import com.raytheon.uf.common.time.DataTime;
+import com.raytheon.uf.viz.core.drawables.AbstractRenderableDisplay;
+import com.raytheon.uf.viz.core.drawables.ResourcePair;
+import com.raytheon.uf.viz.kml.export.graphics.KmlGraphicsTarget;
+
+/**
+ * VizDisplayPane but for KML!
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 5, 2012 bsteffen Initial creation
+ *
+ *
+ *
+ * @author bsteffen
+ * @version 1.0
+ */
+
+public class KmlPane {
+
+ private List resourcesToExport;
+
+ private AbstractRenderableDisplay display;
+
+ private Rectangle bounds;
+
+ private KmlGraphicsTarget target;
+
+ private DataTime displayedTime;
+
+ public KmlPane(AbstractRenderableDisplay display, Rectangle bounds) {
+ this.display = display;
+ this.bounds = bounds;
+ }
+
+ public List getResources(boolean includeMaps,
+ boolean includeHidden) {
+ List rscList = new ArrayList();
+ for (ResourcePair rp : display.getDescriptor().getResourceList()) {
+ if (!rp.getResourceData().equals(rp.getResourceData())) {
+ // A special check for those special resources which will never
+ // work with KML because they don't properly implement equals.
+ // ... like GFE
+ continue;
+ } else if (rp.getProperties().isSystemResource()) {
+ continue;
+ } else if (!includeMaps && rp.getProperties().isMapLayer()) {
+ continue;
+ } else if (!includeHidden && !rp.getProperties().isVisible()) {
+ continue;
+ }
+ rscList.add(rp);
+ }
+ return rscList;
+ }
+
+ public void setDisplay(AbstractRenderableDisplay display) {
+ this.display = display;
+ }
+
+ public void setTarget(KmlGraphicsTarget target) {
+ this.target = target;
+ }
+
+ public List getResourcesToExport() {
+ return resourcesToExport;
+ }
+
+ public void setResourcesToExport(List resourcesToExport) {
+ this.resourcesToExport = resourcesToExport;
+ }
+
+ public AbstractRenderableDisplay getDisplay() {
+ return display;
+ }
+
+ public Rectangle getBounds() {
+ return bounds;
+ }
+
+ public KmlGraphicsTarget getTarget() {
+ return target;
+ }
+
+ public DataTime getDisplayedTime() {
+ return displayedTime;
+ }
+
+ public void setDisplayedTime(DataTime displayedTime) {
+ this.displayedTime = displayedTime;
+ }
+
+}
diff --git a/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/KmlFont.java b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/KmlFont.java
new file mode 100644
index 0000000000..51883cc759
--- /dev/null
+++ b/cave/com.raytheon.uf.viz.kml.export/src/com/raytheon/uf/viz/kml/export/graphics/KmlFont.java
@@ -0,0 +1,185 @@
+/**
+ * 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.kml.export.graphics;
+
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.raytheon.uf.viz.core.drawables.IFont;
+
+/**
+ *
+ * KML has really bad font support so only support the minimum operations
+ * required to avoid errors.
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 1, 2012 bsteffen Initial creation
+ *
+ *
+ *
+ * @author bsteffen
+ * @version 1.0
+ */
+public class KmlFont implements IFont {
+
+ private Font font;
+
+ private float magnification;
+
+ private boolean scaleFont;
+
+ private boolean smoothing;
+
+ public KmlFont(Font font) {
+ this.font = font;
+ this.magnification = 1.0f;
+ }
+
+ public KmlFont() {
+ this(new Font(java.awt.Font.MONOSPACED, Font.BOLD, 14));
+ }
+
+ public KmlFont(String fontName) {
+ this(new Font(fontName, Font.PLAIN, 10));
+
+ }
+
+ public KmlFont(String fontName, float fontSize) {
+ this(new Font(fontName, Font.PLAIN, (int) fontSize));
+ }
+
+ public KmlFont(String fontName, float fontSize, Style[] styles) {
+ this(new Font(fontName, toAwtStyle(styles), (int) fontSize));
+ }
+
+ public KmlFont(File fontFile, float fontSize, Style[] styles)
+ throws FontFormatException, IOException {
+ this(Font.createFont(Font.TRUETYPE_FONT, fontFile).deriveFont(fontSize)
+ .deriveFont(toAwtStyle(styles)));
+ }
+
+ @Override
+ public String getFontName() {
+ return this.font.getFontName();
+ }
+
+ @Override
+ public float getFontSize() {
+ return this.font.getSize2D();
+ }
+
+ @Override
+ public Style[] getStyle() {
+ return toVizStyles(font.getStyle());
+ }
+
+ @Override
+ public void dispose() {
+
+ }
+
+ @Override
+ public IFont deriveWithSize(float size) {
+ return new KmlFont(font.deriveFont(size));
+ }
+
+ @Override
+ public void setMagnification(float magnification) {
+ setMagnification(magnification, true);
+ }
+
+ @Override
+ public void setMagnification(float magnification, boolean scaleFont) {
+ if (scaleFont) {
+ this.font = font.deriveFont(font.getSize2D() * magnification);
+ } else {
+ this.magnification = magnification;
+ }
+ }
+
+ @Override
+ public float getMagnification() {
+ return magnification;
+ }
+
+ @Override
+ public void setSmoothing(boolean smooth) {
+ this.smoothing = smooth;
+ }
+
+ @Override
+ public boolean getSmoothing() {
+ return smoothing;
+ }
+
+ @Override
+ public boolean isScaleFont() {
+ return scaleFont;
+ }
+
+ @Override
+ public void setScaleFont(boolean scaleFont) {
+ this.scaleFont = scaleFont;
+ }
+
+ public Font getFont() {
+ return font;
+ }
+
+ public void setFont(Font font) {
+ this.font = font;
+ }
+
+ private static int toAwtStyle(Style[] styles) {
+ int styleInt = Font.PLAIN;
+ if (styles == null || styles.length == 0) {
+ return styleInt;
+ }
+ for (Style style : styles) {
+ if (style == Style.BOLD) {
+ styleInt |= Font.BOLD;
+ } else if (style == Style.ITALIC) {
+ styleInt |= Font.ITALIC;
+ }
+ }
+ return styleInt;
+ }
+
+ private static Style[] toVizStyles(int style) {
+ List