VLab Issue #5056. Ensemble Tool Feature; fixes #5056

Change-Id: I4333c27b7b10bf3fb5a78509a9f8cf752519083b

Former-commit-id: f5dd6a6d93 [formerly f5dd6a6d93 [formerly 546a3a1ec028f3ee72fa94c8525d4c92902319c3]]
Former-commit-id: 992cb6506f
Former-commit-id: e1cb0fc2da
This commit is contained in:
Evan Polster 2014-11-19 20:58:06 +00:00
parent cff51071eb
commit e7ca46c58a
78 changed files with 15720 additions and 24 deletions

View file

@ -68,6 +68,7 @@ import com.raytheon.viz.core.graphing.xy.XYWindImageData;
* May 07, 2010 bsteffen Initial creation
* Feb 17, 2014 2661 bsteffen Use only u,v for vectors.
* Jun 18, 2014 3242 njensen Overrode getEnsembleId()
* Nov 19, 2014 5056 jing Added get arbitrary record
*
* </pre>
*
@ -356,4 +357,20 @@ public class GridTimeSeriesAdapter extends
public String getEnsembleId() {
return ensembleId;
}
/**
* This method gets an arbitrary record from the cache.
*
* @return - A grid record.
*/
public GridRecord getArbitraryRecord() {
synchronized (cache) {
if (cache.keySet().isEmpty()) {
return null;
} else {
return (GridRecord) cache.keySet().toArray()[0];
}
}
}
}

View file

