Merge "Issue #2906 added synop bufr obs decoder" into development
Former-commit-id:0e44560636
[formerly2df73a6601
] [formerly75b5f5b658
] [formerly0e44560636
[formerly2df73a6601
] [formerly75b5f5b658
] [formerly439dc71571
[formerly75b5f5b658
[formerly d214a46f19a939eec5d23d0c9029b1e85eaad447]]]] Former-commit-id:439dc71571
Former-commit-id:444f846278
[formerly91c478cb8f
] [formerly 409268b2113f7a5a46304f6cf85517c66a4dddcb [formerly8788d46064
]] Former-commit-id: 0c66c8cdc66c3e1b5b8e466ecf0fb764e0d3544c [formerly86b146a3ff
] Former-commit-id:746c4d7908
This commit is contained in:
commit
881429f2fb
22 changed files with 2353 additions and 98 deletions
|
@ -13,7 +13,8 @@ Require-Bundle: com.raytheon.edex.common,
|
|||
javax.persistence,
|
||||
com.raytheon.uf.edex.ndm;bundle-version="1.0.0",
|
||||
com.raytheon.uf.common.localization;bundle-version="1.14.0"
|
||||
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,
|
||||
|
|
|
@ -27,11 +27,11 @@
|
|||
<parameter name="temperature" numDims="1" type="FLOAT" unit="K" />
|
||||
<parameter name="dewpoint" numDims="1" type="FLOAT" unit="K" />
|
||||
<parameter name="windSpeed" numDims="1" type="FLOAT" unit="m/sec" />
|
||||
<parameter name="windDir" numDims="1" type="FLOAT" unit="degree" />
|
||||
<parameter name="windDir" numDims="1" type="FLOAT" unit="°" />
|
||||
<parameter name="windGust" numDims="1" type="FLOAT" unit="m/sec" />
|
||||
<parameter name="peakWindSpeedTime" numDims="1" type="LONG" unit="seconds since 1-1-1970" />
|
||||
<parameter name="peakWindSpeed" numDims="1" type="FLOAT" unit="m/sec" />
|
||||
<parameter name="peakWindDir" numDims="1" type="FLOAT" unit="degree" />
|
||||
<parameter name="peakWindDir" numDims="1" type="FLOAT" unit="°" />
|
||||
<parameter name="seaLevelPress" numDims="1" type="FLOAT" unit="Pa" />
|
||||
<parameter name="altimeter" numDims="1" type="FLOAT" unit="Pa" />
|
||||
<parameter name="stationPress" numDims="1" type="FLOAT" unit="Pa" />
|
||||
|
@ -58,7 +58,7 @@
|
|||
<parameter name="iceCode" numDims="1" type="INT" />
|
||||
<parameter name="wetBulb" numDims="1" type="FLOAT" unit="K" />
|
||||
<parameter name="seaSurfaceTemp" numDims="1" type="FLOAT" unit="K" />
|
||||
<parameter name="platformTrueDirection" numDims="1" type="FLOAT" unit="degree" />
|
||||
<parameter name="platformTrueDirection" numDims="1" type="FLOAT" unit="°" />
|
||||
<parameter name="platformTrueSpeed" numDims="1" type="FLOAT" unit="m/sec" />
|
||||
<parameter name="windWaveHeight" numDims="1" type="FLOAT" unit="m" />
|
||||
<parameter name="windWavePeriod" numDims="1" type="FLOAT" unit="sec" />
|
||||
|
@ -66,10 +66,10 @@
|
|||
<parameter name="wavePeriod" numDims="1" type="FLOAT" unit="sec" />
|
||||
<parameter name="waveSteepness" numDims="1" type="FLOAT" />
|
||||
<parameter name="highResWaveHeight" numDims="1" type="FLOAT" unit="m" />
|
||||
<parameter name="primarySwellWaveDir" numDims="1" type="FLOAT" unit="degree" />
|
||||
<parameter name="primarySwellWaveDir" numDims="1" type="FLOAT" unit="°" />
|
||||
<parameter name="primarySwellWavePeriod" numDims="1" type="FLOAT" unit="sec" />
|
||||
<parameter name="primarySwellWaveHeight" numDims="1" type="FLOAT" unit="m" />
|
||||
<parameter name="secondarySwellWaveDir" numDims="1" type="FLOAT" unit="degree" />
|
||||
<parameter name="secondarySwellWaveDir" numDims="1" type="FLOAT" unit="°" />
|
||||
<parameter name="secondarySwellWavePeriod" numDims="1" type="FLOAT" unit="sec" />
|
||||
<parameter name="secondarySwellWaveHeight" numDims="1" type="FLOAT" unit="m" />
|
||||
<parameter name="numInterWinds" numDims="1" type="INT" />
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
|
@ -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
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -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) {
|
||||
|
|
|
@ -84,10 +84,12 @@ public class BufrFileSeparator {
|
|||
* @throws IOException
|
||||
*/
|
||||
public static List<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<alias base="kg*m^-2">kg m-2</alias>
|
||||
<!-- millimeters are equivalent for precipitation measurements -->
|
||||
<alias base="mm">kg m-2</alias>
|
||||
<alias base="°">Degree</alias>
|
||||
<alias base="°">Degree true</alias>
|
||||
<alias base="min">Minute</alias>
|
||||
<alias base="h">Hour</alias>
|
||||
<alias base="day">Day</alias>
|
||||
|
|
|
@ -96,4 +96,10 @@
|
|||
install-size="0"
|
||||
version="0.0.0"/>
|
||||
|
||||
<plugin
|
||||
id="ucar.nc2.bufrsplitter"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"/>
|
||||
|
||||
</feature>
|
||||
|
|
|
@ -219,13 +219,13 @@
|
|||
install-size="0"
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
|
||||
<plugin
|
||||
id="com.raytheon.uf.common.dataplugin.redbook"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
unpack="false"/>
|
||||
|
||||
<plugin
|
||||
id="com.raytheon.edex.plugin.sfcobs"
|
||||
|
@ -360,4 +360,18 @@
|
|||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
<plugin
|
||||
id="com.raytheon.uf.edex.plugin.bufrobs"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
<plugin
|
||||
id="com.raytheon.uf.common.nc.bufr"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
</feature>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
.,\
|
||||
res/,\
|
||||
utility/
|
||||
src.includes = res/,\
|
||||
utility/
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
|
||||
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
|
||||
|
||||
<bean id="bufrobsProcessor" class="com.raytheon.uf.edex.plugin.bufrobs.BufrObsProcessor" />
|
||||
|
||||
<bean id="bufrFileSeparator" class="com.raytheon.uf.common.nc.bufr.BufrFileSeparator" />
|
||||
|
||||
<bean id="bufrobsSynopticLandDecoder" class="com.raytheon.uf.edex.plugin.bufrobs.synoptic.SynopticLandBufrDecoder">
|
||||
<constructor-arg ref="sfcobsPluginName" />
|
||||
</bean>
|
||||
|
||||
<bean factory-bean="bufrobsProcessor" factory-method="register">
|
||||
<constructor-arg ref="bufrobsSynopticLandDecoder" />
|
||||
</bean>
|
||||
|
||||
<bean id="bufrobsCamelRegistered" factory-bean="contextManager" factory-method="register" depends-on="persistCamelRegistered">
|
||||
<constructor-arg ref="bufrobs-camel" />
|
||||
</bean>
|
||||
|
||||
<bean id="bufrobsDistRegistry" factory-bean="distributionSrv" factory-method="register">
|
||||
<constructor-arg value="bufrobs" />
|
||||
<constructor-arg value="jms-durable:queue:Ingest.bufrobs" />
|
||||
</bean>
|
||||
|
||||
<bean id="bufrObsRecordPopulator" class="com.raytheon.uf.edex.plugin.bufrobs.util.RecordPopulator" />
|
||||
|
||||
<camelContext id="bufrobs-camel" xmlns="http://camel.apache.org/schema/spring" errorHandlerRef="errorHandler"
|
||||
autoStartup="false">
|
||||
|
||||
<endpoint id="bufrobsInboxJmsEndpoint" uri="jms-durable:queue:Ingest.bufrobs" />
|
||||
|
||||
<!-- Begin bufrobs routes -->
|
||||
|
||||
<route id="bufrobsIngestRoute">
|
||||
<from ref="bufrobsInboxJmsEndpoint" />
|
||||
<setHeader headerName="pluginName">
|
||||
<constant>sfcobs</constant>
|
||||
</setHeader>
|
||||
<bean ref="stringToFile" />
|
||||
<doTry>
|
||||
<split streaming="true">
|
||||
<method bean="bufrFileSeparator" method="separate" />
|
||||
<doTry>
|
||||
<pipeline>
|
||||
<bean ref="bufrobsProcessor" />
|
||||
<multicast>
|
||||
<to uri="direct-vm:persistIndexAlert" />
|
||||
<to uri="direct-vm:bufrObsShef" />
|
||||
</multicast>
|
||||
</pipeline>
|
||||
<doCatch>
|
||||
<exception>java.lang.Throwable</exception>
|
||||
<to uri="log:bufrobs?level=ERROR" />
|
||||
</doCatch>
|
||||
</doTry>
|
||||
</split>
|
||||
<doCatch>
|
||||
<exception>java.lang.Throwable</exception>
|
||||
<to uri="log:bufrobs?level=ERROR" />
|
||||
</doCatch>
|
||||
<doFinally>
|
||||
<bean ref="bufrFileSeparator" method="clean" />
|
||||
</doFinally>
|
||||
</doTry>
|
||||
</route>
|
||||
|
||||
<route id="bufrObsShefRoute">
|
||||
<from uri="direct-vm:bufrObsShef" />
|
||||
<bean ref="bufrObsRecordPopulator" method="populate" />
|
||||
<to uri="direct-vm:synopticToShef" />
|
||||
</route>
|
||||
</camelContext>
|
||||
</beans>
|
|
@ -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.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Mar 21, 2014 2906 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @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<String, ParameterDescription> parameterDescriptionMap;
|
||||
|
||||
public static final String localizationTablesDirectory = "bufrobs"
|
||||
+ File.separator + "tables";
|
||||
|
||||
private final Map<String, TranslationTable> translationMap = new HashMap<String, TranslationTable>();
|
||||
|
||||
private volatile Map<CategoryKey, Integer> 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<String, ParameterDescription> createParamDefMap(
|
||||
ParameterDescription[] params) {
|
||||
Map<String, ParameterDescription> map = new HashMap<String, ParameterDescription>(
|
||||
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<File, PointDataContainer> pointMap = new HashMap<File, PointDataContainer>();
|
||||
Map<CategoryKey, Integer> rtypeMap = getReportTypeMap();
|
||||
Integer reportType = rtypeMap.get(key);
|
||||
WMOHeader header = getWmoHeader(parser);
|
||||
List<PluginDataObject> rval = new ArrayList<PluginDataObject>();
|
||||
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<File, PointDataContainer> 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<File, PointDataContainer> 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<Unit<?>> 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<CategoryKey, Integer> 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<CategoryKey, Integer> 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<String, ParameterDescription> 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<TableEntry> 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<String, ParameterDescription> 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<CategoryKey> 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<String, ParameterDescription> getParameterDescriptionMap() {
|
||||
return parameterDescriptionMap;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Apr 2, 2014 2906 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Apr 1, 2014 2906 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @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<AbstractBufrSfcObsDecoder> decoders = new ArrayList<AbstractBufrSfcObsDecoder>();
|
||||
|
||||
/**
|
||||
* @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<CategoryKey> 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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Apr 10, 2014 2906 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Apr 1, 2014 2906 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Apr 1, 2014 2906 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @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<CategoryKey, Integer> getReportTypeMap(
|
||||
String categoryFileName) throws XMLStreamException,
|
||||
LocalizationException {
|
||||
LocalizationFile file = getLocalizedCategoryFile(categoryFileName);
|
||||
if ( file == null){
|
||||
return null;
|
||||
}
|
||||
Map<CategoryKey, Integer> rval = new HashMap<CategoryKey, Integer>();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Mar 21, 2014 2906 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author bclement
|
||||
* @version 1.0
|
||||
*/
|
||||
public class SynopticLandBufrDecoder extends AbstractBufrSfcObsDecoder {
|
||||
|
||||
public static final Set<String> LOCATION_FIELDS = new HashSet<String>(
|
||||
Arrays.asList(SfcObsPointDataTransform.LATITUDE,
|
||||
SfcObsPointDataTransform.LONGITUDE,
|
||||
SfcObsPointDataTransform.STATION_ID,
|
||||
SfcObsPointDataTransform.ELEVATION));
|
||||
|
||||
public static final Set<String> SUB_STRUCT_FIELDS = new HashSet<String>(
|
||||
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<String> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Apr 4, 2014 2906 bclement Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @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());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<aliasList namespace="synoptic_land">
|
||||
<!-- time not included since it is processed before anything else -->
|
||||
<alias base="stationId">WMO station number</alias>
|
||||
<alias base="latitude">Latitude (high accuracy)</alias>
|
||||
<alias base="longitude">Longitude (high accuracy)</alias>
|
||||
<alias base="elevation">Height of station ground above mean sea level</alias>
|
||||
<alias base="temperature">Temperature/dry-bulb temperature</alias>
|
||||
<alias base="dewpoint">Dew-point temperature</alias>
|
||||
<alias base="windSpeed">Wind speed</alias>
|
||||
<alias base="windDir">Wind direction</alias>
|
||||
<alias base="windGust">Maximum wind gust speed</alias>
|
||||
<!-- land synoptic text decoder doesn't set peakWind*, only maritime does
|
||||
<alias base="peakWindSpeedTime"></alias>
|
||||
<alias base="peakWindSpeed"></alias>
|
||||
<alias base="peakWindDir"></alias>
|
||||
-->
|
||||
<alias base="seaLevelPress">Pressure reduced to mean sea level</alias>
|
||||
<!-- synoptic land does not contain altimeter data
|
||||
<alias base="altimeter"></alias>
|
||||
-->
|
||||
<alias base="stationPress">Pressure</alias>
|
||||
<!-- table lookup -->
|
||||
<alias base="pressChangeChar">Characteristic of pressure tendency</alias>
|
||||
<alias base="pressChange3Hour">3-hour pressure change</alias>
|
||||
<!-- table lookup-->
|
||||
<alias base="totCloudAmount">Cloud amount</alias>
|
||||
<alias base="lowCloudHeight">Height of base of cloud</alias>
|
||||
<!-- this is not persisted from the text decoder
|
||||
<alias base="lowCloudAmount></alias>
|
||||
-->
|
||||
<!-- table lookup-->
|
||||
<alias base="lowCloudType">Cloud type</alias>
|
||||
<!-- table lookup-->
|
||||
<alias base="midCloudType">Cloud type-1</alias>
|
||||
<!-- table lookup-->
|
||||
<alias base="hiCloudType">Cloud type-2</alias>
|
||||
<alias base="visibility">Horizontal visibility</alias>
|
||||
<!-- wx_present is the text lookup table index -->
|
||||
<alias base="wx_present">Present weather</alias>
|
||||
<!-- presWeather is the metar-ish weather abbreviation -->
|
||||
<alias base="presWeather">Present weather</alias>
|
||||
<!-- wx_past not used by sfc obs
|
||||
<alias base="wx_past_1">Past weather (1)</alias>
|
||||
<alias base="wx_past_2">Past weather (2)</alias>
|
||||
-->
|
||||
<!-- map all precip to same base, use associated time period in parser -->
|
||||
<alias base="precip">Total precipitation/total water equivalent</alias>
|
||||
<alias base="precip24Hour">Total precipitation past 24 hours</alias>
|
||||
<!-- following not avaiable in land synop
|
||||
<alias base="equivWindSpeed10m"></alias>
|
||||
<alias base="equivWindSpeed20m"></alias>
|
||||
<alias base="iceCode"></alias>
|
||||
<alias base="wetBulb"></alias>
|
||||
<alias base="seaSurfaceTemp"></alias>
|
||||
<alias base="platformTrueDirection"></alias>
|
||||
<alias base="platformTrueSpeed"></alias>
|
||||
<alias base="windWaveHeight"></alias>
|
||||
<alias base="windWavePeriod"></alias>
|
||||
<alias base="waveHeight"></alias>
|
||||
<alias base="wavePeriod"></alias>
|
||||
<alias base="waveSteepness"></alias>
|
||||
<alias base="highResWaveHeight"></alias>
|
||||
<alias base="primarySwellWaveDir"></alias>
|
||||
<alias base="primarySwellWavePeriod"></alias>
|
||||
<alias base="primarySwellWaveHeight"></alias>
|
||||
<alias base="secondarySwellWaveDir"></alias>
|
||||
<alias base="secondarySwellWavePeriod"></alias>
|
||||
<alias base="secondarySwellWaveHeight"></alias>
|
||||
<alias base="numInterWinds"></alias>
|
||||
<alias base="interWindTime"></alias>
|
||||
-->
|
||||
</aliasList>
|
|
@ -0,0 +1,16 @@
|
|||
<categories>
|
||||
<category code="0" description="Surface data — land">
|
||||
<subcategory code="0" description="Hourly synoptic observations from fixed-land stations (SYNOP)"
|
||||
reportType="1001" />
|
||||
<subcategory code="1" description="Intermediate synoptic observations from fixed-land stations (SYNOP)"
|
||||
reportType="1001" />
|
||||
<subcategory code="2" description="Main synoptic observations from fixedland stations (SYNOP)"
|
||||
reportType="1001" />
|
||||
<subcategory code="3" description="Hourly synoptic observations from mobile-land stations (SYNOP MOBIL)"
|
||||
reportType="1002" />
|
||||
<subcategory code="4" description="Intermediate synoptic observations from mobile-land stations (SYNOP MOBIL)"
|
||||
reportType="1002" />
|
||||
<subcategory code="5" description="Main synoptic observations from mobile land stations (SYNOP MOBIL)"
|
||||
reportType="1002" />
|
||||
</category>
|
||||
</categories>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<requestPatterns xmlns:ns2="group">
|
||||
<regex>^IS[IMN]</regex>
|
||||
</requestPatterns>
|
Loading…
Add table
Reference in a new issue