ucar.nc2.iosp.nids.Nidsheader Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
* See LICENSE for license information.
*/
package ucar.nc2.iosp.nids;
import ucar.ma2.*;
import ucar.ma2.DataType;
import ucar.nc2.Attribute;
import ucar.nc2.*;
import ucar.nc2.iosp.nexrad2.NexradStationDB;
import ucar.nc2.iosp.IospHelper;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants.*;
import ucar.nc2.units.DateFormatter;
import ucar.unidata.geoloc.projection.FlatEarth;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.io.bzip2.CBZip2InputStream;
import ucar.unidata.io.bzip2.BZip2ReadException;
import ucar.unidata.util.Parameter;
import java.io.*;
import java.nio.*;
import java.util.*;
import java.util.zip.*;
/**
* This class reads in an NEXRAD level III and TDWR file.
* most file need to go through the header part to have enough info to
* construct the netcdf structure of the radar file
*
* @author caron
*/
class Nidsheader{
final private static boolean useStationDB = false; // use station db for loactions
static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Nidsheader.class);
final static int NEXR_PID_READ = 100;
final static int DEF_NUM_ELEMS = 640; /* default num of elements to send */
final static int DEF_NUM_LINES = 480; /* default num of lines to send */
final static int NEXR_FILE_READ = -1; /* # flag to read entire NIDS file */
final static int NEXR_DIR_READ = 356 ; /* just enough bytes for NIDS directory */
final static int READ_BUFFER_SIZE = 1; /* # of image lines to buffer on read */
final static int ZLIB_BUF_LEN = 4000; /* max size of an uncompressed ZLIB buffer */
byte Z_DEFLATED = 8;
byte DEF_WBITS = 15;
final static int Other = 0;
final static int Base_Reflect = 1;
final static int Velocity = 2;
final static int Comp_Reflect = 3;
final static int Layer_Reflect_Avg = 4;
final static int Layer_Reflect_Max = 5;
final static int Echo_Tops = 6;
final static int Vert_Liquid = 7;
final static int Precip_1 = 8;
final static int Precip_3 = 9;
final static int Precip_Accum = 10;
final static int Precip_Array = 11;
final static int BaseReflect248 = 12;
final static int StrmRelMeanVel = 13;
final static int VAD = 14;
final static int SPECTRUM = 15;
final static int DigitalHybridReflect = 16;
final static int DigitalStormTotalPrecip = 17;
final static int Reflect1 = 18;
final static int Velocity1 = 19;
final static int SPECTRUM1 = 20;
final static int BaseReflectivityDR = 21;
final static int BaseVelocityDV = 22;
final static int EnhancedEcho_Tops = 23;
final static int DigitalVert_Liquid = 24;
final static int DigitalDifferentialReflectivity = 30;
final static int DigitalCorrelationCoefficient = 31;
final static int DigitalDifferentialPhase = 32;
final static int HydrometeorClassification = 33;
final static int OneHourAccumulation = 36;
final static int DigitalAccumulationArray = 37;
final static int StormTotalAccumulation = 38;
final static int DigitalStormTotalAccumulation = 39;
final static int Accumulation3Hour = 40;
final static int Accumulation24Hour = 41;
final static int Digital1HourDifferenceAccumulation = 42;
final static int DigitalTotalDifferenceAccumulation = 43;
final static int DigitalInstantaneousPrecipitationRate = 44;
final static int HypridHydrometeorClassification = 45;
// message header block
short mcode = 0;
short mdate = 0;
int mtime = 0;
int mlength = 0;
short msource = 0;
short mdestId = 0;
short mNumOfBlock = 0;
// production dessciption block
short divider = 0;
double latitude = 0.0;
double lat_min = 0.0;
double lat_max = 0.0;
double longitude = 0.0;
double lon_min = 0.0;
double lon_max = 0.0;
double height = 0;
short pcode = 0;
short opmode = 0;
short volumnScanPattern = 0;
short sequenceNumber = 0;
short volumeScanNumber = 0;
short volumeScanDate = 0;
int volumeScanTime = 0;
short productDate = 0;
int productTime = 0;
short p1 = 0;
short p2 = 0;
short elevationNumber = 0;
short p3 = 0;
short [] threshold = new short[16];
short p4 = 0;
short p5 = 0;
short p6 = 0;
short p7 = 0;
short p8 = 0;
short p9 = 0;
short p10 = 0;
short numberOfMaps = 0;
int offsetToSymbologyBlock = 0;
int offsetToGraphicBlock = 0;
int offsetToTabularBlock = 0;
int block_length = 0;
short number_layers = 0;
String stationId;
String stationName = "XXX";
private boolean noHeader;
DateFormatter formatter = new DateFormatter();
/**
* check if this file is a nids / tdwr file
* @param raf input file
* @return true if valid
*/
public boolean isValidFile( ucar.unidata.io.RandomAccessFile raf) {
try
{
long t = raf.length();
if(t == 0){
throw new IOException("zero length file ");
}
}
catch ( IOException e )
{
return( false );
}
try{
int p = this.readWMO( raf );
if( p == 0 ) return false; // not unidata radar mosiac gini file
}
catch ( IOException e )
{
return( false );
}
return true;
}
/**
* read the header of input file and parsing the WMO part
* @param raf input file
* @return 1 if checking passing
* @throws IOException
*/
int readWMO(ucar.unidata.io.RandomAccessFile raf ) throws IOException
{
int pos = 0;
//long actualSize = 0;
raf.seek(pos);
int readLen = 35;
// Read in the contents of the NEXRAD Level III product head
byte[] b = new byte[readLen];
int rc = raf.read(b);
if ( rc != readLen )
{
// out.println(" error reading nids product header");
return 0;
}
// new check
int iarr2_1 = bytesToInt(b[0], b[1], false);
int iarr2_16 = bytesToInt(b[30], b[31], false);
int iarr2_10 = bytesToInt(b[18], b[19], false);
int iarr2_7 = bytesToInt(b[12], b[13], false);
if ( ( iarr2_1 == iarr2_16 ) &&
( ( iarr2_1 >= 16 ) && ( iarr2_1 <= 299) ) &&
( iarr2_10 == -1 ) &&
( iarr2_7 < 10000 ) ) {
noHeader = true;
return 1;
}
//Get product message header into a string for processing
String pib = new String(b, CDM.utf8Charset);
if( pib.indexOf("SDUS")!= -1){
noHeader = false;
return 1;
} else if ( raf.getLocation().indexOf(".nids") != -1) {
noHeader = true;
return 1;
// } else if(checkMsgHeader(raf) == 1) {
// noHeader = true;
// return 1;
} else
return 0;
}
/**
* read the compressed data
*
* @param offset offset of the compressed data
* @param len length of data to compress
* @return uncompressed byte array
*/
public byte[] getUncompData(int offset, int len){
if( len == 0 ) len = uncompdata.length - offset;
byte[] data = new byte[len];
System.arraycopy(uncompdata, offset, data, 0, len);
return data;
}
//////////////////////////////////////////////////////////////////////////////////
ucar.unidata.io.RandomAccessFile raf;
private ucar.nc2.NetcdfFile ncfile;
//private PrintStream out = System.out;
//private Vinfo myInfo;
private String cmemo, ctilt, ctitle, cunit, cname;
public void setProperty( String name, String value) { }
private int numX ;
private int numX0;
private int numY ;
private int numY0;
private boolean isR = false;
private byte[] uncompdata = null;
/**
* read and parse the header of the nids/tdwr file
* @param raf input file
* @param ncfile output file
* @throws IOException
*/
void read(ucar.unidata.io.RandomAccessFile raf, ucar.nc2.NetcdfFile ncfile ) throws IOException {
int hedsiz; /* NEXRAD header size */
int rc; /* function return status */
int hoff = 0;
int type;
int zlibed;
boolean isZ = false;
int encrypt;
long actualSize ;
int readLen ;
readWMO( raf );
this.ncfile = ncfile;
actualSize = raf.length();
int pos = 0;
raf.seek(pos);
// Read in the whole contents of the NEXRAD Level III product since
// some product require to go through the whole file to build the struct of file.
readLen = (int)actualSize;
byte[] b = new byte[readLen];
rc = raf.read(b);
if ( rc != readLen )
{
log.warn(" error reading nids product header "+raf.getLocation());
}
if( !noHeader ) {
//Get product message header into a string for processing
String pib = new String(b, 0, 100, CDM.utf8Charset);
type = 0;
pos = pib.indexOf ( "\r\r\n" );
while ( pos != -1 ) {
hoff = pos + 3;
type++;
pos = pib.indexOf ( "\r\r\n" , pos+1);
}
raf.seek(hoff);
// Test the next two bytes to see if the image portion looks like
// it is zlib-compressed.
byte[] b2 = new byte[2];
// byte[] b4 = new byte[4];
System.arraycopy(b, hoff, b2, 0, 2);
zlibed = isZlibHed( b2 );
if ( zlibed == 0){
encrypt = IsEncrypt( b2 );
if(encrypt == 1){
log.error( "error reading encryted product "+raf.getLocation());
throw new IOException("unable to handle the product with encrypt code " + encrypt);
}
}
// process product description for station ID
byte[] b3 = new byte[3];
switch ( type ) {
case 0:
log.warn( "ReadNexrInfo:: Unable to seek to ID "+raf.getLocation());
break;
case 1:
case 2:
case 3:
case 4:
System.arraycopy(b, hoff - 6, b3, 0, 3);
stationId = new String(b3, CDM.utf8Charset);
try {
NexradStationDB.init(); // make sure database is initialized
NexradStationDB.Station station = NexradStationDB.get("K"+stationId);
if (station != null) {
stationName = station.name;
}
} catch (IOException ioe) {
log.error("NexradStationDB.init "+raf.getLocation(), ioe);
}
break;
default:
break;
}
if ( zlibed == 1 ) {
isZ = true;
uncompdata = GetZlibedNexr( b, readLen, hoff );
//uncompdata = Nidsiosp.readCompData(hoff, 160) ;
if ( uncompdata == null ) {
log.warn( "ReadNexrInfo: error uncompressing image " +raf.getLocation());
uncompdata = new byte[b.length - hoff];
System.arraycopy(b, hoff, uncompdata, 0, b.length - hoff);
}
}
else {
uncompdata = new byte[b.length-hoff];
System.arraycopy(b, hoff, uncompdata, 0, b.length- hoff);
}
} else {
uncompdata = new byte[b.length];
System.arraycopy(b, 0, uncompdata, 0, b.length);
// stationId = "YYY";
}
byte[] b2 = new byte[2];
ByteBuffer bos = ByteBuffer.wrap(uncompdata);
rc = read_msghead( bos, 0 );
hedsiz = 18;
Pinfo pinfo = read_proddesc( bos, hedsiz );
hedsiz += 102;
// Set product-dependent information
int prod_type = code_typelookup(pinfo.pcode);
setProductInfo(prod_type, pinfo );
//int windb = 0;
int pcode1Number = 0;
int pcode2Number = 0;
int pcode8Number = 0;
int pcode4Number = 0;
int pcode5Number = 0;
int pcode10Number = 0;
int pcode6Number = 0;
int pcode25Number = 0;
int pcode12Number = 0;
int pcode13Number = 0;
int pcode14Number = 0;
int pcode15Number = 0;
int pcode16Number = 0;
int pcode19Number = 0;
int pcode20Number = 0;
int pkcode1Doff[] = null;
int pkcode2Doff[] = null;
int pkcode8Doff[] = null;
int pkcode1Size[] = null;
int pkcode2Size[] = null;
int pkcode8Size[] = null;
int pkcode4Doff[] = null;
int pkcode5Doff[] = null;
int pkcode10Doff[] = null;
int pkcode10Dlen[] = null;
int pkcode6Doff[] = null;
int pkcode6Dlen[] = null;
int pkcode25Doff[] = null;
int pkcode12Doff[] = null;
int pkcode13Doff[] = null;
int pkcode14Doff[] = null;
int pkcode12Dlen[] = null;
int pkcode13Dlen[] = null;
int pkcode14Dlen[] = null;
int pkcode15Dlen[] = null;
int pkcode15Doff[] = null;
int pkcode16Dlen[] = null;
int pkcode16Doff[] = null;
int pkcode19Dlen[] = null;
int pkcode19Doff[] = null;
int pkcode20Dlen[] = null;
int pkcode20Doff[] = null;
// Get product symbology header (needed to get image shape)
ifloop: if ( pinfo.offsetToSymbologyBlock != 0 ) {
// Symbology header
if(pinfo.p8 == 1) {
// TDWR data and the symbology is compressed
int size = shortsToInt(pinfo.p9, pinfo.p10, false);
uncompdata = uncompressed(bos, hedsiz, size);
bos = ByteBuffer.wrap(uncompdata);
}
Sinfo sinfo = read_dividlen( bos, hedsiz );
if( rc == 0 || pinfo.divider != -1 )
{
log.warn( "error in product symbology header "+raf.getLocation());
}
if(sinfo.id != 1)
{
if(pinfo.pcode == 82) {
read_SATab( bos, hedsiz );
}
break ifloop;
}
hedsiz += 10;
// Symbology layer
int klayer = pinfo.offsetToSymbologyBlock*2 + 10;
for(int i=0; i 0)
i = datainput.getInt();
for(int j = 0; j < i; j++)
{
arraylist.add(readInString(datainput));
HashMap hm = addAttributePairs(readInString(datainput));
arraylist.add(hm);
}
return arraylist;
}
/**
* for level3 176 product
*
* @param s
* @return attributes
*/
public HashMap addAttributePairs(String s)
{
java.util.regex.Pattern PARAM_PATTERN =
java.util.regex.Pattern.compile("([\\w*\\s*?]*)\\=([(\\<|\\{|\\[|\\()?\\w*\\s*?\\.?\\,?\\-?\\/?\\%?(\\>|\\}|\\]|\\))?]*)");
HashMap attributes = new HashMap();
for(java.util.regex.Matcher matcher = PARAM_PATTERN.matcher(s);
matcher.find(); attributes.put(matcher.group(1).trim(), matcher.group(2).trim()));
return attributes;
}
/**
* for level3 176 product
*
* @param datainput
* @return string
*/
public static String readInString(ByteBuffer datainput)
throws IOException
{
StringBuffer stringbuffer = new StringBuffer();
int i = datainput.getInt();
for(int j = 0; j < i; j++)
{
char c = (char)(datainput.get() & 0xff);
stringbuffer.append(c);
}
int k = i % 4;
if(k != 0)
k = 4 - k;
for(int l = 0; l < k; l++)
datainput.get();
return stringbuffer.toString();
}
/**
* construct a generic radial dataset for dualpol radial products;
*
*
* @param bos, hoff, hedsiz, isZ, data, threshold
*
* @return soff -- not used
*/
int pcode_generic( ByteBuffer bos, int hoff, int hedsiz, boolean isZ, byte[] data, short[] threshold ) throws IOException
{
byte[] b2 = new byte[2];
int soff = 0;
ArrayList dims = new ArrayList();
int iscale = 1; /* data scale */
bos.get(b2, 0, 2);
bos.get(b2, 0, 2);
bos.get(b2, 0, 2);
readInString(bos); // vname
readInString(bos); // vdesp
bos.getInt(); // code
bos.getInt(); // type
bos.getInt(); // time
readInString(bos); // rnameStr
bos.getFloat(); // lat
bos.getFloat(); // lon
bos.getFloat(); // height
bos.getInt(); // vscanStartTime
bos.getInt(); // eleScanStartTime
float eleAngle = bos.getFloat();
p3 = (short) eleAngle;
bos.getInt(); // volScanNum
bos.getInt(); // opMode
bos.getInt(); // volPattern
bos.getInt(); // eleNum
bos.getDouble(); // skip 8 bytes
parseParameters(bos); // aa - do nothing
List cc = parseComponents(bos); // assuming only radial component
if (cc == null) {
throw new IOException("Error reading components for radial data");
}
int num_radials = (Integer)cc.get(1);
int num_bin = (Integer)cc.get(0);
float rangeToFirstBin = (Float)cc.get(2);
int dataOffset = (Integer)cc.get(3);
numY0 = 0;
numY = num_radials;
numX0 = (int)rangeToFirstBin ; //first_bin;
numX = num_bin;
int nlevel = code_levelslookup( pcode );
int [] levels;
short radp_scale = 1000;
hedsiz = dataOffset;
//prod_info_size = 2 * (int) (num_bin * scale + 0.5);
//dimensions: radial, bin
ncfile.addAttribute(null, new Attribute("cdm_data_type", FeatureType.RADIAL.toString()));
Dimension radialDim = new Dimension("azimuth", num_radials);
ncfile.addDimension( null, radialDim);
Dimension binDim = new Dimension("gate", num_bin);
ncfile.addDimension( null, binDim);
dims.add( radialDim);
dims.add( binDim);
ArrayList dims1 = new ArrayList();
ArrayList dims2 = new ArrayList();
dims1.add(radialDim);
dims2.add(binDim);
isR = true;
// add elevation coordinate variable
String vName = "elevation";
String lName = "elevation angle in degres: 0 = parallel to pedestal base, 90 = perpendicular";
Attribute att = new Attribute(_Coordinate.AxisType, AxisType.RadialElevation.toString());
addParameter(vName, lName, ncfile, dims1, att, DataType.FLOAT, "degrees",hoff, hedsiz, isZ, p3);
// add azimuth coordinate variable
vName = "azimuth";
lName = "azimuth angle in degrees: 0 = true north, 90 = east";
att = new Attribute(_Coordinate.AxisType, AxisType.RadialAzimuth.toString());
addParameter(vName, lName, ncfile, dims1, att, DataType.FLOAT, "degrees",hoff, hedsiz, isZ, 0);
// add gate coordinate variable
vName = "gate";
lName = "Radial distance to the start of gate";
att = new Attribute(_Coordinate.AxisType, AxisType.RadialDistance.toString());
addParameter(vName, lName, ncfile, dims2, att, DataType.FLOAT, "meters",hoff, hedsiz, isZ, radp_scale);
// add radial coordinate variable
vName = "latitude";
lName = "Latitude of the instrument";
att = new Attribute(_Coordinate.AxisType, AxisType.Lat.toString());
addParameter(vName, lName, ncfile, dims1, att, DataType.FLOAT, "degrees",hoff, hedsiz, isZ, 0);
vName = "longitude";
lName = "Longitude of the instrument";
att = new Attribute(_Coordinate.AxisType, AxisType.Lon.toString());
addParameter(vName, lName, ncfile, dims1, att, DataType.FLOAT, "degrees",hoff, hedsiz, isZ, 0);
vName = "altitude";
lName = "Altitude in meters (asl) of the instrument";
att = new Attribute(_Coordinate.AxisType, AxisType.Height.toString());
addParameter(vName, lName, ncfile, dims1, att, DataType.FLOAT, "meters",hoff, hedsiz, isZ, 0);
vName = "rays_time";
lName = "rays time";
att = new Attribute(_Coordinate.AxisType, AxisType.Time.toString());
addParameter(vName, lName, ncfile, dims1, att, DataType.DOUBLE, "milliseconds since 1970-01-01 00:00 UTC"
,hoff, hedsiz, isZ, 0);
if ( pcode == 176) {
levels = getDualpolLevels(threshold);
iscale = 1;
} else {
levels = getLevels(nlevel, threshold);
}
Variable v = new Variable(ncfile, null, null, cname + "_RAW");
v.setDataType(DataType.USHORT);
v.setDimensions(dims);
ncfile.addVariable(null, v);
v.addAttribute( new Attribute(CDM.UNITS, cunit));
String coordinates = "elevation azimuth gate rays_time latitude longitude altitude";
v.addAttribute( new Attribute(_Coordinate.Axes, coordinates));
// v.addAttribute( new Attribute(CDM.UNSIGNED, "true"));
v.setSPobject( new Vinfo (numX, numX0, numY, numY0, hoff, hedsiz, isR, isZ, null, levels, 0, nlevel));
if (cname.startsWith("DigitalInstantaneousPrecipitationRate") ) {
addVariable(cname, ctitle, ncfile, dims, coordinates, DataType.FLOAT,
cunit, hoff, hedsiz, isZ, nlevel, levels, iscale);
}
return soff;
}
/**
* for level3 94 and 99 product
*
* @param num_bin
* @return size
*/
public short addBinSize(short num_bin){
if ((num_bin%2) == 0)
return num_bin;
else
return (short)(num_bin+1);
}
/**
* get the table to calibrate data value
*
* @param nlevel number of level
* @param th thredshold value
* @return
*/
public int[] getLevels(int nlevel, short[] th) {
int [] levels = new int[nlevel];
int ival;
int isign;
for ( int i = 0; i < nlevel; i++ ) { /* calibrated data values */
ival = convertShort2unsignedInt(th[i]);
if ( (ival & 0x00008000) == 0 ) {
isign = -1;
if ( (ival & 0x00000100) == 0 ) isign = 1;
levels[i] = isign * ( ival & 0x000000FF );
} else {
levels[i] = -9999 + ( ival & 0x000000FF);
}
}
return levels;
}
/**
* get the calibrate data values for TDWR data
* @param nlevel
* @param th
* @return
*/
public int[] getTDWRLevels(int nlevel, short[] th) {
int [] levels = new int[ nlevel]; //th[2]+2 ];
int inc = th[1];
levels[0] = -9866;
levels[1] = -9866;
for ( int i = 2; i < nlevel; i++ ) { /* calibrated data values */
levels[i] = th[0] + (i-2) * inc;
}
return levels;
}
/**
* get the calibrate data values for TDWR data
* @param nlevel
* @param th
* @return
*/
public int[] getTDWRLevels1(int nlevel, short[] th) {
int [] levels = new int[ nlevel]; //th[2] ];
int inc = th[1];
for ( int i = 0; i < nlevel; i++ ) { /* calibrated data values */
levels[i] = th[0] + (i) * inc;
}
return levels;
}
/**
* get the calibrate data values for dualpol data
* @param th
* @return
*/
public int[] getDualpolLevels( short[] th) {
int inc = th.length;
int [] levels = new int[ inc]; //th[2] ];
for ( int i = 0; i < inc; i++ ) { /* calibrated data values */
levels[i] = th[i];
}
return levels;
}
/**
* get the calibrate data values for TDWR data
* @param nlevel
* @param th
* @return
*/
public int[] getTDWRLevels2(int nlevel, short[] th) {
int inc = th.length;
int [] levels = new int[ inc]; //th[2] ];
for ( int i = 0; i < inc; i++ ) { /* calibrated data values */
levels[i] = th[i];
}
return levels;
}
/**
* adding new variable to the netcdf file
* @param pName variable name
* @param longName variable long name
* @param nc netcdf file
* @param dims variable dimensions
* @param coordinates variable coordinate
* @param dtype variable type
* @param ut unit string
* @param hoff header offset
* @param hedsiz header size
* @param isZ is compressed file
* @param nlevel calibrated level number
* @param levels calibrate levels
* @param iscale is scale variable
*/
void addVariable(String pName, String longName, NetcdfFile nc, ArrayList dims, String coordinates,
DataType dtype, String ut, long hoff, long hedsiz, boolean isZ, int nlevel, int[] levels, int iscale)
{
Variable v = new Variable(nc, null, null, pName);
v.setDataType(dtype);
v.setDimensions(dims);
ncfile.addVariable(null, v);
v.addAttribute( new Attribute(CDM.LONG_NAME, longName));
v.addAttribute( new Attribute(CDM.UNITS, ut));
v.addAttribute( new Attribute(_Coordinate.Axes, coordinates));
v.setSPobject( new Vinfo (numX, numX0, numY, numY0, hoff, hedsiz, isR, isZ, null, levels, iscale, nlevel));
}
/**
* adding new parameter to the netcdf file
* @param pName variable name
* @param longName variable long name
* @param nc netcdf file
* @param dims variable dimensions
* @param att attribute
* @param dtype data type
* @param ut unit string
* @param hoff header offset
* @param doff data offset
* @param isZ is compressed file
* @param y0 reserved
*/
void addParameter(String pName, String longName, NetcdfFile nc, ArrayList dims, Attribute att,
DataType dtype, String ut, long hoff, long doff, boolean isZ, int y0)
{
String vName = pName;
Variable vVar = new Variable(nc, null, null, vName);
vVar.setDataType(dtype);
if( dims != null ) vVar.setDimensions(dims);
else vVar.setDimensions("");
if(att != null ) vVar.addAttribute(att);
vVar.addAttribute( new Attribute(CDM.UNITS, ut));
vVar.addAttribute( new Attribute(CDM.LONG_NAME, longName));
nc.addVariable(null, vVar);
vVar.setSPobject( new Vinfo (numX, numX0, numY, y0, hoff, doff, isR, isZ, null, null, 0, 0));
}
/**
* misc
* @param lat
* @param lon
* @return
*/
String StnIdFromLatLon(float lat, float lon )
{
return "ID";
}
/**
* Misc
* @param prod_elevation
* @return
*/
private int getProductLevel(int prod_elevation){
int level = 0;
if(prod_elevation== 5)
level = 0;
else if(prod_elevation== 9)
level = 1;
else if(prod_elevation==13 || prod_elevation==15)
level = 2;
else if(prod_elevation==18)
level = 3;
else if(prod_elevation==24)
level = 4;
else if(prod_elevation==31)
level = 6;
return level;
}
/**
* parsing the product information into netcdf dataset
* @param prod_type product type
* @param pinfo product information
*/
void setProductInfo(int prod_type, Pinfo pinfo)
{
/* memo field */
String[] cmode = new String[]{"Maintenance", "Clear Air", "Precip Mode"};
short prod_max = pinfo.p4;
short prod_min = 0;
int prod_elevation = 0;
//int prod_info;
int prod_top;
int radial = 0;
String summary = null;
java.util.Date endDate;
java.util.Date startDate;
String dstring;
double t1 = 124.0 * 1.853 / 111.26;
double t2 = 230 / (111.26 * Math.cos(Math.toRadians(latitude)));
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2; //* Math.cos(Math.toRadians(lat_min));
lon_max = longitude - t2; //* Math.cos(Math.toRadians(lat_min));
startDate = getDate( volumeScanDate, volumeScanTime*1000);
endDate = getDate( volumeScanDate, volumeScanTime*1000);
if (prod_type == SPECTRUM) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Base Specturm Width " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
ctilt = pname_lookup(pcode, prod_elevation/10);
ctitle = "BREF: Base Spectrum Width";
cunit = "Knots";
cname = "SpectrumWidth";
summary = ctilt + " is a radial image of base reflectivity at tilt " + (prod_elevation/10 + 1) + " and range 124 nm";
if(pcode == 28 ){
t1 = t1 * 0.25;
t2 = t2 * 0.25;
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2; //* Math.cos(Math.toRadians(lat_min));
lon_max = longitude - t2; //* Math.cos(Math.toRadians(lat_min));
summary = ctilt + " is a radial image of base reflectivity at tilt " + (prod_elevation/10 + 1) + " and range 32 nm";
}
}
else if (prod_type == DigitalDifferentialReflectivity) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Differential Reflectivity " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
int pLevel = getProductLevel(prod_elevation);
ctilt = pname_lookup(15, pLevel);
ctitle = "Dualpol: Digital Differential Reflectivity";
cunit = "dBz";
cname = "DifferentialReflectivity";
summary = ctilt + " is a radial image of dual pol differential reflectivity field and its range 162 nm";
}
else if (prod_type == DigitalCorrelationCoefficient) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Correlation Coefficient " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
int pLevel = getProductLevel(prod_elevation);
ctilt = pname_lookup(16, pLevel);
ctitle = "Dualpol: Digital Correlation Coefficient";
cunit = " ";
cname = "CorrelationCoefficient";
summary = ctilt + " is a radial image of dual pol Correlation Coefficient field and its range 162 nm";
}
else if (prod_type == DigitalDifferentialPhase) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Differential Phase " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
int pLevel = getProductLevel(prod_elevation);
ctilt = pname_lookup(17, pLevel);
ctitle = "Dualpol: Digital Differential Phase";
cunit = "Degree/km";
cname = "DifferentialPhase";
summary = ctilt + " is a radial image of dual pol Differential Phase field and its range 162 nm";
}
else if (prod_type == HydrometeorClassification ) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Hydrometeor Classification " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
int pLevel = getProductLevel(prod_elevation);
ctilt = pname_lookup(18, pLevel);
ctitle = "Dualpol: Hydrometeor Classification";
cunit = " ";
cname = "HydrometeorClassification";
summary = ctilt + " is a radial image of dual pol Hydrometeor Classification field and its range 162 nm";
}
else if (prod_type == HypridHydrometeorClassification ) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Hyprid Hydrometeor Classification " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
int pLevel = getProductLevel(prod_elevation);
ctilt = pname_lookup(18, pLevel);
ctitle = "Dualpol: Hyprid Hydrometeor Classification";
cunit = " ";
cname = "HypridHydrometeorClassification";
summary = ctilt + " is a radial image of dual pol Hyprid Hydrometeor Classification field and its range 162 nm";
}
else if (prod_type == OneHourAccumulation ) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "One Hour Accumulation " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
ctilt = "OHA";
ctitle = "Dualpol: One Hour Accumulation";
cunit = "IN";
cname = "OneHourAccumulation";
summary = ctilt + " is a radial image of dual pol One Hour Accumulation field and its range 124 nm";
}
else if (prod_type == DigitalAccumulationArray ) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Digital Accumulation Array " + cmode[pinfo.opmode];
ctilt = "DAA";
ctitle = "Dualpol: Digital Accumulation Array";
cunit = "IN";
cname = "DigitalAccumulationArray";
summary = ctilt + " is a radial image of dual pol Digital Accumulation Array field and its range 124 nm";
}
else if (prod_type == StormTotalAccumulation ) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Storm Total Accumulation " + cmode[pinfo.opmode];
ctilt = "PTA";
ctitle = "Dualpol: Storm Total Accumulation";
cunit = "IN";
cname = "StormTotalAccumulation";
summary = ctilt + " is a radial image of dual pol Storm Total Accumulation field and its range 124 nm";
}
else if (prod_type == DigitalStormTotalAccumulation ) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Digital Storm Total Accumulation " + cmode[pinfo.opmode];
ctilt = "DTA";
ctitle = "Dualpol: Digital Storm Total Accumulation";
cunit = "IN";
cname = "DigitalStormTotalAccumulation";
summary = ctilt + " is a radial image of dual pol Digital StormTotal Accumulation field and its range 124 nm";
}
else if (prod_type == Accumulation3Hour ) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Hyprid Hydrometeor Classification " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
int pLevel = getProductLevel(prod_elevation);
ctilt = pname_lookup(18, pLevel);
ctitle = "Dualpol: 3-hour Accumulation";
cunit = "IN";
cname = "Accumulation3Hour";
summary = ctilt + " is a radial image of dual pol 3-hour Accumulation field and its range 124 nm";
}
else if (prod_type == Digital1HourDifferenceAccumulation) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Digital One Hour Difference Accumulation " + cmode[pinfo.opmode];
ctilt = "DOD";
ctitle = "Dualpol: Digital One Hour Difference Accumulation";
cunit = "IN";
cname = "Digital1HourDifferenceAccumulation";
summary = ctilt + " is a radial image of dual pol Digital One Hour Difference Accumulation field and its range 124 nm";
}
else if (prod_type == DigitalTotalDifferenceAccumulation ) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Digital Total Difference Accumulation " + cmode[pinfo.opmode];
ctilt = "DSD";
ctitle = "Dualpol: Digital Total Difference Accumulation";
cunit = "IN";
cname = "DigitalTotalDifferenceAccumulation";
summary = ctilt + " is a radial image of dual pol Digital Total Difference Accumulation field and its range 124 nm";
}
else if (prod_type == DigitalInstantaneousPrecipitationRate ) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Digital Instantaneous Precipitation Rate " + cmode[pinfo.opmode];
ctilt = "DPR";
ctitle = "Dualpol: Digital Instantaneous Precipitation Rate";
cunit = "IN/Hour";
cname = "DigitalInstantaneousPrecipitationRate";
summary = ctilt + " is a radial image of dual pol Digital Instantaneous Precipitation Rate field and its range 124 nm";
}
else if (prod_type == BaseReflectivityDR) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Base Reflectivity DR " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
int pLevel = getProductLevel(prod_elevation);
ctilt = pname_lookup(94, pLevel);
ctitle = "HighResolution: Base Reflectivity";
cunit = "dBz";
cname = "BaseReflectivityDR";
summary = ctilt + " is a radial image of base reflectivity field and its range 248 nm";
}
else if (prod_type == BaseVelocityDV) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Base Velocity DR " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
int pLevel = getProductLevel(prod_elevation);
ctilt = pname_lookup(99, pLevel);
ctitle = "HighResolution: Base Velocity";
cunit = "m/s";
cname = "BaseVelocityDV";
summary = ctilt + " is a radial image of base velocity field and its range 124 nm";
} else if (prod_type == DigitalVert_Liquid) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Digital Hybrid Reflect " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
ctilt = pname_lookup(134, prod_elevation/10);
ctitle = "Digital: Vertical Integ Liquid";
cunit = "kg/m^2";
cname = "DigitalIntegLiquid";
summary = ctilt + " is a radial image high resolution vertical integral liquid and range 248 nm";
}
else if (prod_type == DigitalHybridReflect) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Digital Hybrid Reflect " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
ctilt = pname_lookup(19, prod_elevation/10);
ctitle = "DigitalHybrid: Reflectivity";
cunit = "dBz";
cname = "DigitalHybridReflectivity";
summary = ctilt + " is a radial image of base reflectivity at tilt " + (prod_elevation/10 + 1) + " and range 124 nm";
} else if (prod_type == Base_Reflect || prod_type == Reflect1) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Base Reflct " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
if(prod_type == Reflect1){
ctilt = "R" + prod_elevation/10;
summary = ctilt + " is a radial image of base reflectivity at tilt " + (prod_elevation/10 + 1);
}
else {
ctilt = pname_lookup(19, prod_elevation/10);
summary = ctilt + " is a radial image of base reflectivity at tilt " + (prod_elevation/10 + 1) + " and range 124 nm";
}
ctitle = "BREF: Base Reflectivity";
cunit = "dBz";
cname = "BaseReflectivity";
} else if (prod_type == BaseReflect248) {
radial = 1;
prod_elevation = pinfo.p3;
cmemo = "Base Reflct 248 " + prod_elevation/10 + " DEG " + cmode[pinfo.opmode];
ctilt = pname_lookup(20, prod_elevation/10);
ctitle = "BREF: 248 nm Base Reflectivity";
cunit = "dBz";
cname = "BaseReflectivity248";
summary = ctilt + " is a radial image of base reflectivity at tilt " + (prod_elevation/10 + 1) + " and range 248 nm";
t1 = 248.0 * 1.853 / 111.26;
t2 = 460 / (111.26 * Math.cos(Math.toRadians(latitude)));
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2;
lon_max = longitude - t2;
} else if (prod_type == Comp_Reflect) {
radial = 3;
prod_elevation = -1;
ctilt = pname_lookup(pinfo.pcode, elevationNumber);
if(pinfo.pcode == 36 || pinfo.pcode == 38 ) {
t1 = t1 * 2;
t2 = t2 * 2;
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2;
lon_max = longitude - t2;
}
summary = ctilt + "is a raster image of composite reflectivity";
cmemo = "Composite Reflectivity at " + cmode[pinfo.opmode];
ctitle = "CREF Composite Reflectivity" + ctilt;
cunit = "dBz" ;
cname = "BaseReflectivityComp";
} else if (prod_type == Layer_Reflect_Avg ||
prod_type == Layer_Reflect_Max) {
radial = 3;
prod_elevation = pinfo.p5;
prod_top = pinfo.p6;
ctilt = pname_lookup(pcode, 0);
summary = ctilt + " is a raster image of composite reflectivity at range 124 nm";
cmemo = "Layer Reflct " + prod_elevation + " - " + prod_top + cmode[pinfo.opmode];
t1 = t1 * 4;
t2 = t2 * 4;
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2;
lon_max = longitude - t2;
ctitle = "LREF: Layer Composite Reflectivity" ;
cunit = "dBz" ;
cname = "LayerCompReflect";
} else if (prod_type == EnhancedEcho_Tops) {
radial = 1;
prod_elevation = -1;
summary = "EET is a radial image of echo tops at range 186 nm";
cmemo = "Enhanced Echo Tops [K FT] " + cmode[pinfo.opmode];
ctilt = pname_lookup(135, elevationNumber);
ctitle = "TOPS: Enhanced Echo Tops";
cunit = "K FT" ;
cname = "EnhancedEchoTop";
t1 = t1 * 4;
t2 = t2 * 4;
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2;
lon_max = longitude - t2;
} else if (prod_type == Echo_Tops) {
radial = 3;
prod_elevation = -1;
summary = "NET is a raster image of echo tops at range 124 nm";
cmemo = "Echo Tops [K FT] " + cmode[pinfo.opmode];
ctilt = pname_lookup(41, elevationNumber);
ctitle = "TOPS: Echo Tops";
cunit = "K FT" ;
cname = "EchoTop";
t1 = t1 * 4;
t2 = t2 * 4;
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2;
lon_max = longitude - t2;
} else if (prod_type == Precip_1) {
radial = 1;
prod_elevation = -1;
prod_max /= 10;
endDate = getDate( pinfo.p7, pinfo.p8 * 60 * 1000);
summary = "N1P is a raster image of 1 hour surface rainfall accumulation at range 124 nm";
cmemo = "1-hr Rainfall [IN] " + cmode[pinfo.opmode];
ctilt = pname_lookup(78, elevationNumber);
ctitle = "PRE1: Surface 1-hour Rainfall Total";
cunit = "IN";
cname = "Precip1hr";
t1 = t1 * 2;
t2 = t2 * 2;
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2;
lon_max = longitude - t2;
} else if (prod_type == Precip_3) {
radial = 1;
prod_elevation = -1;
prod_max /= 10;
endDate = getDate( pinfo.p7, pinfo.p8 * 60 * 1000);
summary = "N3P is a raster image of 3 hour surface rainfall accumulation at range 124 nm";
cmemo = "3-hr Rainfall [IN] " + cmode[pinfo.opmode] ;
ctilt = pname_lookup(79, elevationNumber);
ctitle = "PRE3: Surface 3-hour Rainfall Total" ;
cunit = "IN" ;
cname = "Precip3hr";
t1 = t1 * 2;
t2 = t2 * 2;
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2;
lon_max = longitude - t2;
} else if (prod_type == DigitalStormTotalPrecip) {
radial = 1;
prod_elevation = -1;
//startDate = getDate( pinfo.p5, pinfo.p6 * 60 * 1000);
endDate = getDate( pinfo.p7, pinfo.p8 * 60 * 1000);
summary = "DSP is a radial image of digital storm total rainfall";
cmemo = "Digital Strm Total Precip [IN] " + cmode[pinfo.opmode] ;
ctilt = pname_lookup(80, elevationNumber);
ctitle = "DPRE: Digital Storm Total Rainfall" ;
cunit = "IN" ;
cname = "DigitalPrecip";
t1 = t1 * 2;
t2 = t2 * 2;
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2;
lon_max = longitude - t2;
} else if (prod_type == Precip_Accum) {
radial = 1;
prod_elevation = -1;
//startDate = getDate( pinfo.p5, pinfo.p6 * 60 * 1000);
endDate = getDate( pinfo.p7, pinfo.p8 * 60 * 1000);
summary = "NTP is a raster image of storm total rainfall accumulation at range 124 nm";
cmemo = "Strm Tot Rain [IN] " + cmode[pinfo.opmode] ;
ctilt = pname_lookup(80, elevationNumber);
ctitle = "PRET: Surface Storm Total Rainfall" ;
cunit = "IN" ;
cname = "PrecipAccum";
t1 = t1 * 2;
t2 = t2 * 2;
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2;
lon_max = longitude - t2;
} else if (prod_type == Precip_Array) {
radial = 3;
prod_elevation = -1;
summary = "DPA is a raster image of hourly digital precipitation array at range 124 nm";
endDate = getDate( pinfo.p7, pinfo.p8 * 60 * 1000);
cmemo = "Precip Array [IN] " + cmode[pinfo.opmode] ;
ctilt = pname_lookup(81, elevationNumber);
ctitle = "PRET: Hourly Digital Precipitation Array" ;
cunit = "dBA" ;
cname = "PrecipArray";
} else if (prod_type == Vert_Liquid) {
radial = 3;
prod_elevation = -1;
summary = "NVL is a raster image of verticalintegrated liguid at range 124 nm";
cmemo = "Vert Int Lq H2O [mm] " + cmode[pinfo.opmode] ;
ctilt = pname_lookup(57, elevationNumber);
ctitle = "VIL: Vertically-integrated Liquid Water" ;
cunit = "kg/m^2" ;
cname = "VertLiquid";
t1 = t1 * 4;
t2 = t2 * 4;
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2;
lon_max = longitude - t2;
} else if (prod_type == Velocity || prod_type == Velocity1) {
radial = 1;
prod_elevation = pinfo.p3;
prod_min = pinfo.p4;
prod_max = pinfo.p5;
if(prod_type == Velocity) {
ctilt = pname_lookup(pinfo.pcode, prod_elevation/10);
}
else {
ctilt = "V" + prod_elevation/10;
}
if(pinfo.pcode == 25) {
t1 = 32.0 * 1.853 / 111.26;
t2 = 64 / (111.26 * Math.cos(Math.toRadians(latitude)));
lat_min = latitude - t1;
lat_max = latitude + t1;
lon_min = longitude + t2;
lon_max = longitude - t2;
summary = ctilt + " is a radial image of base velocity" + (prod_elevation/10 + 1) + " and range 32 nm";
cunit = "m/s";
}
else {
summary = ctilt + " is a radial image of base velocity at tilt " + (prod_elevation/10 + 1);
cunit = "m/s";
}
cmemo = "Rad Vel "+ prod_elevation/10. + " DEG " + cmode[pinfo.opmode];
ctitle = "VEL: Radial Velocity" ;
cname = "RadialVelocity";
} else if (prod_type == StrmRelMeanVel) {
radial = 1;
prod_elevation = pinfo.p3;
prod_min = pinfo.p4;
prod_max = pinfo.p5;
ctilt = pname_lookup(56, prod_elevation/10);
summary = ctilt + " is a radial image of storm relative mean radial velocity at tilt " + (prod_elevation/10 + 1) + " and range 124 nm";
cmemo = "StrmRelMnVl " + prod_elevation/10. + " DEG " + cmode[pinfo.opmode];
ctitle = "SRMV: Storm Relative Mean Velocity" ;
cunit = "KT" ;
cname = "StormMeanVelocity";
} else if (prod_type == VAD) {
radial = 0;
prod_elevation = pinfo.p3;
prod_min = pinfo.p4;
prod_max = pinfo.p5;
summary = "NVW is VAD wind profile which contains wind barbs and alpha numeric data";
cmemo = "StrmRelMnVl " + prod_elevation/10. + " DEG " + cmode[pinfo.opmode];
ctilt = pname_lookup(48, elevationNumber);
ctitle = "SRMV: Velocity Azimuth Display" ;
cunit = "KT" ;
cname = "VADWindSpeed";
lat_min = latitude;
lat_max = latitude;
lon_min = longitude;
lon_max = longitude;
} else {
ctilt = "error";
ctitle = "error" ;
cunit = "error" ;
cname = "error";
}
/* add geo global att */
ncfile.addAttribute(null, new Attribute("summary", "Nexrad level 3 data are WSR-88D radar products." +
summary ));
ncfile.addAttribute(null, new Attribute("keywords_vocabulary", ctilt));
ncfile.addAttribute(null, new Attribute("conventions", _Coordinate.Convention));
ncfile.addAttribute(null, new Attribute("format", "Level3/NIDS"));
ncfile.addAttribute(null, new Attribute("geospatial_lat_min", new Float(lat_min)));
ncfile.addAttribute(null, new Attribute("geospatial_lat_max", new Float(lat_max)));
ncfile.addAttribute(null, new Attribute("geospatial_lon_min", new Float(lon_min)));
ncfile.addAttribute(null, new Attribute("geospatial_lon_max", new Float(lon_max)));
ncfile.addAttribute(null, new Attribute("geospatial_vertical_min", new Float(height)));
ncfile.addAttribute(null, new Attribute("geospatial_vertical_max", new Float(height)));
ncfile.addAttribute(null, new Attribute("RadarElevationNumber", new Integer(prod_elevation)));
dstring = formatter.toDateTimeStringISO(startDate);
ncfile.addAttribute(null, new Attribute("time_coverage_start", dstring));
dstring = formatter.toDateTimeStringISO(endDate);
ncfile.addAttribute(null, new Attribute("time_coverage_end", dstring));
ncfile.addAttribute(null, new Attribute("data_min", new Float(prod_min)));
ncfile.addAttribute(null, new Attribute("data_max", new Float(prod_max)));
ncfile.addAttribute(null, new Attribute("isRadial", new Integer(radial)));
}
/**
* uncompress the TDWR products
* @param buf compressed buffer
* @param offset data offset
* @param uncomplen uncompressed length
* @return
* @throws IOException
*/
byte[] uncompressed( ByteBuffer buf, int offset, int uncomplen ) throws IOException
{
byte[] header = new byte[offset];
buf.position(0);
buf.get(header);
byte[] out = new byte[offset+uncomplen];
System.arraycopy(header, 0, out, 0, offset);
CBZip2InputStream cbzip2 = new CBZip2InputStream();
int numCompBytes = buf.remaining();
byte[] bufc = new byte[numCompBytes];
buf.get(bufc, 0, numCompBytes);
ByteArrayInputStream bis = new ByteArrayInputStream(bufc, 2, numCompBytes - 2);
//CBZip2InputStream cbzip2 = new CBZip2InputStream(bis);
cbzip2.setStream(bis);
int total = 0;
int nread;
byte[] ubuff = new byte[40000];
byte[] obuff = new byte[40000];
try {
while ((nread = cbzip2.read(ubuff)) != -1) {
if (total + nread > obuff.length) {
byte[] temp = obuff;
obuff = new byte[temp.length * 2];
System.arraycopy(temp, 0, obuff, 0, temp.length);
}
System.arraycopy(ubuff, 0, obuff, total, nread);
total += nread;
}
if (obuff.length >= 0)
System.arraycopy(obuff, 0, out, offset, total);
} catch (BZip2ReadException ioe) {
log.warn("Nexrad2IOSP.uncompress "+raf.getLocation(), ioe);
}
return out;
}
/**
* convert two short into a integer
* @param s1 short one
* @param s2 short two
* @param swapBytes if swap bytes
* @return
*/
public static int shortsToInt(short s1, short s2, boolean swapBytes) {
byte[] b = new byte[4];
b[0] = (byte) (s1 >>> 8);
b[1] = (byte) (s1 >>> 0);
b[2] = (byte) (s2 >>> 8);
b[3] = (byte) (s2 >>> 0);
return bytesToInt(b, false);
}
/**
* convert bytes into integer
* @param bytes bytes array
* @param swapBytes if need to swap
* @return
*/
public static int bytesToInt(byte [] bytes, boolean swapBytes) {
byte a = bytes[0];
byte b = bytes[1];
byte c = bytes[2];
byte d = bytes[3];
if (swapBytes) {
return ((a & 0xff) ) +
((b & 0xff) << 8 ) +
((c & 0xff) << 16 ) +
((d & 0xff) << 24);
} else {
return ((a & 0xff) << 24 ) +
((b & 0xff) << 16 ) +
((c & 0xff) << 8 ) +
((d & 0xff) );
}
}
/*
** Name: read_dividlen
**
** Purpose: Read divider ID header from NEXRAD Level III product
**
*/
Sinfo read_dividlen( ByteBuffer buf, int offset )
{
byte[] b2 = new byte[2];
byte[] b4 = new byte[4];
short D_divider;
short D_id;
Short tShort ;
buf.position(offset);
buf.get(b2, 0, 2);
tShort = (Short)convert(b2, DataType.SHORT, -1);
D_divider = tShort.shortValue();
buf.get(b2, 0, 2);
D_id = (short)getInt(b2, 2);
buf.get(b4, 0, 4);
block_length = getInt(b4, 4);
buf.get(b2, 0, 2);
number_layers = (short)getInt(b2, 2);
return new Sinfo ( D_divider, D_id, block_length, number_layers);
}
void read_SATab( ByteBuffer buf, int offset )
{
byte[] b2 = new byte[2];
short B_divider;
short numPages;
short numChars;
Short tShort ;
buf.position(offset);
buf.get(b2, 0, 2);
tShort = (Short)convert(b2, DataType.SHORT, -1);
B_divider = tShort.shortValue();
if(B_divider != -1){
log.warn( "error reading stand alone tab message "+raf.getLocation());
}
buf.get(b2, 0, 2);
numPages = (Short)convert(b2, DataType.SHORT, -1);
for(int i = 0; i < numPages; i++){
buf.get(b2, 0, 2);
while(getInt(b2, 2) != -1) {
numChars = (short)getInt(b2, 2);
if(numChars < 0){
break;
}
byte[] tmp = new byte[numChars];
buf.get(tmp);
buf.get(b2, 0, 2);
}
}
ArrayList dims = new ArrayList();
Dimension tbDim = new Dimension("pageNumber", numPages);
ncfile.addDimension( null, tbDim);
dims.add( tbDim);
Variable ppage = new Variable(ncfile, null, null, "TabMessagePage");
ppage.setDimensions(dims);
ppage.setDataType(DataType.STRING);
ppage.addAttribute( new Attribute(CDM.LONG_NAME, "Stand Alone Tabular Alphanumeric Product Message"));
ncfile.addVariable(null, ppage);
//ppage.setSPobject( new Vinfo (numPages, 0, tblen, 0, hoff, ppos, isR, false, null, null, 82, 0));
}
/*
** Name: read_msghead
**
** Purpose: Read message header from NEXRAD Level III product
**
**
*/
int read_msghead( ByteBuffer buf, int offset)
{
byte[] b2 = new byte[2];
byte[] b4 = new byte[4];
buf.position(0);
buf.get(b2, 0, 2);
mcode = (short) getInt(b2, 2);
buf.get(b2, 0, 2);
mdate = (short) getInt(b2, 2);
buf.get(b4, 0, 4);
mtime = getInt(b4, 4);
buf.get(b4, 0, 4);
//out.println( "product date is " + dstring);
mlength = getInt(b4, 4);
buf.get(b2, 0, 2);
msource = (short) getInt(b2, 2);
if(stationId == null || stationName == null) {
try {
NexradStationDB.init(); // make sure database is initialized
NexradStationDB.Station station = NexradStationDB.getByIdNumber("000"+Short.toString(msource));
if (station != null) {
stationId = station.id;
stationName = station.name;
}
} catch (IOException ioe) {
log.error("NexradStationDB.init "+raf.getLocation(), ioe);
}
}
buf.get(b2, 0, 2);
mdestId = (short) getInt(b2, 2);
buf.get(b2, 0, 2);
mNumOfBlock = (short) getInt(b2, 2);
return 1;
}
/**
* get unsigned integer from byte array
* @param b
* @param num
* @return
*/
int getUInt( byte[] b, int num )
{
int base=1;
int i;
int word=0;
int bv[] = new int[num];
for (i = 0; i= 0; i-- ) {
word += base * bv[i];
base *= 256;
}
return word;
}
/**
* get signed integer from bytes
* @param b
* @param num
* @return
*/
int getInt( byte[] b, int num )
{
int base=1;
int i;
int word=0;
int bv[] = new int[num];
for (i = 0; i 127 )
{
bv[0] -= 128;
base = -1;
}
/*
** Calculate the integer value of the byte sequence
*/
for ( i = num-1; i >= 0; i-- ) {
word += base * bv[i];
base *= 256;
}
return word;
}
/***
* Concatenate two bytes to a 32-bit int value. a is the high order
* byte in the resulting int representation, unless swapBytes is true, in
* which b is the high order byte.
* @param a high order byte
* @param b low order byte
* @param swapBytes byte order swap flag
* @return 32-bit integer
*/
public static int bytesToInt(byte a, byte b, boolean swapBytes) {
// again, high order bit is expressed left into 32-bit form
if (swapBytes) {
return (a & 0xff) + ((int)b << 8);
} else {
return ((int)a << 8) + (b & 0xff);
}
}
/**
* convert unsigned byte to short
* @param b
* @return
*/
public short convertunsignedByte2Short(byte b)
{
return (short)((b<0)? (short)b + 256 : (short)b);
}
/**
* convert short to unsigned integer
* @param b
* @return
*/
public int convertShort2unsignedInt(short b)
{
return (b<0)? (-1)*b + 32768 : b;
}
/**
* get jave date
* @param julianDays
* @param msecs
* @return
*/
static public java.util.Date getDate(int julianDays, int msecs) {
long total = ((long) (julianDays - 1)) * 24 * 3600 * 1000 + msecs;
return new Date( total);
}
/*
** Name: read_proddesc
**
** Purpose: Read product description header from NEXRAD Level III product
**
**
*/
Pinfo read_proddesc( ByteBuffer buf, int offset ){
byte[] b2 = new byte[2];
byte[] b4 = new byte[4];
int off = offset;
Short tShort;
Integer tInt;
//Double tDouble = null;
/* thredds global att */
ncfile.addAttribute(null, new Attribute("title", "Nexrad Level 3 Data"));
ncfile.addAttribute(null, new Attribute("keywords", "WSR-88D; NIDS"));
ncfile.addAttribute(null, new Attribute("creator_name", "NOAA/NWS"));
ncfile.addAttribute(null, new Attribute("creator_url", "http://www.ncdc.noaa.gov/oa/radar/radarproducts.html"));
ncfile.addAttribute(null, new Attribute("naming_authority", "NOAA/NCDC"));
// ncfile.addAttribute(null, new Attribute("keywords_vocabulary", cname));
//out.println( "offset of buffer is " + off);
buf.position(offset);
buf.get(b2, 0, 2);
tShort = (Short)convert(b2, DataType.SHORT, -1);
divider = tShort.shortValue();
ncfile.addAttribute(null, new Attribute("Divider", tShort));
buf.get(b4, 0, 4);
tInt = (Integer)convert(b4, DataType.INT, -1);
latitude = tInt.intValue()/ 1000.0;
buf.get(b4, 0, 4);
tInt = (Integer)convert(b4, DataType.INT, -1);
longitude = tInt.intValue()/ 1000.0;
buf.get(b2, 0, 2);
height = getInt(b2, 2)*0.3048; // LOOK now in units of meters
if (useStationDB) { // override by station table for more accuracy
try {
NexradStationDB.init(); // make sure database is initialized
NexradStationDB.Station station = NexradStationDB.get("K"+stationId);
if (station != null) {
latitude = station.lat;
longitude = station.lon;
height = station.elev;
stationName = station.name;
}
} catch (IOException ioe) {
log.error("NexradStationDB.init "+raf.getLocation(), ioe);
}
}
ncfile.addAttribute(null, new Attribute("RadarLatitude", new Double(latitude)));
ncfile.addAttribute(null, new Attribute("RadarLongitude", new Double(longitude)));
ncfile.addAttribute(null, new Attribute("RadarAltitude", new Double(height)));
buf.get(b2, 0, 2);
pcode = (short)getInt(b2, 2);
if (stationId != null) ncfile.addAttribute(null, new Attribute("ProductStation", stationId));
if (stationName != null) ncfile.addAttribute(null, new Attribute("ProductStationName", stationName));
buf.get(b2, 0, 2);
opmode = (short)getInt(b2, 2);
ncfile.addAttribute(null, new Attribute("OperationalMode", new Short(opmode)));
buf.get(b2, 0, 2);
volumnScanPattern = (short)getInt(b2, 2);
ncfile.addAttribute(null, new Attribute("VolumeCoveragePatternName", new Short(volumnScanPattern)));
buf.get(b2, 0, 2);
sequenceNumber = (short)getInt(b2, 2);
ncfile.addAttribute(null, new Attribute("SequenceNumber", new Short(sequenceNumber)));
buf.get(b2, 0, 2);
volumeScanNumber = (short)getInt(b2, 2);
ncfile.addAttribute(null, new Attribute("VolumeScanNumber", new Short(volumeScanNumber)));
buf.get(b2, 0, 2);
volumeScanDate = (short)getUInt(b2, 2);
buf.get(b4, 0, 4);
volumeScanTime = getUInt(b4, 4);
buf.get(b2, 0, 2);
productDate = (short)getUInt(b2, 2);
buf.get(b4, 0, 4);
productTime = getUInt(b4, 4);
java.util.Date pDate = getDate( productDate, productTime*1000);
String dstring = formatter.toDateTimeStringISO(pDate);
ncfile.addAttribute(null, new Attribute("DateCreated", dstring));
buf.get(b2, 0, 2);
p1 = (short)getInt(b2, 2);
buf.get(b2, 0, 2);
p2 = (short)getInt(b2, 2);
buf.get(b2, 0, 2);
elevationNumber = (short)getInt(b2, 2);
ncfile.addAttribute(null, new Attribute("ElevationNumber",new Short(elevationNumber)));
buf.get(b2, 0, 2);
p3 = (short)getInt(b2, 2);
off += 40;
if(pcode == 182 || pcode == 186 || pcode == 32
|| pcode == 94 || pcode == 99) {
for(int i = 0; i< 16; i++) {
buf.get(b2, 0, 2);
threshold[i] = (short)bytesToInt(b2[0], b2[1], false);
}
} else if(pcode == 159 || pcode == 161 || pcode == 163
|| pcode == 170 || pcode == 172 || pcode == 173
|| pcode == 174 || pcode == 175 ) {
// Scale hw 31 32
buf.get(b4, 0, 4);
byte[] b44 = {b4[3], b4[2], b4[1], b4[0]};
threshold[0] = (short)(java.nio.ByteBuffer.wrap(b44).order(java.nio.ByteOrder.LITTLE_ENDIAN).getFloat()*100);
// offset hw 33 34
buf.get(b4, 0, 4);
byte[] b45 = {b4[3], b4[2], b4[1], b4[0]};
threshold[1] = (short)(java.nio.ByteBuffer.wrap(b45).order(java.nio.ByteOrder.LITTLE_ENDIAN).getFloat()*100);
// hw 35 reserve
buf.get(b2, 0, 2);
threshold[2] = 0;
// hw 36, 37, 38
for(int i = 3; i< 6; i++) {
buf.get(b2, 0, 2);
threshold[i] = (short)bytesToInt(b2[0], b2[1], false);
}
buf.get(b4, 0, 4);
buf.get(b4, 0, 4);
buf.get(b4, 0, 4);
buf.get(b4, 0, 4);
} else if(pcode == 176) {
// Scale hw 31 32
buf.get(b4, 0, 4);
byte[] b44 = {b4[3], b4[2], b4[1], b4[0]};
threshold[0] = (short)(java.nio.ByteBuffer.wrap(b44).order(java.nio.ByteOrder.LITTLE_ENDIAN).getFloat());
// offset hw 33 34
buf.get(b4, 0, 4);
byte[] b45 = {b4[3], b4[2], b4[1], b4[0]};
threshold[1] = (short)(java.nio.ByteBuffer.wrap(b45).order(java.nio.ByteOrder.LITTLE_ENDIAN).getFloat());
// hw 35 reserve
buf.get(b2, 0, 2);
threshold[2] = 0;
// hw 36, 37, 38
for(int i = 3; i< 6; i++) {
buf.get(b2, 0, 2);
threshold[i] = (short)bytesToInt(b2[0], b2[1], false);
}
buf.get(b4, 0, 4);
buf.get(b4, 0, 4);
buf.get(b4, 0, 4);
buf.get(b4, 0, 4);
}
else {
for(int i = 0; i< 16; i++) {
buf.get(b2, 0, 2);
threshold[i] = (short)getInt(b2, 2);
}
}
off += 32;
buf.get(b2, 0, 2);
p4 = (short)getInt(b2, 2);
//int t1 = getUInt(b2, 2);
buf.get(b2, 0, 2);
p5 = (short)getInt(b2, 2);
//t1 = getUInt(b2, 2);
buf.get(b2, 0, 2);
p6 = (short)getInt(b2, 2);
//t1 = getUInt(b2, 2);
buf.get(b2, 0, 2);
p7 = (short)getInt(b2, 2);
buf.get(b2, 0, 2);
p8 = (short)getInt(b2, 2);
buf.get(b2, 0, 2);
p9 = (short)getInt(b2, 2);
buf.get(b2, 0, 2);
p10 = (short)getUInt(b2, 2) ; //bytesToInt(b2[0], b2[1], true); // getInt(b2, 2); //
off += 14;
buf.get(b2, 0, 2);
numberOfMaps = (short)getInt(b2, 2);
ncfile.addAttribute(null, new Attribute("NumberOfMaps",new Short(numberOfMaps)));
off += 2;
buf.get(b4, 0, 4);
//tInt = (Integer)convert(b4, DataType.INT, -1);
offsetToSymbologyBlock = getInt(b4, 4);
//ncfile.addAttribute(null, new Attribute("offset_symbology_block",new Integer(offsetToSymbologyBlock)));
off += 4;
buf.get(b4, 0, 4);
//tInt = (Integer)convert(b4, DataType.INT, -1);
offsetToGraphicBlock = getInt(b4, 4);
//ncfile.addAttribute(null, new Attribute("offset_graphic_block",new Integer(offsetToGraphicBlock)));
off += 4;
buf.get(b4, 0, 4);
//tInt = (Integer)convert(b4, DataType.INT, -1);
offsetToTabularBlock = getInt(b4, 4);
//ncfile.addAttribute(null, new Attribute("offset_tabular_block",new Integer(offsetToTabularBlock)));
off += 4;
return new Pinfo (divider, latitude, longitude, height, pcode, opmode, threshold,
sequenceNumber, volumeScanNumber, volumeScanDate, volumeScanTime,
productDate, productTime, p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,
elevationNumber, numberOfMaps, offsetToSymbologyBlock,
offsetToGraphicBlock, offsetToTabularBlock);
//return pinfo;
}
//
/**
* this converts a byte array to another primitive array
* @param barray
* @param dataType
* @param nelems
* @param byteOrder
* @return
*/
protected Object convert( byte[] barray, DataType dataType, int nelems, int byteOrder) {
if (dataType == DataType.BYTE) {
return barray;
}
if (dataType == DataType.CHAR) {
return IospHelper.convertByteToChar( barray);
}
ByteBuffer bbuff = ByteBuffer.wrap( barray);
if (byteOrder >= 0)
bbuff.order( byteOrder == ucar.unidata.io.RandomAccessFile.LITTLE_ENDIAN? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
if (dataType == DataType.SHORT) {
ShortBuffer tbuff = bbuff.asShortBuffer();
short[] pa = new short[nelems];
tbuff.get( pa);
return pa;
} else if (dataType == DataType.INT) {
IntBuffer tbuff = bbuff.asIntBuffer();
int[] pa = new int[nelems];
tbuff.get( pa);
return pa;
} else if (dataType == DataType.FLOAT) {
FloatBuffer tbuff = bbuff.asFloatBuffer();
float[] pa = new float[nelems];
tbuff.get( pa);
return pa;
} else if (dataType == DataType.DOUBLE) {
DoubleBuffer tbuff = bbuff.asDoubleBuffer();
double[] pa = new double[nelems];
tbuff.get( pa);
return pa;
}
throw new IllegalStateException();
}
//
/**
* this converts a byte array to a wrapped primitive (Byte, Short, Integer, Double, Float, Long)
* @param barray
* @param dataType
* @param byteOrder
* @return
*/
protected Object convert( byte[] barray, DataType dataType, int byteOrder) {
if (dataType == DataType.BYTE) {
return new Byte( barray[0]);
}
if (dataType == DataType.CHAR) {
return new Character((char) barray[0]);
}
ByteBuffer bbuff = ByteBuffer.wrap( barray);
if (byteOrder >= 0)
bbuff.order( byteOrder == ucar.unidata.io.RandomAccessFile.LITTLE_ENDIAN? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
if (dataType == DataType.SHORT) {
ShortBuffer tbuff = bbuff.asShortBuffer();
return new Short(tbuff.get());
} else if (dataType == DataType.INT) {
IntBuffer tbuff = bbuff.asIntBuffer();
return new Integer(tbuff.get());
} else if (dataType == DataType.LONG) {
LongBuffer tbuff = bbuff.asLongBuffer();
return new Long(tbuff.get());
} else if (dataType == DataType.FLOAT) {
FloatBuffer tbuff = bbuff.asFloatBuffer();
return new Float(tbuff.get());
} else if (dataType == DataType.DOUBLE) {
DoubleBuffer tbuff = bbuff.asDoubleBuffer();
return new Double(tbuff.get());
}
throw new IllegalStateException();
}
//////////////////////////////////////////////////////////////////////////
// utilities
/**
* Flush all data buffers to disk.
* @throws IOException
*/
public void flush() throws IOException {
raf.flush();
}
/**
* Close the file.
* @throws IOException
*/
public void close() throws IOException {
if (raf != null)
raf.close();
}
/**
* Name: IsZlibed
*
* Purpose: Check a two-byte sequence to see if it indicates the start of
* a zlib-compressed buffer
*
*/
int isZlibHed( byte[] buf ){
short b0 = convertunsignedByte2Short(buf[0]);
short b1 = convertunsignedByte2Short(buf[1]);
if ( (b0 & 0xf) == Z_DEFLATED ) {
if ( (b0 >> 4) + 8 <= DEF_WBITS ) {
if ( (((b0 << 8) + b1) % 31)==0 ) {
return 1;
}
}
}
return 0;
}
/*
** Name: IsEncrypt
**
** Purpose: Check a two-byte sequence to see if it indicates the start of
** an encrypted image.
**
*/
int IsEncrypt( byte[] buf )
{
/*
** These tests were deduced from inspection from encrypted NOAAPORT files.
*/
String b = new String(buf, CDM.utf8Charset);
if ( b.startsWith("R3") ) {
return 1;
}
return 0;
}
/*
** Name: GetZlibedNexr
**
** Purpose: Read bytes from a NEXRAD Level III product into a buffer
** This routine reads compressed image data for Level III formatted file.
** We referenced McIDAS GetNexrLine function
*/
byte[] GetZlibedNexr( byte[] buf, int buflen, int hoff ) throws IOException
{
//byte[] uncompr = new byte[ZLIB_BUF_LEN ]; /* decompression buffer */
//long uncomprLen = ZLIB_BUF_LEN; /* length of decompress space */
int doff ; /* # bytes offset to image */
int numin; /* # input bytes processed */
numin = buflen - hoff ;
if( numin <= 0 )
{
log.warn(" No compressed data to inflate "+raf.getLocation());
return null;
}
//byte[] compr = new byte[numin-4]; /* compressed portion */
/*
** Uncompress first portion of the image. This should include:
**
** SHO\r\r\n <--+
** SEQ#\r\r\n | hoff bytes long
** WMO header\r\r\n |
** PIL\r\r\n <--+
**
** -> CCB
** WMO header
** PIL
** portion of the image
**
*/
/* a new copy of buff with only compressed bytes */
System.arraycopy( buf, hoff, buf, hoff, numin - 4);
// decompress the bytes
int resultLength;
int result = 0;
// byte[] inflateData = null;
byte[] tmp;
int uncompLen = 24500; /* length of decompress space */
byte[] uncomp = new byte[uncompLen];
Inflater inflater = new Inflater( false);
inflater.setInput(buf, hoff, numin-4);
int offset = 0;
int limit = 20000;
while ( inflater.getRemaining() > 0 )
{
try {
resultLength = inflater.inflate(uncomp, offset, 4000);
}
catch (DataFormatException ex) {
//System.out.println("ERROR on inflation "+ex.getMessage());
//ex.printStackTrace();
log.error("nids Inflater", ex);
throw new IOException( ex.getMessage(), ex);
}
offset = offset + resultLength;
result = result + resultLength;
if( result > limit ) {
// when uncomp data larger then limit, the uncomp need to increase size
tmp = new byte[ result];
System.arraycopy(uncomp, 0, tmp, 0, result);
uncompLen = uncompLen + 10000;
uncomp = new byte[uncompLen];
System.arraycopy(tmp, 0, uncomp, 0, result);
}
if( resultLength == 0 ) {
int tt = inflater.getRemaining();
byte [] b2 = new byte[2];
System.arraycopy(buf,hoff+numin-4-tt, b2, 0, 2);
if(result+tt > uncompLen){
tmp = new byte[ result];
System.arraycopy(uncomp, 0, tmp, 0, result);
uncompLen = uncompLen + 10000;
uncomp = new byte[uncompLen];
System.arraycopy(tmp, 0, uncomp, 0, result);
}
if( isZlibHed( b2 ) == 0 ) {
System.arraycopy(buf, hoff+numin-4-tt, uncomp, result, tt);
result = result + tt;
break;
}
inflater.reset();
inflater.setInput(buf, hoff+numin-4-tt, tt);
}
}
inflater.end();
/*
** Find out how long CCB is. This is done by using the lower order
** 6 bits from the first uncompressed byte and all 8 bits of the
** second uncompressed byte.
*/
doff = 2 * (((uncomp[0] & 0x3f) << 8) | (uncomp[1] & 0xFF));
for ( int i = 0; i < 2; i++ ) { /* eat WMO and PIL */
while ( (doff < result ) && (uncomp[doff] != '\n') ) doff++;
doff++;
}
byte[] data = new byte[ result - doff];
System.arraycopy(uncomp, doff, data, 0, result - doff);
//
/*
** Copy header bytes to decompression buffer. The objective is to
** create an output buffer that looks like an uncompressed NOAAPORT
** NEXRAD product:
**
** Section Product Example End
** +--------------------------------+
** | |
** 1 | start of product | CTRL-A \r\r\n
** | |
** +--------------------------------+
** | |
** 2 | sequence number | 237 \r\r\n
** | |
** +--------------------------------+
** | |
** 3 | WMO header | SDUS53 KARX 062213 \r\r\n
** | |
** +--------------------------------+
** | |
** 4 | PIL | N0RARX \r\r\n
** | |
** +--------------------------------+
** | |
** 5 | | AAO130006R2 CH-1
** | | Interface Control
** | CCB | Document (ICD)
** | | for the NWS NWSTG
** | | Figure 7-1 p 38
** | |
** +--------------------------------+
** | |
** 6 | WMO header | SDUS53 KARX 062213 \r\r\n
** | |
** +--------------------------------+
** | |
** 7 | PIL | N0RARX \r\r\n
** | |
** +--------------------------------+
** | |
** | |
** | |
** | |
** 8 | image |
** | |
** | |
** | |
** | |
** +--------------------------------+
** | |
** 9 | trailer | \r\r\nETX
** | |
** +--------------------------------+
** | |
** 10 | Unidata floater trailer | \0\0
** | |
** +--------------------------------+
**
** Sections 5-8 are zlib compressed. They must be uncompressed and
** read to find out where the image begins. When this is done, sections
** 5-7 are thrown away and 8 is returned immediately following 4.
** Section 9 and, if it is there, section 10 are also thrown away.
**
*/
return data;
}
/*
** Name: code_lookup
**
** Purpose: Derive some derivable metadata
**
*/
static int code_typelookup( int code )
{
int type;
final int[] types = {
Other, Other, Other, Other, Other, /* 0- 9 */
Other, Other, Other, Other, Other,
Other, Other, Other, Other, Other, /* 10- 19 */
Other, Base_Reflect, Base_Reflect, Base_Reflect, Base_Reflect,
BaseReflect248, Base_Reflect, Velocity, /* 20- 29 */
Velocity, Velocity, Velocity, Velocity, Velocity, SPECTRUM, SPECTRUM,
SPECTRUM, Other, DigitalHybridReflect, Other, Other, /* 30- 39 */
Comp_Reflect, Comp_Reflect, Comp_Reflect, Comp_Reflect, Other,
Other, Echo_Tops, Other, Other, Other, /* 40- 49 */
Other, Other, Other, VAD, Other,
Other, Other, Other, Other, Other, /* 50- 59 */
StrmRelMeanVel, StrmRelMeanVel, Vert_Liquid, Other, Other,
Other, Other, Other, Layer_Reflect_Avg, /* 60- 69 */
Layer_Reflect_Avg, Layer_Reflect_Max,
Layer_Reflect_Max, Other, Other, Other,
Other, Other, Other, Other, Other, /* 70- 79 */
Other, Other, Other, Precip_1, Precip_3,
Precip_Accum, Precip_Array, Other, /* 80- 89 */
Other, Other, Other, Other, Other, Other, Layer_Reflect_Avg,
Layer_Reflect_Max, Other, Other, Other, /* 90- 99 */
BaseReflectivityDR, Other, Other, Other, Other, BaseVelocityDV,
Other, Other, Other, Other, Other, /* 100-109 */
Other, Other, Other, Other, Other,
Other, Other, Other, Other, Other, /* 110-119 */
Other, Other, Other, Other, Other,
Other, Other, Other, Other, Other, /* 120-129 */
Other, Other, Other, Other, Other,
Other, Other, Other, Other, DigitalVert_Liquid, /* 130-139 */
EnhancedEcho_Tops, Other, Other, DigitalStormTotalPrecip, Other,
Other, Other, Other, Other, Other, /* 140-149 */
Other, Other, Other, Other, Other,
Other, Other, Other, Other, Other, /* 150-159 */
Other, Other, Other, Other, DigitalDifferentialReflectivity,
Other, DigitalCorrelationCoefficient, Other, DigitalDifferentialPhase, Other, /* 160-169 */
HydrometeorClassification, Other, Other, Other, OneHourAccumulation,
DigitalAccumulationArray, StormTotalAccumulation, DigitalStormTotalAccumulation,
Accumulation3Hour, Digital1HourDifferenceAccumulation,/* 170-179 */
DigitalTotalDifferenceAccumulation, DigitalInstantaneousPrecipitationRate,
HypridHydrometeorClassification, Other, Other,
Reflect1, Reflect1, Velocity1, Velocity1, Other, /* 180-189 */
SPECTRUM1, Reflect1, Reflect1, Other, Other,
};
if ( code < 0 || code > 189 )
type = Other;
else
type = types[code];
return type;
}
/*
* product id table
* @param code
* @param elevation
* @return
*/
static String pname_lookup( int code, int elevation )
{
String pname = null;
switch( code ){
case 15:
if(elevation == 1)
pname = "NAX";
else if(elevation == 3)
pname = "NBX";
else
pname = "N" + elevation/2 + "X";
break;
case 16:
if(elevation == 1)
pname = "NAC";
else if(elevation == 3)
pname = "NBC";
else
pname = "N" + elevation/2 + "C";
break;
case 17:
if(elevation == 1)
pname = "NAK";
else if(elevation == 3)
pname = "NBK";
else
pname = "N" + elevation/2 + "K";
break;
case 18:
if(elevation == 1)
pname = "NAH";
else if(elevation == 3)
pname = "NBH";
else
pname = "N" + elevation/2 + "H";
break;
case 19:
pname = "N" + elevation + "R";
break;
case 20:
pname = "N0Z";
break;
case 25:
pname = "N0W";
break;
case 27:
pname = "N" + elevation + "V";
break;
case 28:
pname = "NSP";
break;
case 30:
pname = "NSW";
break;
case 36:
pname = "NCO";
break;
case 37:
pname = "NCR";
break;
case 38:
pname = "NCZ";
break;
case 41:
pname = "NET";
break;
case 48:
pname = "NVW";
break;
case 56:
pname = "N" + elevation + "S";
break;
case 57:
pname = "NVL";
break;
case 65:
pname = "NLL";
break;
case 66:
pname = "NML";
break;
case 78:
pname = "N1P";
break;
case 79:
pname = "N3P";
break;
case 80:
pname = "NTP";
break;
case 81:
pname = "DPA";
break;
case 90:
pname = "NHL";
break;
case 94:
if(elevation == 1)
pname = "NAQ";
else if(elevation == 3)
pname = "NBQ";
else
pname = "N" + elevation/2 + "Q";
break;
case 99:
if(elevation == 1)
pname = "NAU";
else if(elevation == 3)
pname = "NBU";
else
pname = "N" + elevation/2 + "U";
break;
case 134:
pname = "DVL";
break;
case 135:
pname = "EET";
break;
case 182:
pname = "DV";
break;
case 187:
case 181:
pname = "R";
break;
case 186:
case 180:
pname = "DR";
break;
case 183:
pname = "V";
break;
case 185:
pname = "SW";
break;
default:
break;
}
return pname;
}
/*
* product resolution
* @param code
* @return
*/
static double code_reslookup( int code )
{
double data_res;
final double[] res = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0- 9 */
0, 0, 0, 0, 0, 0, 1, 2, 4, 1, /* 10- 19 */
2, 4, 0.25, 0.5, 1, 0.25, 0.5, 1, 0.25, 0, /* 20- 29 */
1, 0, 1, 0, 0, 1, 4, 1, 4, 0, /* 30- 39 */
0, 4, 0, 0, 0, 0, 0, 0, 0, 0, /* 40- 49 */
0, 0, 0, 0, 0, 0.5, 1, 4, 0, 0, /* 50- 59 */
0, 0, 0, 4, 4, 4, 4, 0, 0, 0, /* 60- 69 */
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, /* 70- 79 */
1, 4, 0, 0, 0, 0, 0, 0, 0, 4, /* 80- 89 */
4, 0, 0, 0, 1, 0, 0, 0, 0, 0.25, /* 90- 99 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 100-109 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110-119 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 120-129 */
0, 0, 0, 0, 1, 1, 0, 0, 1, 0, /* 130-139 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 140-149 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0.25, /* 150-159 */
0, 0.25, 0, 0.25, 0, 0.25, 0, 0, 0, 2, /* 160-169 */
0.25, 2, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0, 0, /* 170-179 */
0, 150.0, 150.0, 0, 0, 0, 300.0, 0, 0, 0, /* 180-189 */
};
if ( code < 0 || code > 189 )
data_res = 0;
else
data_res = res[code];
return data_res;
}
/*
* product level tabel
* @param code
* @return
*/
static int code_levelslookup( int code )
{
int level;
final int[] levels = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0- 9 */
0, 0, 0, 0, 0, 0, 8, 8, 8, 16, /* 10- 19 */
16, 16, 8, 8, 8, 16, 16, 16, 8, 0, /* 20- 29 */
8, 0, 256, 0, 0, 8, 8, 16, 16, 0, /* 30- 39 */
0, 16, 0, 0, 0, 0, 0, 0, 0, 0, /* 40- 49 */
0, 0, 0, 0, 0, 16, 16, 16, 0, 0, /* 50- 59 */
0, 0, 0, 8, 8, 8, 8, 0, 0, 0, /* 60- 69 */
0, 0, 0, 0, 0, 0, 0, 0, 16, 16, /* 70- 79 */
16, 256, 0, 0, 0, 0, 0, 0, 0, 8, /* 80- 89 */
8, 0, 0, 0, 256, 0, 0, 0, 0, 256, /* 90- 99 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 100-109 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110-119 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 120-129 */
0, 0, 0, 0, 256, 199, 0, 0, 256, 0, /* 130-139 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 140-149 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 256, /* 150-159 */
0, 256, 0, 256, 0, 256, 0, 0, 0, 16, /* 160-169 */
256, 16, 256, 256, 0, 0, 0, 16, 0, 0, /* 170-179 */
0, 16, 256, 0, 0, 0, 256, 0, 0, 0, /* 180-189 */
};
if ( code < 0 || code > 189 )
level = 0;
else
level = levels[code];
return level;
}
// Symbology block info for reading/writing
static class Sinfo {
short divider;
short id;
int blockLength;
short nlayers;
Sinfo( short divider, short id, int length, short layers) {
this.divider = divider;
this.id = id;
this.blockLength = length;
this.nlayers = layers;
}
}
// variable info for reading/writing
static class Vinfo {
int xt;
int x0;
int yt;
int y0;
boolean isRadial; // is it a radial variable?
long hoff; // header offset
long doff;
boolean isZlibed;
int[] pos;
int[] len;
int code;
int level;
Vinfo( int xt, int x0, int yt, int y0,long hoff, long doff, boolean isRadial, boolean isZ,
int[] pos, int[] len, int code, int level ) {
this.xt = xt;
this.yt = yt;
this.x0 = x0;
this.y0 = y0;
this.hoff = hoff;
this.doff = doff;
this.isRadial = isRadial;
this.isZlibed = isZ;
this.pos = pos;
this.len = len;
this.code = code;
this.level = level;
}
}
// product info for reading/writing
static class Pinfo {
short divider, pcode, opmode, sequenceNumber, volumeScanNumber, volumeScanDate, productDate;
// double latitude, longitude;
double height; // meters
int volumeScanTime, productTime;
short p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
int offsetToSymbologyBlock, offsetToGraphicBlock, offsetToTabularBlock;
short [] threshold;
Pinfo() {
// do nothing ;
}
Pinfo (short divider , double latitude, double longitude, double height, short pcode, short opmode, short[] threshold,
short sequenceNumber, short volumeScanNumber, short volumeScanDate, int volumeScanTime,
short productDate, int productTime, short p1,short p2,short p3,short p4,short p5,
short p6,short p7,short p8, short p9,short p10,
short elevationNumber, short numberOfMaps, int offsetToSymbologyBlock,
int offsetToGraphicBlock, int offsetToTabularBlock) {
this.divider = divider;
this.height = height;
this.pcode = pcode;
this.opmode = opmode;
this.sequenceNumber = sequenceNumber ;
this.volumeScanNumber = volumeScanNumber ;
this.volumeScanDate = volumeScanDate ;
this.volumeScanTime = volumeScanTime ;
this.productDate = productDate ;
this.productTime = productTime ;
this.p1 = p1 ;
this.p2 = p2 ;
this.p3 = p3 ;
this.p4 = p4;
this.p5 = p5 ;
this.p6 = p6 ;
this.p7 = p7 ;
this.p8 = p8 ;
this.p9 = p9 ;
this.p10 = p10 ;
this.threshold = threshold;
this.offsetToSymbologyBlock = offsetToSymbologyBlock ;
this.offsetToGraphicBlock = offsetToGraphicBlock ;
this.offsetToTabularBlock = offsetToTabularBlock ;
}
}
}