Merge "Issue #2906 added synop bufr obs decoder" into development

Former-commit-id: 0e44560636 [formerly 2df73a6601] [formerly 75b5f5b658] [formerly 0e44560636 [formerly 2df73a6601] [formerly 75b5f5b658] [formerly 439dc71571 [formerly 75b5f5b658 [formerly d214a46f19a939eec5d23d0c9029b1e85eaad447]]]]
Former-commit-id: 439dc71571
Former-commit-id: 444f846278 [formerly 91c478cb8f] [formerly 409268b2113f7a5a46304f6cf85517c66a4dddcb [formerly 8788d46064]]
Former-commit-id: 0c66c8cdc66c3e1b5b8e466ecf0fb764e0d3544c [formerly 86b146a3ff]
Former-commit-id: 746c4d7908
This commit is contained in:
Nate Jensen 2014-04-10 15:09:39 -05:00 committed by Gerrit Code Review
commit 881429f2fb
22 changed files with 2353 additions and 98 deletions

View file

@ -13,7 +13,8 @@ Require-Bundle: com.raytheon.edex.common,
javax.persistence,
com.raytheon.uf.edex.ndm;bundle-version="1.0.0",
com.raytheon.uf.common.localization;bundle-version="1.14.0"
Export-Package: com.raytheon.edex.plugin.sfcobs.common
Export-Package: com.raytheon.edex.plugin.sfcobs,
com.raytheon.edex.plugin.sfcobs.common
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: com.raytheon.uf.common.dataplugin.sfcobs,
com.raytheon.uf.common.pointdata,

View file

@ -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" />

View file

@ -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) {

View file

@ -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);
}
}
}

View file

@ -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();
}
}

View file

@ -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>

View file

@ -96,4 +96,10 @@
install-size="0"
version="0.0.0"/>
<plugin
id="ucar.nc2.bufrsplitter"
download-size="0"
install-size="0"
version="0.0.0"/>
</feature>

View file

@ -219,13 +219,13 @@
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.raytheon.uf.common.dataplugin.redbook"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
unpack="false"/>
<plugin
id="com.raytheon.edex.plugin.sfcobs"
@ -360,4 +360,18 @@
version="0.0.0"
unpack="false"/>
<plugin
id="com.raytheon.uf.edex.plugin.bufrobs"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.raytheon.uf.common.nc.bufr"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>

View file

@ -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

View file

@ -1,4 +1,8 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
.,\
res/,\
utility/
src.includes = res/,\
utility/

View file

@ -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>

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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());
}
}

View file

@ -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>

View file

@ -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>

View file

@ -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>