diff --git a/edexOsgi/com.raytheon.edex.plugin.sfcobs/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.edex.plugin.sfcobs/META-INF/MANIFEST.MF index 1ceed53715..f7f8d9515a 100644 --- a/edexOsgi/com.raytheon.edex.plugin.sfcobs/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.edex.plugin.sfcobs/META-INF/MANIFEST.MF @@ -11,7 +11,8 @@ Require-Bundle: com.raytheon.edex.common, org.geotools, javax.measure, javax.persistence -Export-Package: com.raytheon.edex.plugin.sfcobs.common +Export-Package: com.raytheon.edex.plugin.sfcobs, + com.raytheon.edex.plugin.sfcobs.common Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Import-Package: com.raytheon.uf.common.dataplugin.sfcobs, com.raytheon.uf.common.pointdata, diff --git a/edexOsgi/com.raytheon.edex.plugin.sfcobs/res/pointdata/sfcobs.xml b/edexOsgi/com.raytheon.edex.plugin.sfcobs/res/pointdata/sfcobs.xml index 88154dbaa2..b386bb7d49 100644 --- a/edexOsgi/com.raytheon.edex.plugin.sfcobs/res/pointdata/sfcobs.xml +++ b/edexOsgi/com.raytheon.edex.plugin.sfcobs/res/pointdata/sfcobs.xml @@ -27,11 +27,11 @@ - + - + @@ -58,7 +58,7 @@ - + @@ -66,10 +66,10 @@ - + - + diff --git a/edexOsgi/com.raytheon.edex.plugin.sfcobs/src/com/raytheon/edex/plugin/sfcobs/SfcObsPointDataTransform.java b/edexOsgi/com.raytheon.edex.plugin.sfcobs/src/com/raytheon/edex/plugin/sfcobs/SfcObsPointDataTransform.java index 1dad2a934f..b23225b004 100644 --- a/edexOsgi/com.raytheon.edex.plugin.sfcobs/src/com/raytheon/edex/plugin/sfcobs/SfcObsPointDataTransform.java +++ b/edexOsgi/com.raytheon.edex.plugin.sfcobs/src/com/raytheon/edex/plugin/sfcobs/SfcObsPointDataTransform.java @@ -20,14 +20,13 @@ package com.raytheon.edex.plugin.sfcobs; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.xml.bind.JAXBException; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -43,7 +42,7 @@ import com.raytheon.uf.common.serialization.SerializationException; import com.raytheon.uf.edex.decodertools.time.TimeTools; /** - * TODO Add Description + * Populates point data views from fields in obs common records. * *
  * 
@@ -53,6 +52,7 @@ import com.raytheon.uf.edex.decodertools.time.TimeTools;
  * ------------ ---------- ----------- --------------------------
  * Oct 1, 2009             jkorman     Initial creation
  * Feb 15,2011  5705       cjeanbap    Added wmoHeader to HDR_PARAMS_LIST.
+ * Apr 04,2014  2906       bclement    made getDescription() and static constants public
  * 
  * 
* @@ -66,143 +66,143 @@ public class SfcObsPointDataTransform { private static Log logger = LogFactory .getLog(SfcObsPointDataTransform.class); - private static final int INT_DEFAULT = -9999; + public static final int INT_DEFAULT = -9999; - private static final float FLOAT_DEFAULT = -9999.0f; + public static final float FLOAT_DEFAULT = -9999.0f; // /pluginName/dataTime/reportType/corIndicator/latitude/longitude // /sfcobs/2009-10-12_12:00:00.0/1001/null/71905/55.28/-77.76 // ** DataUri - private static final String STATION_ID = "stationId"; + public static final String STATION_ID = "stationId"; // ** DataUri - private static final String LATITUDE = "latitude"; + public static final String LATITUDE = "latitude"; // ** DataUri - private static final String LONGITUDE = "longitude"; + public static final String LONGITUDE = "longitude"; - private static final String ELEVATION = "elevation"; + public static final String ELEVATION = "elevation"; - private static final String TIME_OBS = "timeObs"; + public static final String TIME_OBS = "timeObs"; - private static final String TIME_NOMINAL = "timeNominal"; + public static final String TIME_NOMINAL = "timeNominal"; // ** DataUri - private static final String REPORT_TYPE = "reportType"; + public static final String REPORT_TYPE = "reportType"; // ** DataUri - private static final String COR_INDICATOR = "corIndicator"; + public static final String COR_INDICATOR = "corIndicator"; - private static final String DATA_URI = "dataURI"; + public static final String DATA_URI = "dataURI"; - private static final String WMO_HEADER = "wmoHeader"; + public static final String WMO_HEADER = "wmoHeader"; - private static final String RAW_REPORT = "rawReport"; + public static final String RAW_REPORT = "rawReport"; - private static final String PRESS_CHANGE_3HR = "pressChange3Hour"; + public static final String PRESS_CHANGE_3HR = "pressChange3Hour"; - private static final String PRESS_CHANGE_CHAR = "pressChangeChar"; + public static final String PRESS_CHANGE_CHAR = "pressChangeChar"; - private static final String PRECIP1_HOUR = "precip1Hour"; + public static final String PRECIP1_HOUR = "precip1Hour"; - private static final String PRECIP6_HOUR = "precip6Hour"; + public static final String PRECIP6_HOUR = "precip6Hour"; - private static final String PRECIP12_HOUR = "precip12Hour"; + public static final String PRECIP12_HOUR = "precip12Hour"; - private static final String PRECIP18_HOUR = "precip18Hour"; + public static final String PRECIP18_HOUR = "precip18Hour"; - private static final String PRECIP24_HOUR = "precip24Hour"; + public static final String PRECIP24_HOUR = "precip24Hour"; - private static final String TEMPERATURE = "temperature"; + public static final String TEMPERATURE = "temperature"; - private static final String DEWPOINT = "dewpoint"; + public static final String DEWPOINT = "dewpoint"; - private static final String WIND_DIR = "windDir"; + public static final String WIND_DIR = "windDir"; - private static final String WIND_GUST = "windGust"; + public static final String WIND_GUST = "windGust"; - private static final String WIND_SPEED = "windSpeed"; + public static final String WIND_SPEED = "windSpeed"; - private static final String PEAK_WIND_DIR = "peakWindDir"; + public static final String PEAK_WIND_DIR = "peakWindDir"; - private static final String PEAK_WIND_SPEED = "peakWindSpeed"; + public static final String PEAK_WIND_SPEED = "peakWindSpeed"; - private static final String PEAK_WIND_SPEED_TIME = "peakWindSpeedTime"; + public static final String PEAK_WIND_SPEED_TIME = "peakWindSpeedTime"; - private static final String SEA_LEVEL_PRESS = "seaLevelPress"; + public static final String SEA_LEVEL_PRESS = "seaLevelPress"; - private static final String ALTIMETER = "altimeter"; + public static final String ALTIMETER = "altimeter"; - private static final String STATION_PRESS = "stationPress"; + public static final String STATION_PRESS = "stationPress"; - private static final String VISIBILITY = "visibility"; + public static final String VISIBILITY = "visibility"; - private static final String PRES_WEATHER = "presWeather"; + public static final String PRES_WEATHER = "presWeather"; - private static final String WX_PRESENT = "wx_present"; + public static final String WX_PRESENT = "wx_present"; - private static final String WX_PAST_1 = "wx_past_1"; + public static final String WX_PAST_1 = "wx_past_1"; - private static final String WX_PAST_2 = "wx_past_2"; + public static final String WX_PAST_2 = "wx_past_2"; - private static final String CLOUD_AMOUNT_TOT = "totCloudAmount"; + public static final String CLOUD_AMOUNT_TOT = "totCloudAmount"; - private static final String CLOUD_HGT_LOW = "lowCloudHeight"; + public static final String CLOUD_HGT_LOW = "lowCloudHeight"; - private static final String CLOUD_AMOUNT_LOW = "lowCloudAmount"; + public static final String CLOUD_AMOUNT_LOW = "lowCloudAmount"; - private static final String CLOUD_TYPE_LOW = "lowCloudType"; + public static final String CLOUD_TYPE_LOW = "lowCloudType"; - private static final String CLOUD_TYPE_MID = "midCloudType"; + public static final String CLOUD_TYPE_MID = "midCloudType"; - private static final String CLOUD_TYPE_HI = "hiCloudType"; + public static final String CLOUD_TYPE_HI = "hiCloudType"; - private static final String SEA_SFC_TEMP = "seaSurfaceTemp"; + public static final String SEA_SFC_TEMP = "seaSurfaceTemp"; - private static final String ICE_CODE = "iceCode"; + public static final String ICE_CODE = "iceCode"; - private static final String WET_BULB = "wetBulb"; + public static final String WET_BULB = "wetBulb"; - private static final String PLATFORM_DIR = "platformTrueDirection"; + public static final String PLATFORM_DIR = "platformTrueDirection"; - private static final String PLATFORM_SPD = "platformTrueSpeed"; + public static final String PLATFORM_SPD = "platformTrueSpeed"; - private static final String WIND_SPD_EQUIV_10M = "equivWindSpeed10m"; + public static final String WIND_SPD_EQUIV_10M = "equivWindSpeed10m"; - private static final String WIND_SPD_EQUIV_20M = "equivWindSpeed20m"; + public static final String WIND_SPD_EQUIV_20M = "equivWindSpeed20m"; - private static final String WIND_WV_HGT = "windWaveHeight"; + public static final String WIND_WV_HGT = "windWaveHeight"; - private static final String WIND_WV_PD = "windWavePeriod"; + public static final String WIND_WV_PD = "windWavePeriod"; - private static final String WV_HGT = "waveHeight"; + public static final String WV_HGT = "waveHeight"; - private static final String WV_PD = "wavePeriod"; + public static final String WV_PD = "wavePeriod"; - private static final String WV_STEEPNESS = "waveSteepness"; + public static final String WV_STEEPNESS = "waveSteepness"; - private static final String HI_RES_WV_HGT = "highResWaveHeight"; + public static final String HI_RES_WV_HGT = "highResWaveHeight"; - private static final String PRI_SWELL_WV_DIR = "primarySwellWaveDir"; + public static final String PRI_SWELL_WV_DIR = "primarySwellWaveDir"; - private static final String PRI_SWELL_WV_PD = "primarySwellWavePeriod"; + public static final String PRI_SWELL_WV_PD = "primarySwellWavePeriod"; - private static final String PRI_SWELL_WV_HGT = "primarySwellWaveHeight"; + public static final String PRI_SWELL_WV_HGT = "primarySwellWaveHeight"; - private static final String SEC_SWELL_WV_DIR = "secondarySwellWaveDir"; + public static final String SEC_SWELL_WV_DIR = "secondarySwellWaveDir"; - private static final String SEC_SWELL_WV_PD = "secondarySwellWavePeriod"; + public static final String SEC_SWELL_WV_PD = "secondarySwellWavePeriod"; - private static final String SEC_SWELL_WV_HGT = "secondarySwellWaveHeight"; + public static final String SEC_SWELL_WV_HGT = "secondarySwellWaveHeight"; - private static final String NUM_INTER_WINDS = "numInterWinds"; + public static final String NUM_INTER_WINDS = "numInterWinds"; - private static final String INTER_WIND_TIME = "interWindTime"; + public static final String INTER_WIND_TIME = "interWindTime"; - private static final String INTER_WIND_DIR = "interWindDir"; + public static final String INTER_WIND_DIR = "interWindDir"; - private static final String INTER_WIND_SPD = "interWindSpeed"; + public static final String INTER_WIND_SPD = "interWindSpeed"; public static final String HDR_PARAMS_LIST; static { @@ -489,21 +489,28 @@ public class SfcObsPointDataTransform { } /** + * Read description file from classpath for specified type * * @param type * @return - * @throws JAXBException + * @throws SerializationException */ - private PointDataDescription getDescription(String type) + public static PointDataDescription getDescription(String type) throws SerializationException { - InputStream is = this.getClass().getResourceAsStream( - "/res/pointdata/" + type + ".xml"); + InputStream is = SfcObsPointDataTransform.class + .getResourceAsStream("/res/pointdata/" + type + ".xml"); if (is == null) { throw new RuntimeException("Cannot find descriptor for: " + type); } - PointDataDescription d = PointDataDescription.fromStream(is); - - return d; + try { + return PointDataDescription.fromStream(is); + } finally { + try { + is.close(); + } catch (IOException e) { + logger.error(e.getLocalizedMessage(), e); + } + } } public static ObsCommon toSfcObsRecord(PointDataView pdv) { diff --git a/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrFileSeparator.java b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrFileSeparator.java index d08421f5b4..4032e9cdbf 100644 --- a/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrFileSeparator.java +++ b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrFileSeparator.java @@ -84,10 +84,12 @@ public class BufrFileSeparator { * @throws IOException */ public static List separate(File mixedBufrFile) throws IOException { - final File outputBaseDir = DEFAULT_TMP_DIR; final String inputFile = mixedBufrFile.getAbsolutePath(); - final File outputDir = getOutputDir(mixedBufrFile.getName(), - outputBaseDir); + final File outputDir = getOutputDir(mixedBufrFile); + if (outputDir.exists()) { + log.warn("BUFR splitter output directory already exists, is file " + + mixedBufrFile + " being processed twice?"); + } Options options = new Options() { @Override public String getFileSpec() { @@ -115,18 +117,39 @@ public class BufrFileSeparator { * Create a temporary output directory based on the input file name * * @param inputName - * @param outputBaseDir * @return */ - private static File getOutputDir(final String inputName, - final File outputBaseDir) { - String name = inputName + "-" + System.currentTimeMillis() + "-split"; - File rval = new File(outputBaseDir, name); - if (rval.exists()) { - log.warn("BUFR splitter output directory already exists, is file " - + inputName + " being processed twice?"); - } + private static File getOutputDir(final File inputFile) { + String absPath = inputFile.getAbsolutePath(); + /* + * include absolute path hash to account for files with the same name in + * different directories (ie hourly subdirectories) + */ + String hash = Integer.toHexString(absPath.hashCode()); + String name = inputFile.getName() + "-" + hash + "-split"; + File rval = new File(DEFAULT_TMP_DIR, name); return rval; } + /** + * Clean up split files and temp directory + * + * @param mixedBufrFile + */ + public static void clean(File mixedBufrFile) { + File outputDir = getOutputDir(mixedBufrFile); + if (!outputDir.exists()) { + log.debug("Split output directory removed before clean"); + return; + } + for (File f : outputDir.listFiles(BUFR_FILTER)) { + if (!f.delete() && f.exists()) { + log.error("Unable to clean up temporary BUFR file: " + f); + } + } + if (!outputDir.delete() && outputDir.exists()) { + log.error("Unable to clean up temporary BUFR directory: " + + outputDir); + } + } } diff --git a/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrParser.java b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrParser.java index bb754eab7d..d717ac9031 100644 --- a/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrParser.java +++ b/edexOsgi/com.raytheon.uf.common.nc.bufr/src/com/raytheon/uf/common/nc/bufr/BufrParser.java @@ -817,4 +817,14 @@ public class BufrParser { return rval; } + /** + * Get the level of the current structure. 0 indicates that there is no + * current structure. + * + * @return + */ + public int getStructLevel() { + return structStack.size(); + } + } diff --git a/edexOsgi/com.raytheon.uf.common.units/utility/common_static/base/unit/alias/udunits.xml b/edexOsgi/com.raytheon.uf.common.units/utility/common_static/base/unit/alias/udunits.xml index 18eaf2b4f2..d5956f2334 100644 --- a/edexOsgi/com.raytheon.uf.common.units/utility/common_static/base/unit/alias/udunits.xml +++ b/edexOsgi/com.raytheon.uf.common.units/utility/common_static/base/unit/alias/udunits.xml @@ -31,7 +31,7 @@ kg m-2 kg m-2 - Degree + Degree true Minute Hour Day diff --git a/edexOsgi/com.raytheon.uf.edex.cots.feature/feature.xml b/edexOsgi/com.raytheon.uf.edex.cots.feature/feature.xml index 120bc20d5e..623428a747 100644 --- a/edexOsgi/com.raytheon.uf.edex.cots.feature/feature.xml +++ b/edexOsgi/com.raytheon.uf.edex.cots.feature/feature.xml @@ -102,4 +102,10 @@ install-size="0" version="0.0.0"/> + + diff --git a/edexOsgi/com.raytheon.uf.edex.dataplugins.feature/feature.xml b/edexOsgi/com.raytheon.uf.edex.dataplugins.feature/feature.xml index 5f6129c8a4..424863e684 100644 --- a/edexOsgi/com.raytheon.uf.edex.dataplugins.feature/feature.xml +++ b/edexOsgi/com.raytheon.uf.edex.dataplugins.feature/feature.xml @@ -353,4 +353,18 @@ version="0.0.0" unpack="false"/> + + + + diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/META-INF/MANIFEST.MF index 1867e585df..de888c8121 100644 --- a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/META-INF/MANIFEST.MF @@ -10,4 +10,18 @@ Require-Bundle: com.raytheon.uf.common.nc.bufr, ucar.nc2, ucar.nc2.bufrsplitter, com.raytheon.uf.common.status, - org.geotools + org.geotools, + com.raytheon.uf.common.dataplugin, + com.raytheon.uf.common.dataplugin.sfcobs, + com.raytheon.uf.common.util, + com.raytheon.uf.common.localization, + com.raytheon.uf.common.pointdata, + javax.measure, + com.raytheon.edex.plugin.sfcobs, + com.raytheon.uf.common.serialization, + com.raytheon.uf.edex.pointdata, + com.raytheon.uf.common.datastorage, + com.raytheon.uf.common.units, + com.raytheon.uf.edex.database, + com.raytheon.uf.edex.core, + com.raytheon.uf.edex.decodertools diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/build.properties b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/build.properties index 34d2e4d2da..39000608d1 100644 --- a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/build.properties +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/build.properties @@ -1,4 +1,8 @@ source.. = src/ output.. = bin/ bin.includes = META-INF/,\ - . + .,\ + res/,\ + utility/ +src.includes = res/,\ + utility/ diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/res/spring/bufrobs-ingest.xml b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/res/spring/bufrobs-ingest.xml new file mode 100644 index 0000000000..2a87dae620 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/res/spring/bufrobs-ingest.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sfcobs + + + + + + + + + + + + + + + java.lang.Throwable + + + + + + java.lang.Throwable + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/AbstractBufrSfcObsDecoder.java b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/AbstractBufrSfcObsDecoder.java new file mode 100644 index 0000000000..c84d3fb62a --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/AbstractBufrSfcObsDecoder.java @@ -0,0 +1,896 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.edex.plugin.bufrobs; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.measure.converter.UnitConverter; +import javax.measure.unit.SI; +import javax.measure.unit.Unit; +import javax.xml.bind.JAXBException; + +import ucar.nc2.Attribute; +import ucar.nc2.NetcdfFile; +import ucar.nc2.Variable; + +import com.raytheon.edex.plugin.sfcobs.SfcObsDao; +import com.raytheon.edex.plugin.sfcobs.SfcObsPointDataTransform; +import com.raytheon.uf.common.dataplugin.PluginDataObject; +import com.raytheon.uf.common.dataplugin.sfcobs.ObsCommon; +import com.raytheon.uf.common.localization.IPathManager; +import com.raytheon.uf.common.localization.LocalizationContext; +import com.raytheon.uf.common.localization.LocalizationFile; +import com.raytheon.uf.common.localization.PathManagerFactory; +import com.raytheon.uf.common.nc.bufr.BufrDataItem; +import com.raytheon.uf.common.nc.bufr.BufrParser; +import com.raytheon.uf.common.nc.bufr.BufrParser.Event; +import com.raytheon.uf.common.nc.bufr.tables.MappedValue; +import com.raytheon.uf.common.nc.bufr.tables.ParsedTableUnit; +import com.raytheon.uf.common.nc.bufr.tables.TableEntry; +import com.raytheon.uf.common.nc.bufr.tables.TranslationTable; +import com.raytheon.uf.common.nc.bufr.tables.TranslationTable.TableType; +import com.raytheon.uf.common.nc.bufr.tables.TranslationTableManager; +import com.raytheon.uf.common.nc.bufr.time.BufrTimeFieldParser; +import com.raytheon.uf.common.nc.bufr.time.TimeFieldParseException; +import com.raytheon.uf.common.nc.bufr.util.BufrMapper; +import com.raytheon.uf.common.pointdata.ParameterDescription; +import com.raytheon.uf.common.pointdata.PointDataContainer; +import com.raytheon.uf.common.pointdata.PointDataDescription; +import com.raytheon.uf.common.pointdata.PointDataView; +import com.raytheon.uf.common.pointdata.spatial.SurfaceObsLocation; +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.common.units.UnitLookupException; +import com.raytheon.uf.common.units.UnitMapper; +import com.raytheon.uf.edex.decodertools.time.TimeTools; +import com.raytheon.uf.edex.plugin.bufrobs.category.CategoryKey; +import com.raytheon.uf.edex.plugin.bufrobs.category.CategoryParser; +import com.raytheon.uf.edex.wmo.message.WMOHeader; + +/** + * Abstract class for decoding BUFR formatted sfcobs. Contains general methods + * that are oblivious to the specific fields that are in the BUFR structure. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Mar 21, 2014 2906       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public abstract class AbstractBufrSfcObsDecoder { + + public static final String localizationAliasDirectory = "bufrobs" + + File.separator + "alias"; + + public static final String UDUNITS_NAMESPACE = "udunits"; + + public static final String WMO_HEADER_ATTRIB = "WMO Header"; + + public static final String TIME_FIELD = "time"; + + public static final String WMO_BLOCK_FIELD = "WMO block number"; + + public static final String WMO_INDEX_FORMAT = "%02d%03d"; + + protected static final IUFStatusHandler log = UFStatus + .getHandler(AbstractBufrSfcObsDecoder.class); + + private volatile BufrMapper mapper; + + private final Object mapperMutex = new Object(); + + private final SfcObsDao dao; + + private final String pluginName; + + private final PointDataDescription description; + + private final Map parameterDescriptionMap; + + public static final String localizationTablesDirectory = "bufrobs" + + File.separator + "tables"; + + private final Map translationMap = new HashMap(); + + private volatile Map reportTypeMap; + + private final Object reportTypeMutex = new Object(); + + private final UnitMapper unitMapper = UnitMapper.getInstance(); + + /** + * @param pluginName + * @throws BufrObsDecodeException + */ + public AbstractBufrSfcObsDecoder(String pluginName) + throws BufrObsDecodeException { + try { + this.dao = new SfcObsDao(pluginName); + this.pluginName = pluginName; + this.description = SfcObsPointDataTransform + .getDescription(pluginName); + ParameterDescription[] params = description.parameters; + this.parameterDescriptionMap = createParamDefMap(params); + } catch (Exception e) { + throw new BufrObsDecodeException("Unable to initialize decoder", e); + } + } + + /** + * Create mapping from parameter name to description + * + * @param params + * @return + */ + private static Map createParamDefMap( + ParameterDescription[] params) { + Map map = new HashMap( + params.length); + for (ParameterDescription desc : params) { + map.put(desc.getParameterName(), desc); + } + return Collections.unmodifiableMap(map); + } + + /** + * Parse structures from BUFR file and return results in obs common data + * object + * + * @param parser + * @param key + * @return + * @throws BufrObsDecodeException + */ + public PluginDataObject[] decode(BufrParser parser, CategoryKey key) + throws BufrObsDecodeException { + Map pointMap = new HashMap(); + Map rtypeMap = getReportTypeMap(); + Integer reportType = rtypeMap.get(key); + WMOHeader header = getWmoHeader(parser); + List rval = new ArrayList(); + boolean hasMore = true; + while (hasMore) { + try { + try { + ObsCommon record = getNextRecord(parser, reportType, + header, pointMap); + if (record != null) { + rval.add(record); + } else { + hasMore = false; + } + } catch (MissingRequiredDataException e) { + /* missing data required for record, no stack trace */ + log.warn(e.getLocalizedMessage()); + findEndOfStructure(parser); + } catch (BufrObsDecodeException e) { + /* problem with individual structure, try to parse more */ + log.error(e.getLocalizedMessage(), e); + findEndOfStructure(parser); + } + } catch (IOException e) { + /* problem with the parser itself, give up on file */ + throw new BufrObsDecodeException("Problem parsing BUFR file: " + + parser.getFile(), e); + } + } + return rval.toArray(new PluginDataObject[rval.size()]); + } + + /** + * Parse the next obs record or return null if there are no more records + * + * @param parser + * @param reportType + * @param header + * @param pointMap + * @return null if there are no more records + * @throws IOException + * @throws BufrObsDecodeException + */ + private ObsCommon getNextRecord(BufrParser parser, Integer reportType, + WMOHeader header, Map pointMap) + throws IOException, BufrObsDecodeException { + ObsCommon currentRecord = null; + boolean done = false; + while (parser.hasNext() && !done) { + Event e = parser.next(); + switch (e) { + case START_FILE: + log.debug("Started processing BUFR file: " + parser.getFile()); + break; + case START_STRUCTURE: + if (parser.getStructLevel() == 1) { + /* started a new record */ + currentRecord = createNewRecord(parser); + setTime(currentRecord, parser); + setWMOHeaderInfo(currentRecord, header); + currentRecord.setReportType(reportType); + PointDataContainer pdc = getPointDataContainer(pointMap, + currentRecord); + currentRecord.setPointDataView(pdc.append()); + } + break; + case FIELD: + processField(currentRecord, parser); + break; + case END_STRUCTURE: + if (parser.getStructLevel() == 0) { + /* ended record */ + currentRecord = finalizeRecord(parser, currentRecord); + done = true; + } + break; + case END_FILE: + log.debug("Finished processing BUFR file"); + break; + default: + // no action + } + } + return currentRecord; + } + + /** + * Run the parser until it is no longer in the current structure + * + * @param parser + * @throws IOException + */ + private void findEndOfStructure(BufrParser parser) throws IOException { + while (parser.hasNext() && parser.getStructLevel() > 0) { + parser.next(); + } + } + + /** + * Set record fields that are sourced from WMO header + * + * @param currentRecord + * @param header + */ + protected void setWMOHeaderInfo(ObsCommon currentRecord, WMOHeader header) { + currentRecord.setWmoHeader(header.getWmoHeader()); + // Determine if this is a COR'd observation. Value must be in the + // range of "CC[A..Z]" + String cor = header.getBBBIndicator(); + // must have 3 characters. + if ((cor != null) && (cor.length() == 2)) { + if ("CC".equals(cor.substring(0, 2))) { + char c = cor.charAt(2); + if ((c >= 'A') && (c <= 'Z')) { + currentRecord.setCorIndicator(cor); + } + } + } + /* shef converter looks for WMO header in obs text */ + currentRecord.setObsText(header.getWmoHeader()); + } + + /** + * Get WMO header object from NetCDF file + * + * @param parser + * @throws MissingRequiredDataException + */ + protected WMOHeader getWmoHeader(BufrParser parser) + throws MissingRequiredDataException { + NetcdfFile ncfile = parser.getNcfile(); + Attribute attrib = ncfile + .findGlobalAttributeIgnoreCase(WMO_HEADER_ATTRIB); + if (attrib == null) { + throw new MissingRequiredDataException( + "Missing WMO Header in BUFR file: " + parser.getFile()); + } + return new WMOHeader(attrib.getStringValue().getBytes()); + } + + /** + * Construct a new record instance + * + * @param parser + * + * @return + */ + protected ObsCommon createNewRecord(BufrParser parser) { + ObsCommon rval = new ObsCommon(); + rval.setLocation(new SurfaceObsLocation()); + return rval; + } + + /** + * Find point data container for record. Fields used in finding HDF file + * (like time) need to be populated in record. + * + * @param pointMap + * @param record + * @return + */ + protected PointDataContainer getPointDataContainer( + Map pointMap, ObsCommon record) { + File f = this.dao.getFullFilePath(record); + PointDataContainer pdc = pointMap.get(f); + if (pdc == null) { + pdc = PointDataContainer.build(this.description); + pointMap.put(f, pdc); + } + return pdc; + } + + /** + * Process BUFR field and place data in provided record + * + * @param record + * @param parser + * @throws BufrObsDecodeException + */ + abstract protected void processField(ObsCommon record, BufrParser parser) + throws BufrObsDecodeException; + + /** + * @param unitStr + * @return true if unit specified a BUFR lookup table + */ + protected boolean isTableLookup(String unitStr) { + return ParsedTableUnit.TABLE_UNIT_PATTERN.matcher(unitStr).matches(); + } + + /** + * Perform actions to finalize record after parsing is over + * + * @param parser + * + * @param record + * @return + * @throws BufrObsDecodeException + */ + protected ObsCommon finalizeRecord(BufrParser parser, ObsCommon record) + throws BufrObsDecodeException { + return record; + } + + /** + * Lookup translation table for tableId. Results are cached. + * + * @param tableId + * @return + * @throws BufrObsDecodeException + */ + protected TranslationTable getTranslationTable(String tableId) + throws BufrObsDecodeException { + TranslationTable rval; + synchronized (translationMap) { + rval = translationMap.get(tableId); + if (rval == null) { + IPathManager pathMgr = PathManagerFactory.getPathManager(); + LocalizationContext edexStaticBase = pathMgr.getContext( + LocalizationContext.LocalizationType.EDEX_STATIC, + LocalizationContext.LocalizationLevel.BASE); + String fileName = localizationTablesDirectory + File.separator + + getTranslationFile(tableId); + LocalizationFile tableFile = pathMgr.getLocalizationFile( + edexStaticBase, fileName); + if (tableFile == null) { + throw new BufrObsDecodeException( + "Unable to find localization file for BUFR translation table: " + + tableId); + } + try { + TranslationTableManager instance = TranslationTableManager + .getInstance(); + rval = (TranslationTable) instance + .unmarshalFromInputStream(tableFile + .openInputStream()); + translationMap.put(tableId, rval); + } catch (Exception e) { + throw new BufrObsDecodeException( + "Problem creating BUFR translation table: " + + tableId, e); + } + } + } + return rval; + } + + /** + * Get obs-type specific translation table file name + * + * @param tableId + * @return + */ + abstract protected String getTranslationFile(String tableId); + + /** + * Get obs-type specific parameter alias file name + * + * @return + */ + abstract protected String getAliasMapFile(); + + /** + * Get obs-type specific category file name + * + * @return + */ + abstract protected String getCategoryFile(); + + /** + * Get obs-type specific parameter alias mapper + * + * @return + * @throws BufrObsDecodeException + */ + protected BufrMapper getMapper() throws BufrObsDecodeException { + if (mapper == null) { + synchronized (mapperMutex) { + if (mapper == null) { + IPathManager pathMgr = PathManagerFactory.getPathManager(); + File aliasFile = pathMgr + .getStaticFile(localizationAliasDirectory + + File.separator + getAliasMapFile()); + try { + mapper = new BufrMapper(Arrays.asList(aliasFile)); + } catch (JAXBException e) { + throw new BufrObsDecodeException( + "Unable to create alias map", e); + } + } + } + } + return mapper; + } + + /** + * Locate time field in parser and populate time fields in record + * + * @param record + * @param parser + * @throws BufrObsDecodeException + */ + private void setTime(ObsCommon record, BufrParser parser) + throws BufrObsDecodeException { + BufrDataItem timeData = parser.scanForStructField(TIME_FIELD, false); + if (timeData == null) { + throw new MissingRequiredDataException( + "Missing time field value in BUFR file: " + + parser.getFile()); + } + try { + Variable var = timeData.getVariable(); + Calendar cal = BufrTimeFieldParser.processTimeField( + timeData.getValue(), var.getUnitsString()); + record.setDataTime(new DataTime(cal)); + record.setTimeObs(cal); + record.setRefHour(TimeTools.copyToNearestHour(cal)); + } catch (TimeFieldParseException e) { + throw new BufrObsDecodeException("Problem parsing time field: " + + e.getLocalizedMessage() + " in " + parser.getFile(), e); + } + } + + /** + * Handles location-specific fields for obs + * + * @param location + * @param parser + * @param baseName + * @throws BufrObsDecodeException + */ + protected void processLocationField(SurfaceObsLocation location, + BufrParser parser, String baseName) throws BufrObsDecodeException { + if (baseName.equalsIgnoreCase(SfcObsPointDataTransform.STATION_ID)) { + location.setStationId(createStationId(parser)); + } else if (baseName.equalsIgnoreCase(SfcObsPointDataTransform.LATITUDE)) { + Double lat = (Double) getFieldValue(parser, false); + if (lat != null) { + location.assignLatitude(lat); + } + } else if (baseName + .equalsIgnoreCase(SfcObsPointDataTransform.LONGITUDE)) { + Double lon = (Double) getFieldValue(parser, false); + if (lon != null) { + location.assignLongitude(lon); + } + } else if (baseName + .equalsIgnoreCase(SfcObsPointDataTransform.ELEVATION)) { + Number elevation = getInUnits(parser, SI.METER); + if (elevation != null) { + location.setElevation(elevation.intValue()); + } + } else { + log.warn("Unknown location base name: " + baseName); + } + } + + /** + * Find and format WMO Index to stationId + * + * @param parser + * @return null if no station id found + * @throws BufrObsDecodeException + */ + protected String createStationId(BufrParser parser) + throws BufrObsDecodeException { + /* WMO indexes have two parts, block and id */ + Number id = (Number) getFieldValue(parser, false); + if (id == null) { + log.debug("BUFR file " + parser.getFile() + + " missing station id field: " + parser.getFieldName()); + return null; + } + Number block = getWMOBlock(parser); + if (block == null) { + log.debug("BUFR file " + parser.getFile() + + " missing station id field: " + WMO_BLOCK_FIELD); + return null; + } + + return String.format(WMO_INDEX_FORMAT, block, id); + } + + /** + * Find WMO block number in parser + * + * @param parser + * @return null if not found + */ + protected Number getWMOBlock(BufrParser parser) { + BufrDataItem data = parser.scanForStructField(WMO_BLOCK_FIELD, false); + if (data == null) { + return null; + } + return (Number) data.getValue(); + } + + /** + * {@link BufrParser#getFieldScalarValue(boolean)} + * + * @param parser + * @param charArrayAsString + * @return + * @throws BufrObsDecodeException + */ + protected Object getFieldValue(BufrParser parser, boolean charArrayAsString) + throws BufrObsDecodeException { + try { + return parser.getFieldScalarValue(charArrayAsString); + } catch (IOException e) { + throw new BufrObsDecodeException( + "Problem getting value for field: " + parser.getFieldName(), + e); + } + } + + /** + * Get field and convert to specified units + * + * @param parser + * @param toUnit + * @return + * @throws BufrObsDecodeException + */ + protected Number getInUnits(BufrParser parser, Unit toUnit) + throws BufrObsDecodeException { + Object obj = getFieldValue(parser, false); + if (obj == null) { + /* missing value */ + return null; + } + return getInUnits(parser.getFieldVariable(), obj, toUnit); + } + + /** + * Convert provided value to specified units + * + * @param var + * @param obj + * @param toUnit + * @return + * @throws BufrObsDecodeException + */ + protected Number getInUnits(Variable var, Object obj, Unit toUnit) + throws BufrObsDecodeException { + Number rval; + if (obj instanceof Number) { + Number value = (Number) obj; + Unit fromUnit = getSourceUnit(var, toUnit); + UnitConverter converter = fromUnit.getConverterTo(toUnit); + rval = converter.convert(value.doubleValue()); + } else { + log.error("Attempted to convert non-numeric value '" + obj + + "' for field: " + var.getFullName()); + rval = null; + } + return rval; + } + + /** + * Get units used in BUFR file + * + * @param var + * @param toUnit + * @return + * @throws BufrObsDecodeException + */ + protected Unit getSourceUnit(Variable var, Unit toUnit) + throws BufrObsDecodeException { + String fieldUnits = var.getUnitsString(); + Set> results; + try { + results = unitMapper.lookupCompatibleUnits(fieldUnits, + UDUNITS_NAMESPACE, toUnit); + if (results.isEmpty()) { + throw new UnitLookupException( + "No compatible unit mapping found for " + fieldUnits + + " to " + toUnit); + } + } catch (UnitLookupException e) { + throw new BufrObsDecodeException( + "Unable to convert units for field " + var.getFullName() + + ": " + fieldUnits, e); + } + return results.iterator().next(); + } + + /** + * Get mapping of category keys to report type integers. Caches result. + * + * @return + * @throws BufrObsDecodeException + */ + public Map getReportTypeMap() + throws BufrObsDecodeException { + if (reportTypeMap == null) { + synchronized (reportTypeMutex) { + if (reportTypeMap == null) { + reportTypeMap = getReportMapInternal(); + } + } + } + return reportTypeMap; + } + + /** + * Create mapping of category keys to report type integers + * + * @return + * @throws BufrObsDecodeException + */ + private Map getReportMapInternal() + throws BufrObsDecodeException { + try { + return CategoryParser.getReportTypeMap(getCategoryFile()); + } catch (Exception e) { + throw new BufrObsDecodeException(e.getLocalizedMessage(), e); + } + } + + /** + * Process fields that are indexes into BUFR code tables. + * + * @param record + * @param parser + * @param baseName + * @throws BufrObsDecodeException + */ + protected void processLookupTableField(ObsCommon record, BufrParser parser, + String baseName) throws BufrObsDecodeException { + Map paramMap = getParameterDescriptionMap(); + ParameterDescription desc = paramMap.get(baseName); + if (desc == null) { + log.error("Found field without parameter description: " + baseName); + return; + } + Object value = getFieldValue(parser, false); + String entryValue = null; + if (value != null) { + if (!(value instanceof Number)) { + log.error("Found non-numeric table value. Field: " + + parser.getFieldName() + ", Table: " + + parser.getFieldUnits() + ", value: " + value); + return; + } + entryValue = lookupTableEntry(parser, baseName, + ((Number) value).intValue()); + } + PointDataView pdv = record.getPointDataView(); + switch (desc.getType()) { + case INT: + int ival; + if (entryValue != null) { + ival = Integer.valueOf(entryValue); + } else { + ival = SfcObsPointDataTransform.INT_DEFAULT; + } + pdv.setInt(baseName, ival); + break; + case STRING: + pdv.setString(baseName, entryValue); + break; + default: + log.error("Unsupported type for field: " + baseName); + } + } + + /** + * Find parameter value for index key in translation table for field + * + * @param parser + * @param baseName + * @param key + * @return null if not found + * @throws BufrObsDecodeException + */ + protected String lookupTableEntry(BufrParser parser, String baseName, + int key) throws BufrObsDecodeException { + ParsedTableUnit tableUnit = ParsedTableUnit.parse(parser + .getFieldUnits()); + TranslationTable transTable = getTranslationTable(tableUnit + .getTableId()); + TableType type = tableUnit.getType(); + if (!TableType.CODE.equals(type)) { + log.warn("Unsupported table type for field " + baseName + ": " + + type + ". Only first matched value will be used"); + } + List entries = transTable.lookupEntries(key, type); + MappedValue match = null; + /* only flag tables return more than one entry */ + for (TableEntry entry : entries) { + /* there are usually less than 3 mapped values in an entry */ + for (MappedValue value : entry.getMappedValues()) { + if (value.getParameter().equalsIgnoreCase(baseName)) { + match = value; + break; + } + } + } + String rval = null; + if (match != null) { + rval = match.getValue(); + } else { + log.warn("Translation table " + transTable.getBufrTable() + + " missing mapped value for field: " + baseName); + } + return rval; + } + + /** + * Process fields that have quantitative values with associated units + * + * @param record + * @param parser + * @param baseName + * @throws BufrObsDecodeException + */ + protected void processDirectField(ObsCommon record, BufrParser parser, + String baseName) throws BufrObsDecodeException { + Map paramMap = getParameterDescriptionMap(); + ParameterDescription desc = paramMap.get(baseName); + if (desc == null) { + log.error("Found field without parameter description: " + baseName); + return; + } + Object value = getParameterValue(desc, parser); + PointDataView pdv = record.getPointDataView(); + switch (desc.getType()) { + case FLOAT: + float fval; + if (value != null && value instanceof Number) { + fval = ((Number) value).floatValue(); + } else { + fval = SfcObsPointDataTransform.FLOAT_DEFAULT; + } + pdv.setFloat(baseName, fval); + break; + case INT: + int ival; + if (value != null && value instanceof Number) { + ival = ((Number) value).intValue(); + } else { + ival = SfcObsPointDataTransform.INT_DEFAULT; + } + pdv.setInt(baseName, ival); + break; + case STRING: + pdv.setString(baseName, String.valueOf(value)); + break; + default: + log.error("Unsupported type for field: " + baseName); + } + } + + /** + * Get field value given point data view parameter description details (ie + * units) + * + * @param desc + * @param parser + * @return + * @throws BufrObsDecodeException + */ + protected Object getParameterValue(ParameterDescription desc, + BufrParser parser) throws BufrObsDecodeException { + Unit toUnit = null; + if (desc.getUnit() != null) { + toUnit = Unit.valueOf(desc.getUnit()); + } + Object value; + if (toUnit != null) { + value = getInUnits(parser, toUnit); + } else { + value = getFieldValue(parser, false); + } + return value; + } + + /** + * @return set of category keys that this parser handles + * @throws BufrObsDecodeException + */ + public Set getCategoryKeys() throws BufrObsDecodeException { + return getReportTypeMap().keySet(); + } + + /** + * @return the dao + */ + public SfcObsDao getDao() { + return dao; + } + + /** + * @return the pluginName + */ + public String getPluginName() { + return pluginName; + } + + /** + * @return the description + */ + public PointDataDescription getDescription() { + return description; + } + + /** + * @return the parameterDescriptionMap + */ + public Map getParameterDescriptionMap() { + return parameterDescriptionMap; + } + +} diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/BufrObsDecodeException.java b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/BufrObsDecodeException.java new file mode 100644 index 0000000000..0d321a4481 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/BufrObsDecodeException.java @@ -0,0 +1,70 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.edex.plugin.bufrobs; + +/** + * Exception thrown when a BUFR obs cannot be decoded + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Apr 2, 2014  2906      bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class BufrObsDecodeException extends Exception { + + private static final long serialVersionUID = 530575366878300860L; + + /** + * + */ + public BufrObsDecodeException() { + } + + /** + * @param message + */ + public BufrObsDecodeException(String message) { + super(message); + } + + /** + * @param cause + */ + public BufrObsDecodeException(Throwable cause) { + super(cause); + } + + /** + * @param message + * @param cause + */ + public BufrObsDecodeException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/BufrObsProcessor.java b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/BufrObsProcessor.java new file mode 100644 index 0000000000..9cf61cd242 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/BufrObsProcessor.java @@ -0,0 +1,170 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.edex.plugin.bufrobs; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import ucar.nc2.Attribute; +import ucar.nc2.NetcdfFile; + +import com.raytheon.uf.common.dataplugin.PluginDataObject; +import com.raytheon.uf.common.nc.bufr.BufrParser; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.edex.plugin.bufrobs.category.CategoryKey; + +/** + * Entry point for BUFR obs decoding. Determines the correct decoder instance + * using BUFR attributes. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Apr 1, 2014  2906      bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class BufrObsProcessor { + + protected static final IUFStatusHandler log = UFStatus + .getHandler(BufrObsProcessor.class); + + public static final String BUFR_CAT_ATTRIBUTE = "BUFR:category"; + + public static final String BUFR_SUBCAT_ATTRIBUTE = "BUFR:subCategory"; + + private static final List decoders = new ArrayList(); + + /** + * @param bufrFile + * @return + * @throws BufrObsDecodeException + */ + public PluginDataObject[] process(File bufrFile) + throws BufrObsDecodeException { + PluginDataObject[] rval; + try { + BufrParser parser = new BufrParser(bufrFile); + CategoryKey key = getBufrCategory(parser); + AbstractBufrSfcObsDecoder decoder = getDecoder(key); + if (decoder == null) { + throw new BufrObsDecodeException( + "Unable to find decoder for file: " + + bufrFile.getAbsolutePath() + ". Category: " + + key.getCategory() + ", Subcategory: " + + key.getSubcategory()); + } else { + rval = decoder.decode(parser, key); + } + } catch (IOException e) { + throw new BufrObsDecodeException("Unable to read BUFR file: " + + bufrFile, e); + } + return rval; + } + + /** + * Get category key from BUFR file + * + * @param parser + * @return + * @throws BufrObsDecodeException + */ + private CategoryKey getBufrCategory(BufrParser parser) + throws BufrObsDecodeException { + int cat = getIntAttribute(parser, BUFR_CAT_ATTRIBUTE); + int subcat = getIntAttribute(parser, BUFR_SUBCAT_ATTRIBUTE); + return new CategoryKey(cat, subcat); + } + + /** + * Get integer global attribute from BUFR file with specified name + * + * @param parser + * @param name + * @return + * @throws BufrObsDecodeException + */ + private int getIntAttribute(BufrParser parser, String name) + throws BufrObsDecodeException { + NetcdfFile ncfile = parser.getNcfile(); + Attribute attrib = ncfile.findGlobalAttributeIgnoreCase(name); + if (attrib == null) { + throw new BufrObsDecodeException("BUFR file " + parser.getFile() + + " missing required attribute: " + name); + } + Number num = attrib.getNumericValue(); + if (num == null) { + throw new BufrObsDecodeException("BUFR file " + parser.getFile() + + " unexpected type for attribute: " + name); + } + return num.intValue(); + } + + /** + * Registers decoder with processor + * + * @param decoder + * @return + */ + public BufrObsProcessor register(AbstractBufrSfcObsDecoder decoder) { + synchronized (decoders) { + decoders.add(decoder); + } + return this; + } + + /** + * Find the first decoder that supports category key + * + * @param key + * @return + */ + public AbstractBufrSfcObsDecoder getDecoder(CategoryKey key) { + AbstractBufrSfcObsDecoder rval = null; + synchronized (decoders) { + for (AbstractBufrSfcObsDecoder decoder : decoders) { + Set categoryKeys; + try { + categoryKeys = decoder.getCategoryKeys(); + } catch (BufrObsDecodeException e) { + log.error("Problem getting categories from decoder: " + + decoder, e); + continue; + } + if (categoryKeys.contains(key)) { + rval = decoder; + break; + } + } + } + return rval; + } +} diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/MissingRequiredDataException.java b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/MissingRequiredDataException.java new file mode 100644 index 0000000000..2b73de44b4 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/MissingRequiredDataException.java @@ -0,0 +1,70 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.edex.plugin.bufrobs; + +/** + * Exception thrown when BUFR obs is missing required data fields + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Apr 10, 2014 2906       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class MissingRequiredDataException extends BufrObsDecodeException { + + private static final long serialVersionUID = -7605144038224407338L; + + /** + * + */ + public MissingRequiredDataException() { + } + + /** + * @param message + */ + public MissingRequiredDataException(String message) { + super(message); + } + + /** + * @param cause + */ + public MissingRequiredDataException(Throwable cause) { + super(cause); + } + + /** + * @param message + * @param cause + */ + public MissingRequiredDataException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/category/CategoryKey.java b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/category/CategoryKey.java new file mode 100644 index 0000000000..70376551f5 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/category/CategoryKey.java @@ -0,0 +1,103 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.edex.plugin.bufrobs.category; + +/** + * Unique key for BUFR categories from Table A + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Apr 1, 2014  2906      bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class CategoryKey { + + private final int category; + + private final int subcategory; + + /** + * @param category + * @param subcategory + */ + public CategoryKey(int category, int subcategory) { + this.category = category; + this.subcategory = subcategory; + } + + /** + * @return the category + */ + public int getCategory() { + return category; + } + + + /** + * @return the subcategory + */ + public int getSubcategory() { + return subcategory; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + category; + result = prime * result + subcategory; + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CategoryKey other = (CategoryKey) obj; + if (category != other.category) + return false; + if (subcategory != other.subcategory) + return false; + return true; + } + +} diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/category/CategoryParser.java b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/category/CategoryParser.java new file mode 100644 index 0000000000..f5a356380b --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/category/CategoryParser.java @@ -0,0 +1,154 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.edex.plugin.bufrobs.category; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import com.raytheon.uf.common.localization.IPathManager; +import com.raytheon.uf.common.localization.LocalizationContext; +import com.raytheon.uf.common.localization.LocalizationFile; +import com.raytheon.uf.common.localization.PathManagerFactory; +import com.raytheon.uf.common.localization.exception.LocalizationException; + +/** + * Parsing utility for category to report type mapping configuration + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Apr 1, 2014  2906      bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class CategoryParser { + + private static final XMLInputFactory factory = XMLInputFactory + .newInstance(); + + public static final String localizationCategoryDirectory = "bufrobs" + + File.separator + "category"; + + private static final String CAT_TAG = "category"; + + private static final String SUBCAT_TAG = "subcategory"; + + private static final String CODE_ATTRIB = "code"; + + private static final String REPORT_TYPE_ATTRIB = "reportType"; + + private CategoryParser() { + } + + /** + * Read XML configuration mapping category keys to report type integers from + * localization + * + * @param categoryFileName + * @return + * @throws XMLStreamException + * @throws LocalizationException + */ + public static Map getReportTypeMap( + String categoryFileName) throws XMLStreamException, + LocalizationException { + LocalizationFile file = getLocalizedCategoryFile(categoryFileName); + if ( file == null){ + return null; + } + Map rval = new HashMap(); + XMLStreamReader reader = factory.createXMLStreamReader(file + .openInputStream()); + try { + int currentCategoryCode = -1; + while (reader.hasNext()) { + switch (reader.next()) { + case XMLStreamReader.START_ELEMENT: + QName name = reader.getName(); + if (name.getLocalPart().equalsIgnoreCase(CAT_TAG)) { + currentCategoryCode = getIntAttrib(reader, CODE_ATTRIB); + } else if (name.getLocalPart().equalsIgnoreCase(SUBCAT_TAG)) { + int subCatCode = getIntAttrib(reader, CODE_ATTRIB); + int reportType = getIntAttrib(reader, + REPORT_TYPE_ATTRIB); + rval.put(new CategoryKey(currentCategoryCode, + subCatCode), reportType); + } + } + } + } finally { + reader.close(); + } + return rval; + } + + /** + * Get integer attribute from STAX parser element + * + * @param reader + * @param name + * @return + * @throws XMLStreamException + */ + private static int getIntAttrib(XMLStreamReader reader, String name) + throws XMLStreamException { + String valStr = reader.getAttributeValue(null, name); + if (valStr == null) { + throw new XMLStreamException("Tag " + reader.getName() + + " missing required attribute: " + name); + } + try { + return Integer.valueOf(valStr); + } catch (Exception e) { + throw new XMLStreamException("Tag " + reader.getName() + + " invalid integer value for attribute: " + name); + } + } + + /** + * Get category XML configuration from localization + * + * @param categoryFileName + * @return + */ + public static LocalizationFile getLocalizedCategoryFile( + String categoryFileName) { + IPathManager pathMgr = PathManagerFactory.getPathManager(); + LocalizationContext edexStaticBase = pathMgr.getContext( + LocalizationContext.LocalizationType.EDEX_STATIC, + LocalizationContext.LocalizationLevel.BASE); + String fileName = localizationCategoryDirectory + File.separator + + categoryFileName; + return pathMgr.getLocalizationFile(edexStaticBase, fileName); + } + +} diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/synoptic/SynopticLandBufrDecoder.java b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/synoptic/SynopticLandBufrDecoder.java new file mode 100644 index 0000000000..bd21e7d799 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/synoptic/SynopticLandBufrDecoder.java @@ -0,0 +1,338 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.edex.plugin.bufrobs.synoptic; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.measure.unit.NonSI; +import javax.measure.unit.SI; + +import com.raytheon.edex.plugin.sfcobs.SfcObsPointDataTransform; +import com.raytheon.uf.common.dataplugin.PluginException; +import com.raytheon.uf.common.dataplugin.sfcobs.ObsCommon; +import com.raytheon.uf.common.nc.bufr.BufrDataItem; +import com.raytheon.uf.common.nc.bufr.BufrParser; +import com.raytheon.uf.common.nc.bufr.util.BufrMapper; +import com.raytheon.uf.common.nc.bufr.util.TranslationTableGenerator; +import com.raytheon.uf.common.pointdata.PointDataView; +import com.raytheon.uf.common.pointdata.spatial.ObStation; +import com.raytheon.uf.common.pointdata.spatial.SurfaceObsLocation; +import com.raytheon.uf.common.serialization.SerializationException; +import com.raytheon.uf.edex.database.DataAccessLayerException; +import com.raytheon.uf.edex.decodertools.core.IDecoderConstants; +import com.raytheon.uf.edex.plugin.bufrobs.AbstractBufrSfcObsDecoder; +import com.raytheon.uf.edex.plugin.bufrobs.BufrObsDecodeException; +import com.raytheon.uf.edex.plugin.bufrobs.MissingRequiredDataException; +import com.raytheon.uf.edex.pointdata.spatial.ObStationDao; +import com.vividsolutions.jts.geom.Point; + +/** + * Synoptic Land type decoder for BUFR observations. Handles fixed and mobile + * obs. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Mar 21, 2014 2906       bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class SynopticLandBufrDecoder extends AbstractBufrSfcObsDecoder { + + public static final Set LOCATION_FIELDS = new HashSet( + Arrays.asList(SfcObsPointDataTransform.LATITUDE, + SfcObsPointDataTransform.LONGITUDE, + SfcObsPointDataTransform.STATION_ID, + SfcObsPointDataTransform.ELEVATION)); + + public static final Set SUB_STRUCT_FIELDS = new HashSet( + Arrays.asList(SfcObsPointDataTransform.WIND_GUST)); + + public static final String SYNOPTIC_LAND_NAMESPACE = "synoptic_land"; + + public static final String ALIAS_FILE_NAME = SYNOPTIC_LAND_NAMESPACE + + "-alias.xml"; + + public static final String CATEGORY_FILE_NAME = SYNOPTIC_LAND_NAMESPACE + + "-category.xml"; + + public static final String PRECIP_FIELD = "precip"; + + public static final String TIME_PERIOD_FIELD = "Time period or displacement"; + + private final ObStationDao stationDao = new ObStationDao(); + + /** + * @param pluginName + * @throws BufrObsDecodeException + * @throws PluginException + * @throws SerializationException + */ + public SynopticLandBufrDecoder(String pluginName) + throws BufrObsDecodeException { + super(pluginName); + } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.edex.plugin.bufrobs.AbstractBufrObsDecoder#processField + * (com.raytheon.uf.common.dataplugin.sfcobs.ObsCommon, + * com.raytheon.uf.common.nc.bufr.BufrParser, int) + */ + @Override + protected void processField(ObsCommon record, BufrParser parser) + throws BufrObsDecodeException { + BufrMapper mapper = getMapper(); + String bufrName = parser.getFieldName(); + Set baseNames = mapper.lookupBaseNamesOrEmpty(bufrName, + SYNOPTIC_LAND_NAMESPACE); + if (baseNames.isEmpty()) { + log.debug("Skipping unmapped field: " + bufrName); + } + int level = parser.getStructLevel(); + for (String baseName : baseNames) { + if (level == 1) { + /* process top level fields */ + if (LOCATION_FIELDS.contains(baseName)) { + processLocationField(record.getLocation(), parser, baseName); + } else { + processGeneralFields(record, parser, baseName); + } + } else if (level > 1) { + /* process substructure fields */ + if (PRECIP_FIELD.equalsIgnoreCase(baseName)) { + processPrecip(record, parser); + } else if (SUB_STRUCT_FIELDS.contains(baseName)) { + processGeneralFields(record, parser, baseName); + } + } + } + } + + /** + * Determines if field is table lookup or direct and handles accordingly + * + * @param record + * @param parser + * @param baseName + * @throws BufrObsDecodeException + */ + protected void processGeneralFields(ObsCommon record, BufrParser parser, + String baseName) throws BufrObsDecodeException { + String unitStr = parser.getFieldUnits(); + if (unitStr != null && isTableLookup(unitStr)) { + processLookupTableField(record, parser, baseName); + } else { + processDirectField(record, parser, baseName); + } + } + + /** + * Precip doesn't have individual fields for different hour periods. One + * structure contains the general precip field with a time period. This + * method parses the time period to determine what field the precip data is + * for. + * + * @param record + * @param parser + * @throws BufrObsDecodeException + */ + protected void processPrecip(ObsCommon record, BufrParser parser) + throws BufrObsDecodeException { + Number precip = getInUnits(parser, SI.MILLIMETER); + if (precip == null) { + /* missing value */ + return; + } + BufrDataItem timeData = parser.scanForStructField(TIME_PERIOD_FIELD, + false); + if (timeData == null || timeData.getValue() == null) { + log.error("Found precipitation field missing time data. Field: " + + parser.getFieldName() + ", Table: " + + parser.getFieldUnits()); + return; + } + Number period = getInUnits(timeData.getVariable(), timeData.getValue(), + NonSI.HOUR); + PointDataView pdv = record.getPointDataView(); + switch (Math.abs(period.intValue())) { + case 1: + pdv.setFloat(SfcObsPointDataTransform.PRECIP1_HOUR, + precip.intValue()); + break; + case 3: + /* text synop obs doesn't have 3HR precip */ + log.debug("Received precip period not supported by point data view: " + + Math.abs(period.intValue()) + " HOUR"); + break; + case 6: + pdv.setFloat(SfcObsPointDataTransform.PRECIP6_HOUR, + precip.intValue()); + break; + case 12: + pdv.setFloat(SfcObsPointDataTransform.PRECIP12_HOUR, + precip.intValue()); + break; + case 18: + pdv.setFloat(SfcObsPointDataTransform.PRECIP18_HOUR, + precip.intValue()); + break; + case 24: + pdv.setFloat(SfcObsPointDataTransform.PRECIP24_HOUR, + precip.intValue()); + break; + default: + log.error("Unknown precipitation period '" + period.intValue() + + "' in BUFR file " + parser.getFile()); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.edex.plugin.bufrobs.AbstractBufrSfcObsDecoder#finalizeRecord + * (com.raytheon.uf.common.nc.bufr.BufrParser, + * com.raytheon.uf.common.dataplugin.sfcobs.ObsCommon) + */ + @Override + protected ObsCommon finalizeRecord(BufrParser parser, ObsCommon record) + throws BufrObsDecodeException { + record = super.finalizeRecord(parser, record); + finalizePresentWeather(parser, record); + finalizeLocation(parser, record); + return record; + } + + /** + * perform any finishing actions on the present weather field + * + * @param parser + * @param record + */ + protected void finalizePresentWeather(BufrParser parser, + ObsCommon record) { + /* this code comes from the abstract synoptic text decoder */ + // Fixup the present weather string + PointDataView pdv = record.getPointDataView(); + int pWx = pdv.getInt(SfcObsPointDataTransform.WX_PRESENT); + if (pWx != SfcObsPointDataTransform.INT_DEFAULT) { + if (pWx == 28 || (pWx > 41 && pWx < 48)) { + float t = pdv.getFloat(SfcObsPointDataTransform.TEMPERATURE); + if (t != SfcObsPointDataTransform.FLOAT_DEFAULT) { + String wx = (t > 273.15) ? "FG" : "FZFG"; + pdv.setString(SfcObsPointDataTransform.PRES_WEATHER, wx); + } + } + } + } + + /** + * perform any finishing actions on the location field + * + * @param parser + * @param record + * @throws BufrObsDecodeException + */ + protected void finalizeLocation(BufrParser parser, ObsCommon record) + throws BufrObsDecodeException { + SurfaceObsLocation location = record.getLocation(); + Point lonlat = location.getLocation(); + String stationId = location.getStationId(); + if (lonlat == null && stationId == null) { + throw new MissingRequiredDataException( + "Record missing location information in BUFR file: " + + parser.getFile()); + } else if (lonlat == null) { + log.debug("Getting station info from database for BUFR file: " + + parser.getFile()); + try { + Integer type = ObStation.CAT_TYPE_SFC_FXD; + if (record.getReportType() == IDecoderConstants.SYNOPTIC_MOBILE_LAND) { + type = ObStation.CAT_TYPE_SFC_MOB; + } + String gid = ObStation.createGID(type, stationId); + ObStation station = stationDao.queryByGid(gid); + if (station == null) { + throw new MissingRequiredDataException( + "Record missing location information in BUFR file: " + + parser.getFile()); + } + location.setElevation(station.getElevation()); + location.setLocation(station.getLocation()); + } catch (DataAccessLayerException e) { + throw new BufrObsDecodeException( + "Problem querying the database for location", e); + } + } else if (stationId == null) { + log.debug("Generating station id from location for BUFR file: " + + parser.getFile()); + location.generateCoordinateStationId(); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.edex.plugin.bufrobs.AbstractBufrObsDecoder#getAliasMapFile + * () + */ + @Override + protected String getAliasMapFile() { + return ALIAS_FILE_NAME; + } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.edex.plugin.bufrobs.AbstractBufrObsDecoder#getTranslationFile + * (java.lang.String) + */ + @Override + protected String getTranslationFile(String tableId) { + tableId = TranslationTableGenerator.replaceWhiteSpace(tableId, "_"); + return SYNOPTIC_LAND_NAMESPACE + "-" + tableId + ".xml"; + } + + /* + * (non-Javadoc) + * + * @see + * com.raytheon.uf.edex.plugin.bufrobs.AbstractBufrObsDecoder#getCategoryFile + * () + */ + @Override + protected String getCategoryFile() { + return CATEGORY_FILE_NAME; + } + +} diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/util/RecordPopulator.java b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/util/RecordPopulator.java new file mode 100644 index 0000000000..adb3b19c60 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/src/com/raytheon/uf/edex/plugin/bufrobs/util/RecordPopulator.java @@ -0,0 +1,170 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.edex.plugin.bufrobs.util; + +import com.raytheon.edex.plugin.sfcobs.SfcObsPointDataTransform; +import com.raytheon.uf.common.dataplugin.PluginDataObject; +import com.raytheon.uf.common.dataplugin.sfcobs.ObsCommon; +import com.raytheon.uf.common.pointdata.PointDataView; + +/** + * Utility to populate an obs record using the point data view. This is + * necessary because the shef converter looks for data in the record instead of + * the point data view. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Apr 4, 2014  2906      bclement     Initial creation
+ * 
+ * 
+ * + * @author bclement + * @version 1.0 + */ +public class RecordPopulator { + + SfcObsPointDataTransform transform = new SfcObsPointDataTransform(); + + /** + * + */ + public RecordPopulator() { + } + + /** + * @param records + * @return + */ + public PluginDataObject[] populate(PluginDataObject[] records) { + for (PluginDataObject pdo : records) { + if (pdo instanceof ObsCommon) { + populateObs((ObsCommon) pdo); + } + } + return records; + } + + /** + * Populate record fields using the point data view + * + * @param obs + */ + public static void populateObs(ObsCommon obs) { + PointDataView pdv = obs.getPointDataView(); + obs.setTemp(pdv.getNumber(SfcObsPointDataTransform.TEMPERATURE) + .doubleValue()); + obs.setDwpt(pdv.getNumber(SfcObsPointDataTransform.DEWPOINT) + .doubleValue()); + obs.setWindSpeed(pdv.getNumber(SfcObsPointDataTransform.WIND_SPEED) + .doubleValue()); + obs.setWindDirection(pdv.getNumber(SfcObsPointDataTransform.WIND_DIR) + .intValue()); + obs.setWindGust(pdv.getNumber(SfcObsPointDataTransform.WIND_GUST) + .doubleValue()); + obs.setPeakWindSpeed(pdv.getNumber( + SfcObsPointDataTransform.PEAK_WIND_SPEED).doubleValue()); + obs.setPeakWindTime(pdv.getNumber( + SfcObsPointDataTransform.PEAK_WIND_SPEED_TIME).longValue()); + obs.setPeakWindDir(pdv + .getNumber(SfcObsPointDataTransform.PEAK_WIND_DIR).intValue()); + + obs.setPressureSealevel(pdv.getNumber( + SfcObsPointDataTransform.SEA_LEVEL_PRESS).intValue()); + obs.setPressureAltimeter(pdv.getNumber( + SfcObsPointDataTransform.ALTIMETER).intValue()); + obs.setPressureStation(pdv.getNumber( + SfcObsPointDataTransform.STATION_PRESS).intValue()); + obs.setPressChange3Hr(pdv.getNumber( + SfcObsPointDataTransform.PRESS_CHANGE_3HR).doubleValue()); + obs.setPressChangeChar(pdv.getNumber( + SfcObsPointDataTransform.PRESS_CHANGE_CHAR).intValue()); + + obs.setHorzVisibility(pdv + .getNumber(SfcObsPointDataTransform.VISIBILITY).intValue()); + obs.setWx_present(pdv.getNumber(SfcObsPointDataTransform.WX_PRESENT) + .intValue()); + obs.setPresWeather(pdv.getString(SfcObsPointDataTransform.PRES_WEATHER)); + obs.setWx_past_1(pdv.getNumber(SfcObsPointDataTransform.WX_PAST_1) + .intValue()); + obs.setWx_past_2(pdv.getNumber(SfcObsPointDataTransform.WX_PAST_2) + .intValue()); + + obs.setTotalCloudCover(pdv.getNumber( + SfcObsPointDataTransform.CLOUD_AMOUNT_TOT).intValue()); + obs.setCloudBaseHeight(pdv.getNumber( + SfcObsPointDataTransform.CLOUD_HGT_LOW).intValue()); + obs.setLowCloudType(pdv.getNumber( + SfcObsPointDataTransform.CLOUD_TYPE_LOW).intValue()); + obs.setMidCloudType(pdv.getNumber( + SfcObsPointDataTransform.CLOUD_TYPE_MID).intValue()); + obs.setHighCloudType(pdv.getNumber( + SfcObsPointDataTransform.CLOUD_TYPE_HI).intValue()); + + obs.setWind10mSpeed(pdv.getNumber( + SfcObsPointDataTransform.WIND_SPD_EQUIV_10M).doubleValue()); + obs.setWind20mSpeed(pdv.getNumber( + SfcObsPointDataTransform.WIND_SPD_EQUIV_20M).doubleValue()); + + // pdv.setInt(ICE_CODE,record.getIceCode); + obs.setSeaTemp(pdv.getNumber(SfcObsPointDataTransform.SEA_SFC_TEMP) + .doubleValue()); + obs.setWetBulb(pdv.getNumber(SfcObsPointDataTransform.WET_BULB) + .doubleValue()); + + obs.setPlatformDirection(pdv.getNumber( + SfcObsPointDataTransform.PLATFORM_DIR).intValue()); + obs.setPlatformMovement(pdv.getNumber( + SfcObsPointDataTransform.PLATFORM_SPD).doubleValue()); + + obs.setWindWaveHeight(pdv.getNumber( + SfcObsPointDataTransform.WIND_WV_HGT).doubleValue()); + obs.setWindWavePeriod(pdv + .getNumber(SfcObsPointDataTransform.WIND_WV_PD).intValue()); + + obs.setWaveHeight(pdv.getNumber(SfcObsPointDataTransform.WV_HGT) + .doubleValue()); + obs.setWavePeriod(pdv.getNumber(SfcObsPointDataTransform.WV_PD) + .intValue()); + obs.setWaveSteepness(pdv.getNumber( + SfcObsPointDataTransform.WV_STEEPNESS).doubleValue()); + + obs.setHighResWaveHeight(pdv.getNumber( + SfcObsPointDataTransform.HI_RES_WV_HGT).doubleValue()); + + obs.setPrimarySwellWaveDir(pdv.getNumber( + SfcObsPointDataTransform.PRI_SWELL_WV_DIR).doubleValue()); + obs.setPrimarySwellWavePeriod(pdv.getNumber( + SfcObsPointDataTransform.PRI_SWELL_WV_PD).intValue()); + obs.setPrimarySwellWaveHeight(pdv.getNumber( + SfcObsPointDataTransform.PRI_SWELL_WV_HGT).doubleValue()); + + obs.setSecondarySwellWaveDir(pdv.getNumber( + SfcObsPointDataTransform.SEC_SWELL_WV_DIR).doubleValue()); + obs.setSecondarySwellWavePeriod(pdv.getNumber( + SfcObsPointDataTransform.SEC_SWELL_WV_PD).intValue()); + obs.setSecondarySwellWaveHeight(pdv.getNumber( + SfcObsPointDataTransform.SEC_SWELL_WV_HGT).doubleValue()); + } + +} diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/utility/edex_static/base/bufrobs/alias/synoptic_land-alias.xml b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/utility/edex_static/base/bufrobs/alias/synoptic_land-alias.xml new file mode 100644 index 0000000000..63c26267d2 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/utility/edex_static/base/bufrobs/alias/synoptic_land-alias.xml @@ -0,0 +1,92 @@ + + + + + WMO station number + Latitude (high accuracy) + Longitude (high accuracy) + Height of station ground above mean sea level + Temperature/dry-bulb temperature + Dew-point temperature + Wind speed + Wind direction + Maximum wind gust speed + + Pressure reduced to mean sea level + + Pressure + + Characteristic of pressure tendency + 3-hour pressure change + + Cloud amount + Height of base of cloud + + + Cloud type + + Cloud type-1 + + Cloud type-2 + Horizontal visibility + + Present weather + + Present weather + + + Total precipitation/total water equivalent + Total precipitation past 24 hours + + \ No newline at end of file diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/utility/edex_static/base/bufrobs/category/synoptic_land-category.xml b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/utility/edex_static/base/bufrobs/category/synoptic_land-category.xml new file mode 100644 index 0000000000..7c71cfce78 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/utility/edex_static/base/bufrobs/category/synoptic_land-category.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/utility/edex_static/base/distribution/bufrobs.xml b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/utility/edex_static/base/distribution/bufrobs.xml new file mode 100644 index 0000000000..1ae3cd8709 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.plugin.bufrobs/utility/edex_static/base/distribution/bufrobs.xml @@ -0,0 +1,23 @@ + + + + ^IS[IMN] +