@ -100,6 +100,8 @@ import com.vividsolutions.jts.geom.Geometry;
* Dec 19, 2013 DR 16795 D. Friedman Transform pixel coordinate in inspect
* Jun 18, 2014 3242 njensen Added ensembleId to legend
* Aug 15, 2014 3535 njensen Bigger inset map point
* Nov 19, 2014 5056 jing added getAdapter method, and
* changed getName to add level
*
* </pre>
*
@ -134,7 +136,7 @@ public class TimeSeriesResource extends
protected CombineOperation combineOperation;
private Set<DataTime> dataTimes = new TreeSet<DataTime>();
protected Set<DataTime> dataTimes = new TreeSet<DataTime>();
private Job dataRequestJob = new Job("Requesting Time Series Data") {
@ -255,6 +257,10 @@ public class TimeSeriesResource extends
});
}
public AbstractTimeSeriesAdapter<?> getAdapter() {
return adapter;
}
@Override
protected void disposeInternal() {
if (secondaryResource != null) {
@ -485,22 +491,26 @@ public class TimeSeriesResource extends
}
String levelKey = resourceData.getLevelKey();
String levelUnit = levelKey.replaceAll("[^a-zA-Z]", "");
boolean isHeight = levelUnit.equalsIgnoreCase("mb")
|| levelUnit.equalsIgnoreCase("agl")
|| levelUnit.contains("Agl");
// String levelUnit = levelKey.replaceAll("[^a-zA-Z]", "");
// boolean isHeight = levelUnit.equalsIgnoreCase("mb")
// || levelUnit.equalsIgnoreCase("agl")
// || levelUnit.contains("Agl");
// Make legend for point data
StringBuilder sb = new StringBuilder(String.format("%s pt%s %s%s %s%s",
source, resourceData.getPointLetter(), lat, y >= 0 ? "N" : "S",
lon, x >= 0 ? "E" : "W"));
// add level in x legend only if levelKey is not empty
StringBuilder sb = new StringBuilder(String.format(
"%s %s pt%s %s%s %s%s", source, levelKey != null ? levelKey
: "", resourceData.getPointLetter(), lat, y >= 0 ? "N"
: "S", lon, x >= 0 ? "E" : "W"));
if (stnID != null) {
sb.append(" ").append(stnID);
}
if (!isHeight) {
sb.append(" ").append(resourceData.getLevelKey());
}
// if (!isHeight) {
// sb.append(" ").append(resourceData.getLevelKey());
// }
sb.append(String.format(" %s %s %s", adapter.getParameterName(),
"TSer", units != null && units.equals("") == false ? "("
+ units + ")" : ""));

View file

@ -131,6 +131,7 @@ import com.vividsolutions.jts.geom.Coordinate;
* Feb 28, 2014 2791 bsteffen Switch all data to use data source.
* Aug 21, 2014 DR 17313 jgerth Implements ImageProvider
* Oct 07, 2014 3668 bclement Renamed requestJob to requestRunner
* Dec 09, 2014 5056 jing Added data access interfaces
*
* </pre>
*
@ -643,7 +644,7 @@ public abstract class AbstractGridResource<T extends AbstractResourceData>
public abstract List<GeneralGridData> getData(DataTime time,
List<PluginDataObject> pdos) throws VizException;
protected List<GeneralGridData> requestData(DataTime time) {
public List<GeneralGridData> requestData(DataTime time) {
synchronized (requestRunner) {
List<GeneralGridData> data = this.dataMap.get(time);
if (data == null) {
@ -968,18 +969,22 @@ public abstract class AbstractGridResource<T extends AbstractResourceData>
return new ArrayList<PluginDataObject>(list);
}
public Collection<DrawableImage> getImages(IGraphicsTarget target, PaintProperties paintProps) throws VizException {
public Collection<DrawableImage> getImages(IGraphicsTarget target,
PaintProperties paintProps) throws VizException {
if (getCapability(DisplayTypeCapability.class).getDisplayType() != DisplayType.IMAGE) {
throw new VizException("Grid resource not configured for image rendering");
throw new VizException(
"Grid resource not configured for image rendering");
}
Collection<IRenderable> renderables = getOrCreateRenderables(target, paintProps);
Collection<IRenderable> renderables = getOrCreateRenderables(target,
paintProps);
if (renderables.isEmpty()) {
return Collections.emptyList();
}
List<DrawableImage> images = new ArrayList<DrawableImage>();
for (IRenderable renderable : renderables) {
images.addAll(((TileSetRenderable)renderable).getImagesToRender(target, paintProps));
images.addAll(((TileSetRenderable) renderable).getImagesToRender(
target, paintProps));
}
return images;
}

View file

@ -55,6 +55,8 @@ import com.raytheon.uf.viz.datacube.DataCubeContainer;
* Sep 24, 2013 2404 bclement match criteria built using GridStyleUtil
* Jan 14, 2014 2661 bsteffen Switch vectors to u,v only.
* May 05, 2014 3026 mpduff Made getCurrentGribRecord() public
* Nov 19, 2014 5056 jing changed access modifier on getAnyGridRecord
* from private to public
*
* </pre>
*
@ -159,7 +161,7 @@ public class GridResource<T extends AbstractResourceData> extends
return (GridRecord) pdos.get(0);
}
protected GridRecord getAnyGridRecord() {
public GridRecord getAnyGridRecord() {
GridRecord record = getCurrentGridRecord();
if (record == null) {
for (DataTime time : getDataTimes()) {

View file

@ -142,12 +142,20 @@ public class VbSource {
@Override
public boolean equals(Object that) {
return that instanceof VbSource ? this.key.equals(((VbSource) that)
.getKey()) : false;
if (that instanceof VbSource) {
if ((this.key.equals(((VbSource) that).getKey()) && (this
.getCategory().compareTo(((VbSource) that).getCategory()) == 0))) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return key.hashCode();
String newKey = key.concat(category);
return newKey.hashCode();
}
}

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>gov.noaa.gsd.viz.ensemble.feature</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.pde.FeatureBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.FeatureNature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1 @@
bin.includes = feature.xml

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<feature
id="gov.noaa.gsd.viz.ensemble.feature"
label="Ensemble Tool Feature"
version="1.0.0.qualifier"
provider-name="GSD">
<description url="http://www.example.com/description">
[Enter Feature Description here.]
</description>
<copyright url="http://www.example.com/copyright">
[Enter Copyright Description here.]
</copyright>
<license url="http://www.example.com/license">
[Enter License Description here.]
</license>
<plugin
id="gov.noaa.gsd.viz.ensemble"
download-size="0"
install-size="0"
version="0.0.0"/>
</feature>

View file

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

View file

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

View file

@ -0,0 +1,36 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Ensemble-Tool
Bundle-SymbolicName: gov.noaa.gsd.viz.ensemble; singleton:=true
Bundle-Version: 1.14.0401.qualifier
Bundle-Activator: gov.noaa.gsd.viz.ensemble.Activator
Bundle-Vendor: NOAA-ESL-GSD
Eclipse-RegisterBuddy: com.raytheon.uf.viz.core.rsc
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
com.raytheon.viz.grid,
com.raytheon.uf.common.serialization;bundle-version="1.12.1174",
com.raytheon.uf.common.serialization.comm;bundle-version="1.12.1174",
javax.measure;bundle-version="1.0.0",
com.raytheon.uf.common.parameter,
com.raytheon.viz.core.contours,
com.raytheon.uf.common.colormap;bundle-version="1.12.1174",
com.raytheon.viz.core.graphing;bundle-version="1.12.1174",
com.raytheon.uf.common.dataplugin.level,
com.raytheon.uf.viz.d2d.core,
com.raytheon.uf.common.style;bundle-version="1.0.0",
com.raytheon.uf.common.numeric;bundle-version="1.14.0",
com.raytheon.viz.core,
com.raytheon.viz.ui,
com.raytheon.uf.common.comm;bundle-version="1.12.1174",
com.raytheon.uf.viz.xy.timeseries;bundle-version="1.13.0",
com.raytheon.uf.viz.core.rsc,
com.raytheon.uf.viz.xy;bundle-version="1.12.1174",
com.raytheon.uf.viz.d2d.xy.adapters;bundle-version="1.14.0",
com.raytheon.viz.volumebrowser;bundle-version="1.13.0",
com.raytheon.uf.common.derivparam;bundle-version="1.14.0",
com.raytheon.uf.common.inventory;bundle-version="1.14.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Bundle-ActivationPolicy: lazy
Export-Package: gov.noaa.gsd.viz.ensemble.display.control.load,
gov.noaa.gsd.viz.ensemble.navigator.ui.layer

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This_software_was_developed_and_/_or_modified_by_Raytheon_Company,
pursuant_to_Contract_DG133W-05-CQ-1067_with_the_US_Government.
U.S._EXPORT_CONTROLLED_TECHNICAL_DATA
This_software_product_contains_export-restricted_data_whose
export/transfer/disclosure_is_restricted_by_U.S._law._Dissemination
to_non-U.S._persons_whether_in_the_United_States_or_abroad_requires
an_export_license_or_other_authorization.
Contractor_Name:________Raytheon_Company
Contractor_Address:_____6825_Pine_Street,_Suite_340
________________________Mail_Stop_B8
________________________Omaha,_NE_68106
________________________402.291.0100
See_the_AWIPS_II_Master_Rights_File_("Master_Rights_File.pdf")_for
further_licensing_information.
-->
<vbSourceList>
<!-- GFS Ensemble -->
<vbSource key="GEFS" name="GFS Ensemble" category="Ensemble" views="PLANVIEW TIMESERIES" />
<!-- SREF Ensemble -->
<vbSource key="SREF212" name="SREF Ensemble" category="Ensemble" views="PLANVIEW TIMESERIES"/>
</vbSourceList>

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
point="org.eclipse.ui.commands">
<command
id="gov.noaa.gsd.viz.ensemble.activate.layer"
name="Ensembles Tool">
</command>
</extension>
<extension
point="org.eclipse.ui.handlers">
<handler
class="gov.noaa.gsd.viz.ensemble.navigator.action.EnsembleToolLayerManagerAction"
commandId="gov.noaa.gsd.viz.ensemble.activate.layer">
</handler>
</extension>
<extension
point="org.eclipse.ui.menus">
<!--
<menuContribution
locationURI="toolbar:org.eclipse.ui.main.toolbar?after=additions">
<toolbar id="ensemblesToolbar">
<visibleWhen>
<reference
definitionId="com.raytheon.uf.viz.d2d.ui.inD2DActionSet">
</reference>
</visibleWhen>
<command
commandId="gov.noaa.gsd.viz.ensemble.activate.layer"
icon="icons/EnsembleTool.gif"
id="gov.noaa.gsd.viz.ensemble.tool.viewer"
label="Ensembles"
style="push"
tooltip="Ensemble Tools">
</command>
<separator
name="gov.noaa.gsd.viz.ensemble.separator_post"
visible="true">
</separator>
</toolbar>
</menuContribution>
-->
<menuContribution
allPopups="false"
locationURI="menu:tools">
<command
commandId="gov.noaa.gsd.viz.ensemble.activate.layer"
style="push">
</command>
</menuContribution>
</extension>
<extension
point="org.eclipse.ui.views">
<view
allowMultiple="false"
class="gov.noaa.gsd.viz.ensemble.navigator.ui.viewer.EnsembleToolViewer"
fastViewWidthRatio="0.26"
icon="icons/ensemble-tool-3.gif"
id="gov.noaa.gsd.viz.ensemble.tool.viewer"
name="Ensemble Tool"
restorable="false">
</view>
</extension>
<extension
point="com.raytheon.viz.ui.displayCustomizer">
<displayCustomizer
customizer="gov.noaa.gsd.viz.ensemble.display.control.EnsembleToolDisplayCustomizer"
perspective="D2D">
</displayCustomizer>
</extension>
</plugin>

View file

@ -0,0 +1,83 @@
package gov.noaa.gsd.viz.ensemble;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug-in life cycle
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Dec 10, 2014 5056 polster Initial creation
*
* </pre>
*
* @author jing
* @author polster
* @version 1.0
*/
public class Activator extends AbstractUIPlugin {
// The plug-in ID
public static final String PLUGIN_ID = "gov.noaa.gsd.viz.ensemble"; //$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;
}
/**
* Returns an image descriptor for the image file at the given plug-in
* relative path
*
* @param path
* the path
* @return the image descriptor
*/
public static ImageDescriptor getImageDescriptor(String path) {
return imageDescriptorFromPlugin(PLUGIN_ID, path);
}
}

View file

@ -0,0 +1,70 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Calculate mean - std dev for different display types. The algorithm is
* referred to ALPS ensemble.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 1, 2014 5056 jing Initial creation
*
*
*
* </pre>
*/
public class AvgM1StddevCalculator extends EnsembleCalculator {
public AvgM1StddevCalculator() {
super(Calculation.AVG_MINUS_STD_DEV);
}
/**
* Do the "mean - std dev" calculation.
*
* (non-Javadoc)
*
* @see gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator#calculatePoint(float[],
* int)
*/
@Override
protected float calculatePoint(float[] workValue, int length) {
int count = 0;
float result = Float.NaN;
float sum = 0;
float scr0 = 0;
/**
* Prepare the sum, sum by sum, and how many data available.
*/
for (int i = 0; i < length; i++) {
if (Float.isNaN(workValue[i])) {
continue;
}
count++;
sum += workValue[i];
scr0 = workValue[i] * workValue[i];
}
if (count == 0) {
return result;
}
/**
* What is the "mean - std dev".
*/
sum /= count;
scr0 = scr0 / count - sum * sum;
result = (scr0 > 0) ? (float) (sum - Math.sqrt(scr0)) : sum;
return result;
}
}

View file

@ -0,0 +1,61 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Calculate "mean + std dev" for different display types. The algorithm is
* referred to ALPS ensemble.
*
* @author jing
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 2, 2014 5056 jing Initial creation
*
* </pre>
*/
public class AvgP1StddevCalculator extends EnsembleCalculator {
public AvgP1StddevCalculator() {
super(Calculation.AVG_PLUS_STD_DEV);
}
/**
* Do the "mean - std dev" calculation. (non-Javadoc)
*
* @see gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator#calculatePoint(float[],
* int)
*/
@Override
protected float calculatePoint(float[] workValue, int length) {
int count = 0;
float result = Float.NaN;
float sum = 0;
float scr0 = 0;
/**
* Prepare the sum, sum by sum, and how many data available.
*/
for (int i = 0; i < length; i++) {
if (Float.isNaN(workValue[i])) {
continue;
}
count++;
sum += workValue[i];
scr0 = workValue[i] * workValue[i];
}
if (count == 0)
return result;
/**
* What is the "mean + std dev".
*/
sum /= count;
scr0 = scr0 / count - sum * sum;
result = (scr0 > 0) ? (float) (sum + Math.sqrt(scr0)) : sum;
return result;
}
}

View file

@ -0,0 +1,49 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Define the calculation types as constants used in ensemble display and GUI.
*
* @author polster
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* July 1, 2014 5056 polster Initial creation
*
* </pre>
*
*/
public enum Calculation {
AVG_MINUS_STD_DEV("Avg - 1 Std dev"), //
AVG_PLUS_STD_DEV("Avg + 1 Std dev"), //
COMBINED_ENS_REL_FREQ("Combined ERF"), //
DIFFERENCE("Diff"), //
ENSEMBLE_RELATIVE_FREQUENCY("Ens Rel Freq"), //
HISTOGRAM_SAMPLING("Histogram sampling"), //
HISTOGRAM_TEXT("Histogram text"), //
MAX("Max"), //
MEAN("Mean"), //
MIN("Min"), //
MEDIAN("Median"), //
MODE("Mode"), //
NONE("<undefined>"), //
RANGE("Range"), //
SUMMATION("Sum"), //
STANDARD_DEVIATION("Std dev"), //
TRIPLET_ENS_REL_FREQ("Triplet ERF"); //
private String title;
private Calculation(String t) {
title = t;
}
public String getTitle() {
return title;
}
}

View file

@ -0,0 +1,214 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
import java.util.Arrays;
/**
* Value of Relative Frequency of an ensemble set. The range values for the
* calculation, such as minimum, maximum... are in the GUI, should be passed in.
* The definition of the minimum, maximum is the condition, "minimum < maximum"
* as the input for the REF. The concept is from AWIPS I ALPS ensemble GUI.
*
* In the current Ensemble Tool, the GUI use Range, which is passed in and
* converted into the "minimum < maximum" condition.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 2, 2014 5056 jing Initial creation
*
* </pre>
*/
public class ERFCalculator extends EnsembleCalculator {
/**
* None value flag
*/
public final static double MAX_OR_MIN_NO_VALUE = -999999.0;
/**
* Minimum value of the condition from GUI
*/
private double min = 0.25;
/**
* Maximum value of the condition from GUI
*/
private double max = 0.0;
// Segregation Strategies can be default, altSREF, ekdmos25, ekdmos10
private String SegregationStrategies = "default";
/**
* Range description
*/
private String rangeDescription = null;
/**
* Range from GUI
*/
private Range range = null;
/**
* Set the Range description.
*
* @param rd
* - Range description
*/
private void setRangeDescription(String rd) {
rangeDescription = rd;
}
/**
* Get the Range description.
*
* @return - the Range description.
*/
public String getRangeDescription() {
return rangeDescription;
}
/**
* Initial the ERF calculator by processing the ERF condition.
*
* @param r
* - rang to be pass in when constructing the object
*/
public ERFCalculator(Range r) {
// Set the Calculation flag
super(Calculation.RANGE);
range = r;
double minValue = Double.MIN_VALUE;
double maxValue = Double.MAX_VALUE;
// Convert the range to the minimum and maximum.
if ((r.getRangeType() == RangeType.INNER_RANGE)
|| (r.getRangeType() == RangeType.OUTER_RANGE)) {
minValue = r.getLowerRangeThreshold();
maxValue = r.getUpperRangeThreshold();
} else if (r.getRangeType() == RangeType.ABOVE_THRESHOLD) {
minValue = MAX_OR_MIN_NO_VALUE;
maxValue = r.getThreshold();
} else if (r.getRangeType() == RangeType.BELOW_THRESHOLD) {
minValue = r.getThreshold();
maxValue = MAX_OR_MIN_NO_VALUE;
}
min = minValue;
max = maxValue;
// Processing the condition for ERF
if (this.min == MAX_OR_MIN_NO_VALUE && this.max == MAX_OR_MIN_NO_VALUE) {
setRangeDescription("ERF Median ");
} else if (this.min != MAX_OR_MIN_NO_VALUE
&& this.max == MAX_OR_MIN_NO_VALUE) {
setRangeDescription("ERF >" + this.min);
} else if (this.min == MAX_OR_MIN_NO_VALUE
&& this.max != MAX_OR_MIN_NO_VALUE) {
setRangeDescription("ERF <" + this.max);
} else if (this.min != MAX_OR_MIN_NO_VALUE
&& this.max != MAX_OR_MIN_NO_VALUE) {
setRangeDescription("ERF >" + this.min + " & <" + this.max);
}
}
public Range getRange() {
return range;
}
/**
* Do ERF calculation for one point.
*
* (non-Javadoc)
*
* @see gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator#calculatePoint(float[],
* int)
*/
@Override
protected float calculatePoint(float[] workValue, int length) {
float[] poitValues = new float[length];
int totalNum = 0;
// Sum of the data set
for (int i = 0; i < length; i++) {
if (!Float.isNaN(workValue[i])) {
poitValues[totalNum] = workValue[i];
totalNum++;
}
}
// What is the ERF of this point
// Do nothing if no data
if (totalNum == 0) {
return Float.NaN;
}
// How many data match with the conditions?
int matchedNum = 0;
float median = 0;
if (this.min == MAX_OR_MIN_NO_VALUE && this.max == MAX_OR_MIN_NO_VALUE) {
median = getMedian(poitValues);
}
for (int k = 0; k < totalNum; k++) {
if (this.min == MAX_OR_MIN_NO_VALUE
&& this.max == MAX_OR_MIN_NO_VALUE
&& median == poitValues[k]) {
// Most close to mean
setRangeDescription("ERF Median ");
} else if (this.min != MAX_OR_MIN_NO_VALUE
&& this.max == MAX_OR_MIN_NO_VALUE
&& poitValues[k] > this.min) {
matchedNum++;
} else if (this.min == MAX_OR_MIN_NO_VALUE
&& this.max != MAX_OR_MIN_NO_VALUE
&& poitValues[k] < this.max) {
matchedNum++;
} else if (this.min != MAX_OR_MIN_NO_VALUE
&& this.max != MAX_OR_MIN_NO_VALUE
&& poitValues[k] > this.min && poitValues[k] < this.max) {
matchedNum++;
}
}
// ERF of this point in %
float erf = (Float) (matchedNum * 1.0f / totalNum);
erf = (float) Math.round(erf * 100);
return erf;
}
/**
* Calculate the median of a data set.
*
* @param a
* - A data set as the input data.
* @return
*/
private float getMedian(float[] a) {
float[] b = new float[a.length];
System.arraycopy(a, 0, b, 0, b.length);
Arrays.sort(b);
if (a.length % 2 == 0) {
return (b[(b.length / 2) - 1] + b[b.length / 2]) / 2.0f;
} else {
return b[b.length / 2];
}
}
}

View file

@ -0,0 +1,462 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
import com.raytheon.uf.common.geospatial.data.GeographicDataSource;
import com.raytheon.uf.common.geospatial.interpolation.BilinearInterpolation;
import com.raytheon.uf.common.numeric.buffer.FloatBufferWrapper;
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.viz.core.graphing.xy.XYData;
import com.raytheon.viz.core.graphing.xy.XYDataList;
import com.raytheon.viz.grid.rsc.general.GeneralGridData;
import com.raytheon.viz.grid.rsc.general.GridMemoryManager;
/**
* Ensemble calculator for loaded products, which is the base class to process
* and prepare the data for calculating of different display types. The input
* data is one frame data of all ensemble members(generic members, can be any
* loaded same level and same unit product data). The output data is calculation
* result, like mean, mode, max and so on.. The derived classes implements the
* real calculations with interfaces calculatePoint(). Currently implemented
* display types are the plan view and time series, others like time-high,
* cross-section... will be implemented in next Version. Each type is with its
* own data format. plan view is grid and time series is xy points. So the data
* processing is different.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jan 5, 2014 5056 jing Initial creation
*
* </pre>
*/
public abstract class EnsembleCalculator {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(EnsembleCalculator.class);
/**
* What's calculation, like MEAM, ERF, MAX... Is used by GUI
*/
protected Calculation calculationType;
/**
* The display type id load mode, like plan view, time series... Is used by
* ensemble displaying
*/
protected DisplayType displayType;
/**
* Define the display type.
*/
public enum DisplayType {
PLANVIEW, TIMESERIES
}
public EnsembleCalculator(Calculation c, DisplayType displayType) {
calculationType = c;
this.displayType = displayType;
}
/**
* @param c
* : What's calculation
*/
public EnsembleCalculator(Calculation c) {
calculationType = c;
}
public EnsembleCalculator() {
}
public DisplayType getDisplayType() {
return displayType;
}
public void setDisplayType(DisplayType displayType) {
this.displayType = displayType;
}
public String getName() {
return calculationType.getTitle();
}
public Calculation getCalculation() {
return calculationType;
}
/**
* Calculate grid data for plan view display. The input data may with
* different geometries, need do the geometry matching process before
* calculating to make all member grids are in same domain. This makes
* calculation easier and faster, but may cost more memory in some cases.
* Calculating with different geometries is no extra memory cost, but is
* very complicated and slow. The calculation result data is added into the
* grid data monitor.
*
* @param inputData
* - loaded grid data of all members in one frame
* @return - Calculation result grid data
*/
public List<GeneralGridData> calculate(List<List<GeneralGridData>> inputData) {
if (inputData == null || inputData.size() == 0
|| inputData.get(0) == null || inputData.get(0).size() == 0)
return null;
GridGeometry2D geometry = matchGeometry(inputData);
/**
* Suppose the grid data is in same domain and unit, therefore the grid
* size of the result is same as any input member.
*
*/
GeneralGridData[] resaultGridData = new GeneralGridData[inputData
.get(0).size()];
for (int k = 0; k < inputData.get(0).size(); k++) {// List
int imax = inputData.size();
if (inputData.get(0).get(0).isVector()) {
/**
* Vector grid data case Is not used in current implementation
*/
// Get U component data
GeographicDataSource[] dataU = new GeographicDataSource[imax];
for (int i = 0; i < imax; i++) {
dataU[i] = inputData.get(i).get(k).getUComponent();
}
// Get V component data
GeographicDataSource[] dataV = new GeographicDataSource[imax];
for (int i = 0; i < imax; i++) {
dataV[i] = inputData.get(i).get(k).getVComponent();
}
// Do Calculation
GeneralGridData resultData = GeneralGridData
.createVectorDataUV(geometry,
doGridCalculation(dataU, imax, geometry),
doGridCalculation(dataV, imax, geometry),
inputData.get(0).get(0).getDataUnit());
// To be monitored by GridMemoryManager
resultData = GridMemoryManager.getInstance().manage(resultData);
resaultGridData[k] = resultData;
} else {
/**
* Generic grid data case
*/
// Get the grid data
GeographicDataSource[] data = new GeographicDataSource[imax];
for (int i = 0; i < imax; i++) {// ArrayList
data[i] = inputData.get(i).get(k).getScalarData();
}
// Do Calculation
GeneralGridData resultData = GeneralGridData.createScalarData(
geometry, doGridCalculation(data, imax, geometry),
inputData.get(0).get(0).getDataUnit());
// To be monitored by GridMemoryManager
resultData = GridMemoryManager.getInstance().manage(resultData);
resaultGridData[k] = resultData;
}
}
return Arrays.asList(resaultGridData);
}
/**
* Do a grid calculation with members' data. loop through each xy point, get
* all member data and calculate the data set.
*
* @param data
* - member grids
* @param imax
* - How many members
* @param geometry
* - the geometry of the grids
* @return - the grid buffer of the calculation result
*/
protected FloatBufferWrapper doGridCalculation(GeographicDataSource[] data,
int imax, GridGeometry2D geometry) {
if (data == null || imax < 1)
return null;
GridEnvelope2D gridRange = geometry.getGridRange2D();
int numGridPoints = gridRange.width * gridRange.height;
int sizeInBytes = numGridPoints * 4;
float[] result = new float[gridRange.width * gridRange.height];
float[] workValue = new float[imax];
// loop through each xy poit of the grids
for (int y = 0; y < gridRange.height; y++) {
for (int x = 0; x < gridRange.width; x++) {
int countJ = 0;
// Read out all available member data at same location
for (int i = 0; i < imax; i++)
if (!Float.isNaN((float) (data[i].getDataValue(x, y))))
workValue[countJ++] = (float) (data[i].getDataValue(x,
y));
// Do real calculation at this location
result[y * gridRange.width + x] = calculatePoint(workValue,
countJ);
}
}
ByteBuffer directBuffer = ByteBuffer.allocateDirect(sizeInBytes);
directBuffer.order(ByteOrder.nativeOrder());
FloatBuffer dataBuffer = directBuffer.asFloatBuffer();
dataBuffer.put(result);
dataBuffer.position(0);
FloatBufferWrapper wrapper = new FloatBufferWrapper(dataBuffer,
gridRange);
return wrapper;
}
/**
* Make all member grid data with same geometry. Currently, we use the
* geometry of the input member with biggest domain.
*
* @param inputData
* - loaded grid data of all members in one frame
* @return-
*/
private GridGeometry2D matchGeometry(List<List<GeneralGridData>> inputData) {
/**
* Search the biggest one as main geometry, if the members are with
* different geometries. Go through each grid and do re-projection if
* its geometry is different then the main geometry.
*/
// Look for a geometry with most coverage
GridGeometry2D geometry = inputData.get(0).get(0).getGridGeometry();
int maxSize = 0;
for (int k = 0; k < inputData.get(0).size(); k++) {
int imax = inputData.size();
int gridSize = 0;
for (int i = 0; i < imax; i++) {
gridSize = inputData.get(i).get(k).getScalarData()
.getGridGeometry().getGridRange2D().height
* inputData.get(i).get(k).getScalarData()
.getGridGeometry().getGridRange2D().width;
if (gridSize <= 1)
continue;
if (maxSize < gridSize) {
maxSize = gridSize;
geometry = inputData.get(i).get(k).getGridGeometry();
}
}
}
if (maxSize < 0 || geometry == null)
return null;
// Make sure all data with same geometry by re-projecting
BilinearInterpolation bilinear = new BilinearInterpolation();
bilinear.setMissingThreshold(1.0f);
for (int k = 0; k < inputData.get(0).size(); k++) {
int imax = inputData.size();
for (int i = 0; i < imax; i++) {
if (geometry.equals(inputData.get(i).get(k).getGridGeometry()) == true) {
// do nothing for same geometry
} else {
// Do re-projection
try {
inputData.get(i).set(
k,
(inputData.get(i).get(k).reproject(geometry,
bilinear)));
} catch (FactoryException | TransformException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
}
}
}
}
return geometry;
}
/**
* Calculation for time series display. Actually it prepares the data. The
* data processing deals special cases like miss data and time match. Use
* valid time when doing time match. Suppose all members are with same point
* data XYDataList but y can be NaN. Now, We consider the same product
* different size case that each member maybe with different number of
* values on the X axis. For different products case, calculate at all valid
* times with available data.
*
* @param inputData
* - xy point data sets of time series resource members
* @return- xy - the calculated point data set.
*/
public XYDataList calculateTimeSeries(List<XYDataList> inputData) {
XYDataList result = new XYDataList();
if (inputData == null || inputData.isEmpty())
return result;
// Prepare data by using all X axis values, matching time.
ArrayList<Object> xlist = new ArrayList<Object>();
for (XYDataList xyList : inputData) {
ArrayList<XYData> data = xyList.getData();
for (int j = 0; j < xyList.getData().size(); j++) {
if (data.get(j).getX() != null && data.get(j).getY() != null
&& !xlist.contains(data.get(j).getX())) {
boolean matched = false;
for (int k = 0; k < xlist.size(); k++) {
// Remove different object with same valid time
if (((DataTime) xlist.get(k)).getValidTime()
.getTimeInMillis() == ((DataTime) (data.get(j)
.getX())).getValidTime().getTimeInMillis()) {
matched = true;
break;
}
}
if (!matched) {
xlist.add(data.get(j).getX());
}
}
}
}
if (xlist.isEmpty())
return result;
// Sort times
Object[] xValues = xlist.toArray(new Object[xlist.size()]);
// Arrays.sort(xValues); doesn't works
// Temporary solution for sort it, to support Datatime only
Object temp;
for (int i = 0; i < xValues.length; i++) {
for (int j = i + 1; j < xValues.length; j++) {
if (((DataTime) xValues[i]).getValidTime().compareTo(
((DataTime) xValues[j]).getValidTime()) > 0) {
temp = xValues[i];
xValues[i] = xValues[j];
xValues[j] = temp;
}
}
}
// Calculate all points.
result.setData((ArrayList<XYData>) doTimeSeriesCalculation(inputData,
xValues));
return result;
}
/**
* Loop through each time/x point, do calculating for time series display.
*
* @param inputData
* - xy point data sets of time series resource members
* @param xValues
* - time/x values for calculated resource.
* @return- The calculated xy data set.
*/
protected List<XYData> doTimeSeriesCalculation(List<XYDataList> inputData,
Object[] xValues) {
ArrayList<XYData> dataList = new ArrayList<XYData>();
// Calculate for all point
for (int i = 0; i < xValues.length; i++) {
// Calculate for one x point. Look for y data of this x point first.
ArrayList<Float> yValues = new ArrayList<Float>();
for (XYDataList xyList : inputData) {
ArrayList<XYData> data = xyList.getData();
for (int j = 0; j < xyList.getData().size(); j++) {
if (data.get(j).getX() == null
&& data.get(j).getY() == null)
continue;
if (xValues[i].equals(data.get(j).getX())) {
yValues.add(Float.parseFloat(data.get(j).getY()
.toString()));
break;
}
}
}
if (yValues.isEmpty())
continue;
// Put the y values in the work buffer and do calculate the point.
float[] workValue = new float[yValues.size()];
for (int k = 0; k < yValues.size(); k++)
workValue[k] = (float) yValues.get(k);
XYData xy = new XYData(xValues[i], calculatePoint(workValue,
yValues.size()));
dataList.add(xy);
}
return dataList;
}
/**
* Do real calculation for one data set, This interface is implemented in
* the derived classes.
*
* @param workValue
* - inputed data set as a work buffer
* @param length
* - how many data in the work buffer.
* @return- calculated result.
*/
abstract protected float calculatePoint(float[] workValue, int length);
}

View file

@ -0,0 +1,47 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Calculate maximum for different display types.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 1, 2014 5056 jing Initial creation
*
* </pre>
*/
public class MaxCalculator extends EnsembleCalculator {
public MaxCalculator() {
super(Calculation.MAX);
}
/*
* (non-Javadoc)
*
* @see
* gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator#calculatePoint
* (float[], int)
*/
@Override
protected float calculatePoint(float[] workValue, int length) {
float max = Float.NaN;
for (int i = 0; i < length; i++) {
if (Float.isNaN(max)) {
max = workValue[i];
continue;
}
if (max < workValue[i])
max = workValue[i];
}
return max;
}
}

View file

@ -0,0 +1,55 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Calculate mean for different display types.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 1, 2014 5056 jing Initial creation
*
* </pre>
*/
public class MeanCalculator extends EnsembleCalculator {
public MeanCalculator() {
super(Calculation.MEAN);
}
/*
* (non-Javadoc)
*
* @see
* gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator#calculatePoint
* (float[], int)
*/
@Override
protected float calculatePoint(float[] workValue, int length) {
float mean = Float.NaN;
int count = 0;
for (int i = 0; i < length; i++) {
if (Float.isNaN(workValue[i]))
continue;
count++;
if (Float.isNaN(mean)) {
mean = workValue[i];
continue;
}
mean += workValue[i];
}
if (!Float.isNaN(mean) && count > 0)
mean = mean / count;
return mean;
}
}

View file

@ -0,0 +1,66 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Calculate median for different display types.
*
* The algorithm is referred to ALPS ensemble.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 1, 2014 5056 jing Initial creation
*
* </pre>
*/
public class MedianCalculator extends EnsembleCalculator {
public MedianCalculator() {
super(Calculation.MEDIAN);
}
/*
* (non-Javadoc)
*
* @see
* gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator#calculatePoint
* (float[], int)
*/
@Override
protected float calculatePoint(float[] workValue, int length) {
float[] workValue2 = new float[length];
int length2 = 0;
for (int i = 0; i < length; i++) {
if (Float.isNaN(workValue[i]))
continue;
workValue2[length2++] = workValue[i];
}
if (length2 < 1)
return Float.NaN;
if (length2 == 1)
return workValue2[0];
int ni = length2 - 1;
float tempValue;
for (int i = 0; i < ni; i++) {
for (int k = i + 1; k < ni; k++) {
if (workValue[i] < workValue[k])
continue;
tempValue = workValue[i];
workValue[i] = workValue[k];
workValue[k] = tempValue;
}
}
if (length % 2 == 0) {
return (workValue[(length / 2) - 1] + workValue[length / 2]) / 2.0f;
}
return workValue[length / 2];
}
}

View file

@ -0,0 +1,47 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Calculate minimum for different display types.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 1, 2014 5056 jing Initial creation
*
* </pre>
*/
public class MinCalculator extends EnsembleCalculator {
public MinCalculator() {
super(Calculation.MIN);
}
/*
* (non-Javadoc)
*
* @see
* gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator#calculatePoint
* (float[], int)
*/
@Override
protected float calculatePoint(float[] workValue, int length) {
float min = Float.NaN;
for (int i = 0; i < length; i++) {
if (Float.isNaN(min)) {
min = workValue[i];
continue;
}
if (min > workValue[i])
min = workValue[i];
}
return min;
}
}

View file

@ -0,0 +1,82 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Calculate mode for different display types. The algorithm is referred to ALPS
* ensemble developed by James Ramer. It's good for some products in AWIPS, but
* isn't generic. We are checking on other algorithms such as in GEMPAK, to
* determine the final one in later release.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 1, 2014 5056 jing Initial creation
*
* </pre>
*/
public class ModeCalculator extends EnsembleCalculator {
public ModeCalculator() {
super(Calculation.MODE);
}
@Override
protected float calculatePoint(float[] workValue, int length) {
float mode = Float.NaN;
float vmax = Float.MIN_VALUE;
float vmin = Float.MAX_VALUE;
int nok = 0;
for (int i = 0; i < length; i++) {
if (Float.isNaN(workValue[i]))
continue;
nok++;
if (workValue[i] > vmax)
vmax = workValue[i];
if (workValue[i] < vmin)
vmin = workValue[i];
}
if (nok == 0)
return mode;
if (vmax <= vmin)
return vmax;
short[] counts = new short[144];
float vstep = (vmax - vmin) / 11;
vmin -= vstep;
vstep /= 11;
int nmax = 0, icen;
for (int j = 0; j < length; j++) {
if (Float.isNaN(workValue[j]))
continue;
icen = (int) ((workValue[j] - vmin) / vstep);
for (int i = icen - 5; i <= icen + 5; i++) {
counts[i]++;
if (counts[i] > nmax)
nmax = counts[i];
}
}
int i1b, i2b;
i1b = i2b = 0;
for (int i1 = 0; i1 < 144; i1++) {
if (counts[i1] < nmax)
continue;
for (int i2 = i1 + 1; counts[i1] >= nmax; i2++) {
if (i2 - i1 < i2b - i1b)
continue;
i2b = i2;
i1b = i1;
}
}
mode = (float) ((i2b + i1b) * 0.5 * vstep + vmin);
return mode;
}
}

View file

@ -0,0 +1,72 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* The Range is used to pass information between GUI and display in the
* interactive ensemble calculations.
*
*
* @author polster
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* July 1, 2014 5056 polster Initial creation
*
* </pre>
*
*/
public class Range {
private double lowerThreshold = Integer.MIN_VALUE;
private double higherThreshold = Integer.MAX_VALUE;
private double onlyThreshold = Double.MIN_NORMAL;
private RangeType rangeType = RangeType.NONE;
public Range(RangeType rt) {
rangeType = rt;
}
public void setRange(double lowerValue, double higherValue)
throws IllegalArgumentException {
if (lowerValue >= higherValue) {
throw new IllegalArgumentException(
"Low value must be less than high value.");
}
lowerThreshold = lowerValue;
higherThreshold = higherValue;
}
public RangeType getRangeType() {
return rangeType;
}
public double getLowerRangeThreshold() {
return lowerThreshold;
}
public double getUpperRangeThreshold() {
return higherThreshold;
}
public void setThreshold(double t) throws IllegalArgumentException {
if ((rangeType == RangeType.ABOVE_THRESHOLD)
|| (rangeType == RangeType.BELOW_THRESHOLD)) {
onlyThreshold = t;
} else {
throw new IllegalArgumentException(
"RangeType requires two arguments.");
}
}
public double getThreshold() {
return onlyThreshold;
}
}

View file

@ -0,0 +1,67 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Calculate range.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 1, 2014 5056 jing Initial creation
*
* </pre>
*
*/
public class RangeCalculator extends EnsembleCalculator {
public RangeCalculator() {
super(Calculation.RANGE);
}
/*
* (non-Javadoc)
*
* @see
* gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator#calculatePoint
* (float[], int)
*/
@Override
protected float calculatePoint(float[] workValue, int length) {
if (workValue == null || length < 1)
return Float.NaN;
float range = Float.NaN;
float min = Float.NaN;
float max = Float.NaN;
for (int i = 0; i < length; i++) {
if (Float.isNaN(workValue[i]))
continue;
if (Float.isNaN(max) && Float.isNaN(min)) {
max = min = workValue[i];
continue;
}
if (max < workValue[i])
max = workValue[i];
if (min > workValue[i])
min = workValue[i];
}
if (Float.isNaN(max) || Float.isNaN(min)) {
range = Float.NaN;
} else {
range = max - min;
}
return range;
}
}

View file

@ -0,0 +1,26 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Define the range types which are the interactive calculation conditions.
*
* @author polster
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* July 1, 2014 5056 polster Initial creation
*
* </pre>
*
*/
public enum RangeType {
NONE, // no value
INNER_RANGE, // low-value <= x && high-value >= x
OUTER_RANGE, // low-value >= x || high-value <= x
ABOVE_THRESHOLD, // x >= value
BELOW_THRESHOLD; // x <= value
}

View file

@ -0,0 +1,64 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Calculate STDDEV for different display types. The algorithm is referred to
* ALPS ensemble.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 1, 2014 5056 jing Initial creation
*
* </pre>
*/
public class StddevCalculator extends EnsembleCalculator {
public StddevCalculator() {
super(Calculation.STANDARD_DEVIATION);
}
/*
* (non-Javadoc)
*
* @see
* gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator#calculatePoint
* (float[], int)
*/
@Override
protected float calculatePoint(float[] workValue, int length) {
if (workValue == null || length < 1)
return Float.NaN;
float stddev;
int countJ = 0;
float sum = 0;
float scr0 = 0;
for (int i = 0; i < length; i++) {
if (!Float.isNaN(workValue[i])) {
countJ++;
sum += workValue[i];
scr0 += workValue[i] * workValue[i];
}
}
if (countJ == 0) {
stddev = Float.NaN;
} else {
sum /= countJ;
scr0 = scr0 / countJ - sum * sum;
stddev = scr0 > 0 ? (float) Math.sqrt(scr0) : 0;
}
return stddev;
}
}

View file

@ -0,0 +1,48 @@
package gov.noaa.gsd.viz.ensemble.display.calculate;
/**
* Calculate sum.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 1, 2014 5056 jing Initial creation
*
* </pre>
*/
public class SumCalculator extends EnsembleCalculator {
public SumCalculator() {
super(Calculation.SUMMATION);
}
/*
* (non-Javadoc)
*
* @see
* gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator#calculatePoint
* (float[], int)
*/
@Override
protected float calculatePoint(float[] workValue, int length) {
if (workValue == null || length < 1)
return Float.NaN;
float sum = 0;
for (int i = 0; i < length; i++) {
// This solution is for same GridGeometry grids
if (!Float.isNaN(workValue[i]))
sum += workValue[i];
}
return sum;
}
}

View file

@ -0,0 +1,113 @@
package gov.noaa.gsd.viz.ensemble.display.common;
import gov.noaa.gsd.viz.ensemble.display.calculate.Calculation;
/**
* Force implementers of derived concrete classes to provide the metadata
* getters for all the common meteorological components of a resource.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov 17, 2014 5056 polster Initial creation
*
* </pre>
*
* @author jing
* @author polster
* @version 1.0
*/
public abstract class AbstractLegendComponentsProvider {
public AbstractLegendComponentsProvider() {
}
// this will be a root name e.g. for an ensemble group
abstract public String getGroupName();
// this will be unique between resources within a group
// or it will be identical to the getGroupName() method.
abstract public String getUniqueName();
abstract public String getModel();
abstract public String getLocation();
abstract public String getLevel();
abstract public String getParameter();
abstract public String getUnits();
abstract public String getDataTime();
abstract public String getType();
abstract public String getEnsembleId();
abstract public String getEnsembleIdRaw();
abstract public String getStationId();
abstract public Calculation getCalculation();
/*
* An abstract place to put the SREF prettifier.
*/
protected String srefPerturbationPrettyfied(String pert) {
String niceName = "<undef>";
if (pert == null) {
return niceName;
}
String pertPrefix = pert.replaceAll("[0-9]", "");
String pertNumStr = pert.replaceAll("[^0-9]", "");
int pertNumber = Integer.parseInt(pertNumStr);
if ((pertNumber >= 1) && (pertNumber <= 7)) {
if (pertPrefix.startsWith("ctll")) {
niceName = "nmm ctrl-1";
} else if (pertPrefix.startsWith("n")) {
niceName = "nmm n-" + pertNumber;
} else if (pertPrefix.startsWith("p")) {
niceName = "nmm p-" + pertNumber;
}
}
if ((pertNumber >= 8) && (pertNumber <= 14)) {
if (pertPrefix.startsWith("ctll")) {
niceName = "nmb ctrl-1";
} else if (pertPrefix.startsWith("n")) {
niceName = "nmb n-" + (pertNumber - 7);
} else if (pertPrefix.startsWith("p")) {
niceName = "nmb p-" + (pertNumber - 7);
}
}
if ((pertNumber >= 15) && (pertNumber <= 21)) {
if (pertPrefix.startsWith("ctll")) {
niceName = "em ctrl-1";
} else if (pertPrefix.startsWith("n")) {
niceName = "em n-" + (pertNumber - 14);
} else if (pertPrefix.startsWith("p")) {
niceName = "em p-" + (pertNumber - 14);
}
}
niceName = String.format("%-10s", niceName);
return niceName;
}
}

View file

@ -0,0 +1,149 @@
package gov.noaa.gsd.viz.ensemble.display.common;
import gov.noaa.gsd.viz.ensemble.display.calculate.Calculation;
import gov.noaa.gsd.viz.ensemble.display.calculate.ERFCalculator;
import gov.noaa.gsd.viz.ensemble.display.calculate.Range;
import gov.noaa.gsd.viz.ensemble.display.rsc.GeneratedEnsembleGridResource;
import gov.noaa.gsd.viz.ensemble.display.rsc.GeneratedEnsembleGridResourceData;
import gov.noaa.gsd.viz.ensemble.util.Utilities;
import java.util.StringTokenizer;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
/**
* Concrete resolution of accessors of generated grid resource attributes.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov 17, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @author jing
* @version 1.0
*/
public class GeneratedGridResourceHolder extends GenericResourceHolder {
GeneratedEnsembleGridResource<?> currRsc = null;
protected GeneratedGridResourceHolder(AbstractVizResource<?, ?> rsc,
boolean isSelected) {
super(rsc, isSelected);
currRsc = (GeneratedEnsembleGridResource<?>) rsc;
}
@Override
public String getModel() {
return "";
}
@Override
public String getLevel() {
String level;
if ((currRsc.getName() == null) || (currRsc.getName().length() == 0)) {
level = "<level missing>";
} else {
StringTokenizer st = new StringTokenizer(currRsc.getName());
level = st.nextToken();
}
return level;
}
@Override
public String getParameter() {
String parameter;
if ((currRsc.getName() == null) || (currRsc.getName().length() == 0)) {
parameter = "<parameter missing>";
} else {
StringTokenizer st = new StringTokenizer(currRsc.getName());
st.nextToken();
st.nextToken();
parameter = st.nextToken();
}
return parameter;
}
@Override
public String getDataTime() {
return "";
}
@Override
public String getUnits() {
return "";
}
@Override
public Calculation getCalculation() {
return currRsc.getCalculation();
}
public String getGroupName() {
return getUniqueName();
}
public String getUniqueName() {
String s = currRsc.getName();
String nodeLabel = Utilities.removeExtraSpaces(s);
return nodeLabel;
}
@Override
public String getLocation() {
return "";
}
@Override
public String getType() {
return "";
}
@Override
public String getEnsembleId() {
return "";
}
@Override
public String getEnsembleIdRaw() {
return "";
}
@Override
public String getStationId() {
return "";
}
@Override
public int hashCode() {
return getUniqueName().hashCode();
}
public Range getRange() {
Range r = null;
if (GeneratedEnsembleGridResourceData.class.isAssignableFrom(currRsc
.getResourceData().getClass())) {
GeneratedEnsembleGridResourceData grd = (GeneratedEnsembleGridResourceData) currRsc
.getResourceData();
if (ERFCalculator.class.isAssignableFrom(grd.getCalculator()
.getClass())) {
ERFCalculator erf = (ERFCalculator) grd.getCalculator();
r = erf.getRange();
}
grd.getCalculator();
}
return r;
}
}

View file

@ -0,0 +1,73 @@
package gov.noaa.gsd.viz.ensemble.display.common;
import gov.noaa.gsd.viz.ensemble.display.calculate.Calculation;
import gov.noaa.gsd.viz.ensemble.display.rsc.timeseries.GeneratedTimeSeriesResource;
import gov.noaa.gsd.viz.ensemble.util.Utilities;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
/**
* Concrete resolution of accessors of typical time series resource attributes.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov 17, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @author jing
* @version 1.0
*/
public class GeneratedTimeSeriesResourceHolder extends TimeSeriesResourceHolder {
GeneratedTimeSeriesResource<?> currRsc = null;
protected GeneratedTimeSeriesResourceHolder(AbstractVizResource<?, ?> rsc,
boolean isSelected) {
super(rsc, isSelected);
currRsc = (GeneratedTimeSeriesResource<?>) rsc;
}
@Override
public String getModel() {
return "";
}
@Override
public String getEnsembleId() {
return "";
}
@Override
public Calculation getCalculation() {
return currRsc.getCalculation();
}
@Override
public String getParameter() {
String p = "";
if (currRsc.getParameterName() != null) {
p = currRsc.getParameterName();
if (p.compareTo("Height") == 0) {
p = "Hgt";
}
}
return p;
}
@Override
public String getUniqueName() {
String s = currRsc.getName();
String nodeLabel = Utilities.removeExtraSpaces(s);
return nodeLabel;
}
}

View file

@ -0,0 +1,124 @@
package gov.noaa.gsd.viz.ensemble.display.common;
import gov.noaa.gsd.viz.ensemble.display.rsc.GeneratedEnsembleGridResource;
import gov.noaa.gsd.viz.ensemble.display.rsc.histogram.HistogramResource;
import gov.noaa.gsd.viz.ensemble.display.rsc.timeseries.GeneratedTimeSeriesResource;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.xy.timeseries.rsc.TimeSeriesResource;
import com.raytheon.viz.grid.rsc.GridNameGenerator;
/**
* This class represents the abstract container which encapulates an
* AbstractVizResource (that happens to be associated with the Ensemble Tool).
* It provides a poor-man's approach at defining how to parse and extract common
* resource metadata, an interface that doesn't already exist in the viz
* resource class. Similar approaches have been attempted in other resource data
* classes (e.g. getMetaDataMap) but were not necessarily reusable. This class
* exists to attempt to fill an unusual void in the hierarchy of an
* AbstractVizResource as there is no convenient way to extract commonly known
* meteorological metadata information from the resource (needs verification).
*
* This abstact class forces derived classes to define the equals() and hashCode
* methods, among other notable getters.
*
* In order to create a class of this type call the factory method (see
* createResourceHolder).
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov 17, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @author jing
* @version 1.0
*/
public abstract class GenericResourceHolder extends
AbstractLegendComponentsProvider {
AbstractVizResource<?, ?> rsc;
boolean isSelected = false; // either selected or unselected
boolean isGenerated = false; // either generated or a basic (normally
// loaded) resource
public static GenericResourceHolder createResourceHolder(
AbstractVizResource<?, ?> rsc, boolean isSelected) {
GenericResourceHolder genericRsc = null;
if (GeneratedEnsembleGridResource.class
.isAssignableFrom(rsc.getClass())) {
genericRsc = new GeneratedGridResourceHolder(rsc, isSelected);
} else if (GeneratedTimeSeriesResource.class.isAssignableFrom(rsc
.getClass())) {
genericRsc = new GeneratedTimeSeriesResourceHolder(rsc, isSelected);
} else if ((rsc instanceof AbstractVizResource<?, ?>)
&& (rsc instanceof GridNameGenerator.IGridNameResource)) {
genericRsc = new GridResourceHolder(rsc, isSelected);
} else if (TimeSeriesResource.class.isAssignableFrom(rsc.getClass())) {
genericRsc = new TimeSeriesResourceHolder(rsc, isSelected);
} else if (HistogramResource.class.isAssignableFrom(rsc.getClass())) {
genericRsc = new HistogramGridResourceHolder(rsc, isSelected);
}
return genericRsc;
}
@Override
public boolean equals(Object o) {
boolean equals = false;
if (o instanceof GenericResourceHolder) {
GenericResourceHolder gr = (GenericResourceHolder) o;
equals = this.getUniqueName().equals(gr.getUniqueName());
} else {
equals = false;
}
return equals;
}
public abstract int hashCode();
protected GenericResourceHolder() {
}
protected GenericResourceHolder(AbstractVizResource<?, ?> rsc,
boolean isSelected) {
this.rsc = rsc;
this.isSelected = isSelected;
}
public AbstractVizResource<?, ?> getRsc() {
return rsc;
}
public void setRsc(AbstractVizResource<?, ?> rsc) {
this.rsc = rsc;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean isSelected) {
this.isSelected = isSelected;
}
public boolean isGenerated() {
return isGenerated;
}
public void setGenerated(boolean isEnsGenerated) {
this.isGenerated = isEnsGenerated;
}
}

View file

@ -0,0 +1,191 @@
package gov.noaa.gsd.viz.ensemble.display.common;
import gov.noaa.gsd.viz.ensemble.display.calculate.Calculation;
import gov.noaa.gsd.viz.ensemble.util.Utilities;
import com.raytheon.uf.common.dataplugin.level.Level;
import com.raytheon.uf.common.dataplugin.level.mapping.LevelMapping;
import com.raytheon.uf.common.dataplugin.level.mapping.LevelMappingFactory;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.viz.grid.rsc.GridNameGenerator;
import com.raytheon.viz.grid.rsc.GridNameGenerator.IGridNameResource;
import com.raytheon.viz.grid.rsc.GridNameGenerator.LegendParameters;
/**
* Concrete resolution of accessors of typical grid resource attributes.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov 17, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @author jing
* @version 1.0
*/
public class GridResourceHolder extends GenericResourceHolder {
GridNameGenerator.IGridNameResource currRsc = null;
protected GridResourceHolder(AbstractVizResource<?, ?> rsc,
boolean isSelected) {
super(rsc, isSelected);
currRsc = (GridNameGenerator.IGridNameResource) rsc;
}
@Override
public String getModel() {
String model;
if (currRsc.getLegendParameters() == null) {
model = "<model missing>";
} else {
model = currRsc.getLegendParameters().model;
}
return model;
}
@Override
public String getLevel() {
String level;
if (currRsc.getLegendParameters() == null) {
level = "<level missing>";
} else {
level = lookupPlane(currRsc.getLegendParameters().level);
}
return level;
}
@Override
public String getParameter() {
String parameter;
if (currRsc.getLegendParameters() == null) {
parameter = "<param missing>";
} else {
parameter = currRsc.getLegendParameters().parameter;
}
return parameter;
}
@Override
public String getDataTime() {
String datatime;
if ((currRsc.getLegendParameters() == null)
|| (currRsc.getLegendParameters().dataTime == null)) {
datatime = "<datatime missing>";
} else {
LegendParameters legendParams = ((IGridNameResource) rsc)
.getLegendParameters();
datatime = legendParams.dataTime.getLegendString();
}
return datatime;
}
@Override
public String getUnits() {
String units;
if (currRsc.getLegendParameters() == null) {
units = "<units missing>";
} else {
units = currRsc.getLegendParameters().unit;
}
return units;
}
@Override
public String getType() {
String type;
if (currRsc.getLegendParameters() == null) {
type = "<type missing>";
} else {
type = currRsc.getLegendParameters().type;
}
return type;
}
@Override
public String getEnsembleId() {
String ensId = "<ensemble id missing>";
if ((currRsc.getLegendParameters() != null) && (getModel() != null)) {
ensId = currRsc.getLegendParameters().ensembleId;
if ((ensId != null) && (getModel().indexOf("SREF") >= 0)) {
ensId = srefPerturbationPrettyfied(ensId);
}
}
return ensId;
}
@Override
public String getEnsembleIdRaw() {
String ensId = "";
if ((currRsc.getLegendParameters() != null) && (getModel() != null)) {
ensId = currRsc.getLegendParameters().ensembleId;
}
return ensId;
}
private String lookupPlane(Level level) {
LevelMapping mapping = LevelMappingFactory.getInstance(
LevelMappingFactory.VOLUMEBROWSER_LEVEL_MAPPING_FILE)
.getLevelMappingForLevel(level);
if (mapping == null) {
return level.getMasterLevel().getName();
}
return mapping.getDisplayName();
}
public String getGroupName() {
String sb = String.format("%s %s %s %s", getModel(), getLevel(),
getParameter(), getUnits() != null
&& getUnits().equals("") == false ? "(" + getUnits()
+ ")" : "");
return sb;
}
public String getUniqueName() {
String sb = String
.format("%s %s %s %s %s",
getModel(),
getLevel(),
getParameter(),
getUnits() != null && getUnits().equals("") == false ? "("
+ getUnits() + ")"
: "",
getEnsembleId() != null
&& getEnsembleId().equals("") == false ? getEnsembleId()
: "");
String nodeLabel = Utilities.removeExtraSpaces(sb.toString());
return nodeLabel;
}
@Override
public String getLocation() {
return "";
}
@Override
public String getStationId() {
return "";
}
@Override
public Calculation getCalculation() {
return null;
}
@Override
public int hashCode() {
return getUniqueName().hashCode();
}
}

View file

@ -0,0 +1,136 @@
package gov.noaa.gsd.viz.ensemble.display.common;
import gov.noaa.gsd.viz.ensemble.display.calculate.Calculation;
import gov.noaa.gsd.viz.ensemble.display.rsc.histogram.HistogramResource;
import gov.noaa.gsd.viz.ensemble.util.Utilities;
import java.util.StringTokenizer;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
/**
* Concrete resolution of accessors of histogram resource attributes.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov 17, 2014 5056 jing Initial creation
*
* </pre>
*
* @author jing
* @author polster
* @version 1.0
*/
public class HistogramGridResourceHolder extends GenericResourceHolder {
private HistogramResource<?> currRsc = null;
protected HistogramGridResourceHolder(AbstractVizResource<?, ?> rsc,
boolean isSelected) {
super(rsc, isSelected);
currRsc = (HistogramResource<?>) rsc;
}
@Override
public String getModel() {
String model = "";
return model;
}
@Override
public String getLevel() {
String level;
if ((currRsc.getName() == null) || (currRsc.getName().length() == 0)) {
level = "<level missing>";
} else {
StringTokenizer st = new StringTokenizer(currRsc.getName());
level = st.nextToken();
}
return level;
}
@Override
public String getParameter() {
String parameter = "";
return parameter;
}
@Override
public String getDataTime() {
String datatime = "";
return datatime;
}
@Override
public String getUnits() {
String units = "";
if ((currRsc.getName() == null) || (currRsc.getName().length() == 0)) {
units = "<level missing>";
} else {
StringTokenizer st = new StringTokenizer(currRsc.getName());
st.nextToken();
units = st.nextToken();
}
return units;
}
@Override
public Calculation getCalculation() {
// return "color histogram";
if (((HistogramResource<?>) this.rsc).getMode() == HistogramResource.DisplayMode.SAMPLING)
return Calculation.HISTOGRAM_SAMPLING;
else
return Calculation.HISTOGRAM_TEXT;
}
public String getGroupName() {
return getUniqueName();
}
public String getUniqueName() {
String s = currRsc.getName();
String nodeLabel = Utilities.removeExtraSpaces(s);
return nodeLabel;
}
@Override
public String getLocation() {
return "";
}
@Override
public String getType() {
return "";
}
@Override
public String getEnsembleId() {
return "";
}
@Override
public String getEnsembleIdRaw() {
return "";
}
@Override
public String getStationId() {
return "";
}
@Override
public int hashCode() {
return getUniqueName().hashCode();
}
}

View file

@ -0,0 +1,521 @@
package gov.noaa.gsd.viz.ensemble.display.common;
import gov.noaa.gsd.viz.ensemble.display.control.EnsembleResourceManager;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.map.IMapDescriptor;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.xy.timeseries.display.TimeSeriesDescriptor;
import com.raytheon.uf.viz.xy.timeseries.rsc.TimeSeriesResource;
import com.raytheon.viz.ui.editor.AbstractEditor;
/**
* Access and operate the Ensemble Tool resources. Any resources contained in
* this list class will be associated with an editor and is assumed to be
* controlled by the Ensemble Tool.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov 17, 2014 5056 jing Initial creation
*
* </pre>
*
* @author jing
* @author polster
* @version 1.0
*/
public class NavigatorResourceList {
private List<GenericResourceHolder> ensembleToolResources;
private AbstractEditor theEditor;
// contains which ensemble resource is currently used as
// the calculation resource ...
String calculationEnsembleName = null;
public NavigatorResourceList(AbstractEditor editor) {
ensembleToolResources = new CopyOnWriteArrayList<GenericResourceHolder>();
theEditor = editor;
}
public boolean add(GenericResourceHolder emr) {
if (!containsResource(emr)) {
ensembleToolResources.add(emr);
return true;
}
return false;
}
public boolean remove(GenericResourceHolder emr) {
boolean removed = false;
for (GenericResourceHolder gr : ensembleToolResources) {
if (gr == emr) {
ensembleToolResources.remove(gr);
removed = true;
}
}
return removed;
}
public boolean remove(AbstractVizResource<?, ?> rcs) {
boolean removed = false;
for (GenericResourceHolder gr : ensembleToolResources) {
if (gr.getRsc() == rcs) {
ensembleToolResources.remove(gr);
removed = true;
}
}
return removed;
}
public void clear() {
ensembleToolResources.clear();
}
public List<GenericResourceHolder> getResourceHolders() {
return ensembleToolResources;
}
public boolean containsResource(GenericResourceHolder rsc) {
for (GenericResourceHolder emr : ensembleToolResources) {
if (emr.equals(rsc)) {
return true;
}
}
return false;
}
public boolean containsResource(AbstractVizResource<?, ?> rsc) {
for (GenericResourceHolder emr : ensembleToolResources) {
if (emr.getRsc().equals(rsc)) {
return true;
}
}
return false;
}
/**
* All resources
*
* @return
*/
public List<GenericResourceHolder> getAllRscsAsList() {
List<GenericResourceHolder> rscs = new CopyOnWriteArrayList<GenericResourceHolder>();
for (GenericResourceHolder emr : ensembleToolResources) {
rscs.add(emr);
}
return rscs;
}
/**
* Get all loaded and generated resources as GenericResourceHolder
* instances.
*/
public Map<String, List<GenericResourceHolder>> getAllRscsAsMap() {
Map<String, List<GenericResourceHolder>> rscMap = new ConcurrentHashMap<String, List<GenericResourceHolder>>();
EnsembleResourceManager.getInstance().syncRegisteredResource(theEditor);
List<GenericResourceHolder> rscs = EnsembleResourceManager.instance
.getResourceList(theEditor).getUserLoadedRscs();
String currRscName = null;
// let's loop through and find the group names
// and store them in a set of strings (no duplicates
// allowed) so sub-members such as perturbations will
// only be counted once.
Set<String> rscGroupNames = new HashSet<String>();
for (GenericResourceHolder rsc : rscs) {
currRscName = rsc.getGroupName();
if (!rscGroupNames.contains(currRscName)) {
rscGroupNames.add(currRscName);
}
}
Iterator<String> iter = rscGroupNames.iterator();
while (iter.hasNext()) {
String currRsc = iter.next();
List<GenericResourceHolder> members = findAssociatedMembers(currRsc);
if (!members.isEmpty()) {
rscMap.put(currRsc, members);
}
}
// Generated resource add on
rscs = EnsembleResourceManager.instance.getResourceList(theEditor)
.getUserGeneratedRscs();
for (GenericResourceHolder rsc : rscs) {
currRscName = rsc.getUniqueName();
if ((currRscName == null) || (currRscName.length() == 0)) {
currRscName = rsc.getRsc().getName();
}
List<GenericResourceHolder> members = new CopyOnWriteArrayList<GenericResourceHolder>();
members.add(rsc);
rscMap.put(currRscName, members);
}
return rscMap;
}
private List<GenericResourceHolder> findAssociatedMembers(
String resourceGroupName) {
List<GenericResourceHolder> rscs = EnsembleResourceManager.instance
.getResourceList(theEditor).getUserLoadedRscs();
List<GenericResourceHolder> members = new CopyOnWriteArrayList<GenericResourceHolder>();
for (GenericResourceHolder rsc : rscs) {
if (rsc.getUniqueName().startsWith(resourceGroupName)) {
members.add(rsc);
}
}
return members;
}
/**
* All resources for an editor that were not generated.
*
* @return
*/
public List<GenericResourceHolder> getUserLoadedRscs() {
EnsembleResourceManager.getInstance().syncRegisteredResource(theEditor);
List<GenericResourceHolder> rscs = new CopyOnWriteArrayList<GenericResourceHolder>();
for (GenericResourceHolder emr : ensembleToolResources) {
if (!emr.isGenerated()) {
rscs.add(emr);
}
}
return rscs;
}
/**
* All loaded resources of an ensemble model using its unique name (e.g
* "SREF 500MB").
*
* @return
*/
public List<GenericResourceHolder> getUserLoadedRscs(
String uniqueResourceName) {
if (uniqueResourceName == null)
return null;
EnsembleResourceManager.getInstance().syncRegisteredResource(theEditor);
List<GenericResourceHolder> rscs = new CopyOnWriteArrayList<GenericResourceHolder>();
for (GenericResourceHolder emr : ensembleToolResources) {
if (!emr.isGenerated) {
String fullName = emr.getGroupName();
if (fullName.equals(uniqueResourceName)) {
rscs.add(emr);
}
}
}
return rscs;
}
/**
* All loaded resources for ensemble.
*
* @param descriptor
* : the descriptor of the resource
* @param selected
* :if it's selected.
* @return
*/
public Map<String, List<GenericResourceHolder>> getUserLoadedRscs(
IDescriptor descriptor, boolean selected) {
EnsembleResourceManager.getInstance().syncRegisteredResource(theEditor);
Map<String, List<GenericResourceHolder>> rscMap = new ConcurrentHashMap<String, List<GenericResourceHolder>>();
for (GenericResourceHolder emr : ensembleToolResources) {
if (!emr.isGenerated
&& emr.isSelected == selected
&& emr.getRsc().getDescriptor().getClass() == descriptor
.getClass()) { // same type descriptor like Map
if (emr.getRsc().getName() == null
|| emr.getRsc().getName().equals(""))
continue;
String model = emr.getModel();
addResource2Map(rscMap, model, emr);
}
}
return rscMap;
}
/**
* All loaded resources for ensemble.
*
* @param descriptor
* :the descriptor of the resource
* @param selected
* :if it's selected.
* @param level
* :level of the resource.
* @param unit
* : unit of the resource.
* @return
*/
public Map<String, List<GenericResourceHolder>> getUserLoadedRscs(
IDescriptor descriptor, boolean selected, String level, String unit) {
EnsembleResourceManager.getInstance().syncRegisteredResource(theEditor);
Map<String, List<GenericResourceHolder>> rscMap = new ConcurrentHashMap<String, List<GenericResourceHolder>>();
for (GenericResourceHolder emr : ensembleToolResources) {
if (descriptor instanceof IMapDescriptor
&& emr.getRsc().getDescriptor() instanceof IMapDescriptor) {
if (!emr.isGenerated
&& emr.isSelected == selected
&& level.equals(emr.getLevel())
&& unit.equals(emr.getUnits())
// same type descriptor like Map
&& emr.getRsc().getDescriptor().getClass() == descriptor
.getClass()) {
// Model name is the first string
if (emr.getRsc().getName() == null
|| emr.getRsc().getName().equals(""))
continue;
String model = emr.getModel();
addResource2Map(rscMap, model, emr);
}
} else if (descriptor instanceof TimeSeriesDescriptor
&& emr.getRsc().getDescriptor() instanceof TimeSeriesDescriptor) {
if (!emr.isGenerated
&& emr.isSelected == selected
&& level.equals(((TimeSeriesResource) (emr.getRsc()))
.getResourceData().getLevelKey())
&& unit.equals(((TimeSeriesResource) (emr.getRsc()))
.getUnits())
&& emr.getRsc().getDescriptor().getClass() == descriptor
.getClass()) { // same type descriptor like Map
// Model name is the first string
if (emr.getRsc().getName() == null
|| emr.getRsc().getName().equals(""))
continue;
String model = emr.getModel();
addResource2Map(rscMap, model, emr);
}
}
}
return rscMap;
}
/**
* All loaded resources for ensemble.
*
* @param descriptor
* :the descriptor of the resource
* @param selected
* :if it's selected.
* @param unit
* : unit of the resource.
* @return
*/
public Map<String, List<GenericResourceHolder>> getUserLoadedRscs(
IDescriptor descriptor, boolean selected, String unit) {
EnsembleResourceManager.getInstance().syncRegisteredResource(theEditor);
Map<String, List<GenericResourceHolder>> rscMap = new ConcurrentHashMap<String, List<GenericResourceHolder>>();
for (GenericResourceHolder emr : ensembleToolResources) {
if (!emr.isGenerated
&& emr.isSelected == selected
&& unit.equals(emr.getUnits())
// same type descriptor like Map
&& emr.getRsc().getDescriptor().getClass() == descriptor
.getClass()) {
if (emr.getRsc().getName() == null
|| emr.getRsc().getName().equals(""))
continue;
String model = emr.getModel();
addResource2Map(rscMap, model, emr);
}
}
return rscMap;
}
/**
* All generated resources by ensemble display
*
* @return
*/
public List<GenericResourceHolder> getUserGeneratedRscs() {
List<GenericResourceHolder> rscs = new CopyOnWriteArrayList<GenericResourceHolder>();
for (GenericResourceHolder emr : ensembleToolResources) {
if (emr.isGenerated) {
rscs.add(emr);
}
}
return rscs;
}
public List<GenericResourceHolder> getUserGeneratedRscs(boolean selected) {
List<GenericResourceHolder> rscs = new CopyOnWriteArrayList<GenericResourceHolder>();
for (GenericResourceHolder emr : ensembleToolResources) {
if (!emr.isGenerated && emr.isSelected == selected) {
rscs.add(emr);
}
}
return rscs;
}
/**
*
* @param rscMap
* @param model
* @param gr
*/
private void addResource2Map(
Map<String, List<GenericResourceHolder>> rscMap, String model,
GenericResourceHolder gr) {
if (rscMap.containsKey(model)) {
rscMap.get(model).add(gr);
} else {
List<GenericResourceHolder> rscList = new CopyOnWriteArrayList<GenericResourceHolder>();
rscList.add(gr);
rscMap.put(model, rscList);
}
}
private final static String TIME_BASIS_EMPTY_STR = "<no associated time>";
public boolean isEmpty() {
return ensembleToolResources.isEmpty();
}
public static boolean isTimeEmpty(String timeBasisLegend) {
return (timeBasisLegend.compareTo(TIME_BASIS_EMPTY_STR) == 0);
}
// Currently get the first resource loaded to get time from
public String getTimeBasisLegendTime() {
EnsembleResourceManager.getInstance().syncRegisteredResource(theEditor);
// for now this is the first resource name loaded into this editor
String timeBasisLegendTime = null;
if (ensembleToolResources.isEmpty()) {
timeBasisLegendTime = TIME_BASIS_EMPTY_STR;
} else {
GenericResourceHolder g = ensembleToolResources.get(0);
timeBasisLegendTime = g.getDataTime();
}
return timeBasisLegendTime;
}
public String getTimeBasisResourceName() {
EnsembleResourceManager.getInstance().syncRegisteredResource(theEditor);
// for now this is the first resource name loaded into this editor
String timeBasisResourceName = null;
if (ensembleToolResources.isEmpty()) {
timeBasisResourceName = "";
} else {
GenericResourceHolder g = ensembleToolResources.get(0);
timeBasisResourceName = g.getGroupName();
}
return timeBasisResourceName;
}
synchronized public void setEnsembleCalculationResource(String rscGroupName) {
calculationEnsembleName = rscGroupName;
}
synchronized public String getEnsembleCalculationResource() {
return calculationEnsembleName;
}
// only update if the calculationEnsembleName is not already
// set ... this will guarantee that only the first actual ensemble
// resource will get set as the ensemble calculation resource.
public void updateEnsembleCalculationResource() {
if (calculationEnsembleName != null) {
return;
} else {
Map<String, List<GenericResourceHolder>> resourceMap = getAllRscsAsMap();
List<GenericResourceHolder> resources = null;
Set<String> keys = resourceMap.keySet();
Iterator<String> keyNames = keys.iterator();
String currKey = null;
while (keyNames.hasNext()) {
currKey = keyNames.next();
resources = resourceMap.get(currKey);
// ensemble resource found
if (resources.size() > 1) {
calculationEnsembleName = currKey;
break;
}
}
}
}
public Map<String, List<GenericResourceHolder>> getCalculationLoadedRscs(
IDescriptor descriptor, boolean selected) {
EnsembleResourceManager.getInstance().syncRegisteredResource(theEditor);
Map<String, List<GenericResourceHolder>> rscMap = new ConcurrentHashMap<String, List<GenericResourceHolder>>();
for (GenericResourceHolder emr : ensembleToolResources) {
if (!emr.isGenerated
&& emr.isSelected == selected
&& emr.getGroupName().equals(calculationEnsembleName)
// same type descriptor like Map
&& emr.getRsc().getDescriptor().getClass() == descriptor
.getClass()) {
if (emr.getRsc().getName() == null
|| emr.getRsc().getName().equals(""))
continue;
String model = emr.getModel();
addResource2Map(rscMap, model, emr);
}
}
return rscMap;
}
}

View file

@ -0,0 +1,222 @@
package gov.noaa.gsd.viz.ensemble.display.common;
import gov.noaa.gsd.viz.ensemble.display.calculate.Calculation;
import gov.noaa.gsd.viz.ensemble.util.Utilities;
import com.raytheon.uf.common.style.level.SingleLevel;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.d2d.xy.adapters.timeseries.GridTimeSeriesAdapter;
import com.raytheon.uf.viz.xy.timeseries.adapter.AbstractTimeSeriesAdapter;
import com.raytheon.uf.viz.xy.timeseries.rsc.TimeSeriesResource;
import com.vividsolutions.jts.geom.Coordinate;
/**
* Concrete resolution of accessors of time series resource attributes.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov 17, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @author jing
* @version 1.0
*/
public class TimeSeriesResourceHolder extends GenericResourceHolder {
TimeSeriesResource currRsc = null;
protected TimeSeriesResourceHolder(AbstractVizResource<?, ?> rsc,
boolean isSelected) {
super(rsc, isSelected);
currRsc = (TimeSeriesResource) rsc;
}
@Override
public String getModel() {
String model;
if ((currRsc.getResourceData() == null)
|| (currRsc.getResourceData().getSource() == null)) {
model = "<Could not obtain model name>";
} else {
model = currRsc.getResourceData().getSource();
}
return model;
}
@Override
public String getLocation() {
String latlon = "";
if ((currRsc.getResourceData() != null)
&& (currRsc.getResourceData().getCoordinate() != null)) {
Coordinate c = currRsc.getResourceData().getCoordinate();
String ns = c.y >= 0 ? "N" : "S";
String ew = c.x >= 0 ? "E" : "W";
latlon = String.format("pt%s %d%s %d%s", currRsc.getResourceData()
.getPointLetter(), Math.round(Math.abs(c.y)), ns, Math
.round(Math.abs(c.x)), ew);
}
return latlon;
}
@Override
// don't return a level if this is a height time series -- see
// TimeSeriesResource::getName()
public String getLevel() {
String levelKey = currRsc.getResourceData().getLevelKey();
String levelUnit = levelKey.replaceAll("[^a-zA-Z]", "");
boolean isHeight = levelUnit.equalsIgnoreCase("mb")
|| levelUnit.equalsIgnoreCase("agl")
|| levelUnit.contains("Agl");
if (currRsc.getAdapter() != null) {
if (!isHeight) {
SingleLevel level = currRsc.getAdapter().getLevel();
levelKey = (int) level.getValue() + level.getTypeString();
} else {
levelKey = "";
}
} else {
levelKey = "";
}
return levelKey;
}
@Override
public String getUnits() {
String units = currRsc.getUnits();
if (units == null) {
units = "";
}
return units;
}
@Override
public String getParameter() {
String p = "";
if (currRsc.getAdapter() != null) {
p = currRsc.getAdapter().getParameterName();
if (p == null)
p = "";
if (p.compareTo("Height") == 0) {
p = "Hgt";
}
}
return p;
}
@Override
public String getEnsembleId() {
String ensId = "<undef perturbation>";
if (currRsc.getAdapter() instanceof GridTimeSeriesAdapter) {
try {
AbstractTimeSeriesAdapter<?> adapter = currRsc.getAdapter();
if ((((GridTimeSeriesAdapter) adapter) != null)
&& ((((GridTimeSeriesAdapter) adapter)
.getArbitraryRecord()) != null)
&& ((((GridTimeSeriesAdapter) adapter)
.getArbitraryRecord().getInfo()) != null)) {
if (((GridTimeSeriesAdapter) adapter)
.getArbitraryRecord().getInfo()
.getEnsembleId() != null) {
ensId = ((GridTimeSeriesAdapter) adapter)
.getArbitraryRecord().getInfo()
.getEnsembleId();
} else {
ensId = currRsc.getName();
}
if ((getModel() != null)
&& ((ensId != null) && (getModel().indexOf("SREF") >= 0))) {
ensId = srefPerturbationPrettyfied(ensId);
}
}
} catch (Exception e) {
return ensId;
}
}
return ensId;
}
@Override
public String getEnsembleIdRaw() {
return "";
}
@Override
public String getStationId() {
String stnID = "";
if (currRsc.getResourceData().getMetadataMap()
.get("location.stationId") != null) {
stnID = currRsc.getResourceData().getMetadataMap()
.get("location.stationId").getConstraintValue();
}
return stnID;
}
public String getGroupName() {
String sb = String.format("%s %s %s %s %s", getModel(), getLevel(),
getParameter(), getLocation(), getUnits() != null
&& getUnits().equals("") == false ? "(" + getUnits()
+ ")" : "");
String nodeLabel = Utilities.removeExtraSpaces(sb);
return nodeLabel;
}
public String getUniqueName() {
String sb = String
.format("%s %s %s %s %s %s",
getModel(),
getLevel(),
getParameter(),
getLocation(),
getUnits() != null && getUnits().equals("") == false ? "("
+ getUnits() + ")"
: "",
getEnsembleId() != null
&& getEnsembleId().equals("") == false ? getEnsembleId()
: "");
String nodeLabel = Utilities.removeExtraSpaces(sb);
return nodeLabel;
}
@Override
public String getDataTime() {
return "";
}
@Override
public String getType() {
return "";
}
@Override
public Calculation getCalculation() {
return null;
}
@Override
public int hashCode() {
return getUniqueName().hashCode();
}
}

View file

@ -0,0 +1,220 @@
package gov.noaa.gsd.viz.ensemble.display.common;
import gov.noaa.gsd.viz.ensemble.display.rsc.GeneratedEnsembleGridResource;
import java.util.ArrayList;
import java.util.List;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.map.IMapDescriptor;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.DisplayType;
import com.raytheon.uf.viz.core.rsc.ResourceList;
import com.raytheon.uf.viz.core.rsc.capabilities.DensityCapability;
import com.raytheon.viz.grid.rsc.general.AbstractGridResource;
import com.raytheon.viz.grid.rsc.general.D2DGridResource;
import com.raytheon.viz.ui.EditorUtil;
import com.raytheon.viz.ui.editor.AbstractEditor;
/**
* Utilities for the ensemble display.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Dec 9, 2014 5056 jing Initial creation
*
* </pre>
*
* @author jing
* @version 1.0
*/
public class Utilities {
private Utilities() {
super();
}
/*
* All ensemble resources of all ensemble model products.
*/
public static List<D2DGridResource> getResourcesEnsembleModels() {
ArrayList<D2DGridResource> ensembleResources = new ArrayList<D2DGridResource>();
// check all resources
IMapDescriptor desc = (IMapDescriptor) (getEditor()
.getActiveDisplayPane().getDescriptor());
ResourceList rscList = desc.getResourceList();
for (ResourcePair rp : rscList) {
AbstractVizResource<?, ?> rsc = rp.getResource();
if (!(rsc instanceof AbstractGridResource))
continue;
if (rsc instanceof GeneratedEnsembleGridResource)
continue;
if (!isResourceEnsemble((D2DGridResource) rsc))
continue;
D2DGridResource ensembleResource = ((D2DGridResource) rsc);
ensembleResources.add(ensembleResource);
}
return ensembleResources;
}
/*
* All ensemble resources of one ensemble model products.
*/
public static List<D2DGridResource> getResourcesEnsembleModel(String model) {
ArrayList<D2DGridResource> ensembleResources = new ArrayList<D2DGridResource>();
// check all resources
IMapDescriptor desc = (IMapDescriptor) (getEditor()
.getActiveDisplayPane().getDescriptor());
ResourceList rscList = desc.getResourceList();
for (ResourcePair rp : rscList) {
AbstractVizResource<?, ?> rsc = rp.getResource();
if (!(rsc instanceof AbstractGridResource))
continue;
if (rsc instanceof GeneratedEnsembleGridResource)
continue;
if (!isResourceEnsemble((D2DGridResource) rsc))
continue;
if (!((D2DGridResource) rsc).getLegendParameters().model
.equals(model))
continue;
D2DGridResource ensembleResource = ((D2DGridResource) rsc);
ensembleResources.add(ensembleResource);
}
return ensembleResources;
}
/*
* All resources of different models same time.
*/
// get density base on the number of a ensemble products
public static double getEnsembleDensityRsc(AbstractGridResource<?> rsc) {
if (!isResourceEnsemble((D2DGridResource) rsc)) {
return ((D2DGridResource) rsc).getCapability(
DensityCapability.class).getDensity();
}
// this rsc belong to which products
String model = ((D2DGridResource) rsc).getLegendParameters().model;
// How many member of the model
List<String> members = Utilities.getEnsembleMembers(model);
if (members == null)
return ((D2DGridResource) rsc).getCapability(
DensityCapability.class).getDensity();
// Which density is better, temp. table to assign densities
double density = .33;
if (members.size() < 10) {
density = 2.0;
} else if (members.size() < 20) {
density = 1.5;
} else if (members.size() < 40) {
density = 1.0;
} else if (members.size() < 60) {
density = .75;
} else if (members.size() < 80) {
density = .5;
}
return density;
}
// Get loaded ensemble models. This function will be used by other without
// have a object
public static List<String> getEnsembleModels() {
ArrayList<String> models = new ArrayList<String>();
// check all resources
IDescriptor desc = (getEditor().getActiveDisplayPane().getDescriptor());
ResourceList rscList = desc.getResourceList();
for (ResourcePair rp : rscList) {
AbstractVizResource<?, ?> rsc = rp.getResource();
if (!(rsc instanceof AbstractGridResource))
continue;
if (rsc instanceof GeneratedEnsembleGridResource)
continue;
if (!isResourceEnsemble((D2DGridResource) rsc))
continue;
String model = ((D2DGridResource) rsc).getLegendParameters().model;
if (models.contains(model))
continue;
models.add(model);
}
return models;
}
// Get ensemble members of a model. This function will be used by other
// without have a object
public static List<String> getEnsembleMembers(String model) {
ArrayList<String> members = new ArrayList<String>();
// check all resources
IMapDescriptor desc = (IMapDescriptor) (getEditor()
.getActiveDisplayPane().getDescriptor());
ResourceList rscList = desc.getResourceList();
for (ResourcePair rp : rscList) {
AbstractVizResource<?, ?> rsc = rp.getResource();
if (!(rsc instanceof AbstractGridResource))
continue;
if (rsc instanceof GeneratedEnsembleGridResource)
continue;
if (!isResourceEnsemble((D2DGridResource) rsc))
continue;
if (!((D2DGridResource) rsc).getLegendParameters().model
.equals(model))
continue;
String member = ((D2DGridResource) rsc).getLegendParameters().ensembleId;
if (members.contains(member))
continue;
members.add(member);
}
return members;
}
// If it is a contour ensemble resource
public static boolean isResourceEnsemble(D2DGridResource rsc) {
// What display type?
// AbstractGriddedDisplay gridDisplay=
// (AbstractGriddedDisplay) rsc.getDescriptor().getRenderableDisplay();
if (!rsc.getDisplayType().equals(DisplayType.CONTOUR))
return false;
// Is it ensemble grid? Just use the getLegendParameters().ensembleId
// now
// Should check on grid data records later
if (rsc.getLegendParameters().ensembleId == null
|| rsc.getLegendParameters().ensembleId.isEmpty())
return false;
return true;
}
/**
* Get the current Editor.
*
* @return
*/
private static AbstractEditor getEditor() {
AbstractEditor editor = (AbstractEditor) EditorUtil.getActiveEditor();
return editor;
}
}

View file

@ -0,0 +1,625 @@
package gov.noaa.gsd.viz.ensemble.display.control;
import gov.noaa.gsd.viz.ensemble.display.common.GenericResourceHolder;
import gov.noaa.gsd.viz.ensemble.display.common.NavigatorResourceList;
import gov.noaa.gsd.viz.ensemble.display.rsc.histogram.HistogramResource;
import gov.noaa.gsd.viz.ensemble.navigator.ui.layer.EnsembleToolManager;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.IDisposeListener;
import com.raytheon.uf.viz.core.rsc.IRefreshListener;
import com.raytheon.uf.viz.core.rsc.ResourceList;
import com.raytheon.uf.viz.core.rsc.capabilities.DensityCapability;
import com.raytheon.uf.viz.xy.timeseries.rsc.TimeSeriesResource;
import com.raytheon.viz.grid.rsc.general.AbstractGridResource;
import com.raytheon.viz.ui.editor.AbstractEditor;
//jing test
/**
* Handle all ensemble products member resources, reference to any loaded grid
* product may be treated as ensemble member, and ensemble calculation result
* resources. This is the key architectural support class connecting the
* ensemble display, GUI and D2D core. It tracks and the status of the handled
* resources, controls them, keeps the ensemble tool works with D2D core display
* synchronously. All registered resources are in the ensembleToolResourcesMap.
*
*
* @author jing
* @author polster
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb, 2014 5056 jing Initial creation
*
* </pre>
*/
public class EnsembleResourceManager implements IRefreshListener,
IDisposeListener {
private final static double DEFAULT_DENSITY = 0.15;
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(EnsembleResourceManager.class);
/**
* The ensemble marked resource list holds the loaded and generated
* resources and flags, is used by ensemble display, GUI and calculation.
*/
private ConcurrentHashMap<AbstractEditor, NavigatorResourceList> ensembleToolResourcesMap;
/**
* The instance of the manager, a singleton.
*/
public static EnsembleResourceManager instance = null;
/**
* The listeners to any resource, GUI... change event Register to any bus or
* event list for to catch interesting events
*/
private EnsembleResourceManager() {
ensembleToolResourcesMap = new ConcurrentHashMap<AbstractEditor, NavigatorResourceList>();
}
/**
* Get the resource manager.
*
* @return
*/
public static EnsembleResourceManager getInstance() {
if (instance == null) {
instance = new EnsembleResourceManager();
}
return instance;
}
/**
* call this, for example, at EnsembleToolManager initialization time when a
* new tool layer is created but there are no resources yet added to it.
*
* @param editor
* - The product is loaded into which editor/window.
*/
public void registerToolLayer(AbstractEditor editor) {
if (ensembleToolResourcesMap.get(editor) == null) {
ensembleToolResourcesMap.put(editor, new NavigatorResourceList(
editor));
}
}
/**
* Get the resources in an editor.
*
* @param editor
* - The product is loaded into which editor/window.
* @return - list of resources.
*/
public NavigatorResourceList getResourceList(AbstractEditor editor) {
NavigatorResourceList list = null;
if (editor != null) {
list = ensembleToolResourcesMap.get(editor);
}
return list;
}
/**
* Clean the resources in an editor.
*
* @param editor
* - The product is loaded into which editor/window.
*/
public void clearResourceList(AbstractEditor editor) {
ensembleToolResourcesMap.get(editor).clear();
}
/**
* Register a loaded model product in ensemble status
*
* @param rsc
* - the loaded product resource
* @param guiUpdate
* - if need update GUI
*/
public void registerResource(AbstractVizResource<?, ?> rsc,
AbstractEditor editor, boolean guiUpdate) {
if ((rsc == null) || (!isToolLayerReady()))
return;
// if this is the first resource ever registered using this editor
// then create the resource list and map it to the editor ...
if (ensembleToolResourcesMap.get(editor) == null) {
ensembleToolResourcesMap.put(editor, new NavigatorResourceList(
editor));
if (!EnsembleToolManager.getInstance().hasToolLayer(editor)) {
EnsembleToolManager.getInstance().addToolLayer(editor);
}
}
// no duplicates
if (rsc == null
|| (ensembleToolResourcesMap.get(editor).containsResource(rsc))) {
return;
}
// TODO: Must refactor so we don't use setSystemResource(boolean)
// method.
rsc.getProperties().setSystemResource(true);
rsc.registerListener((IRefreshListener) this);
rsc.registerListener((IDisposeListener) this);
// Set to default density for all loaded resources if can
// But it may be unmatched with current display. Fix it later
if (rsc.getCapability(DensityCapability.class) != null) {
DensityCapability densityCapability = (DensityCapability) rsc
.getCapability(DensityCapability.class);
densityCapability.setDensity(DEFAULT_DENSITY);
}
GenericResourceHolder ensToolResource = GenericResourceHolder
.createResourceHolder(rsc, true);
ensToolResource.setGenerated(false);
ensembleToolResourcesMap.get(editor).add(ensToolResource);
syncRegisteredResource(editor);
if (guiUpdate) {
notifyClientListChanged();
// now update the calculation ensemble resource, if not already
// set ... there can only be one, and it will be the first
// ensemble resource loaded into this editor ...
ensembleToolResourcesMap.get(editor)
.updateEnsembleCalculationResource();
}
}
/**
* Remove a resource from the tracking list.
*
* @param gr
* - the tracked resource.
* @param editor
* - loaded editor.
* @param notifyGUI
* - if update the GUI.
*/
public void unregisterResource(GenericResourceHolder gr,
AbstractEditor editor, boolean notifyGUI) {
if (gr == null
|| ensembleToolResourcesMap.get(editor).getResourceHolders()
.isEmpty())
return;
ensembleToolResourcesMap.get(editor).remove(gr);
gr.getRsc().unregisterListener((IRefreshListener) this);
gr.getRsc().unregisterListener((IDisposeListener) this);
syncRegisteredResource(editor);
// if requested, notify client the known loaded resources have changed
if (notifyGUI) {
notifyClientListChanged();
}
}
/**
* Register Ensemble generated resource. after creating or deleting a
* ensemble generated resource.
*
* @param rsc
* - the generated resource by calculation.
*/
public void registerGenerated(AbstractVizResource<?, ?> rsc,
AbstractEditor editor) {
if ((rsc == null) || (!isToolLayerReady()))
return;
// This may be the first resource ever registered using this editor. If
// so, then create the resource list and associate it with the editor
// using the map ...
if (ensembleToolResourcesMap.get(editor) == null) {
ensembleToolResourcesMap.put(editor, new NavigatorResourceList(
editor));
if (!EnsembleToolManager.getInstance().hasToolLayer(editor)) {
EnsembleToolManager.getInstance().addToolLayer(editor);
}
}
// Only one instance of a generated resource is saved. If a duplicate is
// created then have it overwrite the existing one.
for (GenericResourceHolder gr : ensembleToolResourcesMap.get(editor)
.getUserGeneratedRscs()) {
// Remove any resource in this list, since it was unloaded/ isn't
// existing.
if (!gr.getRsc().getDescriptor().getResourceList()
.containsRsc(gr.getRsc())) {
ensembleToolResourcesMap.get(editor).remove(gr);
} else if (rsc.getClass().cast(rsc).getName()
.equals(gr.getRsc().getClass().cast(gr.getRsc()).getName())) {
// Same generated resource name, unload old one
ensembleToolResourcesMap.get(editor).remove(gr);
gr.getRsc().getDescriptor().getResourceList()
.removeRsc(gr.getRsc());
}
}
// Set to default density for all loaded resources if can
// But it may be unmatched with current display. Fix it later
// TODO: why are we testing for this for tool layer ready yet again?
// Set resource as a system resource so we don't show the legend
// in the main map, because we will display it in the ensemble
// navigator view.
if (isToolLayerReady()) {
// TODO: Must refactor so we don't use setSystemResource(boolean)
// method.
rsc.getProperties().setSystemResource(true);
rsc.registerListener((IRefreshListener) this);
rsc.registerListener((IDisposeListener) this);
}
GenericResourceHolder ensToolResource = GenericResourceHolder
.createResourceHolder(rsc, true);
ensToolResource.setGenerated(true);
ensembleToolResourcesMap.get(editor).add(ensToolResource);
syncRegisteredResource(editor);
// notify any client?
notifyClientListChanged();
}
/**
* Remove a generated resource from the tracking list.
*
* @param gr
* - the tracked resource.
* @param editor
* - loaded editor.
* @param notifyGUI
* - if update the GUI.
*/
public void unregisterGenerated(GenericResourceHolder gr,
AbstractEditor editor, boolean notifyGUI) {
if (gr == null
|| ensembleToolResourcesMap.get(editor).getResourceHolders()
.isEmpty())
return;
ensembleToolResourcesMap.get(editor).remove(gr);
gr.getRsc().unregisterListener((IRefreshListener) this);
gr.getRsc().unregisterListener((IDisposeListener) this);
// notify client the generated resource change?
syncRegisteredResource(editor);
// if requested, notify client the known loaded resources have changed
if (notifyGUI) {
notifyClientListChanged();
}
}
/**
* Get the resource name string
*
* @param editor
* - resource editor
* @return- resource name
*/
public String getTimeBasisResourceName(AbstractEditor editor) {
return getResourceList(editor).getTimeBasisResourceName();
}
/**
* Get the resource legend time
*
* @param editorr
* - resource editor
* @return- legend time
*/
public String getTimeBasisLegendTime(AbstractEditor editor) {
return getResourceList(editor).getTimeBasisLegendTime();
}
/**
* Handle ENS GUI change
*
* @param editor
*/
public void updateFrameChanges(AbstractEditor editor) {
syncRegisteredResource(editor);
// update generated ensemble Resource if need
updateGenerated(editor);
}
/**
* Synchronous the registered resource list within editor(s) and GUI.
* --verify if the resources in the list are existing. --Remove any resource
* in this list, if it was unloaded. --notify GUI if there is any change.
*/
public void syncRegisteredResource(AbstractEditor editor) {
if (ensembleToolResourcesMap.get(editor) == null) {
return;
}
if (ensembleToolResourcesMap.get(editor).getAllRscsAsList() == null
|| ensembleToolResourcesMap.get(editor).getAllRscsAsList()
.isEmpty())
return;
for (GenericResourceHolder gr : ensembleToolResourcesMap.get(editor)
.getAllRscsAsList()) {
// verify if the resources in the list are existing.
if (!gr.getRsc().getDescriptor().getResourceList()
.containsRsc(gr.getRsc())) {
// Remove any resource in this list, since it was unloaded/
// isn't existing.
ensembleToolResourcesMap.get(editor).remove(gr);
// notify GUI if there is any change.
notifyClientListChanged();
} else {
// don't show legend if Tool Layer is editable
if (isToolLayerReady()) {
gr.getRsc().getProperties().setSystemResource(true);
}
// Marks to unselected if the resource is not visible
// set resource selection to true.
if (gr.getRsc().getProperties().isVisible()) {
gr.setSelected(true);
} else {
gr.setSelected(false);
}
}
}
}
/**
* * check for all ensemble interested resources if they are out of control
* by the ensemble resource manager. Current interested resources are
* AbstractGridResource ETimeSeriesResouece, HistogramResource
*
* @param editor
* @return
*/
public ResourceList searchUnryncRegisteredResource(AbstractEditor editor) {
ResourceList unRegisteredResources = new ResourceList();
// The registered resources for this editor in the manager
List<GenericResourceHolder> resourceList = ensembleToolResourcesMap
.get(editor).getAllRscsAsList();
IDescriptor desc = (editor.getActiveDisplayPane().getDescriptor());
ResourceList rscList = desc.getResourceList();
for (ResourcePair rp : rscList) {
AbstractVizResource<?, ?> rsc = rp.getResource();
if ((rsc instanceof AbstractGridResource)
|| (rsc instanceof TimeSeriesResource)
|| (rsc instanceof HistogramResource)) {
boolean isRegistered = false;
for (GenericResourceHolder rcsHolder : resourceList) {
if (rcsHolder.getRsc() == rsc) {
isRegistered = true;
}
}
if (!isRegistered) {
unRegisteredResources.add(rp);
}
}
}
return unRegisteredResources;
}
/**
* check if the ensemble GUI is reday
*
* @return
*/
public boolean isToolLayerReady() {
return EnsembleToolManager.getInstance().isReady();
}
/**
* Check generated resources update data and display if need
*
* @param editor
*/
public void updateGenerated(AbstractEditor editor) {
if (ensembleToolResourcesMap.get(editor) == null) {
return;
}
if (ensembleToolResourcesMap.get(editor).getUserGeneratedRscs() == null
|| ensembleToolResourcesMap.get(editor).getUserGeneratedRscs()
.isEmpty())
return;
// Check each resource if need update
for (GenericResourceHolder rsc : ensembleToolResourcesMap.get(editor)
.getUserGeneratedRscs()) {
// TODO
}
}
/**
* post process in this level
*
* @param editor
*/
public void disposal(AbstractEditor editor) {
clearResourceList(editor);
saveResourceList(editor);
// notify client
notifyClientListChanged();
}
/**
* Marked ResourceList change event should be sent to client by the event
* bus. The client can be GUI, calculator, etc, ...
*/
private void notifyClientListChanged() {
if (isToolLayerReady()) {
EnsembleToolManager.getInstance().refreshView();
}
}
/**
* Save the registered resource list into a file
*
* @param editor
*/
private void saveResourceList(AbstractEditor editor) {
clearResourceList(editor);
// notify client
notifyClientListChanged();
}
private static long LAST_TIME = 0;
private final int BUNCH_REFRESH_REQUESTS_PERIOD = 500;
private Thread forceReset = null;
@Override
public void refresh() {
// but wait for other resource pairs to catch up ...
if (((LAST_TIME == 0) || ((System.currentTimeMillis() - LAST_TIME) > BUNCH_REFRESH_REQUESTS_PERIOD))) {
if (LAST_TIME == 0) {
// TODO: wait for the resource (e.g. unit conversion) to
// update. We probably need to look for a better solution.
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// ignore
}
}
LAST_TIME = System.currentTimeMillis();
/**
* Register the resource in to the resource manager
*/
if (forceReset != null) {
synchronized (forceReset) {
// restart the timer for another interval to pass
// before forcing a "final" refresh
if (forceReset.isAlive()) {
forceReset.interrupt();
}
forceReset = null;
forceReset = new Thread(new ForceRefreshIfNecessary(
BUNCH_REFRESH_REQUESTS_PERIOD));
forceReset.start();
}
} else {
forceReset = new Thread(new ForceRefreshIfNecessary(
BUNCH_REFRESH_REQUESTS_PERIOD));
forceReset.start();
}
}
}
private class ForceRefreshIfNecessary implements Runnable {
int waitForReset = 0;
ForceRefreshIfNecessary(int waitTime) {
waitForReset = waitTime;
}
@Override
public void run() {
try {
Thread.sleep(waitForReset);
notifyClientListChanged();
} catch (InterruptedException e) {
/* ignore */
}
}
}
/**
* post process for other interfaces
*
*/
@Override
public void disposed(AbstractVizResource<?, ?> rsc) {
// TODO Find editor for resource ...
// TODO ResourceManager.getInstance().unregisterResource(rsc);
notifyClientListChanged();
}
/**
* Set ensemble calculation resource name.
*
* @param editor
* @param rscGroupName
*/
public void setEnsembleCalculationResourceName(AbstractEditor editor,
String rscGroupName) {
if (ensembleToolResourcesMap.get(editor) != null) {
ensembleToolResourcesMap.get(editor)
.setEnsembleCalculationResource(rscGroupName);
}
}
/**
* Get ensemble calculation resource name.
*
* @param editor
* @return
*/
public String getEnsembleCalculationResourceName(AbstractEditor editor) {
String name = "";
if (ensembleToolResourcesMap.get(editor) != null) {
name = ensembleToolResourcesMap.get(editor)
.getEnsembleCalculationResource();
}
return name;
}
}

View file

@ -0,0 +1,337 @@
package gov.noaa.gsd.viz.ensemble.display.control;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.IRenderableDisplay;
import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.IInitListener;
import com.raytheon.uf.viz.core.rsc.ResourceList;
import com.raytheon.uf.viz.core.rsc.ResourceList.AddListener;
import com.raytheon.uf.viz.core.rsc.ResourceList.RemoveListener;
import com.raytheon.uf.viz.xy.timeseries.rsc.TimeSeriesResource;
import com.raytheon.viz.grid.rsc.general.GridResource;
import com.raytheon.viz.ui.EditorUtil;
import com.raytheon.viz.ui.editor.AbstractEditor;
import com.raytheon.viz.ui.perspectives.IRenderableDisplayCustomizer;
/**
* Notice ensemble resource manager a resource has been changed. TODO This
* description needs to be improved once we do an improvement DR.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Dec 9, 2014 5056 epolster jing Initial creation
*
* </pre>
*
* @author polster
* @author jing
* @version 1.0
*/
public class EnsembleToolDisplayCustomizer implements
IRenderableDisplayCustomizer {
/**
* The ensemble marked resource list holds the loaded and generated
* resources and flags, is used by ensemble display, GUI and calculation.
*/
static final private List<ResourcePair> batchedPairs = new CopyOnWriteArrayList<ResourcePair>();
/** List of listeners we have for renderable displays */
private final List<EnsembleToolRscLoadListener> listeners = new ArrayList<EnsembleToolRscLoadListener>();
@Override
public void customizeDisplay(IRenderableDisplay display) {
boolean add = true;
for (EnsembleToolRscLoadListener listener : listeners) {
if (display == listener.getDisplay()) {
add = false;
break;
}
}
if (add) {
listeners.add(new EnsembleToolRscLoadListener(display));
}
}
@Override
public void uncustomizeDisplay(IRenderableDisplay display) {
EnsembleToolRscLoadListener toRemove = null;
for (EnsembleToolRscLoadListener listener : listeners) {
if (listener.getDisplay() == display) {
toRemove = listener;
break;
}
}
if (toRemove != null) {
toRemove.dispose();
listeners.remove(toRemove);
}
}
private static class EnsembleToolRscLoadListener implements AddListener,
RemoveListener, IInitListener {
// for batching purposes, keep track of the amount of time spent between
// requests
private static long LAST_TIME = 0;
// maximum wait period for batching
private final int ALLOW_FOR_BUNCHED_REQUESTS_PERIOD = 1400;
// thread which acts upon batched requests
private Thread forceReset = null;
// the display we are listening to
private final IRenderableDisplay display;
private boolean resourcesAdded = false;
public EnsembleToolRscLoadListener(IRenderableDisplay display) {
this.display = display;
IDescriptor descriptor = display.getDescriptor();
ResourceList list = descriptor.getResourceList();
if (hasCompatibleResource(list)) {
addResources(descriptor);
}
list.addPostAddListener(this);
list.addPostRemoveListener(this);
}
public void dispose() {
ResourceList list = display.getDescriptor().getResourceList();
list.removePostAddListener(this);
list.removePostRemoveListener(this);
}
/**
* Notice a resource has been removed. (non-Javadoc)
*
* @see com.raytheon.uf.viz.core.rsc.ResourceList.RemoveListener#notifyRemove
* (com.raytheon.uf.viz.core.drawables.ResourcePair) TODO: This
* approach will be improved in an upcoming DR
*/
@Override
public synchronized void notifyRemove(ResourcePair rp)
throws VizException {
/**
* Pass,if the resource is not interested by ensemble tool
*
*/
if (!isCompatibleResource(rp)) {
return;
}
/**
* Remove it from the resource manager and update GUI Should notice
* GUI
*/
if (getEditor() != null) {
EnsembleResourceManager.getInstance().syncRegisteredResource(
getEditor());
}
}
/**
* Notice a resource has been added. (non-Javadoc)
*
* @see com.raytheon.uf.viz.core.rsc.ResourceList.AddListener#notifyAdd(com
* .raytheon .uf.viz.core.drawables.ResourcePair)
*/
@Override
public void notifyAdd(ResourcePair rp) throws VizException {
/**
* Return if the resource is not interested by ensemble tool
*
*/
if (!isCompatibleResource(rp)) {
return;
}
// add the resource pair to the staging list ...
addButNoDuplicates(rp);
// but wait for other resource pairs to catch up ...
if (((LAST_TIME == 0) || ((System.currentTimeMillis() - LAST_TIME) > ALLOW_FOR_BUNCHED_REQUESTS_PERIOD))) {
if (LAST_TIME == 0) {
// TODO: wait for the resource (e.g. unit conversion) to
// update. We probably need to look for a better solution.
try {
Thread.sleep(150);
} catch (InterruptedException e) {
// ignore
}
}
LAST_TIME = System.currentTimeMillis();
/**
* Register the resource in to the resource manager
*/
if (forceReset != null) {
synchronized (forceReset) {
// restart the timer for another interval to pass
// before forcing a "final" refresh
if (forceReset.isAlive()) {
forceReset.interrupt();
}
forceReset = null;
forceReset = new Thread(new ForceRefreshIfNecessary(
ALLOW_FOR_BUNCHED_REQUESTS_PERIOD));
forceReset.start();
}
} else {
forceReset = new Thread(new ForceRefreshIfNecessary(
ALLOW_FOR_BUNCHED_REQUESTS_PERIOD));
forceReset.start();
}
}
}
// add the resource pair if it is not already in the list.
private void addButNoDuplicates(ResourcePair rp) {
boolean found = false;
for (ResourcePair orp : EnsembleToolDisplayCustomizer.batchedPairs) {
if (orp.getResource().getName()
.compareTo(rp.getResource().getName()) == 0) {
found = true;
break;
}
}
if (!found) {
EnsembleToolDisplayCustomizer.batchedPairs.add(rp);
}
}
private class ForceRefreshIfNecessary implements Runnable {
int waitForReset = 0;
ForceRefreshIfNecessary(int waitTime) {
waitForReset = waitTime;
}
@Override
public void run() {
try {
Thread.sleep(waitForReset);
addResourceToManager(EnsembleToolDisplayCustomizer.batchedPairs);
} catch (InterruptedException e) {
/* ignore */
}
}
}
/**
* Pass a resource into the ensemble resource manager.
*
* @param resourcePairs
*/
private void addResourceToManager(List<ResourcePair> resourcePairs) {
Iterator<ResourcePair> pairsIter = resourcePairs.iterator();
ResourcePair rp = null;
while (pairsIter.hasNext()) {
rp = pairsIter.next();
if (pairsIter.hasNext()) {
EnsembleResourceManager.getInstance().registerResource(
rp.getResource(), getEditor(), false);
} else {
EnsembleResourceManager.getInstance().registerResource(
rp.getResource(), getEditor(), true);
}
}
}
/**
* Get the current Editor.
*
* @return
*/
private static AbstractEditor getEditor() {
AbstractEditor editor = (AbstractEditor) EditorUtil
.getActiveEditor();
return editor;
}
/**
*
* @param list
* @return
*/
private boolean hasCompatibleResource(ResourceList list) {
for (ResourcePair rp : list) {
if (isCompatibleResource(rp)) {
return true;
}
}
return false;
}
private boolean isCompatibleResource(ResourcePair rp) {
AbstractVizResource<?, ?> resource = rp.getResource();
if (resource != null) {
if (resource instanceof GridResource
|| resource instanceof TimeSeriesResource) {
return true;
}
}
return false;
}
private synchronized void addResources(IDescriptor descriptor) {
if (!resourcesAdded) {
ResourceList list = descriptor.getResourceList();
list.instantiateResources(descriptor, true);
resourcesAdded = true;
}
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.core.rsc.IInitListener#inited(com.raytheon.uf
* .viz.core.rsc.AbstractVizResource)
*/
@Override
public synchronized void inited(AbstractVizResource<?, ?> rsc) {
if (rsc instanceof GridResource) {
if (!resourcesAdded) {
addResources(rsc.getDescriptor());
}
}
}
/**
* Get the IRenderableDisplay
*
* @return the display
*/
public IRenderableDisplay getDisplay() {
return display;
}
}
}

View file

@ -0,0 +1,430 @@
package gov.noaa.gsd.viz.ensemble.display.control.load;
import gov.noaa.gsd.viz.ensemble.display.calculate.Calculation;
import gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator;
import gov.noaa.gsd.viz.ensemble.display.common.GenericResourceHolder;
import gov.noaa.gsd.viz.ensemble.display.common.TimeSeriesResourceHolder;
import gov.noaa.gsd.viz.ensemble.display.control.EnsembleResourceManager;
import gov.noaa.gsd.viz.ensemble.display.rsc.GeneratedEnsembleGridResourceData;
import gov.noaa.gsd.viz.ensemble.display.rsc.histogram.HistogramResource;
import gov.noaa.gsd.viz.ensemble.display.rsc.histogram.HistogramResourceData;
import gov.noaa.gsd.viz.ensemble.display.rsc.timeseries.GeneratedTimeSeriesResourceData;
import java.util.ArrayList;
import java.util.List;
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.exception.VizException;
import com.raytheon.uf.viz.core.map.IMapDescriptor;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.xy.timeseries.display.TimeSeriesDescriptor;
import com.raytheon.viz.ui.VizWorkbenchManager;
import com.raytheon.viz.ui.editor.AbstractEditor;
/**
* Loads the generated ensemble resource(s) into active or specified display
* editor and panel. The resource type is dependent upon the display type. The
* resource(s) will be registered in the Resource manager;
*
* @author jing
* @author polster
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jan 2014 5056 jing Initial creation
*
* </pre>
*/
public class GeneratedDataLoader {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(GeneratedDataLoader.class);
public enum GeneratedloadMode {
SAME_UNIT, SAME_UNIT_AND_LEVEL
}
private GeneratedloadMode generatedloadMode = GeneratedloadMode.SAME_UNIT_AND_LEVEL;
private AbstractEditor editor = null;
// levels includes: Level + Parameter (e.g. 500MB)
private List<String> levels;
private List<String> units;
public GeneratedDataLoader(AbstractEditor e, GeneratedloadMode glm) {
generatedloadMode = glm;
levels = new ArrayList<String>();
units = new ArrayList<String>();
editor = e;
}
/**
* Load into current active editor and panel of Plan-View
*
* @param calculator
* - the calculator of the loading overlay.
*/
public void loadToMapEditor(final EnsembleCalculator calculator) {
AbstractEditor theEditor = (AbstractEditor) VizWorkbenchManager
.getInstance().getActiveEditor();
if (!(theEditor.getActiveDisplayPane().getDescriptor() instanceof IMapDescriptor)) {
return;
}
searchLoadedResourcesMapEditor(theEditor);
if (units.isEmpty()) {
return;
}
if (generatedloadMode == GeneratedloadMode.SAME_UNIT_AND_LEVEL
&& !levels.isEmpty()) {
Thread t = null;
for (final String level : levels) {
for (final String unit : units) {
// load to the map editor
t = new Thread() {
public void run() {
AbstractEditor theEditor = (AbstractEditor) VizWorkbenchManager
.getInstance().getActiveEditor();
if (!(theEditor.getActiveDisplayPane()
.getDescriptor() instanceof IMapDescriptor))
return;
GeneratedEnsembleGridResourceData ensembleData = new GeneratedEnsembleGridResourceData(
calculator,
theEditor.getActiveDisplayPane()
.getDescriptor(), level, unit);
LoadProperties loadProperties = new LoadProperties();
try {
ensembleData.construct(loadProperties,
theEditor.getActiveDisplayPane()
.getDescriptor());
} catch (VizException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
}
theEditor.refresh();
}
};
t.start();
}
}
}
}
/**
* Load into current active editor and panel of Time Series
*
* @param calculator
* - the calculator of the loading overlay.
*/
public void loadToTimeSeriesEditor(final EnsembleCalculator calculator) {
AbstractEditor theEditor = (AbstractEditor) VizWorkbenchManager
.getInstance().getActiveEditor();
if (!(theEditor.getActiveDisplayPane().getDescriptor() instanceof TimeSeriesDescriptor))
return;
searchLoadedResourcesTimeSeriesEditor(theEditor);
if (units.isEmpty())
return;
if (generatedloadMode == GeneratedloadMode.SAME_UNIT_AND_LEVEL
&& !levels.isEmpty()) {
Thread t = null;
for (final String level : levels) {
for (final String unit : units) {
// load to the map editor
t = new Thread() {
public void run() {
// Temp solution!!!!
AbstractEditor theEditor = (AbstractEditor) VizWorkbenchManager
.getInstance().getActiveEditor();
if (!(theEditor.getActiveDisplayPane()
.getDescriptor() instanceof TimeSeriesDescriptor))
return;
GeneratedTimeSeriesResourceData ensembleData = new GeneratedTimeSeriesResourceData(
calculator, level, unit);
LoadProperties loadProperties = new LoadProperties();
try {
ensembleData.construct(loadProperties,
theEditor.getActiveDisplayPane()
.getDescriptor());
} catch (VizException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
}
theEditor.refresh();
}
};
t.start();
}
}
}
}
// Load into current active editor and panel
// of ...
/**
* Load into editor
*
* @param calculator
*/
public void load(final EnsembleCalculator calculator) {
if (editor.getActiveDisplayPane().getDescriptor() instanceof TimeSeriesDescriptor) {
loadToTimeSeriesEditor(calculator);
} else if (editor.getActiveDisplayPane().getDescriptor() instanceof IMapDescriptor) {
loadToMapEditor(calculator);
}
}
/**
* Load a overlay, such the histogram,,fire weather...
*
* @param overlay
*/
public void loadOverlay(final Calculation overlay) {
AbstractEditor theEditor = (AbstractEditor) VizWorkbenchManager
.getInstance().getActiveEditor();
if (!(theEditor.getActiveDisplayPane().getDescriptor() instanceof IMapDescriptor)) {
return;
}
searchLoadedResourcesMapEditor(theEditor);
if (units.isEmpty()) {
return;
}
if (generatedloadMode == GeneratedloadMode.SAME_UNIT_AND_LEVEL
&& !levels.isEmpty()) {
Thread t = null;
for (final String level : levels) {
for (final String unit : units) {
// load to the map editor
t = new Thread() {
public void run() {
AbstractEditor theEditor = (AbstractEditor) VizWorkbenchManager
.getInstance().getActiveEditor();
if (!(theEditor.getActiveDisplayPane()
.getDescriptor() instanceof IMapDescriptor)) {
return;
}
if (overlay == Calculation.HISTOGRAM_SAMPLING) {
// how to pass the level and unit into the
// HistogramResource
HistogramResourceData resourceData = new HistogramResourceData(
theEditor.getActiveDisplayPane()
.getDescriptor(), level, unit,
HistogramResource.DisplayMode.SAMPLING);
LoadProperties loadProperties = new LoadProperties();
try {
resourceData.construct(loadProperties,
theEditor.getActiveDisplayPane()
.getDescriptor());
} catch (VizException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
}
} else if (overlay == Calculation.HISTOGRAM_TEXT) {
// how to pass the level and unit into the
// HistogramResource
HistogramResourceData resourceData = new HistogramResourceData(
theEditor.getActiveDisplayPane()
.getDescriptor(),
level,
unit,
HistogramResource.DisplayMode.TEXT_HISTGRAM);
LoadProperties loadProperties = new LoadProperties();
try {
resourceData.construct(loadProperties,
theEditor.getActiveDisplayPane()
.getDescriptor());
} catch (VizException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
}
}
theEditor.refresh();
}
};
t.start();
}
}
}
}
/**
* Load into current active panel of all editors in the main window
*
* @param calculator
*/
public void loadToMultipleEditors(EnsembleCalculator calculator) {
// TODO
// searchLoadedResources();
}
/**
* Load into all panels of current active editor. Each panel may with
* different display type, such as Plan-view and Time-Series. This is for
* supporting interactive * @param calculator
*/
public void loadToMultiplePanels(EnsembleCalculator calculator) {
// TODO
// searchLoadedResources();
}
/**
* Get the load mode of the generated resource.
*
* @return
*/
public GeneratedloadMode getGeneratedloadMode() {
return generatedloadMode;
}
/**
* Set the load mode of the generated resource.
*
* @param generatedloadMode
* - load mode
*/
public void setGeneratedloadMode(GeneratedloadMode generatedloadMode) {
this.generatedloadMode = generatedloadMode;
}
/**
* Get all levels of the resources.
*
* @return- levels string list
*/
public List<String> getLevels() {
return levels;
}
/**
* set levels of the resources.
*
* @param levels
*/
public void setLevel(List<String> levels) {
this.levels = levels;
}
/**
* Get units of the resources.
*
* @return
*/
public List<String> getUnits() {
return units;
}
/**
* Set units of the resources.
*
* @param units
*/
public void setUnits(List<String> units) {
this.units = units;
}
/**
* Based current generated ensemble load mode, search the level and unit in
* the loaded resource, which are used to decide the generated ensemble
* resource(s)
*/
private void searchLoadedResourcesMapEditor(AbstractEditor editor) {
levels.clear();
units.clear();
List<GenericResourceHolder> rscs = EnsembleResourceManager.instance
.getResourceList(editor).getUserLoadedRscs();
// Search levels and units in the current loaded resource
for (GenericResourceHolder gr : rscs) {
// TODO: How about resource with other descriptors?
if (gr.getUniqueName() == null || gr.getUniqueName().equals(""))
continue;
// levels.add(..)
String level = gr.getLevel();
if (!levels.contains(level)) {
levels.add(level);
}
// units.add(...)
String unit = gr.getUnits();
if (!units.contains(unit)) {
units.add(unit);
}
}
}
/**
* Search the levels and units in the loaded resources
*
* @param editor
*/
private void searchLoadedResourcesTimeSeriesEditor(AbstractEditor editor) {
levels.clear();
units.clear();
List<GenericResourceHolder> rscs = EnsembleResourceManager.instance
.getResourceList(editor).getUserLoadedRscs();
// Search levels and units in the current loaded resource
for (GenericResourceHolder gr : rscs) {
if (gr instanceof TimeSeriesResourceHolder) {
if (gr.getRsc().getName() == null || gr.getRsc().equals(""))
continue;
// levels.add(..)
String level = gr.getLevel();
if (!levels.contains(level)) {
levels.add(level);
}
// units.add(...)
String unit = gr.getUnits();
if (!units.contains(unit))
units.add(unit);
}
}
}
}

View file

@ -0,0 +1,45 @@
package gov.noaa.gsd.viz.ensemble.display.control.multipanels;
import com.raytheon.uf.viz.core.IDisplayPane;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.viz.ui.editor.AbstractEditor;
/**
* Manage ensemble display Panes. Support the multiple panel display in the
* ensemble. For example show up the plan view and time series windows at same
* time, select a location and update the point in the time series.
*
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb, 2014 5056 jing Initial creation
*
* </pre>
*/
public class EnsemblePanelManager {
AbstractEditor theEditor;
public void load(IDisplayPane pane, AbstractVizResource<?, ?> rsc) {
// TODO
}
public void unload(IDisplayPane pane, AbstractVizResource<?, ?> rsc) {
// TODO
}
public void unloadAll(IDisplayPane pane, AbstractVizResource<?, ?> rsc) {
// TODO
}
public void cleanPanes(IDisplayPane pane, AbstractVizResource<?, ?> rsc) {
// TODO
}
}

View file

@ -0,0 +1,397 @@
package gov.noaa.gsd.viz.ensemble.display.rsc;
import gov.noaa.gsd.viz.ensemble.display.calculate.Calculation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.measure.unit.Unit;
import org.eclipse.swt.graphics.RGB;
import com.raytheon.uf.common.dataplugin.grid.GridRecord;
import com.raytheon.uf.common.dataplugin.grid.dataset.DatasetInfo;
import com.raytheon.uf.common.dataplugin.grid.dataset.DatasetInfoLookup;
import com.raytheon.uf.common.dataplugin.grid.util.GridLevelTranslator;
import com.raytheon.uf.common.geospatial.ReferencedCoordinate;
import com.raytheon.uf.common.parameter.Parameter;
import com.raytheon.uf.common.style.ParamLevelMatchCriteria;
import com.raytheon.uf.common.style.level.Level;
import com.raytheon.uf.common.style.level.SingleLevel;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.map.IMapDescriptor;
import com.raytheon.uf.viz.core.rsc.AbstractNameGenerator;
import com.raytheon.uf.viz.core.rsc.AbstractResourceData;
import com.raytheon.uf.viz.core.rsc.DisplayType;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.RenderingOrderFactory.ResourceOrder;
import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.DisplayTypeCapability;
import com.raytheon.viz.grid.rsc.GridNameGenerator;
import com.raytheon.viz.grid.rsc.GridNameGenerator.IGridNameResource;
import com.raytheon.viz.grid.rsc.GridNameGenerator.LegendParameters;
import com.raytheon.viz.grid.rsc.GridResourceData;
import com.raytheon.viz.grid.rsc.general.GeneralGridData;
import com.raytheon.viz.grid.rsc.general.GridResource;
import com.raytheon.viz.grid.xml.FieldDisplayTypesFactory;
/**
* Based on the loaded ensemble product(s) data generated new resource with a
* calculate method. Implement steps: First-Basic contour display. Second-
* Vector-wind bar display. Third- Image display and sampling. Forth-
* Auto-updating and other. Issue:Since extend the GridResource, how to black
* request data from EDEX? Current solution is to override and minor change
* related
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jan 2014 5056 jing Initial creation
*
* </pre>
*/
@SuppressWarnings({ "hiding", "rawtypes", "unchecked" })
public class GeneratedEnsembleGridResource<GeneratedEnsembleGridResourceData>
extends GridResource implements IGridNameResource {
private Parameter parameter = null;
private GridRecord randomRec = null;
private ParamLevelMatchCriteria paramLevelMatchCriteria = null;
private ArrayList<String> models = null;
private Calculation calculation = Calculation.NONE;
protected Random rand;
/**
* Calculate method like mean
*/
// protected EnsembleCalculator calculator;
/**
* The descriptor for plan view;
*/
protected IDescriptor mapDescriptor;
/**
* Constructor
*
* @param resourceData
* - resource data to construct the generated grid resource.
* @param loadProperties
* @param calculator
*/
public GeneratedEnsembleGridResource(
GeneratedEnsembleGridResourceData resourceData,
LoadProperties loadProperties, IMapDescriptor mapDescriptor) {
super((GridResourceData) resourceData, loadProperties);
this.setDescriptor(mapDescriptor);
// pass name generator. not works, May implement it later
if (((AbstractResourceData) resourceData).getNameGenerator() == null) {
((AbstractResourceData) resourceData)
.setNameGenerator(new GridNameGenerator());
}
this.getProperties().setSystemResource(true);
// Set color
ColorableCapability colorable = (ColorableCapability) this
.getCapability(ColorableCapability.class);
rand = new Random();
RGB color = new RGB(rand.nextInt(206) + 50, rand.nextInt(206) + 50,
rand.nextInt(206) + 50);
colorable.setColor(color);
};
/**
* Constructor
*
* @param resourceData
* - resource data to construct the generated grid resource.
* @param loadProperties
*/
public GeneratedEnsembleGridResource(
GeneratedEnsembleGridResourceData resourceData,
LoadProperties loadProperties) {
super((GridResourceData) resourceData, loadProperties);
ColorableCapability colorable = (ColorableCapability) this
.getCapability(ColorableCapability.class);
RGB color = getRandomColor();
colorable.setColor(color);
}
/**
* generate a random color
*
* @return
*/
private RGB getRandomColor() {
rand = new Random();
final int lowerFilter = 80;
final int upperFilter = 200;
final int skewToBrightness = 256 - upperFilter;
int r = rand.nextInt(upperFilter);
if (r < lowerFilter) {
r = lowerFilter;
}
int g = rand.nextInt(upperFilter);
if (g < lowerFilter) {
g = lowerFilter;
}
int b = rand.nextInt(upperFilter);
if (b < lowerFilter) {
b = lowerFilter;
}
r += skewToBrightness;
g += skewToBrightness;
b += skewToBrightness;
return new RGB(r, g, b);
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.viz.grid.rsc.general.AbstractGridResource#initInternal(com
* .raytheon.uf.viz.core.IGraphicsTarget)
*/
@Override
protected void initInternal(IGraphicsTarget target) throws VizException {
// Set DisplayTypeCapability with CONTOUR IMAGE
if (parameter != null) {
String paramAbbrev = parameter.getAbbreviation();
((DisplayTypeCapability) this
.getCapability(DisplayTypeCapability.class))
.setAlternativeDisplayTypes(FieldDisplayTypesFactory
.getInstance().getDisplayTypes(paramAbbrev));
}
super.initInternal(target);
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.grid.rsc.GridNameGenerator.IGridNameResource#
* getLegendParameters()
*/
@Override
public LegendParameters getLegendParameters() {
GridRecord record = getCurrentGridRecord();
if (record == null) {
record = getAnyGridRecord();
if (record == null) {
return null;
}
}
LegendParameters legendParams = new LegendParameters();
DatasetInfo info = DatasetInfoLookup.getInstance().getInfo(
record.getDatasetId());
if (info == null) {
legendParams.model = record.getDatasetId();
} else {
legendParams.model = info.getTitle();
}
legendParams.level = record.getLevel();
legendParams.parameter = record.getParameter().getName();
legendParams.ensembleId = record.getEnsembleId();
legendParams.dataTime = descriptor.getFramesInfo().getTimeForResource(
this);
if (stylePreferences != null) {
legendParams.unit = stylePreferences.getDisplayUnitLabel();
}
if (legendParams.unit == null || legendParams.unit.isEmpty()) {
if (record.getParameter().getUnit().equals(Unit.ONE)) {
legendParams.unit = "";
} else {
legendParams.unit = record.getParameter().getUnitString();
}
}
List<DisplayType> displayTypes = FieldDisplayTypesFactory.getInstance()
.getDisplayTypes(record.getParameter().getAbbreviation());
DisplayType displayType = getDisplayType();
if (displayTypes != null && !displayTypes.isEmpty()
&& displayTypes.get(0).equals(displayType)) {
// The default type does not display in the legend
legendParams.type = "";
} else if (displayType == DisplayType.STREAMLINE) {
legendParams.type = "Streamlines";
} else if (displayType == DisplayType.BARB) {
legendParams.type = "Wind Barbs";
} else if (displayType == DisplayType.ARROW) {
legendParams.type = "Arrows";
} else if (displayType == DisplayType.IMAGE) {
legendParams.type = "Img";
}
return legendParams;
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.grid.rsc.general.GridResource#getName()
*/
@Override
public String getName() {
if (resourceData == null) {
return super.getName();
}
AbstractNameGenerator generator = resourceData.getNameGenerator();
if (generator == null) {
return (int) (randomRec.getLevel().getLevelonevalue())
+ randomRec.getLevel().getMasterLevel().getName() + " "
+ calculation.getTitle() + " " + parameter.getName();
}
return calculation.getTitle() + " " + generator.getName(this);
}
/**
* Update a calculation display if the data is changed The real update will
* be implemented later.
*
* @param dataMap
* -The loaded member data
*
* TODO
*/
public void updateData(Map<DataTime, List<GeneralGridData>> dataMap) {
// Replace the data. Clear all the data and related before setting
clearRequestedData();
this.setData(dataMap);
// Set the data times
this.dataTimes.addAll(dataMap.keySet());
// If there is no any ensemble member, unload this resource
// If any member data is changed re-calculate and update display
}
public ArrayList<String> getModels() {
return models;
}
public void setModels(ArrayList<String> models) {
this.models = models;
}
public Calculation getCalculation() {
return calculation;
}
public void setCalculation(Calculation calculation) {
this.calculation = calculation;
}
/**
* Set parameter of any grid resource
*/
public void setParameter(GridResource rcs) {
if (parameter != null)
return;
GridRecord randomRec = rcs.getAnyGridRecord();
setMatchCriteria(randomRec);
if (randomRec != null) {
parameter = randomRec.getParameter();
this.randomRec = randomRec;
}
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.grid.rsc.general.GridResource#getMatchCriteria()
*/
@Override
public ParamLevelMatchCriteria getMatchCriteria() {
return paramLevelMatchCriteria;
}
/**
* Match criteria for generated grid displaying.
*
* @param record
* - any grid record for get the parameters
*/
private void setMatchCriteria(GridRecord record) {
if (record == null) {
return;
}
ParamLevelMatchCriteria matchCriteria = new ParamLevelMatchCriteria();
matchCriteria.setParameterName(new ArrayList<String>());
matchCriteria.setLevels(new ArrayList<Level>());
matchCriteria.setCreatingEntityNames(new ArrayList<String>());
String parameter = record.getParameter().getAbbreviation();
SingleLevel level = GridLevelTranslator.constructMatching(record
.getLevel());
String creatingEntity = record.getDatasetId();
if (!matchCriteria.getParameterNames().contains(parameter)) {
matchCriteria.getParameterNames().add(parameter);
}
if (!matchCriteria.getLevels().contains(level)) {
matchCriteria.getLevels().add(level);
}
if (!matchCriteria.getCreatingEntityNames().contains(creatingEntity)) {
matchCriteria.getCreatingEntityNames().add(creatingEntity);
}
paramLevelMatchCriteria = matchCriteria;
}
/**
* Implement later(after image loading), see D2DGridResource Data
* (non-Javadoc)
*
* @see com.raytheon.viz.grid.rsc.general.AbstractGridResource#inspect(com.raytheon
* .uf.common.geospatial.ReferencedCoordinate)
*/
@Override
public String inspect(ReferencedCoordinate coord) throws VizException {
// TODO
return "Not implemented";
}
@Override
public ResourceOrder getResourceOrder() {
return ResourceOrder.HIGHEST;
}
}

View file

@ -0,0 +1,424 @@
package gov.noaa.gsd.viz.ensemble.display.rsc;
import gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator;
import gov.noaa.gsd.viz.ensemble.display.common.GenericResourceHolder;
import gov.noaa.gsd.viz.ensemble.display.common.Utilities;
import gov.noaa.gsd.viz.ensemble.display.control.EnsembleResourceManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.map.MapDescriptor;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.ResourceProperties;
import com.raytheon.viz.grid.rsc.GridResourceData;
import com.raytheon.viz.grid.rsc.general.D2DGridResource;
import com.raytheon.viz.grid.rsc.general.GeneralGridData;
import com.raytheon.viz.grid.rsc.general.GridResource;
import com.raytheon.viz.ui.EditorUtil;
import com.raytheon.viz.ui.editor.AbstractEditor;
/**
* Construct the GeneratedEnsembleGridResource and provide data for it by
* calculating with member data
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jan 2014 5056 jing Initial creation
*
* </pre>
*/
public class GeneratedEnsembleGridResourceData extends GridResourceData {
private IDescriptor mapDescriptor = null;
/**
* The grid resources holding the ensemble data for input to the ensemble
* calculator The String key is the loaded model name of an ensemble
* product, same model ran multiple times or multiple models ran same time.
* Consider the last two case later. Need an ensemble manager to handle the
* resources, so the loaded products can register and unregister when
* loading and unloading. To do so, may be need extend D2DGridResource to
* EnsembleGridResource(need EnsembleGridResourceData too) which can provide
* more interfaces
*
*/
private Map<String, List<GenericResourceHolder>> dataHolders = new ConcurrentHashMap<String, List<GenericResourceHolder>>();
/**
* Calculate loaded ensemble data to generate new data, such as mean.
*/
protected EnsembleCalculator calculator = null;
/**
* Flag if frozen loaded data of members
*/
private boolean isFrozenData = false;
/**
* Currently consider one resource case only. The multiple generated
* resource may be created for difference level or unit. Do this later with
* List<GeneratedEnsembleGridResource<GeneratedEnsembleGridResourceData>>
* resources.
*/
protected GeneratedEnsembleGridResource<GeneratedEnsembleGridResourceData> resource = null;
// like "500MB", "" is not available or don't care.
protected String level = "";
// Like "Height", "" is not available or don't care.
protected String unit = "";
// create an empty metadata map
protected HashMap<String, RequestConstraint> metadataMap = new HashMap<String, RequestConstraint>();
public GeneratedEnsembleGridResourceData() {
super();
this.setMetadataMap(metadataMap);
setRetrieveData(false);
}
public GeneratedEnsembleGridResourceData(EnsembleCalculator calculator,
IDescriptor md) {
super();
this.mapDescriptor = md;
this.calculator = calculator;
this.setRetrieveData(false);
// No time request in time matching
this.setRequeryNecessaryOnTimeMatch(false);
this.setMetadataMap(metadataMap);
}
public GeneratedEnsembleGridResourceData(EnsembleCalculator calculator,
IDescriptor md, String level, String unit) {
super();
this.mapDescriptor = md;
this.calculator = calculator;
this.level = level;
this.unit = unit;
// the interface to disable the request
this.setRetrieveData(false);
// No time request in time matching
this.setRequeryNecessaryOnTimeMatch(false);
this.setMetadataMap(metadataMap);
}
/**
* Construct the resource whatever level and unit
*/
@Override
public AbstractVizResource<?, ?> construct(LoadProperties loadProperties,
IDescriptor descriptor) throws VizException {
AbstractVizResource<?, ?> rsc = null;
rsc = new GeneratedEnsembleGridResource<Object>(this, loadProperties);
resource = (GeneratedEnsembleGridResource<GeneratedEnsembleGridResourceData>) rsc;
resource.setCalculation(calculator.getCalculation());
update();
ResourceProperties rp = new ResourceProperties();
ResourcePair pair = new ResourcePair();
pair.setResource(rsc);
pair.setProperties(rp);
descriptor.getResourceList().add(pair);
// TODO: Is this the correct way to associate the editor with the
// resource?
AbstractEditor editor = EditorUtil
.getActiveEditorAs(AbstractEditor.class);
EnsembleResourceManager.getInstance().registerGenerated(
(AbstractVizResource<?, ?>) resource, editor);
return rsc;
}
/**
* Uses the dataHolders to generate the new data for ensemble display
*
* @return
*/
@SuppressWarnings("unchecked")
public Map<DataTime, List<GeneralGridData>> calculate() {
// searchDataHolders();
// Do calculation with members of all models by looping
Map<DataTime, List<GeneralGridData>> dataMap = new ConcurrentHashMap<DataTime, List<GeneralGridData>>();
// Loop calculate. This solution is re-calculating all frames.
// The second solution is just re-calculating changed or new frames
List<DataTime> dataTimes = getAllMemberDataTimes();
for (DataTime time : dataTimes) {
List<List<GeneralGridData>> inputData = getAllData(time);
if (inputData == null || inputData.size() < 1) {
continue;
}
List<GeneralGridData> grids = calculator.calculate(inputData);
if (grids == null || grids.size() < 1) {
continue;
}
dataMap.put(time, grids);
}
return dataMap;
}
/**
* Search ensemble members resources grouped by model used for test
*/
private void searchDataHolders() {
// search models of ensemble
List<String> models = Utilities.getEnsembleModels();
if (models == null || models.size() < 1 || resource == null) {
return;
}
if (!dataHolders.isEmpty()) {
dataHolders.clear();
}
// Add all ensemble member resources into the dataHolders
for (String model : models) {
List<D2DGridResource> rcsList = Utilities
.getResourcesEnsembleModel(model);
if (rcsList != null) {
if (rcsList.size() > 0) {
resource.setParameter(rcsList.get(0));
}
}
}
// Add loaded product(s) which are not ensemble product(s)
// but same level and type of data(same unit) into the dataHolders
// We can load same model with different run time to treat it as
// an ensemble products. Add the members in the dataHolders too.
}
/**
* Get all member resources in the manager
*
* @return
*/
public List<D2DGridResource> getMemberResources() {
// whatever level or unit in this implementation
Set<String> keys = dataHolders.keySet();
List<D2DGridResource> members = new ArrayList<D2DGridResource>();
for (String key : keys) {
List<GenericResourceHolder> rcsList = dataHolders.get(key);
if (rcsList == null || rcsList.size() == 0) {
continue;
}
for (int i = 0; i < rcsList.size(); i++) {
if (rcsList.get(i).getRsc() instanceof D2DGridResource) {
members.add((D2DGridResource) rcsList.get(i).getRsc());
}
}
}
return members;
}
/**
* Available Data Times of all members
*/
private List<DataTime> getAllMemberDataTimes() {
// Test just use frame time instead real data time
DataTime[] dataTimes = this.mapDescriptor.getFramesInfo()
.getFrameTimes();
List<DataTime> times = new ArrayList<DataTime>(Arrays.asList(dataTimes));
return times;
}
/**
* Get data set in all member of one frame. The calculation should deal with
* issues, -In same ensemble products, different grid data number of member.
* -Different products for same level and unit are treated as ensemble
* members. -Should keep members in same geometry(cover biggest area) by
* re-projecting some members. The re-projection in once only if need.
* -Whatever time matched data of the frames, just use them for calculation.
*/
private List<List<GeneralGridData>> getAllData(DataTime time) {
ArrayList<List<GeneralGridData>> allData = new ArrayList<List<GeneralGridData>>();
List<D2DGridResource> members = getMemberResources();
// Get the frame index by the frame time
DataTime[] frameTimes = this.mapDescriptor.getFramesInfo()
.getFrameTimes();
if (frameTimes == null || frameTimes.length < 1) {
return allData;
}
int i;
for (i = 0; i < frameTimes.length; i++)
if (time.equals(frameTimes[i])) {
break;
}
if (i >= frameTimes.length) {
return allData;
}
for (D2DGridResource member : members) {
// Get a data time matched the frame, an option
// DataTime[] dataTimes =member.getDataTimes();
// use requestData()
// Whatever matched time in the frame, just use it for calculation.
// User can
// control the matching from D2D GUI
DataTime memberTime = this.mapDescriptor.getFramesInfo()
.getTimeForResource(member, i);
if (memberTime == null
|| memberTime.compareTo(frameTimes[0]) < 0
|| memberTime.compareTo(frameTimes[frameTimes.length - 1]) > 0) {
continue;
}
List<GeneralGridData> data = member.requestData(memberTime);
if (data != null) {
allData.add(data);
}
}
return allData;
}
/**
* Disable retrieve data in all members
*/
private void disableMembersRetrieveData() {
if (isFrozenData()) {
List<D2DGridResource> members = getMemberResources();
for (D2DGridResource member : members) {
// It doesn't work? Check out later
member.getResourceData().setRetrieveData(false);
}
}
}
/**
* Enable retrieve data in all members
*/
private void enableMembersRetrieveData() {
List<D2DGridResource> members = getMemberResources();
for (D2DGridResource member : members) {
member.getResourceData().setRetrieveData(true);
}
}
/**
* Update by checking on the loaded ensemble product resources Re-calculate
* with the ensemble calculator to generate a new product(like mean), update
* the dataMap in the AbstractGridResource Is it possible to update the
* dataMap? The AbstractGridResource methods can be over written?
* UsesetData(Map<DataTime, List<GeneralGridData>> data)
*
* @throws VizException
*/
public void update() throws VizException {
// TODO: Is this the correct way to associate the editor with the
// resource?
AbstractEditor editor = EditorUtil
.getActiveEditorAs(AbstractEditor.class);
if (level != null && !level.equals("") && unit != null
&& !unit.equals("")) {
// Same level and unit case
dataHolders = EnsembleResourceManager
.getInstance()
.getResourceList(editor)
.getUserLoadedRscs((IDescriptor) new MapDescriptor(), true,
level, unit);
// dataHolders =
// ResourceManager.getInstance().getResourceList(editor).getCalculationLoadedRscs((IDescriptor)new
// MapDescriptor(), true);
} else if (unit != null && !unit.equals("")) {
// same unit whatever level case
dataHolders = EnsembleResourceManager
.getInstance()
.getResourceList(editor)
.getUserLoadedRscs((IDescriptor) new MapDescriptor(), true,
unit);
} else {
// whatever level or unit,for test only
dataHolders = EnsembleResourceManager.getInstance()
.getResourceList(editor)
.getUserLoadedRscs((IDescriptor) new MapDescriptor(), true);
}
if (!dataHolders.isEmpty() && !dataHolders.keySet().isEmpty()) {
disableMembersRetrieveData();
resource.setParameter((GridResource<?>) (dataHolders.get(
(dataHolders.keySet().toArray())[0]).get(0).getRsc()));
resource.updateData(calculate());
enableMembersRetrieveData();
}
}
public EnsembleCalculator getCalculator() {
return calculator;
}
@Override
public boolean equals(Object obj) {
return false;
}
public boolean isFrozenData() {
return isFrozenData;
}
/**
* For GUI controls data updating.
*
* @return
*/
public void setFrozenData(boolean isFrozenData) {
this.isFrozenData = isFrozenData;
}
public Map<String, List<GenericResourceHolder>> getDataHolders() {
return dataHolders;
}
}

View file

@ -0,0 +1,86 @@
/**
* 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 gov.noaa.gsd.viz.ensemble.display.rsc.histogram;
import org.eclipse.swt.widgets.Event;
import com.raytheon.uf.common.geospatial.ReferencedCoordinate;
import com.raytheon.uf.viz.core.IDisplayPaneContainer;
import com.raytheon.viz.ui.input.InputAdapter;
import com.vividsolutions.jts.geom.Coordinate;
/**
* Ensemble default input handler for sampling. Is a modification of the
* SamplingInputAdapter.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* July, 2014 5056 jing Initial creation
* </pre>
* @param <T>
*/
public class EnsSamplingInputAdapter<T extends EnsSamplingResource> extends
InputAdapter {
private T resource;
public EnsSamplingInputAdapter(T resource) {
this.resource = resource;
}
@Override
public boolean handleMouseMove(int x, int y) {
IDisplayPaneContainer container = resource.getResourceContainer();
Coordinate c = container.translateClick(x, y);
if (c != null) {
resource.sampleCoord = new ReferencedCoordinate(c);
} else {
resource.sampleCoord = null;
}
if (resource.isSampling()) {
resource.issueRefresh();
}
return false;
}
@Override
public boolean handleMouseDownMove(int x, int y, int mouseButton) {
return handleMouseMove(x, y);
}
public boolean handleMouseExit(Event event) {
resource.sampleCoord = null;
if (resource.isSampling()) {
resource.issueRefresh();
}
return false;
}
public boolean handleMouseEnter(Event event) {
return handleMouseMove(event.x, event.y);
}
}

View file

@ -0,0 +1,563 @@
/**
* 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 gov.noaa.gsd.viz.ensemble.display.rsc.histogram;
import gov.noaa.gsd.viz.ensemble.display.control.EnsembleResourceManager;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.swt.graphics.RGB;
import com.raytheon.uf.common.geospatial.ReferencedCoordinate;
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.IDisplayPaneContainer;
import com.raytheon.uf.viz.core.IExtent;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.IGraphicsTarget.HorizontalAlignment;
import com.raytheon.uf.viz.core.IGraphicsTarget.TextStyle;
import com.raytheon.uf.viz.core.IGraphicsTarget.VerticalAlignment;
import com.raytheon.uf.viz.core.drawables.AbstractRenderableDisplay;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.IFont;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.AbstractResourceData;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.IInputHandler;
import com.raytheon.uf.viz.core.rsc.IInputHandler.InputPriority;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.RenderingOrderFactory.ResourceOrder;
import com.raytheon.uf.viz.core.rsc.ResourceList;
import com.raytheon.uf.viz.core.rsc.ResourceProperties;
import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.MagnificationCapability;
import com.raytheon.uf.viz.core.sampling.ISamplingResource;
import com.vividsolutions.jts.geom.Coordinate;
/**
* Ensemble sampling resource, draws sample text to the screen. also picks up
* mouse events Is a modification of the SamplingResource.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* July, 2014 5056 jing Initial creation
*
* @param <T>
*/
public class EnsSamplingResource extends
AbstractVizResource<AbstractResourceData, IDescriptor> implements
ISamplingResource {
protected static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(EnsSamplingResource.class);
/**
* The result of a hover operation: a set of strings and corresponding
* colors
*
*/
protected static class SampleResult {
public SampleResult() {
}
public String[] labels;
public RGB[] colors;
}
boolean sampling = false;
private IInputHandler inputAdapter = getSamplingInputHandler();
protected ReferencedCoordinate sampleCoord;
// private IFont hoverFont = null;
protected IFont hoverFont = null;
private boolean errorInHovering = false;
private VerticalAlignment verticalAlignment = VerticalAlignment.TOP;
/**
* @param resourceData
* @param loadProperties
*/
// public EnsSamplingResource(GenericResourceData resourceData,
public EnsSamplingResource(AbstractResourceData resourceData,
LoadProperties loadProperties) {
super(resourceData, loadProperties);
}
protected IInputHandler getSamplingInputHandler() {
return new EnsSamplingInputAdapter<EnsSamplingResource>(this);
}
/*
* (non-Javadoc)
*
* @see com.raytheon.uf.viz.core.rsc.AbstractVizResource#disposeInternal()
*/
@Override
protected void disposeInternal() {
IDisplayPaneContainer container = getResourceContainer();
if (container != null) {
container.unregisterMouseHandler(inputAdapter);
}
if (hoverFont != null) {
hoverFont.dispose();
}
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.core.rsc.AbstractVizResource#initInternal(com.raytheon
* .uf.viz.core.IGraphicsTarget)
*/
@Override
protected void initInternal(IGraphicsTarget target) throws VizException {
IDisplayPaneContainer container = getResourceContainer();
if (container != null) {
container.registerMouseHandler(inputAdapter,
InputPriority.SYSTEM_RESOURCE);
}
hoverFont = target.initializeFont(getClass().getName());
}
/*
* (non-Javadoc)
*
* @see com.raytheon.uf.viz.core.rsc.sampling.ISamplingResource#isSampling()
*/
@Override
public boolean isSampling() {
return sampling;
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.core.rsc.sampling.ISamplingResource#setSampling(boolean
* )
*/
@Override
public void setSampling(boolean sampling) {
this.sampling = sampling;
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.core.rsc.AbstractVizResource#paintInternal(com.raytheon
* .uf.viz.core.IGraphicsTarget,
* com.raytheon.uf.viz.core.drawables.PaintProperties)
*/
@Override
protected void paintInternal(IGraphicsTarget target,
PaintProperties paintProps) throws VizException {
if (sampleCoord == null || isSampling() == false
|| !(EnsembleResourceManager.getInstance().isToolLayerReady())) {
return;
}
hoverFont.setMagnification(getCapability(MagnificationCapability.class)
.getMagnification().floatValue());
SampleResult result = doHover(sampleCoord, descriptor.getResourceList());
paintResult(target, paintProps, sampleCoord, result);
}
protected SampleResult doHover(ReferencedCoordinate coord,
ResourceList resources) throws VizException {
SampleResult result = new SampleResult();
List<String> labelList = new ArrayList<String>();
List<RGB> colorList = new ArrayList<RGB>();
try {
int size = resources.size();
for (int i = size - 1; i >= 0; --i) {
ResourcePair rp = resources.get(i);
String retVal = recursiveHoverSearch(rp, coord);
if (retVal != null && retVal.length() > 0) {
RGB color = null;
if (rp.getResource().hasCapability(
ColorableCapability.class)) {
color = rp.getResource()
.getCapability(ColorableCapability.class)
.getColor();
}
int p1, p2;
p1 = 0;
while ((p2 = retVal.indexOf('\n', p1)) >= 0) {
colorList.add(color);
labelList.add(retVal.substring(p1, p2));
p1 = p2 + 1;
}
String s = retVal.substring(p1);
if (s.length() > 0) {
colorList.add(color);
labelList.add(retVal.substring(p1));
}
}
}
} catch (Throwable t) {
statusHandler.handle(Priority.PROBLEM, "Error sampling resources: "
+ t.getLocalizedMessage(), t);
}
result.labels = labelList.toArray(new String[labelList.size()]);
result.colors = colorList.toArray(new RGB[colorList.size()]);
return result;
}
private String recursiveHoverSearch(ResourcePair rp,
ReferencedCoordinate coordinate) throws VizException {
ResourceProperties props = rp.getProperties();
AbstractVizResource<?, ?> rsc = rp.getResource();
if (rsc != null && rsc.getStatus() == ResourceStatus.INITIALIZED
&& props.isVisible()) {
String curVal = rsc.inspect(coordinate);
if (curVal != null && curVal.length() > 0) {
return curVal;
}
}
return null;
}
protected void paintResult(IGraphicsTarget target,
PaintProperties paintProps, ReferencedCoordinate coord,
SampleResult result) throws VizException {
verticalAlignment = VerticalAlignment.TOP;
target.clearClippingPlane();
try {
if (result != null) {
double[] world = new double[] { coord.getObject().x,
coord.getObject().y };
double[] pixel = descriptor.worldToPixel(world);
Coordinate c = new Coordinate(pixel[0], pixel[1]);
int canvasWidth = paintProps.getCanvasBounds().width;
double extentWidth = paintProps.getView().getExtent()
.getWidth();
double ratioX = canvasWidth / extentWidth;
if (result.labels.length > 0) {
List<String[]> strsToUse = new ArrayList<String[]>();
List<RGB> colorsToUse = new ArrayList<RGB>();
HorizontalAlignment[] alignments = new HorizontalAlignment[result.labels.length];
boolean[] modified = new boolean[result.labels.length];
for (int i = 0; i < modified.length; ++i) {
modified[i] = false;
alignments[i] = HorizontalAlignment.LEFT;
String[] tmp = new String[] { result.labels[i],
result.labels[i] };
strsToUse.add(tmp);
}
adjustStrings(target, paintProps, strsToUse, modified,
alignments, c, ratioX, null);
HorizontalAlignment horizontalAlignment = alignments[0];
boolean good = true;
for (int i = 1; i < alignments.length && good; ++i) {
if (horizontalAlignment != alignments[i]) {
good = false;
}
}
if (!good) {
// not all the same, figure out alignments!!!
int maxLen = 0;
int i = 0;
for (String[] s : strsToUse) {
if (s[0].length() > maxLen) {
maxLen = s[0].length();
horizontalAlignment = alignments[i];
}
++i;
}
adjustStrings(target, paintProps, strsToUse, modified,
alignments, c, ratioX, horizontalAlignment);
}
List<String> actualStrs = new ArrayList<String>();
for (int i = 0; i < strsToUse.size(); ++i) {
String[] strs = strsToUse.get(i);
for (int j = 1; j < strs.length; ++j) {
actualStrs.add(strs[j]);
colorsToUse.add(result.colors[i]);
}
}
String[] newStrs = actualStrs.toArray(new String[actualStrs
.size()]);
double referencePtY = adjustLabelWrapY(
target,
newStrs,
c.y
+ ((AbstractRenderableDisplay.CURSOR_HEIGHT) / ratioX),
paintProps.getView().getExtent(), ratioX);
if (horizontalAlignment == HorizontalAlignment.RIGHT) {
c.x -= (target.getStringBounds(hoverFont, newStrs,
TextStyle.BLANKED).getWidth() / ratioX);
}
target.drawStrings(hoverFont, newStrs, c.x, referencePtY,
0.0, IGraphicsTarget.TextStyle.BLANKED,
colorsToUse.toArray(new RGB[colorsToUse.size()]),
HorizontalAlignment.LEFT, verticalAlignment);
}
}
errorInHovering = false;
} catch (Exception e) {
if (errorInHovering) {
// Keep down the number of error messages
statusHandler.handle(
Priority.PROBLEM,
"Error painting sample text: "
+ e.getLocalizedMessage(), e);
}
errorInHovering = true;
}
}
private void adjustStrings(IGraphicsTarget target,
PaintProperties paintProps, List<String[]> strsToUse,
boolean[] modified, HorizontalAlignment[] alignments, Coordinate c,
double ratio, HorizontalAlignment targetAlignment) {
List<String[]> strsToUseInternal = new ArrayList<String[]>();
for (int i = 0; i < strsToUse.size(); ++i) {
String str = strsToUse.get(i)[0];
String[] split = str.split("[ ]");
boolean done = false;
if ((strsToUse.get(i) == null) || (strsToUse.get(i).length == 0)) {
continue;
}
int divideBy = strsToUse.get(i).length - 1;
int maxDivisions = 0;
for (int j = 0; j < split.length; ++j) {
if (split[j].isEmpty() == false) {
++maxDivisions;
}
}
if (alignments[i] == targetAlignment) {
strsToUseInternal.add(strsToUse.get(i));
} else {
String[] test = new String[] { str };
while (!done) {
if (divideBy > maxDivisions
|| alignments[i] == targetAlignment) {
done = true;
continue;
}
int approxLenPerStr = str.length() / divideBy;
List<String> strs = new ArrayList<String>();
for (int j = 0; j < split.length;) {
String line = split[j++];
while (j < split.length) {
String s = split[j];
if (s.length() + line.length() <= approxLenPerStr) {
if (!s.isEmpty()) {
if (j == split.length - 1
&& split[1].equalsIgnoreCase("=")) {
line = split[split.length - 1];
} else {
line += " " + s;
}
} else {
line += " ";
}
++j;
} else {
break;
}
}
strs.add(line);
}
test = strs.toArray(new String[strs.size()]);
HorizontalAlignment alignment = adjustLabelWrapX(target,
test, c.x, paintProps.getView().getExtent(), ratio,
alignments[i]);
if (alignment == alignments[i]
&& (targetAlignment == null || alignment == targetAlignment)) {
// the alignment was not changed and we are the target
// alignment, we are done
done = true;
} else {
if (targetAlignment == null) {
// alignment changed, check to see if it changes
// back
HorizontalAlignment tmpAlignment = alignment;
alignment = adjustLabelWrapX(target, test, c.x,
paintProps.getView().getExtent(), ratio,
alignment);
if (alignment != tmpAlignment) {
// we moved back, we need to divide and
// conquer
alignments[i] = HorizontalAlignment.LEFT;
modified[i] = true;
divideBy++;
} else {
// we are good at this alignment
alignments[i] = alignment;
done = true;
}
} else {
// we need to be the targetAlignment
alignment = adjustLabelWrapX(target, test, c.x,
paintProps.getView().getExtent(), ratio,
targetAlignment);
if (alignment == targetAlignment) {
// we are fine at other alignment also, use it:
alignments[i] = alignment;
done = true;
} else {
alignments[i] = targetAlignment;
modified[i] = true;
divideBy++;
}
}
}
}
String[] addTo = new String[test.length + 1];
addTo[0] = str;
for (int j = 0; j < test.length; ++j) {
addTo[j + 1] = test[j];
}
strsToUseInternal.add(addTo);
}
}
strsToUse.clear();
strsToUse.addAll(strsToUseInternal);
}
/**
* Adjusts the x label if the width of the longest label extends the extent
*
* @param target
* @param labels
* @param x
* @param extent
* @param ratio
* @return
*/
private HorizontalAlignment adjustLabelWrapX(IGraphicsTarget target,
String[] labels, double x, IExtent extent, double ratio,
HorizontalAlignment horizontalAlignment) {
double referencePoint = x;
// Find the max width of the label in pixels
double maxWidth = 0;
IFont font = hoverFont;
for (String label : labels) {
Rectangle2D bounds = target.getStringBounds(font, label);
if (bounds.getWidth() > maxWidth) {
maxWidth = bounds.getWidth();
}
}
// Get the width in gl space
double widthInGl = maxWidth / ratio;
if (horizontalAlignment == HorizontalAlignment.LEFT) {
// Check to see if text extends screen extent
if (referencePoint + widthInGl > extent.getMaxX()) {
horizontalAlignment = HorizontalAlignment.RIGHT;
}
} else {
// Check to see if text extends screen extent
if (referencePoint - widthInGl < extent.getMinX()) {
horizontalAlignment = HorizontalAlignment.LEFT;
}
}
return horizontalAlignment;
}
/**
* Adjusts the y label position if the stacked labels exceeds the screen
* extent height
*
* @param target
* @param labels
* @param y
* @param extent
* @param ratio
* @return
*/
private double adjustLabelWrapY(IGraphicsTarget target, String[] labels,
double y, IExtent extent, double ratio) {
double referencePoint = y;
double totalHeight = target.getStringBounds(hoverFont, labels,
TextStyle.BLANKED).getHeight();
// convert to gl space
double maxHeightInGl = (totalHeight) / ratio;
// check to see if height extends map height
if (referencePoint + maxHeightInGl > extent.getMaxY()) {
verticalAlignment = VerticalAlignment.BOTTOM;
referencePoint -= (AbstractRenderableDisplay.CURSOR_HEIGHT + 2)
/ ratio;
}
// return adjusted point
return referencePoint;
}
@Override
public ResourceOrder getResourceOrder() {
return ResourceOrder.HIGHEST;
}
}

View file

@ -0,0 +1,604 @@
package gov.noaa.gsd.viz.ensemble.display.rsc.histogram;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.swt.graphics.RGB;
import com.raytheon.uf.common.geospatial.ReferencedCoordinate;
import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.viz.core.IDisplayPane;
import com.raytheon.uf.viz.core.IDisplayPaneContainer;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.IInputHandler;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.ResourceList;
import com.raytheon.uf.viz.core.rsc.ResourceProperties;
import com.raytheon.uf.viz.core.rsc.capabilities.BlendableCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.MagnificationCapability;
import com.raytheon.viz.grid.rsc.general.AbstractGridResource;
import com.raytheon.viz.grid.rsc.general.D2DGridResource;
import com.raytheon.viz.ui.editor.IMultiPaneEditor;
import com.raytheon.viz.ui.input.preferences.MousePreferenceManager;
/**
* D2D ensemble sampling resources, supports all pane sampling and long left
* click sampling as well Implement steps: 1, ensemble text values
* sampling--done 100% 2, Text histogram/ color Text histogram--done 90% 3,
* Graphics histogram/distribution view 4, interactive graphics histogram.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* July, 2014 5056 jing Initial creation
*
* </pre>
*/
public class HistogramResource<HistogramResoureData> extends
EnsSamplingResource {
public enum DisplayMode {
SAMPLING, TEXT_HISTGRAM, COLOR_TEXT_HISTGRAM, GRAPHIC_HISTGRAM
}
// Level+name:500MB
private String level;
private String unit;
protected Random rand;
protected DisplayMode mode;
public DisplayMode getMode() {
return mode;
}
/**
* Constructor
*
* @param histogramResourceData
* @param loadProperties
*/
public HistogramResource(HistogramResourceData histogramResourceData,
LoadProperties loadProperties, IDescriptor descriptor,
String level, String unit, DisplayMode mode) {
super(histogramResourceData, loadProperties);
this.level = level;
this.unit = unit;
this.mode = mode;
this.setSampling(true);
this.setDescriptor(descriptor);
ColorableCapability colorable = this
.getCapability(ColorableCapability.class);
rand = new Random();
RGB color = new RGB(rand.nextInt(206) + 50, rand.nextInt(206) + 50,
rand.nextInt(206) + 50);
colorable.setColor(color);
}
private class D2DMouseAdapter extends
EnsSamplingInputAdapter<HistogramResource<?>> {
// private static final String INSPECT_PREF =
// "com.raytheon.viz.ui.input.inspect";
private static final String INSPECT_PREF_HIST = "com.raytheon.viz.ui.input.inspect.hist";
protected Job job;
protected long timeUp;
private MousePreferenceManager prefManager = MousePreferenceManager
.getInstance();
private boolean inspectForced = false;
D2DMouseAdapter() {
super(HistogramResource.this);
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.ui.input.IInputHandler#handleMouseDown(int,
* int, int)
*/
@Override
public boolean handleMouseDown(int x, int y, int mouseButton) {
super.handleMouseDown(x, y, mouseButton);
if (prefManager.handleClick(INSPECT_PREF_HIST, mouseButton)
&& isSampling() == false) {
inspectForced = true;
setSampling(true);
issueRefresh();
return false;
} else if (prefManager.handleLongClick(INSPECT_PREF_HIST,
mouseButton) && isSampling() == false) {
timeUp = 0L;
if (job == null) {
job = new Job("InspectAdapter") {
@Override
protected IStatus run(IProgressMonitor monitor) {
if (timeUp == 0L) {
inspectForced = true;
setSampling(true);
issueRefresh();
}
return Status.OK_STATUS;
}
};
}
if (job.getState() != Job.RUNNING) {
job.schedule(500);
}
return false;
}
return false;
}
/*
* (non-Javadoc)
*
* @see com.raytheon.viz.ui.input.IInputHandler#handleMouseUp(int, int,
* int)
*/
@Override
public boolean handleMouseUp(int x, int y, int mouseButton) {
super.handleMouseUp(x, y, mouseButton);
if (prefManager.handleLongClick(INSPECT_PREF_HIST, mouseButton)) {
timeUp = System.currentTimeMillis();
}
if (inspectForced) {
inspectForced = false;
setSampling(false);
issueRefresh();
}
return false;
}
}
private boolean allPanelSampling = false;
@Override
protected IInputHandler getSamplingInputHandler() {
return new D2DMouseAdapter();
}
/*
* (non-Javadoc)
*
* @see com.raytheon.uf.viz.d2d.core.sampling.ID2DSamplingResource#
* setAllPanelSampling(boolean)
*/
// @Override
public void setAllPanelSampling(boolean allPanelSampling) {
this.allPanelSampling = allPanelSampling;
}
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.viz.d2d.core.sampling.ID2DSamplingResource#isAllPanelSampling
* ()
*/
// @Override
public boolean isAllPanelSampling() {
IDisplayPaneContainer container = getResourceContainer();
if (container instanceof IMultiPaneEditor) {
// Only all panel sample if we have 1 displayed pane count
return (allPanelSampling & (((IMultiPaneEditor) container)
.displayedPaneCount() == 1));
}
return allPanelSampling;
}
/**
* Generate sample histogram text/graphic with grid resources
*/
/*
* (non-Javadoc)
*
* @see
* gov.noaa.gsd.viz.ensemble.display.rsc.histogram.EnsSamplingResource#doHover
* (com.raytheon.uf.common.geospatial.ReferencedCoordinate,
* com.raytheon.uf.viz.core.rsc.ResourceList)
*/
@Override
protected SampleResult doHover(ReferencedCoordinate coord,
ResourceList resources) throws VizException {
SampleResult result = new SampleResult();
if (this.mode == DisplayMode.SAMPLING) {
result = doHoverSampling(coord, resources);
} else if (this.mode == DisplayMode.TEXT_HISTGRAM) {
result = doHoverText(coord, resources);
}
return result;
}
/**
* Get the sampling lables and colors of current location
*
* @param coord
* - current location
* @param resources
* - member resources of the caculations
* @return
* @throws VizException
*/
protected SampleResult doHoverSampling(ReferencedCoordinate coord,
ResourceList resources) throws VizException {
SampleResult result = new SampleResult();
List<String> labelList = new ArrayList<String>();
List<RGB> colorList = new ArrayList<RGB>();
// List<>
try {
int size = resources.size();
for (int i = size - 1; i >= 0; --i) {
ResourcePair rp = resources.get(i);
String retVal = recursiveHoverSearchSampling(rp, coord);
if (retVal != null && retVal.length() > 0) {
RGB color = null;
if (rp.getResource().hasCapability(
ColorableCapability.class)) {
color = rp.getResource()
.getCapability(ColorableCapability.class)
.getColor();
}
int p1, p2;
p1 = 0;
while ((p2 = retVal.indexOf('\n', p1)) >= 0) {
colorList.add(color);
labelList.add(retVal.substring(p1, p2));
p1 = p2 + 1;
}
String s = retVal.substring(p1);
if (s.length() > 0) {
colorList.add(color);
labelList.add(retVal.substring(p1));
}
}
}
} catch (Throwable t) {
statusHandler.handle(Priority.PROBLEM, "Error sampling resources: "
+ t.getLocalizedMessage(), t);
}
result.labels = labelList.toArray(new String[labelList.size()]);
result.colors = colorList.toArray(new RGB[colorList.size()]);
return result;
}
/**
* Get the sampling label for one resource.
*
* @param rp
* - one resource pair
* @param coordinate
* - current location
* @return - lable string
* @throws VizException
*/
private String recursiveHoverSearchSampling(ResourcePair rp,
ReferencedCoordinate coordinate) throws VizException {
ResourceProperties props = rp.getProperties();
AbstractVizResource<?, ?> rsc = rp.getResource();
if (!(rsc instanceof AbstractGridResource))
return null;
if (rsc != null && rsc.getStatus() == ResourceStatus.INITIALIZED
&& props.isVisible()) {
String curVal = null;
Map<String, Object> result = rsc.interrogate(coordinate);
if (result == null || result.isEmpty())
return null;
Set<String> keys = result.keySet();
if (keys == null || keys.isEmpty())
return null;
for (String key : keys) {
if (key.contains("unit"))
continue;
if (curVal != null) {
// curVal = curVal + "/"+key+ ":"+ String.format("%.2f",
// result.get(key));
curVal = curVal + "/"
+ String.format("%.2f", result.get(key));
} else {
// curVal = key+ ":"+ String.format("%.2f",
// result.get(key));
curVal = String.format("%.2f", result.get(key));
}
}
if (curVal != null && curVal.length() > 0) {
return curVal;
}
}
return null;
}
/**
* Text histogram display by sampling each member resource and creating a
* histogram text.
*
* @param coord
* @param resources
* @return
* @throws VizException
*/
protected SampleResult doHoverText(ReferencedCoordinate coord,
ResourceList resources) throws VizException {
SampleResult result = new SampleResult();
List<String> labelList = new ArrayList<String>();
// List<RGB> colorList = new ArrayList<RGB>();
List<Float> values = new ArrayList<Float>();
List<AbstractVizResource<?, ?>> rscs = new ArrayList<AbstractVizResource<?, ?>>();
try {
int size = resources.size();
for (int i = size - 1; i >= 0; --i) {
ResourcePair rp = resources.get(i);
float retVal = recursiveHoverSearchText(rp, coord);
if (retVal != Float.NaN) {
values.add(retVal);
rscs.add(rp.getResource());
}
}
} catch (Throwable t) {
statusHandler.handle(Priority.PROBLEM, "Error sampling resources: "
+ t.getLocalizedMessage(), t);
}
TextHistogram textHistogram = new TextHistogram(false);
labelList = textHistogram.interrogate(rscs, values, unit);
result.labels = labelList.toArray(new String[labelList.size()]);
RGB color = this.getCapability(ColorableCapability.class).getColor();
RGB[] colorList = new RGB[labelList.size()];
for (int j = 0; j < colorList.length; j++)
colorList[j] = color;
result.colors = colorList;
return result;
}
/**
* Searxh sampling data for text histogram in one resource pair
*
* @param rp
* @param coordinate
* @return
* @throws VizException
*/
private float recursiveHoverSearchText(ResourcePair rp,
ReferencedCoordinate coordinate) throws VizException {
ResourceProperties props = rp.getProperties();
AbstractVizResource<?, ?> rsc = rp.getResource();
if (!(rsc instanceof AbstractGridResource))
return Float.NaN;
float curVal = Float.NaN;
if (rsc != null && rsc.getStatus() == ResourceStatus.INITIALIZED
&& props.isVisible()) {
Map<String, Object> result = rsc.interrogate(coordinate);
if (result == null || result.isEmpty())
return Float.NaN;
Set<String> keys = result.keySet();
if (keys == null || keys.isEmpty())
return Float.NaN;
for (String key : keys) {
if (key.contains("unit")) {
this.unit = result.get(key).toString();
} else if (key.contains("value")) {
curVal = Float.parseFloat(result.get(key).toString());
}
}
}
return curVal;
}
/**
* Remove any resource not existing in the resource manager.
*
* @param resources
* @return
*/
private ResourceList filterResource(ResourceList resources) {
ResourceList filteredlist = new ResourceList();
try {
((HistogramResourceData) getResourceData()).update();
} catch (VizException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
List<D2DGridResource> matchRscs = ((HistogramResourceData) getResourceData())
.getAllMembersResources();
int size = resources.size();
for (int i = size - 1; i >= 0; --i) {
ResourcePair rp = resources.get(i);
if (matchRscs.contains(rp.getResource()))
filteredlist.add(rp);
}
return filteredlist;
}
public void setLevelUnit(String level, String unit) {
this.level = level;
this.unit = unit;
}
public String getLevel() {
return level;
}
public String getUnit() {
return unit;
}
@Override
public String getName() {
if (mode == DisplayMode.SAMPLING)
return level + " " + unit + " Ensemble Sampling";
else
return level + " " + unit + " Histogram Text";
}
/*
* (non-Javadoc)
*
* @see gov.noaa.gsd.viz.ensemble.display.rsc.histogram.EnsSamplingResource#
* paintInternal(com.raytheon.uf.viz.core.IGraphicsTarget,
* com.raytheon.uf.viz.core.drawables.PaintProperties)
*/
@Override
protected void paintInternal(IGraphicsTarget target,
PaintProperties paintProps) throws VizException {
if (sampleCoord == null) {
return;
}
if (isAllPanelSampling() == false) {
hoverFont.setMagnification(getCapability(
MagnificationCapability.class).getMagnification()
.floatValue());
SampleResult result = doHover(sampleCoord,
filterResource(descriptor.getResourceList()));
paintResult(target, paintProps, sampleCoord, result);
return;
}
if (isSampling() == false) {
return;
}
IDisplayPaneContainer container = getResourceContainer();
if (container == null) {
return;
}
ResourceList rList = new ResourceList();
List<ResourceList> blendedLists = new ArrayList<ResourceList>();
List<ResourcePair> invisibleList = new ArrayList<ResourcePair>();
IDisplayPane[] panes = container.getDisplayPanes();
if (panes.length == 4) {
// Awips1 puts four panels in the wrong order.
panes = new IDisplayPane[] { panes[0], panes[1], panes[3], panes[2] };
}
for (IDisplayPane pane : panes) {
ResourceList rscList = filterResource(pane.getDescriptor()
.getResourceList());
for (ResourcePair pair : rscList) {
if (pair.getResource() == null
|| !pair.getProperties().isVisible()) {
continue;
}
if (!pair.getResource()
.hasCapability(BlendableCapability.class)) {
rList.add(pair);
continue;
}
ResourceList list = pair.getResource()
.getCapability(BlendableCapability.class)
.getResourceList();
ResourcePair rp = list.get(0);
if (!rp.getProperties().isVisible()) {
invisibleList.add(rp);
rp.getProperties().setVisible(true);
}
rList.add(rp);
blendedLists.add(list);
}
}
int i = 1;
while (true) {
boolean done = true;
for (ResourceList list : blendedLists) {
if (list.size() <= i) {
continue;
}
ResourcePair rp = list.get(i);
if (!rp.getProperties().isVisible()) {
invisibleList.add(rp);
rp.getProperties().setVisible(true);
}
rList.add(rp);
done = false;
}
if (done) {
break;
}
i++;
}
// doHover goes in reverse list order
Collections.reverse(rList);
paintResult(target, paintProps, sampleCoord,
doHover(sampleCoord, rList));
for (ResourcePair pair : invisibleList) {
pair.getProperties().setVisible(false);
}
}
}

View file

@ -0,0 +1,226 @@
package gov.noaa.gsd.viz.ensemble.display.rsc.histogram;
import gov.noaa.gsd.viz.ensemble.display.common.GenericResourceHolder;
import gov.noaa.gsd.viz.ensemble.display.control.EnsembleResourceManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.map.MapDescriptor;
import com.raytheon.uf.viz.core.rsc.AbstractResourceData;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.ResourceProperties;
import com.raytheon.viz.grid.rsc.general.D2DGridResource;
import com.raytheon.viz.grid.rsc.general.GeneralGridData;
import com.raytheon.viz.ui.EditorUtil;
import com.raytheon.viz.ui.editor.AbstractEditor;
/**
* Construct D2D ensamle Sampling resources and provide data.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* July, 2014 5056 jing Initial creation
*
* </pre>
*/
public class HistogramResourceData extends AbstractResourceData {
private IDescriptor mapDescriptor = null;
/**
* The grid resources holding the ensemble data for input to the ensemble
* calculator The String key is the loaded model name of an ensemble
* product, same model ran multiple times or multiple models ran same time.
* Consider the last two case later. Need an ensemble manager to handle the
* resources, so the loaded products can register and unregister when
* loading and unloading. To do so, may be need extend D2DGridResource to
* EnsembleGridResource(need EnsembleGridResourceData too) which can provide
* more interfaces
*
*/
private Map<String, List<GenericResourceHolder>> dataHolders = new ConcurrentHashMap<String, List<GenericResourceHolder>>();
/**
* Currently consider one resource case only. The multiple generated
* resource may be created for difference level or unit. Do this later with
* List<GeneratedEnsembleGridResource<GeneratedEnsembleGridResourceData>>
* resources.
*/
protected HistogramResource<HistogramResourceData> resource = null;
// like "500MB", "" is not available or don't care.
protected String level = "";
// Like "Height", "" is not available or don't care.
protected String unit = "";
// Display mode
protected HistogramResource.DisplayMode mode;
public HistogramResourceData(HistogramResource.DisplayMode mode) {
super();
this.mode = mode;
}
public HistogramResourceData(IDescriptor md, String level, String unit,
HistogramResource.DisplayMode mode) {
this.mapDescriptor = md;// need not at here?
this.level = level;
this.unit = unit;
this.mode = mode;
}
/**
* Construct the resource whatever level and unit
*/
@SuppressWarnings("unchecked")
@Override
public AbstractVizResource<?, ?> construct(LoadProperties loadProperties,
IDescriptor descriptor) throws VizException {
AbstractVizResource<?, ?> rsc = null;
rsc = new HistogramResource<HistogramResourceData>(this,
loadProperties, descriptor, level, unit, mode);
resource = (HistogramResource<HistogramResourceData>) rsc;
update();
ResourceProperties rp = new ResourceProperties();
ResourcePair pair = new ResourcePair();
pair.setResource(rsc);
pair.setProperties(rp);
descriptor.getResourceList().add(pair);
// Register to the ensemble resource manager
AbstractEditor editor = EditorUtil
.getActiveEditorAs(AbstractEditor.class);
EnsembleResourceManager.getInstance().registerGenerated(
(AbstractVizResource<?, ?>) resource, editor);
return rsc;
}
public Map<String, List<GenericResourceHolder>> getDataHolders() {
return dataHolders;
}
/**
* Search ensemble members resources grouped by model
*
*/
public List<D2DGridResource> getAllMembersResources() {
// whatever level or unit in this implementation
Set<String> keys = dataHolders.keySet();
List<D2DGridResource> members = new ArrayList<D2DGridResource>();
for (String key : keys) {
List<GenericResourceHolder> rcsList = dataHolders.get(key);
if (rcsList == null || rcsList.size() == 0) {
continue;
}
for (int i = 0; i < rcsList.size(); i++) {
if (rcsList.get(i).getRsc() instanceof D2DGridResource) {
members.add((D2DGridResource) rcsList.get(i).getRsc());
}
}
}
return members;
}
/**
* TODO: Available Data Times if all member may be used in future,
*/
private List<DataTime> getAllMemberDataTimes() {
// Use frame time instead real data time
DataTime[] dataTimes = this.mapDescriptor.getFramesInfo()
.getFrameTimes();
List<DataTime> times = new ArrayList<DataTime>(Arrays.asList(dataTimes));
return times;
}
/**
* TODO: Get data set in all member; may be used in future, but the data
* time should be checked if it's available time
*/
private List<List<GeneralGridData>> getAllData(DataTime time) {
List<List<GeneralGridData>> allData = new ArrayList<List<GeneralGridData>>();
List<D2DGridResource> members = getAllMembersResources();
for (D2DGridResource member : members) {
List<GeneralGridData> data = member.requestData(time);
if (data != null) {
allData.add(data);
}
}
return allData;
}
@Override
public void update(Object updateData) {
// TODO
}
/**
* Update by checking on the loaded ensemble product resources Re-calculate
* with the ensemble calculator to generate a new product(like mean), update
* the dataMap in the AbstractGridResource Is it possible to update the
* dataMap? The AbstractGridResource methods can be over written?
* UsesetData(Map<DataTime, List<GeneralGridData>> data)
*
* @throws VizException
*/
public void update() throws VizException {
AbstractEditor editor = EditorUtil
.getActiveEditorAs(AbstractEditor.class);
if (level != null && !level.equals("") && unit != null
&& !unit.equals("")) {
// Same level and unit case
dataHolders = EnsembleResourceManager
.getInstance()
.getResourceList(editor)
.getUserLoadedRscs((IDescriptor) new MapDescriptor(), true,
level, unit);
} else if (unit != null && !unit.equals("")) {
// TODO: same unit whatever level case
} else {
// TODO: whatever level or unit,for test only
}
}
@Override
public boolean equals(Object obj) {
// TODO
return false;
}
}

View file

@ -0,0 +1,424 @@
package gov.noaa.gsd.viz.ensemble.display.rsc.histogram;
import java.util.ArrayList;
import java.util.List;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
/**
* Generate the text of the histogram. This algorithm is from ALPS(C++) by James
* Ramer. First step: implement single color; Second step:multi-color.
*
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* July, 2014 5056 jing Initial creation
*
* </pre>
*/
public class TextHistogram {
private final int BLOCK_SIZE = 400;
private float _minBin = 10;
private float _minLbl = 10;
private boolean isColorHist;
private String unit = "";
public TextHistogram(boolean isColorDiagram) {
isColorHist = false;
}
public String makeHistogram(List<Float> data) {
return null;
}
// -------------------------------------------------------------
// char * defaultMultiColor()
//
// For a multi color sample string for which all characters will have the
// color of the sampling overlay, put a space after all non-whitespace
// characters. Returns pointer to terminating null.
// -- implementation
// ---------------------------------------------------------
// TODO
private char[] defaultMultiColor(char[] wrkStr) {
int newSize = wrkStr.length;
for (char ch : wrkStr) {
if (ch > ' ') {
newSize++;
}
}
char[] newStr = new char[newSize];
int j = 0;
for (int i = 0; i < wrkStr.length; i++) {
newStr[j] = wrkStr[i];
j++;
if (wrkStr[i] > ' ') {
newStr[j] = ' ';
j++;
}
}
return newStr;
}
// -------------------------------------------------------------
// void computeBinDelta(float & delta, float & delLbl)
//
// An approximate size for the bins on the ordinate of a histogram is
// input in delta. This is changed to a rounded value for ploting, and
// delLbl is how often to label the ordinate.
// -- implementation
// ---------------------------------------------------------
// ---------------------------------------------------------------------------
// private void computeBinDelta(float & delta, float & delLbl)
private void computeBinDelta(float delta, float delLbl) {
float ee = (float) Math.pow(10, (int) Math.log10(delta));
delta /= ee;
while (delta > 10) {
delta /= 10;
ee *= 10;
}
while (delta < 1) {
delta *= 10;
ee /= 10;
}
if (delta > 7) {
delta = 10 * ee;
delLbl = 50 * ee;
} else if (delta < 1.414) {
delta = ee;
delLbl = 5 * ee;
} else if (delta > 3.333) {
delta = 5 * ee;
delLbl = 20 * ee;
} else {
delta = 2 * ee;
delLbl = 10 * ee;
}
}
// -----------------------------------------------------------------
// TextString FunctionOverlayDepict::interogate()
// Adds one or both color bars to image data, as required.
//
// New multi-color sampling overlays need to have an instance added for
// their
// function name in DepictSeq::samplingCategory().
// ---------------------------------------------------------------------------
public List<String> interrogate(List<AbstractVizResource<?, ?>> rscs,
List<Float> values, String unit) {
List<String> histStr = new ArrayList<String>();
// Most common case; computational overlay, sample with _implDepict
this.unit = unit;
// Check if histogram sampling is possible.
if (rscs.size() == 0) {
histStr.add("NO DATA");
return histStr;
}
// Show a depiction of the weights being used.
int i, j, n, n2;
// Histogram case, the most complex, start by gathering the values.
// If none are toggled on, we will base this on all inputs which exist.
float minVal, maxVal;
minVal = maxVal = 0;
n = rscs.size();
n2 = 2 * n;
if (values.size() == 0) {
histStr.add("NO VALUES");
return histStr;
}
for (i = j = 0; i < n2; i++) {
j = i % n;
if (i == j) {
n2 = n;
}
float oneval = values.get(j);
if (oneval == Float.NaN) {
continue;
}
if (minVal == maxVal && maxVal == 0) {
minVal = maxVal = oneval;
} else if (oneval < minVal) {
minVal = oneval;
} else if (oneval > maxVal) {
maxVal = oneval;
}
}
// Compute the best size to use for the value bins (dv).
// This responds to density by using smaller increments along
// the x-axis when density is larger.
char[] wrkstr = new char[BLOCK_SIZE];
if (minVal >= maxVal) {
wrkstr = (values.size() + " values of " + maxVal + "\n")
.toCharArray();
histStr.add(wrkstr.toString());
return histStr;
}
float dv = _minBin;
dv = (float) .5;
float delLbl = _minLbl * dv / _minBin;
if (dv == 0.5) {
delLbl = 1;
} else if (dv == 0 || (maxVal - minVal) > dv * 25) {
float minDv = (maxVal > -minVal ? maxVal : -minVal) / 5000;
if (dv < minDv) {
dv = minDv;
}
computeBinDelta(dv, delLbl);
} else if (dv != _minBin) {
computeBinDelta(dv, delLbl);
}
int nbMax = 20;
if ((maxVal - minVal) / dv > nbMax) {
float dvSeed, dvPrev;
for (dvSeed = dvPrev = dv, i = 0; i < 100; i++) {
dvSeed *= 1.4;
dv = dvSeed;
computeBinDelta(dv, delLbl);
if (dv == dvPrev) {
continue;
}
if ((maxVal - minVal) / dv < nbMax) {
break;
}
dvPrev = dvSeed = dv;
}
}
// We want edge of the bins to be at half the bin size, containing at
// least
// two even values of del2.
float del2 = delLbl * 2;
float edge = dv * (((int) (minVal / dv)) - (float) 0.5);
while (minVal - edge > dv) {
edge += dv;
}
while (edge > minVal) {
edge -= dv;
}
minVal = edge;
edge = dv * (((int) (maxVal / dv)) + (float) 0.5);
while (edge - maxVal > dv) {
edge -= dv;
}
while (edge < maxVal) {
edge += dv;
}
maxVal = edge;
int nbins = (int) (0.5 + (maxVal - minVal) / dv);
float mean2 = (minVal + maxVal) / 2;
mean2 = mean2 > 0 ? del2 * (int) (0.5 + mean2 / del2) : -del2
* (int) (0.5 - mean2 / del2);
while (minVal > mean2 - del2 && maxVal < mean2 + del2) {
nbins += 2;
minVal -= dv;
maxVal += dv;
}
// Determine the proper y-axis for the counts, we want about 10 steps in
// the vertical axis, and the max value rounded to the nearest 5 counts.
Integer[] counts = new Integer[nbins];
for (int k = 0; k < nbins; k++) {
counts[k] = 0;
}
nbins--;
int maxCount = 0;
for (i = 0; i < values.size(); i++) {
j = (int) ((values.get(i) - minVal) / dv);
if (j < 0) {
j = 0;
}
if (j > nbins) {
j = nbins;
}
counts[j]++;
if (counts[j] > maxCount) {
maxCount = counts[j];
}
}
maxCount = ((4 + maxCount) / 5) * 5;
i = values.size() / 2;
// while (maxCount<i) maxCount += 5;
while (maxCount < i) {
maxCount += 2;
}
int dCount = 1;
int eCount = 1;
while (maxCount > dCount * eCount * 10) {
if (dCount == 5) {
eCount *= 10;
dCount = 1;
} else {
dCount = dCount == 1 ? 2 : 5;
}
}
dCount *= eCount;
// TextString histStr;
eCount = (maxCount / dCount) * dCount;
if (eCount == maxCount) {
eCount -= dCount;
}
eCount++;
// First line, the top
String tmpStr = String.format("%3d", maxCount);
tmpStr += "| ";
histStr.add(tmpStr);
while (eCount > 0) {
int k = 0;
for (char ch : " | ".toCharArray()) {
wrkstr[k] = ch;
k++;
}
int cp = 6;
for (j = 0; j <= nbins; j++, cp++) {
if (counts[j] == 0 || counts[j] < eCount) {
wrkstr[cp] = ' ';
} else {
wrkstr[cp] = 'x';
}
}
wrkstr[cp++] = '\n';
wrkstr[cp] = '\0';
histStr.add(String.copyValueOf(wrkstr, 0, cp - 1));
eCount -= dCount;
}
// X-axis line
int k = 0;
for (char ch : " --".toCharArray()) {
wrkstr[k] = ch;
k++;
}
edge = minVal + dv / 2;
int cp = 6;
for (j = 0; j <= nbins; j++, cp++, edge += dv) {
float d = edge / del2;
if (d < 0) {
d = -d;
}
d -= (int) d;
if (d < 0.01 || d > 0.99) {
wrkstr[cp] = '|';
} else if (d > 0.49 && d < 0.51) {
wrkstr[cp] = ':';
} else {
wrkstr[cp] = '-';
}
}
wrkstr[cp++] = '\n';
wrkstr[cp] = '\0';
// if (idLists)
// defaultMultiColor(wrkstr);
histStr.add(String.copyValueOf(wrkstr, 0, cp - 1));
// Mark X-axis
for (k = 0; k < BLOCK_SIZE; k++) {
wrkstr[k] = ' ';
}
String scr = "";
for (k = 0; k < BLOCK_SIZE; k++) {
scr += ' ';
}
cp = 0;
dCount = 0;
edge = minVal + dv / 2;
edge = edge > 0 ? del2 * (int) (0.5 + edge / del2) : -del2
* (int) (0.5 - edge / del2);
while (edge < minVal) {
edge += del2;
}
while (edge < maxVal) {
scr = String.format("%.4f", edge);
scr = String.copyValueOf(scr.toCharArray(), 0, 4);
i = scr.length();
j = (int) ((edge - minVal) / dv) + 5 - i / 2;
for (k = 0; k < i; k++) {
wrkstr[k + j] = scr.charAt(k);
}
cp = i + j;
dCount++;
edge += del2;
}
edge -= delLbl;
if (dCount >= 2) {
// TODO
;
} else if (edge > maxVal) {
edge -= del2;
scr = String.format("%.1f", edge);
scr.length();
j = (int) ((edge - minVal) / dv) + 5 - i / 2;
for (k = 0; k < i; k++) {
wrkstr[k + j] = scr.charAt(k);
}
} else {
scr = String.format("%.1f", edge);
scr.length();
j = (int) ((edge - minVal) / dv) + 5 - i / 2;
for (k = 0; k < i; k++) {
wrkstr[k + j] = scr.charAt(k);
}
cp = i + j;
}
wrkstr[cp++] = '\n';
wrkstr[cp] = '\0';
histStr.add(String.copyValueOf(wrkstr, 0, cp - 1));
return histStr;
}
}

View file

@ -0,0 +1,314 @@
package gov.noaa.gsd.viz.ensemble.display.rsc.timeseries;
import gov.noaa.gsd.viz.ensemble.display.calculate.Calculation;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.TreeSet;
import javax.measure.unit.Unit;
import org.eclipse.swt.graphics.RGB;
import com.raytheon.uf.common.dataplugin.PluginDataObject;
import com.raytheon.uf.common.dataplugin.grid.GridRecord;
import com.raytheon.uf.common.dataplugin.grid.util.GridLevelTranslator;
import com.raytheon.uf.common.dataplugin.level.Level;
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.style.StyleException;
import com.raytheon.uf.common.style.level.SingleLevel;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.AbstractResourceData;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.capabilities.ColorableCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.OutlineCapability;
import com.raytheon.uf.viz.d2d.xy.adapters.timeseries.GridTimeSeriesAdapter;
import com.raytheon.uf.viz.xy.timeseries.adapter.AbstractTimeSeriesAdapter;
import com.raytheon.uf.viz.xy.timeseries.rsc.TimeSeriesResource;
import com.raytheon.viz.core.graphing.util.GraphPrefsFactory;
import com.raytheon.viz.core.graphing.xy.XYData;
import com.raytheon.viz.core.rsc.ICombinedResourceData.CombineOperation;
/**
* Uses cached data and parameters of loaded members in the time series display,
* generates an new data and construct a resource for it. Implement steps:
* 1,Basic calculation display in same time points on X axis case 2, Basic
* calculation display including different time points on X axis case
* 3,Interactive select a location from map 4, Continuously change the location.
* Should check on if the performance is OK, because current D2D Time series
* display architecture makes the display very slow on getting a point data and
* repeatedly display the background graphics. Maybe the baseline code will be
* improved someday.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb, 2014 5056 jing Initial creation
*
*
* </pre>
*/
public class GeneratedTimeSeriesResource<T extends AbstractResourceData>
extends TimeSeriesResource {
private static final IUFStatusHandler statusHandler = UFStatus
.getHandler(GeneratedTimeSeriesResource.class);
// TODO : Needed for future use.
// private final SimpleDateFormat timeSampleFormat = new SimpleDateFormat(
// "HH:mm'Z' EEE");
private Calculation calculation;
/** the level I would prefer to use **/
protected Level preferredLevel = null;
/** the unit to convert to, matches preferredLevel unit **/
protected Unit<?> preferredUnit = Unit.ONE;
/** a map of levels to units for quick lookup **/
protected Map<Level, Unit<?>> levelUnitMap = new HashMap<Level, Unit<?>>();
/** first received parameter name **/
protected String parameterName = "";
protected String parameterAbbreviation = "";
protected Random rand;
protected String[] titles = null;
public GeneratedTimeSeriesResource(GeneratedTimeSeriesResourceData data,
LoadProperties props) {
super(data, props, null);
// remember the resource data
resourceData = data;
// Set line thickness
OutlineCapability outLine = (OutlineCapability) this
.getCapability((OutlineCapability.class));
outLine.setOutlineWidth(3);
// Set color
ColorableCapability colorable = (ColorableCapability) this
.getCapability(ColorableCapability.class);
rand = new Random();
RGB color = new RGB(rand.nextInt(206) + 50, rand.nextInt(206) + 50,
rand.nextInt(206) + 50);
colorable.setColor(color);
// Initial the data times for needing in the system, it will be updated
this.dataTimes = new HashSet<DataTime>(data.getAllDataTimes());
}
@Override
public String[] getTitles() {
return titles;
}
public void setTitles(String[] titles) {
this.titles = titles;
}
/**
* Update the calculation generated time series resource when any member
* resource or other changed. Will be fully implemented later.
*/
public void updateData() {
// Setup generated resource
// TODO resource.secondaryResource? consider it later
// TODO resource.combineOperation?consider it later
this.setData(((GeneratedTimeSeriesResourceData) this.getResourceData())
.calculate());
// Set the data times for painting, that need changes dataTimes
// to protected in TimeSeriesResource class.
dataTimes = new TreeSet<DataTime>();
for (XYData d : data.getData()) {
dataTimes.add((DataTime) d.getX());
}
}
public String getParameterName() {
return parameterName;
}
public Calculation getCalculation() {
return calculation;
}
public void setCalculation(Calculation c) {
calculation = c;
}
/**
* Prepare the parameters with any time series record.
*/
public void setParameters() {
AbstractTimeSeriesAdapter<?> adapter = ((GeneratedTimeSeriesResourceData) resourceData)
.getAnyMembersResource().getAdapter();
if (!(adapter instanceof GridTimeSeriesAdapter)) {
return;
}
setTitles(((GeneratedTimeSeriesResourceData) resourceData)
.getAnyMembersResource().getTitles());
PluginDataObject pdo = ((GridTimeSeriesAdapter) adapter)
.getArbitraryRecord();
// store off records by level
if (pdo instanceof GridRecord) {
GridRecord record = (GridRecord) pdo;
// set preferredLevel to first level
if (preferredLevel == null) {
preferredLevel = record.getLevel();
((GeneratedTimeSeriesResourceData) resourceData).level = preferredLevel
.toString();
preferredUnit = record.getParameter().getUnit();
((GeneratedTimeSeriesResourceData) resourceData).unit = preferredUnit
.toString();
parameterName = record.getParameter().getName();
parameterAbbreviation = record.getParameter().getAbbreviation();
if (parameterName == null || parameterName.isEmpty()) {
if (parameterAbbreviation == null) {
parameterAbbreviation = "";
}
parameterName = parameterAbbreviation;
}
}
// add Unit to levelUnitMap if needed ( quick look-ups )
Level lvl = record.getLevel();
Unit<?> unit = levelUnitMap.get(lvl);
if (unit == null) {
unit = record.getParameter().getUnit();
levelUnitMap.put(lvl, unit);
}
} else {
// this shouldn't happen, code expects all pdo's for
// GribTimeSeriesAdapter to be GridRecords
String message = "Unexpected PluginDataObject type; got "
+ pdo.getClass().getName() + " expected GridRecord";
statusHandler.handle(Priority.PROBLEM, message, new Exception(
message));
}
// Added interface to TimeSeriesResource solution
// parameterName = this.getAdapter().getParameterName();
}
@Override
protected void initInternal(IGraphicsTarget target) throws VizException {
if (secondaryResource != null) {
secondaryResource.setDescriptor(this.descriptor);
secondaryResource.init(target);
}
// Load the Graph Preferences
if (prefs == null) {
try {
String name = preferredLevel.getMasterLevel().getName();
SingleLevel level = GridLevelTranslator.construct(name);
level.setValue(preferredLevel.getLevelonevalue());
prefs = GraphPrefsFactory.buildPreferences(
((GeneratedTimeSeriesResourceData) resourceData)
.getYParameter().code, level);
} catch (StyleException e) {
throw new VizException(e);
}
}
if (prefs != null && prefs.getDisplayUnits() != null) {
units = prefs.getDisplayUnitLabel();
}
if (secondaryResource != null
&& combineOperation != CombineOperation.NONE) {
// TODO
// combineData();
}
}
/*
* implement later for secondaryResource
*
* @Override private void combineData() { if (secondaryResource.prefs !=
* null && secondaryResource.prefs.getDisplayUnits() != null && prefs !=
* null && prefs.getDisplayUnits() != null) { }
*/
/**
* Get the legend name of this resource
*/
@Override
public String getName() {
String resultingName = "<No Generated Times Series: "
+ calculation.getTitle() + ">";
String levelKey = resourceData.getLevelKey();
String levelUnit = levelKey.replaceAll("[^a-zA-Z]", "");
boolean isHeight = levelUnit.equalsIgnoreCase("mb")
|| levelUnit.equalsIgnoreCase("agl")
|| levelUnit.contains("Agl");
NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(1);
String lon = "";
String lat = "";
if (resourceData.getCoordinate() != null) {
double x = resourceData.getCoordinate().x;
double y = resourceData.getCoordinate().y;
lon = nf.format(Math.abs(x));
lat = nf.format(Math.abs(y));
String stnID = "";
String source = calculation.getTitle();
// Make legend for point data
StringBuilder sb = new StringBuilder(String.format(
"%s pt%s %s%s %s%s", source, resourceData.getPointLetter(),
lat, y >= 0 ? "N" : "S", lon, x >= 0 ? "E" : "W"));
if (stnID != null) {
sb.append(" ").append(stnID);
}
if (!isHeight) {
sb.append(" ").append(resourceData.getLevelKey());
}
sb.append(String.format(" %s %s %s", parameterName, "TSer",
units != null && units.equals("") == false ? "(" + units
+ ")" : ""));
resultingName = sb.toString();
}
return resultingName;
}
}

View file

@ -0,0 +1,307 @@
package gov.noaa.gsd.viz.ensemble.display.rsc.timeseries;
import gov.noaa.gsd.viz.ensemble.display.calculate.EnsembleCalculator;
import gov.noaa.gsd.viz.ensemble.display.common.GenericResourceHolder;
import gov.noaa.gsd.viz.ensemble.display.control.EnsembleResourceManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.ResourceProperties;
import com.raytheon.uf.viz.xy.timeseries.display.TimeSeriesDescriptor;
import com.raytheon.uf.viz.xy.timeseries.rsc.TimeSeriesResource;
import com.raytheon.uf.viz.xy.timeseries.rsc.TimeSeriesResourceData;
import com.raytheon.viz.core.graphing.xy.XYDataList;
import com.raytheon.viz.ui.EditorUtil;
import com.raytheon.viz.ui.editor.AbstractEditor;
/**
* Uses cached data of loaded members in the time series display, generates an
* new data and construct a resource for it.
*
* @author jing
* @version 1.0
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Feb, 2014 5056 jing Initial creation
*
*
* </pre>
*/
public class GeneratedTimeSeriesResourceData extends TimeSeriesResourceData {
private Map<String, List<GenericResourceHolder>> dataHolders = new ConcurrentHashMap<String, List<GenericResourceHolder>>();
/**
* Calculate loaded ensemble data to generate new data, such as mean.
*/
protected EnsembleCalculator calculator = null;
protected GeneratedTimeSeriesResource<GeneratedTimeSeriesResourceData> resource = null;
// like "500MB", "" is not available or don't care.
protected String level;
// Like "Height", "" is not available or don't care.
protected String unit;
// create an empty metadata map
protected HashMap<String, RequestConstraint> metadataMap = new HashMap<String, RequestConstraint>();
public GeneratedTimeSeriesResourceData(EnsembleCalculator calculator,
String level, String unit) {
super();
this.calculator = calculator;
this.level = level;
this.unit = unit;
// interface to disable the request
this.setRetrieveData(false);
// No time request in time matching
this.setRequeryNecessaryOnTimeMatch(false);
this.setMetadataMap(metadataMap);
}
/**
* Construct the resource whatever level and unit
*/
@Override
public AbstractVizResource<?, ?> construct(LoadProperties loadProperties,
IDescriptor descriptor) throws VizException {
update();
// Not data, don't construct the resource for avoid problem
if (dataHolders == null || dataHolders.isEmpty()
|| dataHolders.keySet().isEmpty()) {
return null;
}
resource = new GeneratedTimeSeriesResource<GeneratedTimeSeriesResourceData>(
this, loadProperties);
resource.setCalculation(calculator.getCalculation());
resource.setDescriptor((TimeSeriesDescriptor) descriptor);
this.metadataMap = new HashMap<String, RequestConstraint>();
// set the resource
setup();
resource.updateData();
// update();
ResourceProperties rp = new ResourceProperties();
ResourcePair pair = new ResourcePair();
pair.setResource(resource);
pair.setProperties(rp);
descriptor.getResourceList().add(pair);
// TODO: Is this the correct way to associate the editor with the
// resource?
AbstractEditor editor = EditorUtil
.getActiveEditorAs(AbstractEditor.class);
EnsembleResourceManager.getInstance().registerGenerated(
(AbstractVizResource<?, ?>) resource, editor);
return resource;
}
// Uses the dataHolders to generate the new data for ensemble display
@SuppressWarnings("unchecked")
public XYDataList calculate() {
return calculator.calculateTimeSeries(getAllData());
}
public void update() throws VizException {
// TODO: Is this the correct way to associate the editor with the
// resource?
AbstractEditor editor = EditorUtil
.getActiveEditorAs(AbstractEditor.class);
if (level != null && !level.equals("") && unit != null
&& !unit.equals("")) {
// Same level and unit case
dataHolders = EnsembleResourceManager
.getInstance()
.getResourceList(editor)
.getUserLoadedRscs(
(IDescriptor) new TimeSeriesDescriptor(), true,
level, unit);
// TODO: Reserved for future use.
// dataHolders =
// ResourceManager.getInstance().getResourceList(editor).getCalculationLoadedRscs((IDescriptor)new
// TimeSeriesDescriptor(), true);
// } else if (level != null && !level.equals("")
// && unit != null && !unit.equals("")){
// //same unit whatever level case
// dataHolders =
// ResourceManager.getInstance().getResourceList(editor)
// .getUserLoadedRscs((IDescriptor)new TimeSeriesDescriptor(),
// false, unit);
} else {
// TODO: Reserved for future use.
// whatever level or unit,for test only
// dataHolders =
// ResourceManager.getInstance().getResourceList(editor)
// .getUserLoadedRscs((IDescriptor)new TimeSeriesDescriptor(),
// false);
dataHolders = EnsembleResourceManager
.getInstance()
.getResourceList(editor)
.getUserLoadedRscs(
(IDescriptor) new TimeSeriesDescriptor(), true);
}
}
/**
* Set up the generated resourceData with a member resource.
*/
private void setup() {
List<TimeSeriesResource> rscs = getAllMembersResources();
if (rscs.isEmpty()) {
return;
}
resource.setParameters();
// Set up generated resource data
this.level = rscs.get(0).getResourceData().getLevelKey();
this.unit = rscs.get(0).getUnits();
this.setCoordinate(rscs.get(0).getResourceData().getCoordinate());
this.setCombineOperation(rscs.get(0).getResourceData()
.getCombineOperation());
this.setFrozenTime(rscs.get(0).getResourceData().getFrozenTime());
this.setLevelKey(rscs.get(0).getResourceData().getLevelKey());
this.setPointCoordinate(rscs.get(0).getResourceData()
.getPointCoordinate());
this.setPointLetter(rscs.get(0).getResourceData().getPointLetter());
this.setYParameter(rscs.get(0).getResourceData().getYParameter());
this.setXParameter(rscs.get(0).getResourceData().getXParameter());
}
/**
* May need not the Adapter if the generated resource and data can be set
* correctly using a member resource.
*
* @param object
* @return
*/
// private AbstractTimeSeriesAdapter<?> getAdapter(PluginDataObject object){
// GeneratedTimeSeriesAdapter adapter = new GeneratedTimeSeriesAdapter();
// return null;
// }
/**
* get the data available times from all members
*/
public List<DataTime> getAllDataTimes() {
List<DataTime> dataTimes = new ArrayList<DataTime>();
List<TimeSeriesResource> members = getAllMembersResources();
for (TimeSeriesResource member : members) {
DataTime[] meberDataTimes = member.getDataTimes();
if (meberDataTimes != null && meberDataTimes.length != 0) {
for (int i = 0; i < meberDataTimes.length - 1; i++) {
if (!dataTimes.contains(meberDataTimes[i])) {
dataTimes.add(meberDataTimes[i]);
}
}
}
}
return dataTimes;
}
/**
* Get all data of members for calculation
*
* @return
*/
private List<XYDataList> getAllData() {
List<XYDataList> allData = new ArrayList<XYDataList>();
List<TimeSeriesResource> members = getAllMembersResources();
for (TimeSeriesResource member : members) {
XYDataList data = member.getData();
if (data != null) {
allData.add(data);
}
}
return allData;
}
public TimeSeriesResource getAnyMembersResource() {
List<TimeSeriesResource> members = getAllMembersResources();
if (members == null || members.isEmpty()) {
return null;
}
return members.get(0);
}
private List<TimeSeriesResource> getAllMembersResources() {
// whatever level or unit in this implementation
Set<String> keys = dataHolders.keySet();
List<TimeSeriesResource> members = new ArrayList<TimeSeriesResource>();
for (String key : keys) {
List<GenericResourceHolder> rcsList = dataHolders.get(key);
if (rcsList == null || rcsList.size() == 0) {
continue;
}
for (int i = 0; i < rcsList.size(); i++) {
if (rcsList.get(i).getRsc() instanceof TimeSeriesResource) {
members.add((TimeSeriesResource) rcsList.get(i).getRsc());
}
}
}
return members;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
}

View file

@ -0,0 +1,41 @@
package gov.noaa.gsd.viz.ensemble.navigator.action;
import gov.noaa.gsd.viz.ensemble.navigator.ui.layer.EnsembleToolManager;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import com.raytheon.viz.ui.tools.AbstractTool;
/**
* This is how you start the Ensemble Tool.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Dec 7, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @version 1.0
*/
public class EnsembleToolLayerManagerAction extends AbstractTool {
public EnsembleToolLayerManagerAction() {
}
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
EnsembleToolManager.getInstance().execute(event);
return null;
}
}

View file

@ -0,0 +1,94 @@
package gov.noaa.gsd.viz.ensemble.navigator.ui.layer;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import com.raytheon.viz.ui.editor.AbstractEditor;
/**
* This is the part listener for the editors associated with Ensemble Tool.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov 17, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @version 1.0
*/
public class EnsembleEditorPartListener implements IPartListener2 {
private AbstractEditor editor = null;
public EnsembleEditorPartListener(AbstractEditor editor) {
this.editor = editor;
}
@Override
public void partActivated(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
EnsembleToolManager.getInstance().switchToEditor(editor, false);
}
}
@Override
public void partDeactivated(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
// TODO
}
}
@Override
public void partBroughtToTop(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
// TODO
}
}
@Override
public void partClosed(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
EnsembleToolManager.getInstance().unloadActiveToolLayer();
}
}
@Override
public void partOpened(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
// TODO
}
}
@Override
public void partHidden(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
// TODO
}
}
@Override
public void partVisible(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
// TODO
}
}
@Override
public void partInputChanged(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
// TODO
}
}
private boolean isThisPart(IWorkbenchPartReference ref) {
IWorkbenchPart part = ref.getPart(false);
return part != null && part.equals(editor);
}
}

View file

@ -0,0 +1,653 @@
package gov.noaa.gsd.viz.ensemble.navigator.ui.layer;
import gov.noaa.gsd.viz.ensemble.display.calculate.AvgM1StddevCalculator;
import gov.noaa.gsd.viz.ensemble.display.calculate.AvgP1StddevCalculator;
import gov.noaa.gsd.viz.ensemble.display.calculate.Calculation;
import gov.noaa.gsd.viz.ensemble.display.calculate.ERFCalculator;
import gov.noaa.gsd.viz.ensemble.display.calculate.MaxCalculator;
import gov.noaa.gsd.viz.ensemble.display.calculate.MeanCalculator;
import gov.noaa.gsd.viz.ensemble.display.calculate.MedianCalculator;
import gov.noaa.gsd.viz.ensemble.display.calculate.MinCalculator;
import gov.noaa.gsd.viz.ensemble.display.calculate.ModeCalculator;
import gov.noaa.gsd.viz.ensemble.display.calculate.Range;
import gov.noaa.gsd.viz.ensemble.display.calculate.RangeCalculator;
import gov.noaa.gsd.viz.ensemble.display.calculate.StddevCalculator;
import gov.noaa.gsd.viz.ensemble.display.calculate.SumCalculator;
import gov.noaa.gsd.viz.ensemble.display.common.GenericResourceHolder;
import gov.noaa.gsd.viz.ensemble.display.common.NavigatorResourceList;
import gov.noaa.gsd.viz.ensemble.display.control.EnsembleResourceManager;
import gov.noaa.gsd.viz.ensemble.display.control.load.GeneratedDataLoader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.viz.core.IExtent;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.drawables.AbstractDescriptor;
import com.raytheon.uf.viz.core.drawables.IDescriptor;
import com.raytheon.uf.viz.core.drawables.IDescriptor.IFrameChangedListener;
import com.raytheon.uf.viz.core.drawables.PaintProperties;
import com.raytheon.uf.viz.core.drawables.ResourcePair;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.rsc.AbstractNameGenerator;
import com.raytheon.uf.viz.core.rsc.AbstractResourceData;
import com.raytheon.uf.viz.core.rsc.AbstractVizResource;
import com.raytheon.uf.viz.core.rsc.IInitListener;
import com.raytheon.uf.viz.core.rsc.IInputHandler;
import com.raytheon.uf.viz.core.rsc.IRefreshListener;
import com.raytheon.uf.viz.core.rsc.IResourceDataChanged;
import com.raytheon.uf.viz.core.rsc.LoadProperties;
import com.raytheon.uf.viz.core.rsc.RenderingOrderFactory.ResourceOrder;
import com.raytheon.uf.viz.core.rsc.ResourceList;
import com.raytheon.uf.viz.core.rsc.ResourceProperties;
import com.raytheon.uf.viz.core.rsc.capabilities.EditableCapability;
import com.raytheon.uf.viz.core.rsc.tools.GenericToolsResourceData;
import com.raytheon.uf.viz.xy.timeseries.rsc.TimeSeriesResource;
import com.raytheon.viz.ui.EditorUtil;
import com.raytheon.viz.ui.editor.AbstractEditor;
import com.vividsolutions.jts.geom.Coordinate;
/**
* The EnsembleToolLayer is an AbstractVizResource that the user can turn on or
* off (i.e. make editable or not). Typically there is one instance of this
* class per AbstractEditor window. This class is meant to be controlled by the
* EnsembleToolManager. Though this class is decoupled from the other classes in
* this plug-in, the idea is that the EnsembleToolLayer is the class which knows
* how to have access to its virtually contained resources and how to act upon
* them. In the greater picture, the tool layer controls the entire state of the
* Ensemble Tool; if the activel tool layer is turned off (made "not editable")
* then the Ensemble Tool is also disabled, but if the user switches to another
* editor that has an EnsembleToolLayer that is "editable", the manager will
* become active again. .
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov 16, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @version 1.0
*/
public class EnsembleToolLayer extends
AbstractVizResource<AbstractResourceData, AbstractDescriptor> implements
IInputHandler, IResourceDataChanged, IRefreshListener,
IFrameChangedListener, IInitListener {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(EnsembleToolLayer.class);
public static final String DEFAULT_NAME = "Ensembles";
protected Coordinate initialDragPos = null;
protected Coordinate continualDragPos = null;
protected IExtent activeBoundingArea = null;
protected boolean isClosingToolLayer = false;
private boolean isDisposed = true;
private Object[] expandedElements = new Object[0];
private TreePath[] expandedTreePaths = new TreePath[0];
// TODO: Let's try and use the ResourceManager at next sprint ...
private Map<String, List<GenericResourceHolder>> ensemblesTree = new HashMap<String, List<GenericResourceHolder>>();
public EnsembleToolLayer(
GenericToolsResourceData<EnsembleToolLayer> resourceData,
LoadProperties loadProperties) {
super(resourceData, loadProperties);
registerListener((IInitListener) this);
resourceData
.setNameGenerator(new EnsembleToolLayerNameGeneratorWithTimeStampBasis());
}
synchronized public Map<String, List<GenericResourceHolder>> getEnsembleResources() {
AbstractEditor editor = EnsembleToolManager.getInstance().findEditor(
this);
if (EnsembleResourceManager.getInstance().getResourceList(editor) != null) {
ensemblesTree = EnsembleResourceManager.getInstance()
.getResourceList(editor).getAllRscsAsMap();
} else {
ensemblesTree = null;
}
return ensemblesTree;
}
public Object[] getExpandedElements() {
return expandedElements;
}
public void setExpandedElements(Object[] ee) {
expandedElements = ee;
}
public TreePath[] getExpandedTreePaths() {
return expandedTreePaths;
}
public void setExpandedTreePaths(TreePath[] expandedTreePaths) {
this.expandedTreePaths = expandedTreePaths;
}
public void unloadAllResources(AbstractEditor editor) {
if (EnsembleResourceManager.getInstance().getResourceList(editor) == null)
return;
Map<String, List<GenericResourceHolder>> allRscs = EnsembleResourceManager
.getInstance().getResourceList(editor).getAllRscsAsMap();
List<GenericResourceHolder> currEnsembleMembers = null;
Set<String> keySet = allRscs.keySet();
Iterator<String> iter = keySet.iterator();
while (iter.hasNext()) {
String currRscListName = iter.next();
currEnsembleMembers = allRscs.get(currRscListName);
for (GenericResourceHolder gr : currEnsembleMembers) {
if (gr.getRsc() instanceof TimeSeriesResource) {
gr.getRsc().unload(
editor.getActiveDisplayPane().getDescriptor()
.getResourceList());
gr.getRsc().dispose();
} else {
gr.getRsc().unload();
gr.getRsc().dispose();
}
EnsembleResourceManager.getInstance().unregisterResource(gr,
editor, false);
}
}
EnsembleResourceManager.getInstance().updateFrameChanges(editor);
if ((getResourceContainer() != null)
&& (getResourceContainer().getActiveDisplayPane() != null)) {
getResourceContainer().getActiveDisplayPane().refresh();
}
}
public ResourcePair getResourcePair(AbstractVizResource<?, ?> rsc) {
ResourcePair foundRp = null;
ResourceList rscList = null;
if (TimeSeriesResource.class.isAssignableFrom(rsc.getClass())) {
AbstractEditor editor = EnsembleToolManager.getInstance()
.findEditor(this);
rscList = editor.getActiveDisplayPane().getDescriptor()
.getResourceList();
} else {
rscList = getResourceContainer().getActiveDisplayPane()
.getDescriptor().getResourceList();
}
for (ResourcePair rp : rscList) {
if (rsc.equals(rp.getResource())) {
foundRp = rp;
break;
}
}
return foundRp;
}
public void unloadAllResourcesByName(String ensembleName) {
AbstractEditor editor = EnsembleToolManager.getInstance().findEditor(
this);
List<GenericResourceHolder> ensembleMembers = EnsembleResourceManager
.getInstance().getResourceList(editor)
.getUserLoadedRscs(ensembleName);
int total = ensembleMembers.size();
int count = 0;
for (GenericResourceHolder gr : ensembleMembers) {
count++;
if (count < total) {
EnsembleResourceManager.getInstance().unregisterResource(gr,
editor, false);
} else {
EnsembleResourceManager.getInstance().unregisterResource(gr,
editor, true);
}
gr.getRsc().unload();
}
EnsembleResourceManager.getInstance().updateFrameChanges(editor);
getResourceContainer().getActiveDisplayPane().refresh();
}
public void propertiesChanged(ResourceProperties updatedProps) {
// TODO
}
public boolean isDisposed() {
return isDisposed;
}
@Override
protected void disposeInternal() {
descriptor.removeFrameChangedListener(this);
AbstractEditor editor = EnsembleToolManager.getInstance().findEditor(
this);
unloadAllResources(editor);
EnsembleToolManager.getInstance().removeToolLayer(this);
EnsembleResourceManager.getInstance().updateFrameChanges(editor);
isDisposed = true;
}
@Override
protected void initInternal(IGraphicsTarget target) throws VizException {
isDisposed = false;
descriptor.getResourceList().getProperties(this)
.setRenderingOrderId("HIGHEST");
}
@Override
public void inited(AbstractVizResource<?, ?> initedRsc) {
descriptor.addFrameChangedListener(this);
resourceData.addChangeListener(this);
registerListener((IRefreshListener) this);
}
@Override
protected void paintInternal(IGraphicsTarget target,
PaintProperties paintProps) throws VizException {
}
@Override
public void resourceChanged(ChangeType type, Object object) {
if (type == ChangeType.DATA_REMOVE) {
// TODO
}
if (type == ChangeType.CAPABILITY) {
Class<?> ct = object.getClass();
// TODO
if (getCapability(EditableCapability.class).getClass()
.isAssignableFrom(ct)) {
// TODO
}
}
issueRefresh();
}
public void transferFocusToEditor() {
// Get the active window
IWorkbenchWindow window = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow();
if (window == null) {
return;
}
IEditorPart editor = window.getActivePage().getActiveEditor();
if (editor != null) {
editor.setFocus();
}
}
@Override
public void refresh() {
// TODO
}
@Override
public boolean handleMouseMove(int x, int y) {
boolean handledMouseAction = false;
// TODO
return handledMouseAction;
}
@Override
public boolean handleMouseDownMove(int x, int y, int mouseButton) {
boolean handledMouseAction = false;
// TODO
return handledMouseAction;
}
@Override
public boolean handleMouseUp(int x, int y, int mouseButton) {
boolean alreadyHandled = false;
// TODO
return alreadyHandled;
}
@Override
public boolean handleKeyUp(int keyCode) {
boolean handledKeyAction = false;
// TODO
return handledKeyAction;
}
@Override
public boolean handleMouseDown(int x, int y, int mouseButton) {
// TODO
return false;
}
@Override
public boolean handleMouseHover(int x, int y) {
// TODO
return false;
}
@Override
public boolean handleDoubleClick(int x, int y, int button) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean handleMouseWheel(Event event, int x, int y) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean handleMouseExit(Event event) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean handleMouseEnter(Event event) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean handleKeyDown(int keyCode) {
// TODO Auto-generated method stub
return false;
}
public void calculate(Calculation algorithm, final Range range) {
final AbstractEditor editor = EnsembleToolManager.getInstance()
.findEditor(this);
if (algorithm == Calculation.ENSEMBLE_RELATIVE_FREQUENCY) {
Thread t = null;
t = new Thread() {
public void run() {
ERFCalculator erfRsc = new ERFCalculator(range);
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.load(erfRsc);
}
};
t.start();
}
}
public void calculate(Calculation algorithm) {
final AbstractEditor editor = EnsembleToolManager.getInstance()
.findEditor(this);
if (algorithm == Calculation.MEAN) {
Thread t = null;
t = new Thread() {
public void run() {
// Load the mean overlay
MeanCalculator meanRsc = new MeanCalculator();
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.load(meanRsc);
}
};
t.start();
} else if (algorithm == Calculation.MIN) {
Thread t = null;
t = new Thread() {
public void run() {
// Load the min overlay
MinCalculator minRsc = new MinCalculator();
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.load(minRsc);
}
};
t.start();
} else if (algorithm == Calculation.MAX) {
Thread t = null;
t = new Thread() {
public void run() {
// Load the max overlay
MaxCalculator maxRsc = new MaxCalculator();
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.load(maxRsc);
}
};
t.start();
} else if (algorithm == Calculation.MEDIAN) {
Thread t = null;
t = new Thread() {
public void run() {
// Load the median overlay
MedianCalculator medianRsc = new MedianCalculator();
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.load(medianRsc);
}
};
t.start();
} else if (algorithm == Calculation.MODE) {
Thread t = null;
t = new Thread() {
public void run() {
// Load the median overlay
ModeCalculator modeRsc = new ModeCalculator();
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.load(modeRsc);
}
};
t.start();
} else if (algorithm == Calculation.RANGE) {
Thread t = null;
t = new Thread() {
public void run() {
// Load the range overlay
RangeCalculator rangeRsc = new RangeCalculator();
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.load(rangeRsc);
}
};
t.start();
} else if (algorithm == Calculation.SUMMATION) {
Thread t = null;
t = new Thread() {
public void run() {
// Load the sum overlay
SumCalculator sumRsc = new SumCalculator();
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.load(sumRsc);
}
};
t.start();
} else if (algorithm == Calculation.STANDARD_DEVIATION) {
Thread t = null;
t = new Thread() {
public void run() {
// Load the sum overlay
StddevCalculator stddevRsc = new StddevCalculator();
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.load(stddevRsc);
}
};
t.start();
} else if (algorithm == Calculation.AVG_MINUS_STD_DEV) {
Thread t = null;
t = new Thread() {
public void run() {
// Load the sum overlay
AvgM1StddevCalculator m1StddevRsc = new AvgM1StddevCalculator();
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.load(m1StddevRsc);
}
};
t.start();
} else if (algorithm == Calculation.AVG_PLUS_STD_DEV) {
Thread t = null;
t = new Thread() {
public void run() {
// Load the sum overlay
AvgP1StddevCalculator p1StddevRsc = new AvgP1StddevCalculator();
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.load(p1StddevRsc);
}
};
t.start();
} else if (algorithm == Calculation.HISTOGRAM_SAMPLING) {
Thread t = null;
t = new Thread() {
public void run() {
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.loadOverlay(Calculation.HISTOGRAM_SAMPLING);
}
};
t.start();
} else if (algorithm == Calculation.HISTOGRAM_TEXT) {
Thread t = null;
t = new Thread() {
public void run() {
GeneratedDataLoader loader = new GeneratedDataLoader(
editor,
GeneratedDataLoader.GeneratedloadMode.SAME_UNIT_AND_LEVEL);
loader.loadOverlay(Calculation.HISTOGRAM_TEXT);
}
};
t.start();
}
}
protected void setEditable(boolean makeEditable) {
getCapability(EditableCapability.class).setEditable(makeEditable);
}
@Override
public void frameChanged(IDescriptor descriptor, DataTime oldTime,
DataTime newTime) {
EnsembleToolManager.getInstance().updateLegendTimeInfo();
}
class EnsembleToolLayerNameGeneratorWithTimeStampBasis extends
AbstractNameGenerator {
@Override
public String getName(AbstractVizResource<?, ?> resource) {
String timeBasis = EnsembleToolManager.getInstance()
.getTimeBasisLegendTime();
if (NavigatorResourceList.isTimeEmpty(timeBasis)) {
timeBasis = "";
} else {
timeBasis = " (" + timeBasis + ") ";
}
AbstractEditor editor = (AbstractEditor) EditorUtil
.getActiveEditor();
String fullName = EnsembleToolLayer.DEFAULT_NAME + " "
+ editor.getTitle().trim() + timeBasis;
return fullName;
}
}
@Override
public ResourceOrder getResourceOrder() {
return ResourceOrder.HIGHEST;
}
}

View file

@ -0,0 +1,123 @@
package gov.noaa.gsd.viz.ensemble.navigator.ui.viewer;
import gov.noaa.gsd.viz.ensemble.navigator.ui.layer.EnsembleToolManager;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.PlatformUI;
import com.raytheon.uf.viz.core.VizApp;
/**
* The part listener for the EnsembleToolViewer class (ViewPart). The most
* critical reason for this listener is to turn on/off the CAVE tool layer
* editability flag (i.e. whether the tool layer has control of user
* interactivity or not) when the view state changes (becomes
* activated/deactivated).
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 8, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @version 1.0
*/
public class EnsembleViewPartListener implements IPartListener2 {
public static boolean isHidden = false;
private EnsembleToolViewer ensembleToolViewer = null;
public EnsembleViewPartListener(EnsembleToolViewer fsv) {
ensembleToolViewer = fsv;
}
@Override
public void partActivated(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
EnsembleToolManager.getInstance().setEditable(true);
}
}
@Override
public void partDeactivated(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
VizApp.runAsync(new Runnable() {
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// ignore
}
if (isHidden) {
EnsembleToolManager.getInstance().setEditable(false);
}
}
});
}
}
@Override
public void partHidden(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
isHidden = true;
}
}
@Override
public void partVisible(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
isHidden = false;
}
}
@Override
public void partClosed(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
isHidden = false;
VizApp.runAsync(new Runnable() {
public void run() {
if (!PlatformUI.getWorkbench().isClosing()) {
EnsembleToolManager.getInstance().close();
}
}
});
}
}
@Override
public void partBroughtToTop(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
// TODO
}
}
@Override
public void partOpened(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
// TODO
}
}
@Override
public void partInputChanged(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
// TODO
}
}
private boolean isThisPart(IWorkbenchPartReference ref) {
IWorkbenchPart part = ref.getPart(false);
return part != null && part.equals(ensembleToolViewer);
}
}

