Issue #2906 added synop bufr obs decoder
fixed units in sfcobs point data description made getDescription() and static constants public in point data transform fixed typo in udunits alias file created new decoder for synop bufr obs added decoder and dependencies to features (cherry picked from commit421153a246
[formerly1e2ad93e65
] [formerly663337e661
] [formerly972d18cd3b
[formerly663337e661
[formerly b35756921a0ba5ff240b80f92322da90345c6eaf]]]) Conflicts: edexOsgi/com.raytheon.edex.plugin.sfcobs/META-INF/MANIFEST.MF edexOsgi/com.raytheon.uf.edex.dataplugins.feature/feature.xml Former-commit-id:d352bd0755
[formerly1701fdb4bc
[formerly e4d20dc93173f5504eed1946e45b554656c95cae]] Former-commit-id:1701fdb4bc
Former-commit-id:569633c888
This commit is contained in:
parent
7451a3194b
commit
9616e56ba6
22 changed files with 2351 additions and 96 deletions
|
@ -11,7 +11,8 @@ Require-Bundle: com.raytheon.edex.common,
|
|||
org.geotools,
|
||||
javax.measure,
|
||||
javax.persistence
|
||||
Export-Package: com.raytheon.edex.plugin.sfcobs.common
|
||||
Export-Package: com.raytheon.edex.plugin.sfcobs,
|
||||
com.raytheon.edex.plugin.sfcobs.common
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
|
||||
Import-Package: com.raytheon.uf.common.dataplugin.sfcobs,
|
||||
com.raytheon.uf.common.pointdata,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -102,4 +102,10 @@
|
|||
install-size="0"
|
||||
version="0.0.0"/>
|
||||
|
||||
<plugin
|
||||
id="ucar.nc2.bufrsplitter"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"/>
|
||||
|
||||
</feature>
|
||||
|
|
|
@ -353,4 +353,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