diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncscat/src/gov/noaa/nws/ncep/common/dataplugin/ncscat/NcscatMode.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncscat/src/gov/noaa/nws/ncep/common/dataplugin/ncscat/NcscatMode.java new file mode 100644 index 0000000000..730bea429c --- /dev/null +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncscat/src/gov/noaa/nws/ncep/common/dataplugin/ncscat/NcscatMode.java @@ -0,0 +1,220 @@ +/** + * + */ +package gov.noaa.nws.ncep.common.dataplugin.ncscat; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * NcscatMode - Enum class to centralize and encapsulate all the things that + * vary among different satellite and data feed types. + * + * //TODO: Consider moving this information entirely to the bundle and/or + * preferences (.xml/.prm) files, so the Java code can be completely agnostic + * about satellite data types; would allow extended 'configurability', at the + * expense of slightly longer bundle/preference files... + * + * This code has been developed by the SIB for use in the AWIPS2 system. + * + *
+ * SOFTWARE HISTORY + * Date Ticket# Engineer Description + * ------------ ---------- ----------- -------------------------- + * 02 Jun 2010 235B B. Hebbard Initial creation. + * 03 Feb 2011 235E B. Hebbard Add support for ambiguity variants. + * 16 Aug 2012 B. Hebbard Add OSCAT / OSCAT_HI + * 11 Apr 2014 1128 B. Hebbard Add longitudeCoding field; change wind sense from boolean to enum. + * 21 Oct 2014 R4865 B. Hebbard (TTR 984) Add file-header length and reportType fields to enable refactor to make decoding more robust (and thereby prevent date and other corruption) + * + *+ * + * @author bhebbard + * @version 1.0 + */ + +public enum NcscatMode { + + // The only ordering constraint here is that if A.pointsPerRow divides + // B.pointsPerRow and A and B have the same byteOrder, then A should come + // before B. (Currently, no such cases exist.) This is so that we can check + // an unknown data file for 'consistency' with these types, in order, and + // choose the first one that matches. (In the hypothetical case above, A and + // B could both pass consistency checking, but we'd want to choose A.) + + // @formatter:off ppr hdr + QUIKSCAT ( 76, 0, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "quikscat" ), + QUIKSCAT_HI ( 152, 0, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "quikscat-hi"), + ASCAT ( 42, 0, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "ascat"), + ASCAT_HI ( 82, 0, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "ascat-hi"), + EXASCT ( 42, 0, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN, "Exasct"), + EXASCT_HI ( 82, 0, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN, "Exasct-hi"), + OSCAT ( 36, 0, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "oscat"), + OSCAT_HI ( 76, 0, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN, "oscat-hi"), + WSCAT ( 79, 56, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.SIGNED, ByteOrder.LITTLE_ENDIAN, "wscat"), + UNKNOWN ( 76, 0, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN, "unknown"); + // @formatter:on + + private int pointsPerRow; // number of Wind Vector Cell in each scan row + // across satellite track + + private WindDirectionSense windDirectionSense; // is the numeric wind + // direction the "from" + // (METEOROLOGICAL) + // direction, or the "to" + // (OCEANOGRAPHIC) direction? + + private LongitudeCoding longitudeCoding; // is the two-byte value a SIGNED + // (-18000 -- +18000) or UNSIGNED + // (0 -- 36000) representation of + // the (scaled by 100) longitude + // east of Greenwich? + + private ByteOrder byteOrder; // endianess of data in the byte stream + + private String reportType; // string to use in reportType field of DB + + private int valuesPerPoint = 9; // number of (short int) data values per + // point + + private int bytesPerValue = 2; // all are short int + + private int fileHeaderLength; // length in bytes of per-file header field, + // if any, in input file, before repeating + // per-row data begins; of interest to decoder + + private int rowHeaderLength = 8; // length in bytes of per-row header field + // (2 bytes each for day, hour, min, sec) + + private int bytesPerRow; // length in bytes of each row, comprising row + // header and all data values for all points in + // that row + + public int getBytesPerRow() { + return bytesPerRow; + } + + // Constructor + NcscatMode(int pointsPerRow, int fileHeaderLength, + WindDirectionSense windDirectionSense, + LongitudeCoding longitudeCoding, ByteOrder byteOrder, + String reportType) { + this.pointsPerRow = pointsPerRow; + this.fileHeaderLength = fileHeaderLength; + this.windDirectionSense = windDirectionSense; + this.longitudeCoding = longitudeCoding; + this.byteOrder = byteOrder; + this.reportType = reportType; + + bytesPerRow = rowHeaderLength + pointsPerRow * valuesPerPoint + * bytesPerValue; + } + + public String getReportType() { + return reportType; + } + + public int getPointsPerRow() { + return pointsPerRow; + } + + public int getFileHeaderLength() { + return fileHeaderLength; + } + + public WindDirectionSense getWindDirectionSense() { + return windDirectionSense; + } + + public LongitudeCoding getLongitudeCoding() { + return longitudeCoding; + } + + public ByteOrder getByteOrder() { + return byteOrder; + } + + public enum WindDirectionSense { // numeric direction value gives... + METEOROLOGICAL, // degrees FROM which wind is blowing + OCEANOGRAPHIC // degrees TO which wind is blowing + } + + public enum LongitudeCoding { // 2-byte wvc_lon (Wind Vector Cell - + // longitude) field is (x0.01 deg)... + SIGNED, // SIGNED short (-18000..+18000) + UNSIGNED // UNSIGNED short (0..36000 east of Greenwich) + } + + public static NcscatMode stringToMode(String name) { + // Given a string, return the corresponding enum + NcscatMode returnValue = null; + name = name.toUpperCase(); + name = name.replaceAll("-", "_"); + // TODO: Remove ambiguity number?? + try { + returnValue = valueOf(name); + } catch (IllegalArgumentException e) { + // TODO: Signal unrecognized Ncscat mode string + returnValue = UNKNOWN; + } + return returnValue; + } + + public boolean consistentWith(ByteBuffer byteBuffer) { + // Given a ByteBuffer containing ingested binary data of unknown + // satellite type (mode), determine whether data are consistent + // with "this" mode. We do this by seeing if date/hour fields + // repeat where they would be expected to appear, and contain + // reasonable values for those fields. + + // TODO: Consider moving this to decoder...? (Kind of prefer + // encapsulating it here, but also like enums to be rather + // minimalistic...? -bh) + + byteBuffer.order(byteOrder); + int base = fileHeaderLength; + for (int cycle = 0; cycle < 2; cycle++) { + + // DAY (of year) candidate fields of consecutive rows out of + // range... + short thisCandidateDayOfYear = byteBuffer.getShort(base); + short nextCandidateDayOfYear = byteBuffer.getShort(base + + bytesPerRow); + if (thisCandidateDayOfYear < 1 || thisCandidateDayOfYear > 366 + || nextCandidateDayOfYear < 1 + || nextCandidateDayOfYear > 366) + // ...means (right away) data can't be this type + return false; + // ...but if they don't match... + if (thisCandidateDayOfYear != nextCandidateDayOfYear) { + // ...can't rule it out (since first two rows could straddle a + // day boundary)...but can skip checking hour and go to check + // next pair of rows... + break; + } + + // HOUR candidate fields (of consecutive rows) in range...? + short thisCandidateHour = byteBuffer.getShort(base + 2); + short nextCandidateHour = byteBuffer.getShort(base + 2 + + bytesPerRow); + if (thisCandidateHour < 0 || thisCandidateHour > 23 + || nextCandidateHour < 0 || nextCandidateHour > 23) + return false; + // ...and if they do match (as well as day field above), then we + // conclude consistency + if (byteBuffer.getShort(base) == byteBuffer.getShort(base + + bytesPerRow)) { + return true; + } + // ...but if they don't, again, first two rows could straddle an + // hour boundary, so go to next pair of rows (cycle)... + base += bytesPerRow; + } + // We've made it through 2 consecutive cycles, and neither (1st/2nd + // nor 2nd/3rd) day+hour match, so conclude (assuming real consecutive + // rows are not separated by an hour or more) data not consistent with + // this type + return false; + } + +} diff --git a/ncep/gov.noaa.nws.ncep.edex.plugin.ncscat/src/gov/noaa/nws/ncep/edex/plugin/ncscat/decoder/NcscatDecoder.java b/ncep/gov.noaa.nws.ncep.edex.plugin.ncscat/src/gov/noaa/nws/ncep/edex/plugin/ncscat/decoder/NcscatDecoder.java index de3f3f3a8b..2ae65aaa39 100644 --- a/ncep/gov.noaa.nws.ncep.edex.plugin.ncscat/src/gov/noaa/nws/ncep/edex/plugin/ncscat/decoder/NcscatDecoder.java +++ b/ncep/gov.noaa.nws.ncep.edex.plugin.ncscat/src/gov/noaa/nws/ncep/edex/plugin/ncscat/decoder/NcscatDecoder.java @@ -2,7 +2,8 @@ * * Ncscat Decoder * - * This java class decodes Ascat quikscat (NESDIS File) raw data. + * This Java class decodes ocean winds scatterometer/radiometer (NESDIS File) raw data. + * * HISTORY * * Date Author Description @@ -10,6 +11,7 @@ * 11/2009 Uma Josyula Initial creation * 01/2011 B. Hebbard Handle ambiguity variants * 07/2012 B. Hebbard Handle OSCAT / OSCAT_HI + * 10/2014 B. Hebbard Get reportType from NcscatMode, instead of if-then-else'ing over hardcode integer lengths * * This code has been developed by the SIB for use in the AWIPS2 system. */ @@ -67,7 +69,7 @@ public class NcscatDecoder extends AbstractDecoder { NcscatProcessing sProcess = new NcscatProcessing(); sProcess.setInputFileName(plugin); messageData = sProcess.separate(data); - recLength = NcscatProcessing.getRecordLength(); + recLength = sProcess.getRecordLength(); if (messageData != null) { record = new NcscatRecord(); @@ -76,38 +78,7 @@ public class NcscatDecoder extends AbstractDecoder { record.setEndTime(sProcess.getEndTime()); record.setRecordLength(recLength); record.setDataTime(new DataTime(sProcess.getStartTime())); - - if (record.getRecordLength() == 688) { - - if (plugin.equalsIgnoreCase("oscat")) { - record.setReportType("oscat-hi"); - } else { - record.setReportType("quikscat"); - } - } else if (record.getRecordLength() == 1372) { - record.setReportType("quikscat-hi"); - } else if (record.getRecordLength() == 382) { - - if (plugin.equalsIgnoreCase("ascatx")) { - - record.setReportType("Exasct"); - } else { - - record.setReportType("ascat"); - } - } else if (record.getRecordLength() == 742) { - - if (plugin.equalsIgnoreCase("ascatx")) { - - record.setReportType("Exasct-hi"); - } else { - record.setReportType("ascat-hi"); - } - } else if (record.getRecordLength() == 328) { - record.setReportType("oscat"); - } else if (record.getRecordLength() == 715) { - record.setReportType("wscat"); - } + record.setReportType(sProcess.getNcscatMode().getReportType()); // If this is an (numbered) 'ambiguity' variant, add suffix to // reportType @@ -178,20 +149,16 @@ public class NcscatDecoder extends AbstractDecoder { } // TODO: Review this temporary fix by BH to see if a broader // refactor might be better. + // Problem: Without the added 'else' below, non-local variable - // "plugin" will - // retain its previous value if the pattern matcher fails above; - // this will happen - // if the file suffix/extension (after the ".") is shorter than 5 - // characters. - // Scenario: A file with suffix ".ascatx" is ingested, then later a - // ".qsct" file. + // "plugin" will retain its previous value if the pattern matcher + // fails above; this will happen if the file suffix/extension (after + // the ".") is shorter than 5 characters. Scenario: A file with + // suffix ".ascatx" is ingested, then later a ".qsct" file. // Variable "plugin" will be set to "ascatx" by the first, but will - // retain this - // same value for the second; logic in NcscatProcessing.doSeparate() - // will as a - // result interpret the big-endian ".qsct" data as little endian - // format. + // retain this same value for the second; logic in + // NcscatProcessing.doSeparate() will as a result interpret the + // big-endian ".qsct" data as little endian format. // Alternate (better): Could make "plugin" local to this method else { plugin = ""; @@ -200,20 +167,14 @@ public class NcscatDecoder extends AbstractDecoder { // NcscatProcessing // Handle ambiguity variants: In addition to files containing the - // primary - // (greatest likelihood) wind vector solutions, for some data types - // we also - // receive (typically 2 or 4) 'ambiguity' variants for the same - // point locations, - // but with (lesser likelihood) solutions. Since these cannot be - // distinguished - // from the primary files based on data format/content alone, we - // look for a - // (prearranged) telltale pattern somewhere in the file name. If - // found, we - // remember the indicated ambiguity number, for later tagging of the - // database - // record. + // primary (greatest likelihood) wind vector solutions, for some + // data types we also receive (typically 2 or 4) 'ambiguity' + // variants for the same point locations, but with (lesser + // likelihood) solutions. Since these cannot be distinguished from + // the primary files based on data format/content alone, we + // look for a (prearranged) telltale pattern somewhere in the file + // name. If found, we remember the indicated ambiguity number, for + // later tagging of the database record. pattern = Pattern.compile("ambig([1-9])"); matcher = pattern.matcher(fileName); if (matcher.find()) { diff --git a/ncep/gov.noaa.nws.ncep.edex.plugin.ncscat/src/gov/noaa/nws/ncep/edex/plugin/ncscat/util/NcscatProcessing.java b/ncep/gov.noaa.nws.ncep.edex.plugin.ncscat/src/gov/noaa/nws/ncep/edex/plugin/ncscat/util/NcscatProcessing.java index 2a4591280e..1836da5379 100644 --- a/ncep/gov.noaa.nws.ncep.edex.plugin.ncscat/src/gov/noaa/nws/ncep/edex/plugin/ncscat/util/NcscatProcessing.java +++ b/ncep/gov.noaa.nws.ncep.edex.plugin.ncscat/src/gov/noaa/nws/ncep/edex/plugin/ncscat/util/NcscatProcessing.java @@ -7,14 +7,18 @@ *
* Uma Josyula 11/2009 Creation * B. Hebbard 07/2012 Handle OSCAT / OSCAT_HI + * B. Hebbard R4865/TTR984 10/2014 Tighten code that infers satellite type + * (from date/hour field recurrence) and + * date handling to prevent garbage from being + * interpreted as dates decades in the future. ** * This code has been developed by the SIB for use in the AWIPS system. */ - package gov.noaa.nws.ncep.edex.plugin.ncscat.util; +import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatMode; import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatPoint; import java.nio.ByteBuffer; @@ -24,369 +28,357 @@ import java.util.Calendar; import java.util.List; import java.util.NoSuchElementException; -import javax.xml.bind.JAXBException; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.Logger; -import com.raytheon.uf.common.serialization.SerializationUtil; - - public class NcscatProcessing { + private final Logger log = Logger.getLogger(getClass().getName()); + private final Log theLogger = LogFactory.getLog(getClass()); - private final Logger log = Logger.getLogger(getClass().getName()); - private final Log theLogger = LogFactory.getLog(getClass()); - private List
- * SOFTWARE HISTORY - * Date Ticket# Engineer Description - * ------------ ---------- ----------- -------------------------- - * 02 Jun 2010 235B B. Hebbard Initial creation. - * 03 Feb 2011 235E B. Hebbard Add support for ambiguity variants. - * 16 Aug 2012 B. Hebbard Add OSCAT / OSCAT_HI - * 11 Apr 2014 1128 B. Hebbard Add longitudeCoding field; change wind sense from boolean to enum. - * - *- * - * @author bhebbard - * @version 1.0 - */ - -public enum NcscatMode { - - // @formatter:off - QUIKSCAT ( 76, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN ), - QUIKSCAT_HI ( 152, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN ), - ASCAT ( 42, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN ), - ASCAT_HI ( 82, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN ), - EXASCT ( 42, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN ), - EXASCT_HI ( 82, WindDirectionSense.OCEANOGRAPHIC, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN ), - OSCAT ( 36, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN ), - OSCAT_HI ( 76, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.LITTLE_ENDIAN ), - WSCAT ( 79, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.SIGNED, ByteOrder.LITTLE_ENDIAN ), - UNKNOWN ( 76, WindDirectionSense.METEOROLOGICAL, LongitudeCoding.UNSIGNED, ByteOrder.BIG_ENDIAN ); - // @formatter:on - - private int pointsPerRow; // number of Wind Vector Cell in each scan row - // across satellite track - - private WindDirectionSense windDirectionSense; // is the numeric wind - // direction the "from" - // (METEOROLOGICAL) - // direction, or the "to" - // (OCEANOGRAPHIC) direction? - - private LongitudeCoding longitudeCoding; // is the two-byte value a SIGNED - // (-18000 -- +18000) or UNSIGNED - // (0 -- 36000) representation of - // the (scaled by 100) longitude - // east of Greenwich? - - private ByteOrder byteOrder; // endianess of data in the byte stream - - // TODO: could add more here, to simplify (switch) code with more table - // driven logic. But see above note about .xml/.prm... - - // Constructor - NcscatMode(int pointsPerRow, WindDirectionSense windDirectionSense, - LongitudeCoding longitudeCoding, ByteOrder byteOrder) { - this.pointsPerRow = pointsPerRow; - this.windDirectionSense = windDirectionSense; - this.longitudeCoding = longitudeCoding; - this.byteOrder = byteOrder; - } - - public int getPointsPerRow() { - return pointsPerRow; - } - - public WindDirectionSense getWindDirectionSense() { - return windDirectionSense; - } - - public LongitudeCoding getLongitudeCoding() { - return longitudeCoding; - } - - public ByteOrder getByteOrder() { - return byteOrder; - } - - public static NcscatMode stringToMode(String name) { - // Given a string, return the corresponding enum - NcscatMode returnValue = null; - name = name.toUpperCase(); - name = name.replaceAll("-", "_"); - // TODO: Remove ambiguity number?? - try { - returnValue = valueOf(name); - } catch (IllegalArgumentException e) { - // TODO: Signal unrecognized Ncscat mode string - returnValue = UNKNOWN; - } - return returnValue; - } - - public enum WindDirectionSense { // numeric direction value gives... - METEOROLOGICAL, // degrees FROM which wind is blowing - OCEANOGRAPHIC // degrees TO which wind is blowing - } - - public enum LongitudeCoding { // 2-byte wvc_lon (Wind Vector Cell - - // longitude) field is (x0.01 deg)... - SIGNED, // SIGNED short (-18000..+18000) - UNSIGNED // UNSIGNED short (0..36000 east of Greenwich) - } - -} diff --git a/ncep/gov.noaa.nws.ncep.viz.rsc.ncscat/src/gov/noaa/nws/ncep/viz/rsc/ncscat/rsc/NcscatResource.java b/ncep/gov.noaa.nws.ncep.viz.rsc.ncscat/src/gov/noaa/nws/ncep/viz/rsc/ncscat/rsc/NcscatResource.java index 38961d7dff..1664dcb45c 100644 --- a/ncep/gov.noaa.nws.ncep.viz.rsc.ncscat/src/gov/noaa/nws/ncep/viz/rsc/ncscat/rsc/NcscatResource.java +++ b/ncep/gov.noaa.nws.ncep.viz.rsc.ncscat/src/gov/noaa/nws/ncep/viz/rsc/ncscat/rsc/NcscatResource.java @@ -1,7 +1,10 @@ package gov.noaa.nws.ncep.viz.rsc.ncscat.rsc; +import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatMode; import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatPoint; import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatRecord; +import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatMode.LongitudeCoding; +import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatMode.WindDirectionSense; import gov.noaa.nws.ncep.gempak.parameters.colorbar.ColorBarOrientation; import gov.noaa.nws.ncep.ui.pgen.display.DisplayElementFactory; import gov.noaa.nws.ncep.ui.pgen.display.IDisplayable; @@ -13,8 +16,6 @@ import gov.noaa.nws.ncep.viz.resources.AbstractNatlCntrsResource; import gov.noaa.nws.ncep.viz.resources.INatlCntrsResource; import gov.noaa.nws.ncep.viz.resources.colorBar.ColorBarResource; import gov.noaa.nws.ncep.viz.resources.colorBar.ColorBarResourceData; -import gov.noaa.nws.ncep.viz.rsc.ncscat.rsc.NcscatMode.LongitudeCoding; -import gov.noaa.nws.ncep.viz.rsc.ncscat.rsc.NcscatMode.WindDirectionSense; import gov.noaa.nws.ncep.viz.ui.display.ColorBar; import gov.noaa.nws.ncep.viz.ui.display.NCMapDescriptor; diff --git a/ncep/gov.noaa.nws.ncep.viz.rsc.ncscat/src/gov/noaa/nws/ncep/viz/rsc/ncscat/rsc/NcscatResourceData.java b/ncep/gov.noaa.nws.ncep.viz.rsc.ncscat/src/gov/noaa/nws/ncep/viz/rsc/ncscat/rsc/NcscatResourceData.java index 79382a646b..ef6b6a2b9c 100644 --- a/ncep/gov.noaa.nws.ncep.viz.rsc.ncscat/src/gov/noaa/nws/ncep/viz/rsc/ncscat/rsc/NcscatResourceData.java +++ b/ncep/gov.noaa.nws.ncep.viz.rsc.ncscat/src/gov/noaa/nws/ncep/viz/rsc/ncscat/rsc/NcscatResourceData.java @@ -4,6 +4,7 @@ import gov.noaa.nws.ncep.viz.resources.AbstractNatlCntrsRequestableResourceData; import gov.noaa.nws.ncep.viz.resources.INatlCntrsResourceData; import gov.noaa.nws.ncep.viz.resources.attributes.RGBColorAdapter; import gov.noaa.nws.ncep.viz.ui.display.ColorBar; +import gov.noaa.nws.ncep.common.dataplugin.ncscat.NcscatMode; import gov.noaa.nws.ncep.ui.pgen.display.IVector.VectorType; import java.util.regex.Matcher;