View file

@ -0,0 +1,22 @@
package gov.noaa.gsd.viz.ensemble.navigator.ui.viewer;
/**
* Convenience enumeration for ViewPart states.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 8, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @version 1.0
*/
public enum ViewerWindowState {
SHOW_WITH_FOCUS, SHOW_WITHOUT_FOCUS, MINIMIZED, CLOSE;
}

View file

@ -0,0 +1,109 @@
package gov.noaa.gsd.viz.ensemble.util;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
/**
* This class is used to map unique colors to given GFS ensemble perturbation
* members, which are identified by their hard-coded perturbation memeber names
* (ctl1, ctl2, n1, n2 ... p4, p5). It has been created in support of
* simplifying the process of allowing the user to color an entire ensemble set
* of members using a color gradient.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 8, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @version 1.0
*/
public class ChosenGEFSColors {
static public ChosenGEFSColors getInstance() {
if (SINGLETON == null) {
SINGLETON = new ChosenGEFSColors();
}
return SINGLETON;
}
static private ChosenGEFSColors SINGLETON = null;
private ChosenGEFSColors() {
}
private Color color = SWTResourceManager.NEON_PURPLE;
public Color getColor() {
return color;
}
public void setColor(Color c) {
color = c;
}
public Color getGradientByEnsembleId(String name) {
Color c = null;
final int totalSteps = 12;
int pertNumber = getPerturbationIndex(name);
c = getGradientColor(color, totalSteps, pertNumber);
return c;
}
private Color getGradientColor(Color c, int totalSteps, int currStep) {
RGB rgb = c.getRGB();
float[] hsb = rgb.getHSB();
hsb[1] = (float) currStep / (float) totalSteps;
RGB nrgb = new RGB(hsb[0], hsb[1], hsb[2]);
return SWTResourceManager.getColor(nrgb);
}
public String getSrefPerturbationPrefix(String member) {
return member.replaceAll("[0-9]", "");
}
// TODO: this is a poor man's solution wokring against hard-coded names:
// .... such as ctl1, ctl2, n1, n2 ... p4, p5
public int getPerturbationIndex(String member) {
int pert = 0;
if (member.startsWith("ctl1")) {
pert = 12;
} else if (member.startsWith("ctl2")) {
pert = 11;
} else if (member.startsWith("n1")) {
pert = 10;
} else if (member.startsWith("n2")) {
pert = 9;
} else if (member.startsWith("n3")) {
pert = 8;
} else if (member.startsWith("n4")) {
pert = 7;
} else if (member.startsWith("n5")) {
pert = 6;
} else if (member.startsWith("p1")) {
pert = 5;
} else if (member.startsWith("p2")) {
pert = 4;
} else if (member.startsWith("p3")) {
pert = 3;
} else if (member.startsWith("p4")) {
pert = 2;
} else if (member.startsWith("p5")) {
pert = 1;
}
return pert;
}
}

View file

@ -0,0 +1,143 @@
package gov.noaa.gsd.viz.ensemble.util;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
/**
* This class is used to map unique colors to given SREF ensemble perturbation
* members, which are identified by their hard-coded perturbation memeber names.
* It has been created in support of simplifying the process of allowing the
* user to color the different groups of perturbation members (NMM, NMB, EM)
* with their own color gradient using a base color for each group (e.g. NMM =
* RED, NMB = BLUE, EM = GREEN).
*
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 8, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @version 1.0
*/
public class ChosenSREFColors {
static public ChosenSREFColors getInstance() {
if (SINGLETON == null) {
SINGLETON = new ChosenSREFColors();
}
return SINGLETON;
}
static private ChosenSREFColors SINGLETON = null;
private ChosenSREFColors() {
}
private Color nmm_color = SWTResourceManager.CHERRY_RED;
private Color nmb_color = SWTResourceManager.SPRING_GREEN;
private Color em_color = SWTResourceManager.DEEP_BLUE;
public Color get_EM_color() {
return em_color;
}
public void set_EM_color(Color em_color) {
this.em_color = em_color;
}
public Color get_NMM_color() {
return nmm_color;
}
public void set_NMM_color(Color nmm_color) {
this.nmm_color = nmm_color;
}
public Color get_NMB_color() {
return nmb_color;
}
public void set_NMB_color(Color nmb_color) {
this.nmb_color = nmb_color;
}
// TODO: this is a poor man's solution wokring against hard-coded names:
// .... such as ctll1, ctll8, ctll15, n1, n2, n3, n4, p5, p6, p7 etc.
public Color getGradientByEnsembleId(String name) {
Color c = null;
float[] saturationSchedule = { 1.0f, 0.70f, 0.60f, 0.50f, 0.40f, 0.25f,
0.15f };
int pertNumber = getPerturbationIndex(name);
int shiftedPertNum = 0;
Color startColor = null;
// int totalSteps = 7;
// first model is NMM ...
if ((pertNumber >= 1) && (pertNumber <= 7)) {
startColor = nmm_color;
c = getGradientColor(startColor, saturationSchedule[pertNumber - 1]);
}
// second model is NMB ...
if ((pertNumber >= 8) && (pertNumber <= 14)) {
startColor = nmb_color;
shiftedPertNum = pertNumber - 7;
c = getGradientColor(startColor,
saturationSchedule[(shiftedPertNum - 1)]);
}
// third model is EM ...
if ((pertNumber >= 15) && (pertNumber <= 21)) {
startColor = em_color;
shiftedPertNum = pertNumber - 14;
c = getGradientColor(startColor,
saturationSchedule[(shiftedPertNum - 1)]);
}
return c;
}
@SuppressWarnings("unused")
private Color getGradientColor(Color c, int totalSteps, int currStep) {
RGB rgb = c.getRGB();
float[] hsb = rgb.getHSB();
hsb[1] = (float) currStep / (float) totalSteps;
RGB nrgb = new RGB(hsb[0], hsb[1], hsb[2]);
return SWTResourceManager.getColor(nrgb);
}
private Color getGradientColor(Color c, float ratio) {
RGB rgb = c.getRGB();
float[] hsb = rgb.getHSB();
hsb[1] = ratio;
RGB nrgb = new RGB(hsb[0], hsb[1], hsb[2]);
return SWTResourceManager.getColor(nrgb);
}
public String getPerturbationPrefix(String member) {
return member.replaceAll("[0-9]", "");
}
public int getPerturbationIndex(String member) {
String pertNumStr = member.replaceAll("[^0-9]", "");
int pert = Integer.parseInt(pertNumStr);
return pert;
}
}

View file

@ -0,0 +1,158 @@
package gov.noaa.gsd.viz.ensemble.util;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.ColorDialog;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import com.raytheon.viz.ui.dialogs.CaveJFACEDialog;
/**
* This class is a Dialog which allows users to change the colors of the GFS
* ensemble perturbation members. It is used as a convenience feature to make it
* easy to create a gradient of colors given a base color.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 8, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @version 1.0
*/
public class EnsembleGEFSColorChooser extends CaveJFACEDialog {
/**
* Create the dialog.
*
* @param parentShell
*/
public EnsembleGEFSColorChooser(Shell parentShell) {
super(parentShell);
}
/**
* Create contents of the dialog.
*
* @param parent
*/
@Override
protected Control createDialogArea(Composite parent) {
Composite container = (Composite) super.createDialogArea(parent);
GridLayout gridLayout = (GridLayout) container.getLayout();
gridLayout.numColumns = 5;
gridLayout.makeColumnsEqualWidth = false;
Label label_modelName_GEFS = new Label(container, SWT.BORDER
| SWT.CENTER);
label_modelName_GEFS.setAlignment(SWT.CENTER);
GridData gd_label_modelName_GEFS = new GridData(SWT.CENTER, SWT.CENTER,
false, false, 1, 1);
gd_label_modelName_GEFS.heightHint = 20;
gd_label_modelName_GEFS.widthHint = 40;
label_modelName_GEFS.setLayoutData(gd_label_modelName_GEFS);
label_modelName_GEFS.setText("GEFS");
Label label_colon_GEFS = new Label(container, SWT.NONE);
label_colon_GEFS.setText(":");
final Composite label_color_GEFS = new Composite(container, SWT.BORDER);
label_color_GEFS.setForeground(ChosenGEFSColors.getInstance()
.getColor());
label_color_GEFS.setBackground(SWTResourceManager.WHITE);
GridData gd_label_color_GEFS = new GridData(SWT.LEFT, SWT.CENTER,
false, false, 3, 1);
gd_label_color_GEFS.heightHint = 24;
gd_label_color_GEFS.widthHint = 116;
gd_label_color_GEFS.minimumWidth = 116;
gd_label_color_GEFS.minimumHeight = 24;
label_color_GEFS.setLayoutData(gd_label_color_GEFS);
label_color_GEFS.setSize(116, 24);
applyGradientBG(label_color_GEFS);
label_color_GEFS.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
ColorDialog cd = new ColorDialog(getShell());
cd.setRGB(ChosenGEFSColors.getInstance().getColor().getRGB());
cd.setText("Choose GEFS lower color");
RGB result = cd.open();
if (result != null) {
Color c = SWTResourceManager.getColor(result);
RGB rgb = c.getRGB();
float[] hsb = rgb.getHSB();
hsb[1] = 1.0f;
RGB nrgb = new RGB(hsb[0], hsb[1], hsb[2]);
Color nc = SWTResourceManager.getColor(nrgb);
ChosenGEFSColors.getInstance().setColor(nc);
label_color_GEFS.setForeground(nc);
applyGradientBG(label_color_GEFS);
}
}
});
container.pack();
return container;
}
private static Image oldImage = null;
public static void applyGradientBG(Composite c) {
Rectangle rect = c.getClientArea();
Image newImage = new Image(c.getDisplay(), rect.width, rect.height);
GC gc = new GC(newImage);
gc.setBackground(SWTResourceManager.WHITE);
gc.fillRectangle(0, 0, rect.width, rect.height);
gc.setForeground(c.getForeground());
gc.setBackground(c.getBackground());
gc.fillGradientRectangle(0, 0, rect.width, rect.height, false);
c.setBackgroundImage(newImage);
gc.dispose();
if (oldImage != null) {
oldImage.dispose();
oldImage = newImage;
}
}
/**
* Create contents of the button bar.
*
* @param parent
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
true);
createButton(parent, IDialogConstants.CANCEL_ID,
IDialogConstants.CANCEL_LABEL, false);
}
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText("Choose Color Range");
}
}

View file

@ -0,0 +1,258 @@
package gov.noaa.gsd.viz.ensemble.util;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.ColorDialog;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import com.raytheon.viz.ui.dialogs.CaveJFACEDialog;
/**
* This class is a Dialog which allows users to change the colors of the SREF
* ensemble perturbation members. It is used as a convenience feature to make it
* easy to create a gradient of colors given a base color.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 8, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @version 1.0
*/
public class EnsembleSREFColorChooser extends CaveJFACEDialog {
/**
* Create the dialog.
*
* @param parentShell
*/
public EnsembleSREFColorChooser(Shell parentShell) {
super(parentShell);
}
/**
* Create contents of the dialog.
*
* @param parent
*/
@Override
protected Control createDialogArea(Composite parent) {
Composite container = (Composite) super.createDialogArea(parent);
GridLayout gridLayout = (GridLayout) container.getLayout();
gridLayout.numColumns = 5;
gridLayout.makeColumnsEqualWidth = false;
Label label_modelName_NMM = new Label(container, SWT.BORDER
| SWT.CENTER);
label_modelName_NMM.setAlignment(SWT.CENTER);
GridData gd_label_modelName_NMM = new GridData(SWT.CENTER, SWT.CENTER,
false, false, 1, 1);
gd_label_modelName_NMM.heightHint = 20;
gd_label_modelName_NMM.widthHint = 40;
label_modelName_NMM.setLayoutData(gd_label_modelName_NMM);
label_modelName_NMM.setText("NMM");
Label label_colon_NMM = new Label(container, SWT.NONE);
label_colon_NMM.setText(":");
final Composite label_color_NMM = new Composite(container, SWT.BORDER);
label_color_NMM.setForeground(ChosenSREFColors.getInstance()
.get_NMM_color());
label_color_NMM.setBackground(SWTResourceManager.WHITE);
GridData gd_label_color_NMM = new GridData(SWT.LEFT, SWT.CENTER, false,
false, 3, 1);
gd_label_color_NMM.heightHint = 24;
gd_label_color_NMM.widthHint = 116;
gd_label_color_NMM.minimumWidth = 116;
gd_label_color_NMM.minimumHeight = 24;
label_color_NMM.setLayoutData(gd_label_color_NMM);
label_color_NMM.setSize(116, 24);
applyGradientBG(label_color_NMM);
label_color_NMM.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
ColorDialog cd = new ColorDialog(getShell());
cd.setRGB(ChosenSREFColors.getInstance().get_NMM_color()
.getRGB());
cd.setText("Choose SREF-NMM color");
RGB result = cd.open();
if (result != null) {
Color c = SWTResourceManager.getColor(result);
RGB rgb = c.getRGB();
float[] hsb = rgb.getHSB();
hsb[1] = 1.0f;
RGB nrgb = new RGB(hsb[0], hsb[1], hsb[2]);
Color nc = SWTResourceManager.getColor(nrgb);
ChosenSREFColors.getInstance().set_NMM_color(nc);
label_color_NMM.setForeground(nc);
applyGradientBG(label_color_NMM);
}
}
});
Label label_modelName_NMB = new Label(container, SWT.BORDER
| SWT.CENTER);
label_modelName_NMB.setAlignment(SWT.CENTER);
GridData gd_label_modelName_NMB = new GridData(SWT.CENTER, SWT.CENTER,
false, false, 1, 1);
gd_label_modelName_NMB.heightHint = 20;
gd_label_modelName_NMB.widthHint = 40;
label_modelName_NMB.setLayoutData(gd_label_modelName_NMB);
label_modelName_NMB.setText("NMB");
Label label_colon_NMB = new Label(container, SWT.NONE);
label_colon_NMB.setText(":");
final Composite label_color_NMB = new Composite(container, SWT.BORDER);
label_color_NMB.setForeground(ChosenSREFColors.getInstance()
.get_NMB_color());
label_color_NMB.setBackground(SWTResourceManager.WHITE);
GridData gd_label_color_NMB = new GridData(SWT.LEFT, SWT.CENTER, false,
false, 3, 1);
gd_label_color_NMB.heightHint = 24;
gd_label_color_NMB.widthHint = 116;
gd_label_color_NMB.minimumWidth = 116;
gd_label_color_NMB.minimumHeight = 24;
label_color_NMB.setLayoutData(gd_label_color_NMB);
label_color_NMB.setSize(116, 24);
applyGradientBG(label_color_NMB);
label_color_NMB.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
ColorDialog cd = new ColorDialog(getShell());
cd.setRGB(ChosenSREFColors.getInstance().get_NMB_color()
.getRGB());
cd.setText("Choose SREF-NMB color");
RGB result = cd.open();
if (result != null) {
Color c = SWTResourceManager.getColor(result);
RGB rgb = c.getRGB();
float[] hsb = rgb.getHSB();
hsb[1] = 1.0f;
RGB nrgb = new RGB(hsb[0], hsb[1], hsb[2]);
Color nc = SWTResourceManager.getColor(nrgb);
ChosenSREFColors.getInstance().set_NMB_color(nc);
label_color_NMB.setForeground(nc);
applyGradientBG(label_color_NMB);
}
}
});
Label label_modelName_EM = new Label(container, SWT.BORDER | SWT.CENTER);
label_modelName_EM.setAlignment(SWT.CENTER);
GridData gd_label_modelName_EM = new GridData(SWT.CENTER, SWT.CENTER,
false, false, 1, 1);
gd_label_modelName_EM.heightHint = 20;
gd_label_modelName_EM.widthHint = 40;
label_modelName_EM.setLayoutData(gd_label_modelName_EM);
label_modelName_EM.setText("EM");
Label label_colon_EM = new Label(container, SWT.NONE);
label_colon_EM.setText(":");
final Composite label_color_EM = new Composite(container, SWT.BORDER);
label_color_EM.setForeground(ChosenSREFColors.getInstance()
.get_EM_color());
label_color_EM.setBackground(SWTResourceManager.WHITE);
GridData gd_label_color_EM = new GridData(SWT.LEFT, SWT.CENTER, false,
false, 3, 1);
gd_label_color_EM.heightHint = 24;
gd_label_color_EM.widthHint = 116;
gd_label_color_EM.minimumWidth = 116;
gd_label_color_EM.minimumHeight = 24;
label_color_EM.setLayoutData(gd_label_color_EM);
label_color_EM.setSize(116, 24);
applyGradientBG(label_color_EM);
label_color_EM.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
ColorDialog cd = new ColorDialog(getShell());
cd.setRGB(ChosenSREFColors.getInstance().get_EM_color()
.getRGB());
cd.setText("Choose SREF-EM color");
RGB result = cd.open();
if (result != null) {
Color c = SWTResourceManager.getColor(result);
RGB rgb = c.getRGB();
float[] hsb = rgb.getHSB();
hsb[1] = 1.0f;
RGB nrgb = new RGB(hsb[0], hsb[1], hsb[2]);
Color nc = SWTResourceManager.getColor(nrgb);
ChosenSREFColors.getInstance().set_EM_color(nc);
label_color_EM.setForeground(nc);
applyGradientBG(label_color_EM);
}
}
});
container.pack();
return container;
}
private static Image oldImage = null;
public static void applyGradientBG(Composite c) {
Rectangle rect = c.getClientArea();
Image newImage = new Image(c.getDisplay(), rect.width, rect.height);
GC gc = new GC(newImage);
gc.setBackground(SWTResourceManager.WHITE);
gc.fillRectangle(0, 0, rect.width, rect.height);
gc.setForeground(c.getForeground());
gc.setBackground(c.getBackground());
gc.fillGradientRectangle(0, 0, rect.width, rect.height, false);
c.setBackgroundImage(newImage);
gc.dispose();
if (oldImage != null) {
oldImage.dispose();
oldImage = newImage;
}
}
/**
* Create contents of the button bar.
*
* @param parent
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
true);
createButton(parent, IDialogConstants.CANCEL_ID,
IDialogConstants.CANCEL_LABEL, false);
}
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText("Choose Color Range");
}
}

View file

@ -0,0 +1,497 @@
/*******************************************************************************
* Copyright (c) 2011 Google, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Google, Inc. - initial API and implementation
*******************************************************************************/
package gov.noaa.gsd.viz.ensemble.util;
/**
*
* This class is automatically generated by Google's Windows Builder Pro.
* It is made of of convenience utilites to make it easier for the developer
* to create, manipulate, and dispose of images.
*
<pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov-23-2011 5056 epolster Initial Creation.
*
* </pre>
*
* @author epolster
* @version 1
*
*
*/
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.CompositeImageDescriptor;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.osgi.framework.Bundle;
/**
* Utility class for managing OS resources associated with SWT/JFace controls
* such as colors, fonts, images, etc.
*
* !!! IMPORTANT !!! Application code must explicitly invoke the
* <code>dispose()</code> method to release the operating system resources
* managed by cached objects when those objects and OS resources are no longer
* needed (e.g. on application shutdown)
*
* This class may be freely distributed as part of any application or plugin.
* <p>
*
* @author scheglov_ke
* @author Dan Rubel
*/
public class ImageResourceManager extends SWTResourceManager {
// //////////////////////////////////////////////////////////////////////////
//
// Image
//
// //////////////////////////////////////////////////////////////////////////
private static Map<ImageDescriptor, Image> m_descriptorImageMap = new HashMap<ImageDescriptor, Image>();
/**
* Returns an {@link ImageDescriptor} stored in the file at the specified
* path relative to the specified class.
*
* @param clazz
* the {@link Class} relative to which to find the image
* descriptor.
* @param path
* the path to the image file.
* @return the {@link ImageDescriptor} stored in the file at the specified
* path.
*/
public static ImageDescriptor getImageDescriptor(Class<?> clazz, String path) {
return ImageDescriptor.createFromFile(clazz, path);
}
/**
* Returns an {@link ImageDescriptor} stored in the file at the specified
* path.
*
* @param path
* the path to the image file.
* @return the {@link ImageDescriptor} stored in the file at the specified
* path.
*/
public static ImageDescriptor getImageDescriptor(String path) {
try {
return ImageDescriptor
.createFromURL(new File(path).toURI().toURL());
} catch (MalformedURLException e) {
return null;
}
}
/**
* Returns an {@link Image} based on the specified {@link ImageDescriptor}.
*
* @param descriptor
* the {@link ImageDescriptor} for the {@link Image}.
* @return the {@link Image} based on the specified {@link ImageDescriptor}.
*/
public static Image getImage(ImageDescriptor descriptor) {
if (descriptor == null) {
return null;
}
Image image = m_descriptorImageMap.get(descriptor);
if (image == null) {
image = descriptor.createImage();
m_descriptorImageMap.put(descriptor, image);
}
return image;
}
/**
* Maps images to decorated images.
*/
@SuppressWarnings("unchecked")
private static Map<Image, Map<Image, Image>>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
/**
* Returns an {@link Image} composed of a base image decorated by another
* image.
*
* @param baseImage
* the base {@link Image} that should be decorated.
* @param decorator
* the {@link Image} to decorate the base image.
* @return {@link Image} The resulting decorated image.
*/
public static Image decorateImage(Image baseImage, Image decorator) {
return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
}
/**
* Returns an {@link Image} composed of a base image decorated by another
* image.
*
* @param baseImage
* the base {@link Image} that should be decorated.
* @param decorator
* the {@link Image} to decorate the base image.
* @param corner
* the corner to place decorator image.
* @return the resulting decorated {@link Image}.
*/
public static Image decorateImage(final Image baseImage,
final Image decorator, final int corner) {
if (corner <= 0 || corner >= LAST_CORNER_KEY) {
throw new IllegalArgumentException("Wrong decorate corner");
}
Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[corner];
if (cornerDecoratedImageMap == null) {
cornerDecoratedImageMap = new HashMap<Image, Map<Image, Image>>();
m_decoratedImageMap[corner] = cornerDecoratedImageMap;
}
Map<Image, Image> decoratedMap = cornerDecoratedImageMap.get(baseImage);
if (decoratedMap == null) {
decoratedMap = new HashMap<Image, Image>();
cornerDecoratedImageMap.put(baseImage, decoratedMap);
}
//
Image result = decoratedMap.get(decorator);
if (result == null) {
final Rectangle bib = baseImage.getBounds();
final Rectangle dib = decorator.getBounds();
final Point baseImageSize = new Point(bib.width, bib.height);
CompositeImageDescriptor compositImageDesc = new CompositeImageDescriptor() {
@Override
protected void drawCompositeImage(int width, int height) {
drawImage(baseImage.getImageData(), 0, 0);
if (corner == TOP_LEFT) {
drawImage(decorator.getImageData(), 0, 0);
} else if (corner == TOP_RIGHT) {
drawImage(decorator.getImageData(), bib.width
- dib.width, 0);
} else if (corner == BOTTOM_LEFT) {
drawImage(decorator.getImageData(), 0, bib.height
- dib.height);
} else if (corner == BOTTOM_RIGHT) {
drawImage(decorator.getImageData(), bib.width
- dib.width, bib.height - dib.height);
}
}
@Override
protected Point getSize() {
return baseImageSize;
}
};
//
result = compositImageDesc.createImage();
decoratedMap.put(decorator, result);
}
return result;
}
/**
* Dispose all of the cached images.
*/
public static void disposeImages() {
SWTResourceManager.disposeImages();
// dispose ImageDescriptor images
{
for (Iterator<Image> I = m_descriptorImageMap.values().iterator(); I
.hasNext();) {
I.next().dispose();
}
m_descriptorImageMap.clear();
}
// dispose decorated images
for (int i = 0; i < m_decoratedImageMap.length; i++) {
Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[i];
if (cornerDecoratedImageMap != null) {
for (Map<Image, Image> decoratedMap : cornerDecoratedImageMap
.values()) {
for (Image image : decoratedMap.values()) {
image.dispose();
}
decoratedMap.clear();
}
cornerDecoratedImageMap.clear();
}
}
// dispose plugin images
{
for (Iterator<Image> I = m_URLImageMap.values().iterator(); I
.hasNext();) {
I.next().dispose();
}
m_URLImageMap.clear();
}
}
// //////////////////////////////////////////////////////////////////////////
//
// Plugin images support
//
// //////////////////////////////////////////////////////////////////////////
/**
* Maps URL to images.
*/
private static Map<String, Image> m_URLImageMap = new HashMap<String, Image>();
/**
* Provider for plugin resources, used by WindowBuilder at design time.
*/
public interface PluginResourceProvider {
URL getEntry(String symbolicName, String path);
}
/**
* Instance of {@link PluginResourceProvider}, used by WindowBuilder at
* design time.
*/
private static PluginResourceProvider m_designTimePluginResourceProvider = null;
/**
* Returns an {@link Image} based on a plugin and file path.
*
* @param plugin
* the plugin {@link Object} containing the image
* @param name
* the path to the image within the plugin
* @return the {@link Image} stored in the file at the specified path
*
* @deprecated Use {@link #getPluginImage(String, String)} instead.
*/
@Deprecated
public static Image getPluginImage(Object plugin, String name) {
try {
URL url = getPluginImageURL(plugin, name);
if (url != null) {
return getPluginImageFromUrl(url);
}
} catch (Throwable e) {
// Ignore any exceptions
}
return null;
}
/**
* Returns an {@link Image} based on a {@link Bundle} and resource entry
* path.
*
* @param symbolicName
* the symbolic name of the {@link Bundle}.
* @param path
* the path of the resource entry.
* @return the {@link Image} stored in the file at the specified path.
*/
public static Image getPluginImage(String symbolicName, String path) {
try {
URL url = getPluginImageURL(symbolicName, path);
if (url != null) {
return getPluginImageFromUrl(url);
}
} catch (Throwable e) {
// Ignore any exceptions
}
return null;
}
/**
* Returns an {@link Image} based on given {@link URL}.
*/
private static Image getPluginImageFromUrl(URL url) {
try {
try {
String key = url.toExternalForm();
Image image = m_URLImageMap.get(key);
if (image == null) {
InputStream stream = url.openStream();
try {
image = getImage(stream);
m_URLImageMap.put(key, image);
} finally {
stream.close();
}
}
return image;
} catch (Throwable e) {
// Ignore any exceptions
}
} catch (Throwable e) {
// Ignore any exceptions
}
return null;
}
/**
* Returns an {@link ImageDescriptor} based on a plugin and file path.
*
* @param plugin
* the plugin {@link Object} containing the image.
* @param name
* the path to th eimage within the plugin.
* @return the {@link ImageDescriptor} stored in the file at the specified
* path.
*
* @deprecated Use {@link #getPluginImageDescriptor(String, String)}
* instead.
*/
@Deprecated
public static ImageDescriptor getPluginImageDescriptor(Object plugin,
String name) {
try {
try {
URL url = getPluginImageURL(plugin, name);
return ImageDescriptor.createFromURL(url);
} catch (Throwable e) {
// Ignore any exceptions
}
} catch (Throwable e) {
// Ignore any exceptions
}
return null;
}
/**
* Returns an {@link ImageDescriptor} based on a {@link Bundle} and resource
* entry path.
*
* @param symbolicName
* the symbolic name of the {@link Bundle}.
* @param path
* the path of the resource entry.
* @return the {@link ImageDescriptor} based on a {@link Bundle} and
* resource entry path.
*/
public static ImageDescriptor getPluginImageDescriptor(String symbolicName,
String path) {
try {
URL url = getPluginImageURL(symbolicName, path);
if (url != null) {
return ImageDescriptor.createFromURL(url);
}
} catch (Throwable e) {
// Ignore any exceptions
}
return null;
}
/**
* Returns an {@link URL} based on a {@link Bundle} and resource entry path.
*/
private static URL getPluginImageURL(String symbolicName, String path) {
// try runtime plugins
{
Bundle bundle = Platform.getBundle(symbolicName);
if (bundle != null) {
return bundle.getEntry(path);
}
}
// try design time provider
if (m_designTimePluginResourceProvider != null) {
return m_designTimePluginResourceProvider.getEntry(symbolicName,
path);
}
// no such resource
return null;
}
/**
* Returns an {@link URL} based on a plugin and file path.
*
* @param plugin
* the plugin {@link Object} containing the file path.
* @param name
* the file path.
* @return the {@link URL} representing the file at the specified path.
* @throws Exception
*/
private static URL getPluginImageURL(Object plugin, String name)
throws Exception {
// try to work with 'plugin' as with OSGI BundleContext
try {
Class<?> BundleClass = Class.forName("org.osgi.framework.Bundle"); //$NON-NLS-1$
Class<?> BundleContextClass = Class
.forName("org.osgi.framework.BundleContext"); //$NON-NLS-1$
if (BundleContextClass.isAssignableFrom(plugin.getClass())) {
Method getBundleMethod = BundleContextClass.getMethod(
"getBundle", new Class[0]); //$NON-NLS-1$
Object bundle = getBundleMethod.invoke(plugin, new Object[0]);
//
Class<?> PathClass = Class
.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$
Constructor<?> pathConstructor = PathClass
.getConstructor(new Class[] { String.class });
Object path = pathConstructor
.newInstance(new Object[] { name });
//
Class<?> IPathClass = Class
.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$
Class<?> PlatformClass = Class
.forName("org.eclipse.core.runtime.Platform"); //$NON-NLS-1$
Method findMethod = PlatformClass.getMethod(
"find", new Class[] { BundleClass, IPathClass }); //$NON-NLS-1$
return (URL) findMethod.invoke(null, new Object[] { bundle,
path });
}
} catch (Throwable e) {
// Ignore any exceptions
}
// else work with 'plugin' as with usual Eclipse plugin
{
Class<?> PluginClass = Class
.forName("org.eclipse.core.runtime.Plugin"); //$NON-NLS-1$
if (PluginClass.isAssignableFrom(plugin.getClass())) {
//
Class<?> PathClass = Class
.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$
Constructor<?> pathConstructor = PathClass
.getConstructor(new Class[] { String.class });
Object path = pathConstructor
.newInstance(new Object[] { name });
//
Class<?> IPathClass = Class
.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$
Method findMethod = PluginClass.getMethod(
"find", new Class[] { IPathClass }); //$NON-NLS-1$
return (URL) findMethod.invoke(plugin, new Object[] { path });
}
}
return null;
}
// //////////////////////////////////////////////////////////////////////////
//
// General
//
// //////////////////////////////////////////////////////////////////////////
/**
* Dispose of cached objects and their underlying OS resources. This should
* only be called when the cached objects are no longer needed (e.g. on
* application shutdown).
*/
public static void dispose() {
disposeColors();
disposeFonts();
disposeImages();
}
}

View file

@ -0,0 +1,657 @@
/*******************************************************************************
* Copyright (c) 2011 Google, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Google, Inc. - initial API and implementation
*******************************************************************************/
package gov.noaa.gsd.viz.ensemble.util;
/**
*
* This class is automatically generated by Google's Windows Builder Pro.
* It is made of of convenience utilites to make it easier for the developer
* to create, manipulate, and dispose of colors and fonts.
*
<pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Dec-9-2011 5056 epolster Initial Creation.
*
* </pre>
*
* @author epolster
* @version 1
*
*
*/
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
/**
* Utility class for managing OS resources associated with SWT controls such as
* colors, fonts, images, etc.
* <p>
* !!! IMPORTANT !!! Application code must explicitly invoke the
* <code>dispose()</code> method to release the operating system resources
* managed by cached objects when those objects and OS resources are no longer
* needed (e.g. on application shutdown)
* <p>
* This class may be freely distributed as part of any application or plugin.
* <p>
*
* @author scheglov_ke
* @author Dan Rubel
*/
public class SWTResourceManager {
private static Map<RGB, Color> m_colorMap = new HashMap<RGB, Color>();
public static final Color NEAR_WHITE = SWTResourceManager.getColor(new RGB(
240, 240, 240));
public static final Color LIGHTER_GRAY = SWTResourceManager
.getColor(new RGB(216, 216, 216));
public static final Color LIGHT_GRAY = SWTResourceManager.getColor(new RGB(
210, 210, 210));
public static final Color MEDIUM_GRAY = SWTResourceManager
.getColor(new RGB(160, 160, 160));
public static final Color DARKER_GRAY = SWTResourceManager
.getColor(new RGB(130, 130, 130));
public static final Color BRIGHT_YELLOW = SWTResourceManager
.getColor(new RGB(255, 255, 0));
public static final Color LIGHTER_YELLOW = SWTResourceManager
.getColor(new RGB(255, 255, 102));
public static final Color PALE_DULL_YELLOW = SWTResourceManager
.getColor(new RGB(255, 255, 150));
public static final Color LIGHT_YELLOW = SWTResourceManager
.getColor(new RGB(255, 255, 220));
public static final Color PALE_DULL_ORANGE = SWTResourceManager
.getColor(new RGB(255, 204, 153));
public static final Color PALE_LIGHT_AZURE = SWTResourceManager
.getColor(new RGB(215, 239, 255));
public static final Color PALE_DULL_AZURE = SWTResourceManager
.getColor(new RGB(168, 215, 255));
public static final Color PALE_WEAK_MAGENTA = SWTResourceManager
.getColor(new RGB(255, 204, 255));
public static final Color PALE_WEAK_GREEN = SWTResourceManager
.getColor(new RGB(204, 255, 204));
public static final Color PALE_WEAK_BLUE = SWTResourceManager
.getColor(new RGB(203, 229, 255));
public static final Color PASTEL_LIGHT_BLUE = SWTResourceManager
.getColor(new RGB(134, 181, 245));
public static final Color PALE_DULL_VIOLET = SWTResourceManager
.getColor(new RGB(204, 204, 255));
public static final Color WHITE = SWTResourceManager.getColor(new RGB(255,
255, 255));
public static final Color BLACK = SWTResourceManager.getColor(new RGB(0, 0,
0));
// Crayola Colors ...
public static final Color BLUE = SWTResourceManager.getColor(new RGB(31,
117, 254));
public static final Color SLATE_BLUE = SWTResourceManager.getColor(new RGB(
0, 153, 255));
public static final Color CERULEAN = SWTResourceManager.getColor(new RGB(
29, 172, 214));
public static final Color GREEN = SWTResourceManager.getColor(new RGB(0,
204, 0));
public static final Color CARIBBEAN_GREEN = SWTResourceManager
.getColor(new RGB(0, 204, 153));
public static final Color SLATE_GREEN = SWTResourceManager
.getColor(new RGB(51, 153, 102));
public static final Color SEA_GREEN = SWTResourceManager.getColor(new RGB(
51, 204, 204));
public static final Color LIGHT_OLIVE = SWTResourceManager
.getColor(new RGB(153, 204, 0));
public static final Color YELLOW = SWTResourceManager.getColor(new RGB(255,
255, 102));
public static final Color ATOMIC_TANGERINE = SWTResourceManager
.getColor(new RGB(255, 164, 116));
public static final Color BURNT_ORANGE = SWTResourceManager
.getColor(new RGB(255, 127, 73));
public static final Color NEON_CARROT = SWTResourceManager
.getColor(new RGB(255, 163, 67));
public static final Color PEACH = SWTResourceManager.getColor(new RGB(255,
204, 153));
public static final Color RED = SWTResourceManager.getColor(new RGB(255,
51, 0));
public static final Color SLATE_RED = SWTResourceManager.getColor(new RGB(
255, 153, 153));
public static final Color WILD_WATERMELON = SWTResourceManager
.getColor(new RGB(252, 108, 133));
public static final Color PINK_FLAMINGO = SWTResourceManager
.getColor(new RGB(252, 116, 253));
public static final Color HOT_MAGENTA = SWTResourceManager
.getColor(new RGB(252, 29, 206));
public static final Color PURPLE = SWTResourceManager.getColor(new RGB(153,
51, 255));
public static final Color SEPIA = SWTResourceManager.getColor(new RGB(165,
105, 79));
public static final Color NEON_PURPLE = SWTResourceManager
.getColor(new RGB(217, 0, 255));
public static final Color CHERRY_RED = SWTResourceManager.getColor(new RGB(
255, 0, 31));
public static final Color SPRING_GREEN = SWTResourceManager
.getColor(new RGB(0, 204, 68));
public static final Color DEEP_BLUE = SWTResourceManager.getColor(new RGB(
3, 2, 255));
// //////////////////////////////////////////////////////////////////////////
//
// Color
//
// //////////////////////////////////////////////////////////////////////////
/**
* Returns the system {@link Color} matching the specific ID. The color is a
* system color so should never be disposed of by the caller.
*
* @param systemColorID
* the ID value for the color
* @return the system {@link Color} matching the specific ID
*/
public static Color getSystemColor(int systemColorID) {
Display display = Display.getCurrent();
return display.getSystemColor(systemColorID);
}
/**
* Returns a {@link Color} given its red, green and blue component values.
*
* @param r
* the red component of the color
* @param g
* the green component of the color
* @param b
* the blue component of the color
* @return the {@link Color} matching the given red, green and blue
* component values
*/
public static Color getColor(int r, int g, int b) {
return getColor(new RGB(r, g, b));
}
/**
* Returns a {@link Color} given its RGB value.
*
* @param rgb
* the {@link RGB} value of the color
* @return the {@link Color} matching the RGB value
*/
public static Color getColor(RGB rgb) {
Color color = m_colorMap.get(rgb);
if (color == null) {
Display display = Display.getCurrent();
color = new Color(display, rgb);
m_colorMap.put(rgb, color);
}
return color;
}
/**
* Dispose of all the cached {@link Color}'s.
*/
public static void disposeColors() {
for (Color color : m_colorMap.values()) {
color.dispose();
}
m_colorMap.clear();
}
// //////////////////////////////////////////////////////////////////////////
//
// Image
//
// //////////////////////////////////////////////////////////////////////////
/**
* Maps image paths to images.
*/
private static Map<String, Image> m_imageMap = new HashMap<String, Image>();
/**
* Returns an {@link Image} encoded by the specified {@link InputStream}.
*
* @param stream
* the {@link InputStream} encoding the image data
* @return the {@link Image} encoded by the specified input stream
*/
protected static Image getImage(InputStream stream) throws IOException {
try {
Display display = Display.getCurrent();
ImageData data = new ImageData(stream);
if (data.transparentPixel > 0) {
return new Image(display, data, data.getTransparencyMask());
}
return new Image(display, data);
} finally {
stream.close();
}
}
/**
* Returns an {@link Image} stored in the file at the specified path.
*
* @param path
* the path to the image file
* @return the {@link Image} stored in the file at the specified path
*/
public static Image getImage(String path) {
Image image = m_imageMap.get(path);
if (image == null) {
try {
image = getImage(new FileInputStream(path));
m_imageMap.put(path, image);
} catch (Exception e) {
image = getMissingImage();
m_imageMap.put(path, image);
}
}
return image;
}
/**
* Returns an {@link Image} stored in the file at the specified path
* relative to the specified class.
*
* @param clazz
* the {@link Class} relative to which to find the image
* @param path
* the path to the image file, if starts with <code>'/'</code>
* @return the {@link Image} stored in the file at the specified path
*/
public static Image getImage(Class<?> clazz, String path) {
String key = clazz.getName() + '|' + path;
Image image = m_imageMap.get(key);
if (image == null) {
try {
image = getImage(clazz.getResourceAsStream(path));
m_imageMap.put(key, image);
} catch (Exception e) {
image = getMissingImage();
m_imageMap.put(key, image);
}
}
return image;
}
private static final int MISSING_IMAGE_SIZE = 10;
/**
* @return the small {@link Image} that can be used as placeholder for
* missing image.
*/
private static Image getMissingImage() {
Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE,
MISSING_IMAGE_SIZE);
//
GC gc = new GC(image);
gc.setBackground(getSystemColor(SWT.COLOR_RED));
gc.fillRectangle(0, 0, MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
gc.dispose();
//
return image;
}
/**
* Style constant for placing decorator image in top left corner of base
* image.
*/
public static final int TOP_LEFT = 1;
/**
* Style constant for placing decorator image in top right corner of base
* image.
*/
public static final int TOP_RIGHT = 2;
/**
* Style constant for placing decorator image in bottom left corner of base
* image.
*/
public static final int BOTTOM_LEFT = 3;
/**
* Style constant for placing decorator image in bottom right corner of base
* image.
*/
public static final int BOTTOM_RIGHT = 4;
/**
* Internal value.
*/
protected static final int LAST_CORNER_KEY = 5;
/**
* Maps images to decorated images.
*/
@SuppressWarnings("unchecked")
private static Map<Image, Map<Image, Image>>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
/**
* Returns an {@link Image} composed of a base image decorated by another
* image.
*
* @param baseImage
* the base {@link Image} that should be decorated
* @param decorator
* the {@link Image} to decorate the base image
* @return {@link Image} The resulting decorated image
*/
public static Image decorateImage(Image baseImage, Image decorator) {
return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
}
/**
* Returns an {@link Image} composed of a base image decorated by another
* image.
*
* @param baseImage
* the base {@link Image} that should be decorated
* @param decorator
* the {@link Image} to decorate the base image
* @param corner
* the corner to place decorator image
* @return the resulting decorated {@link Image}
*/
public static Image decorateImage(final Image baseImage,
final Image decorator, final int corner) {
if (corner <= 0 || corner >= LAST_CORNER_KEY) {
throw new IllegalArgumentException("Wrong decorate corner");
}
Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[corner];
if (cornerDecoratedImageMap == null) {
cornerDecoratedImageMap = new HashMap<Image, Map<Image, Image>>();
m_decoratedImageMap[corner] = cornerDecoratedImageMap;
}
Map<Image, Image> decoratedMap = cornerDecoratedImageMap.get(baseImage);
if (decoratedMap == null) {
decoratedMap = new HashMap<Image, Image>();
cornerDecoratedImageMap.put(baseImage, decoratedMap);
}
//
Image result = decoratedMap.get(decorator);
if (result == null) {
Rectangle bib = baseImage.getBounds();
Rectangle dib = decorator.getBounds();
//
result = new Image(Display.getCurrent(), bib.width, bib.height);
//
GC gc = new GC(result);
gc.drawImage(baseImage, 0, 0);
if (corner == TOP_LEFT) {
gc.drawImage(decorator, 0, 0);
} else if (corner == TOP_RIGHT) {
gc.drawImage(decorator, bib.width - dib.width, 0);
} else if (corner == BOTTOM_LEFT) {
gc.drawImage(decorator, 0, bib.height - dib.height);
} else if (corner == BOTTOM_RIGHT) {
gc.drawImage(decorator, bib.width - dib.width, bib.height
- dib.height);
}
gc.dispose();
//
decoratedMap.put(decorator, result);
}
return result;
}
/**
* Dispose all of the cached {@link Image}'s.
*/
public static void disposeImages() {
// dispose loaded images
{
for (Image image : m_imageMap.values()) {
image.dispose();
}
m_imageMap.clear();
}
// dispose decorated images
for (int i = 0; i < m_decoratedImageMap.length; i++) {
Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[i];
if (cornerDecoratedImageMap != null) {
for (Map<Image, Image> decoratedMap : cornerDecoratedImageMap
.values()) {
for (Image image : decoratedMap.values()) {
image.dispose();
}
decoratedMap.clear();
}
cornerDecoratedImageMap.clear();
}
}
}
// //////////////////////////////////////////////////////////////////////////
//
// Font
//
// //////////////////////////////////////////////////////////////////////////
/**
* Maps font names to fonts.
*/
private static Map<String, Font> m_fontMap = new HashMap<String, Font>();
/**
* Maps fonts to their bold versions.
*/
private static Map<Font, Font> m_fontToBoldFontMap = new HashMap<Font, Font>();
/**
* Returns a {@link Font} based on its name, height and style.
*
* @param name
* the name of the font
* @param height
* the height of the font
* @param style
* the style of the font
* @return {@link Font} The font matching the name, height and style
*/
public static Font getFont(String name, int height, int style) {
return getFont(name, height, style, false, false);
}
/**
* Returns a {@link Font} based on its name, height and style.
* Windows-specific strikeout and underline flags are also supported.
*
* @param name
* the name of the font
* @param size
* the size of the font
* @param style
* the style of the font
* @param strikeout
* the strikeout flag (warning: Windows only)
* @param underline
* the underline flag (warning: Windows only)
* @return {@link Font} The font matching the name, height, style, strikeout
* and underline
*/
public static Font getFont(String name, int size, int style,
boolean strikeout, boolean underline) {
String fontName = name + '|' + size + '|' + style + '|' + strikeout
+ '|' + underline;
Font font = m_fontMap.get(fontName);
if (font == null) {
FontData fontData = new FontData(name, size, style);
if (strikeout || underline) {
try {
Class<?> logFontClass = Class
.forName("org.eclipse.swt.internal.win32.LOGFONT"); //$NON-NLS-1$
Object logFont = FontData.class
.getField("data").get(fontData); //$NON-NLS-1$
if (logFont != null && logFontClass != null) {
if (strikeout) {
logFontClass
.getField("lfStrikeOut").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
}
if (underline) {
logFontClass
.getField("lfUnderline").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
}
}
} catch (Throwable e) {
System.err
.println("Unable to set underline or strikeout" + " (probably on a non-Windows platform). " + e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
font = new Font(Display.getCurrent(), fontData);
m_fontMap.put(fontName, font);
}
return font;
}
/**
* Returns a bold version of the given {@link Font}.
*
* @param baseFont
* the {@link Font} for which a bold version is desired
* @return the bold version of the given {@link Font}
*/
public static Font getBoldFont(Font baseFont) {
Font font = m_fontToBoldFontMap.get(baseFont);
if (font == null) {
FontData fontDatas[] = baseFont.getFontData();
FontData data = fontDatas[0];
font = new Font(Display.getCurrent(), data.getName(),
data.getHeight(), SWT.BOLD);
m_fontToBoldFontMap.put(baseFont, font);
}
return font;
}
/**
* Dispose all of the cached {@link Font}'s.
*/
public static void disposeFonts() {
// clear fonts
for (Font font : m_fontMap.values()) {
font.dispose();
}
m_fontMap.clear();
// clear bold fonts
for (Font font : m_fontToBoldFontMap.values()) {
font.dispose();
}
m_fontToBoldFontMap.clear();
}
// //////////////////////////////////////////////////////////////////////////
//
// Cursor
//
// //////////////////////////////////////////////////////////////////////////
/**
* Maps IDs to cursors.
*/
private static Map<Integer, Cursor> m_idToCursorMap = new HashMap<Integer, Cursor>();
/**
* Returns the system cursor matching the specific ID.
*
* @param id
* int The ID value for the cursor
* @return Cursor The system cursor matching the specific ID
*/
public static Cursor getCursor(int id) {
Integer key = Integer.valueOf(id);
Cursor cursor = m_idToCursorMap.get(key);
if (cursor == null) {
cursor = new Cursor(Display.getDefault(), id);
m_idToCursorMap.put(key, cursor);
}
return cursor;
}
/**
* Dispose all of the cached cursors.
*/
public static void disposeCursors() {
for (Cursor cursor : m_idToCursorMap.values()) {
cursor.dispose();
}
m_idToCursorMap.clear();
}
// //////////////////////////////////////////////////////////////////////////
//
// General
//
// //////////////////////////////////////////////////////////////////////////
/**
* Dispose of cached objects and their underlying OS resources. This should
* only be called when the cached objects are no longer needed (e.g. on
* application shutdown).
*/
public static void dispose() {
disposeColors();
disposeImages();
disposeFonts();
disposeCursors();
}
}

View file

@ -0,0 +1,148 @@
package gov.noaa.gsd.viz.ensemble.util;
/**
<pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov-20-2011 epolster Initial Creation.
*
* </pre>
*
* @author epolster
* @version 1
*
*
*/
import java.util.Random;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
/**
* Generic Utilities class to contain a hodge-podge of utility capabilties.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 8, 2014 5056 polster Initial creation
*
* </pre>
*
* @author polster
* @version 1.0
*/
public class Utilities {
private Utilities() {
super();
}
public static String padRight(String s, int n) {
return String.format("%1$-" + n + "s", s);
}
public static String padLeft(String s, int n) {
return String.format("%1$" + n + "s", s);
}
public static String removeExtraSpaces(String source) {
String result = source.replaceAll("\\s+", " ");
return result;
}
public static String trimQuotes(String pqs) {
if (pqs.startsWith("\"")) {
pqs = pqs.substring(1);
}
if (pqs.endsWith("\"")) {
pqs = pqs.substring(0, pqs.length() - 1);
}
return pqs;
}
public static RGB getRandomColor() {
Random rand = new Random();
final int lowerFilter = 80;
final int upperFilter = 200;
final int skewToBrightness = 256 - upperFilter;
int r = rand.nextInt(upperFilter);
if (r < lowerFilter)
r = lowerFilter;
int g = rand.nextInt(upperFilter);
if (g < lowerFilter)
g = lowerFilter;
int b = rand.nextInt(upperFilter);
if (b < lowerFilter)
b = lowerFilter;
r += skewToBrightness;
g += skewToBrightness;
b += skewToBrightness;
return new RGB(r, g, b);
}
/**
* Create a gradient between a color and White.
*
* @param color1
* The color to gradiate.
*
* @param ratio
* Blend ratio. 0.5 will give even blend, 1.0 will return color1,
* 0.0 will return color2 and so on.
* @return Blended color.
*/
public static Color gradient(Color color1, double ratio) {
float r = (float) ratio;
float ir = (float) 1.0 - r;
RGB rgb1 = color1.getRGB();
RGB rgb2 = SWTResourceManager.WHITE.getRGB();
int r1 = rgb1.red;
int b1 = rgb1.blue;
int g1 = rgb1.green;
int red = 255;
int blue = 255;
int green = 255;
if ((b1 > r1) && (b1 > g1)) {
red = (int) (rgb1.red * ir + rgb2.red * r);
green = (int) (rgb1.green * ir + rgb2.green * r);
blue = b1;
} else if ((r1 > b1) && (r1 > g1)) {
red = r1;
blue = (int) (rgb1.blue * ir + rgb2.blue * r);
green = (int) (rgb1.green * ir + rgb2.green * r);
} else if ((g1 > b1) && (g1 > r1)) {
red = (int) (rgb1.red * ir + rgb2.red * r);
blue = (int) (rgb1.blue * ir + rgb2.blue * r);
green = g1;
} else {
red = (int) (rgb1.red * ir + rgb2.red * r);
blue = (int) (rgb1.blue * ir + rgb2.blue * r);
green = (int) (rgb1.green * ir + rgb2.green * r);
}
RGB blend = new RGB(red, blue, green);
Color color = SWTResourceManager.getColor(blend);
return color;
}
}