/* * (c) 1998-2016 University Corporation for Atmospheric Research/Unidata */ package ucar.nc2.iosp.gini; import ucar.nc2.*; import ucar.nc2.constants.*; import ucar.nc2.units.DateFormatter; import ucar.unidata.geoloc.*; import ucar.unidata.geoloc.projection.LambertConformal; import ucar.unidata.geoloc.projection.Stereographic; import ucar.unidata.geoloc.projection.Mercator; import ucar.ma2.Array; import ucar.ma2.DataType; import ucar.unidata.util.Parameter; import java.io.*; import java.util.*; import java.util.zip.Inflater; import java.util.zip.DataFormatException; import java.text.*; import java.nio.*; /** * Netcdf header reading and writing for version 3 file format. * This is used by Giniiosp. */ class Giniheader { static private final int GINI_PIB_LEN = 21; // gini product identification block static private final int GINI_PDB_LEN = 512; // gini product description block static private final int GINI_HED_LEN = GINI_PDB_LEN + GINI_PIB_LEN; // gini product header static private final double DEG_TO_RAD = 0.017453292; private boolean debug = false; private ucar.nc2.NetcdfFile ncfile; static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Giniheader.class); int dataStart = 0; // where the data starts protected int Z_type = 0; static public boolean isValidFile(ucar.unidata.io.RandomAccessFile raf) { try { return validatePIB(raf); } catch (IOException e) { return false; } } static private int findWMOHeader(String pib) { int pos = pib.indexOf("KNES"); if (pos == -1) pos = pib.indexOf("CHIZ"); if (pos != -1) { /* 'KNES' or 'CHIZ' found */ pos = pib.indexOf("\r\r\n"); /* ----- UPC mod 20030710 ----- */ if (pos != -1) { /* CR CR NL found */ pos = pos + 3; } } else { pos = 0; } return pos; } static boolean validatePIB(ucar.unidata.io.RandomAccessFile raf) throws IOException { int pos = 0; raf.seek(pos); // gini header process String pib = raf.readString(GINI_PIB_LEN + GINI_HED_LEN); return findWMOHeader(pib) != 0; } byte[] readPIB(ucar.unidata.io.RandomAccessFile raf) throws IOException { int pos = 0; raf.seek(pos); // gini header process byte[] b = new byte[GINI_PIB_LEN + GINI_HED_LEN]; byte[] buf = new byte[GINI_HED_LEN]; byte[] head = new byte[GINI_PDB_LEN]; raf.readFully(b); String pib = new String(b, CDM.utf8Charset); pos = findWMOHeader(pib); dataStart = pos + GINI_PDB_LEN; // Test the next two bytes to see if the image portion looks like // it is zlib-compressed byte[] b2 = new byte[] {b[pos], b[pos + 1]}; int pos1 = 0; if (Giniiosp.isZlibHed(b2)) { Z_type = 1; Inflater inflater = new Inflater(false); inflater.setInput(b, pos, GINI_HED_LEN); try { int resultLength = inflater.inflate(buf, 0, GINI_HED_LEN); if (resultLength != GINI_HED_LEN) log.warn("GINI: Zlib inflated image header size error"); } catch (DataFormatException ex) { log.error("ERROR on inflation " + ex.getMessage()); ex.printStackTrace(); throw new IOException(ex.getMessage()); } int inflatedLen = GINI_HED_LEN - inflater.getRemaining(); String inf = new String(buf, CDM.utf8Charset); pos1 = findWMOHeader(inf); System.arraycopy(buf, pos1, head, 0, GINI_PDB_LEN); dataStart = pos + inflatedLen; } else { System.arraycopy(b, pos, head, 0, GINI_PDB_LEN); } if (pos == 0 && pos1 == 0) { throw new IOException("Error on Gini File"); } return head; } void read(ucar.unidata.io.RandomAccessFile raf, ucar.nc2.NetcdfFile ncfile) throws IOException { this.ncfile = ncfile; int proj; /* projection type indicator */ /* 1 - Mercator */ /* 3 - Lambert Conf./Tangent Cone*/ /* 5 - Polar Stereographic */ int ent_id; /* GINI creation entity */ int sec_id; /* GINI sector ID */ int phys_elem; /* 1 - Visible, 2- 3.9IR, 3 - 6.7IR ..*/ int nx; int ny; int pole; int gyear; int gmonth; int gday; int ghour; int gminute; int gsecond; double lonv; /* meridian parallel to y-axis */ double lon1 = 0.0, lon2 = 0.0; double lat1 = 0.0, lat2 = 0.0; double latt; double imageScale = 0.0; byte[] head = readPIB(raf); ByteBuffer bos = ByteBuffer.wrap(head); Attribute att = new Attribute(CDM.CONVENTIONS, "GRIB"); this.ncfile.addAttribute(null, att); bos.position(0); //sat_id = (int )( raf.readByte()); Byte nv = bos.get(); att = new Attribute("source_id", nv); this.ncfile.addAttribute(null, att); nv = bos.get(); ent_id = nv.intValue(); att = new Attribute("entity_id", nv); this.ncfile.addAttribute(null, att); nv = bos.get(); sec_id = nv.intValue(); att = new Attribute("sector_id", nv); this.ncfile.addAttribute(null, att); nv = bos.get(); phys_elem = nv.intValue(); att = new Attribute("phys_elem", nv); this.ncfile.addAttribute(null, att); bos.position(bos.position() + 4); gyear = (int) (bos.get()); gyear += (gyear < 50) ? 2000 : 1900; //TODO: Find example where this hack is necessary gmonth = (int) (bos.get()); gday = (int) (bos.get()); ghour = (int) (bos.get()); gminute = (int) (bos.get()); gsecond = (int) (bos.get()); DateFormat dformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); dformat.setTimeZone(java.util.TimeZone.getTimeZone("GMT")); Calendar cal = Calendar.getInstance(); cal.set(Calendar.MILLISECOND, 0); cal.set(gyear, gmonth - 1, gday, ghour, gminute, gsecond); cal.setTimeZone(java.util.TimeZone.getTimeZone("GMT")); String dstring = dformat.format(cal.getTime()); Dimension dimT = new Dimension("time", 1, true, false, false); ncfile.addDimension(null, dimT); String timeCoordName = "time"; Variable taxis = new Variable(ncfile, null, null, timeCoordName); taxis.setDataType(DataType.DOUBLE); taxis.setDimensions("time"); taxis.addAttribute(new Attribute(CDM.LONG_NAME, "time since base date")); taxis.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Time.toString())); double[] tdata = new double[1]; tdata[0] = cal.getTimeInMillis(); Array dataA = Array.factory(DataType.DOUBLE.getPrimitiveClassType(), new int[]{1}, tdata); taxis.setCachedData(dataA, false); DateFormatter formatter = new DateFormatter(); taxis.addAttribute(new Attribute(CDM.UNITS, "msecs since " + formatter.toDateTimeStringISO(new Date(0)))); ncfile.addVariable(null, taxis); //att = new Attribute( "Time", dstring); //this.ncfile.addAttribute(null, att); this.ncfile.addAttribute(null, new Attribute("time_coverage_start", dstring)); this.ncfile.addAttribute(null, new Attribute("time_coverage_end", dstring)); bos.get(); /* skip a byte for hundreds of seconds */ nv = bos.get(); att = new Attribute("ProjIndex", nv); this.ncfile.addAttribute(null, att); proj = nv.intValue(); if (proj == 1) { att = new Attribute("ProjName", "MERCATOR"); } else if (proj == 3) { att = new Attribute("ProjName", "LAMBERT_CONFORNAL"); } else if (proj == 5) { att = new Attribute("ProjName", "POLARSTEREOGRAPHIC"); } this.ncfile.addAttribute(null, att); /* ** Get grid dimensions */ nx = bos.getShort(); att = new Attribute("NX", nx); this.ncfile.addAttribute(null, att); ny = bos.getShort(); att = new Attribute("NY", ny); this.ncfile.addAttribute(null, att); ProjectionImpl projection = null; double dxKm = 0.0, dyKm = 0.0, latin, lonProjectionOrigin; switch (proj) { case 1: /* Mercator */ /* ** Get the latitude and longitude of first and last "grid" points */ /* Latitude of first grid point */ lat1 = readScaledInt(bos); att = new Attribute("Latitude0", lat1); this.ncfile.addAttribute(null, att); lon1 = readScaledInt(bos); att = new Attribute("Longitude0", lon1); this.ncfile.addAttribute(null, att); /* Longitude of last grid point */ bos.get(); /* skip one byte */ lat2 = readScaledInt(bos); att = new Attribute("LatitudeN", lat2); this.ncfile.addAttribute(null, att); lon2 = readScaledInt(bos); att = new Attribute("LongitudeN", lon2); this.ncfile.addAttribute(null, att); /* ** Hack to catch incorrect sign of lon2 in header. */ // if ( lon1 > 0.0 && lon2 < 0.0 ) lon2 *= -1; double lon_1 = lon1; double lon_2 = lon2; if (lon1 < 0) lon_1 += 360.0; if (lon2 < 0) lon_2 += 360.0; lonv = (lon_1 + lon_2) / 2.0; if (lonv > 180.0) lonv -= 360.0; if (lonv < -180.0) lonv += 360.0; /* ** Get the "Latin" parameter. The ICD describes this value as: ** "Latin - The latitude(s) at which the Mercator projection cylinder ** intersects the earth." It should read that this is the latitude ** at which the image resolution is that defined by octet 41. */ bos.getInt(); /* skip 4 bytes */ bos.get(); /* skip 1 byte */ /* Latitude of proj cylinder intersects */ latin = readScaledInt(bos); att = new Attribute("LatitudeX", latin); this.ncfile.addAttribute(null, att); projection = new Mercator(lonv, latin); break; case 3: /* Lambert Conformal */ case 5: /* Polar Stereographic */ /* ** Get lat/lon of first grid point */ lat1 = readScaledInt(bos); lon1 = readScaledInt(bos); /* ** Get Lov - the orientation of the grid; i.e. the east longitude of ** the meridian which is parallel to the y-aixs */ bos.get(); /* skip one byte */ lonv = readScaledInt(bos); lonProjectionOrigin = lonv; att = new Attribute("Lov", lonv); this.ncfile.addAttribute(null, att); /* ** Get distance increment of grid */ dxKm = readScaledInt(bos); att = new Attribute("DxKm", dxKm); this.ncfile.addAttribute(null, att); dyKm = readScaledInt(bos); att = new Attribute("DyKm", dyKm); this.ncfile.addAttribute(null, att); /* calculate the lat2 and lon2 */ if (proj == 5) { latt = 60.0; /* Fixed for polar stereographic */ imageScale = (1. + Math.sin(DEG_TO_RAD * latt)) / 2.; } lat2 = lat1 + dyKm * (ny - 1) / 111.26; /* Convert to east longitude */ if (lonv < 0.) lonv += 360.; if (lon1 < 0.) lon1 += 360.; lon2 = lon1 + dxKm * (nx - 1) / 111.26 * Math.cos(DEG_TO_RAD * lat1); /* ** Convert to normal longitude to McIDAS convention */ lonv = (lonv > 180.) ? -(360. - lonv) : lonv; lon1 = (lon1 > 180.) ? -(360. - lon1) : lon1; lon2 = (lon2 > 180.) ? -(360. - lon2) : lon2; /* ** Check high bit of octet for North or South projection center */ nv = bos.get(); pole = nv.intValue(); pole = (pole > 127) ? -1 : 1; att = new Attribute("ProjCenter", pole); this.ncfile.addAttribute(null, att); bos.get(); /* skip one byte for Scanning mode */ latin = readScaledInt(bos); att = new Attribute("Latin", latin); this.ncfile.addAttribute(null, att); if (proj == 3) projection = new LambertConformal(latin, lonProjectionOrigin, latin, latin); else // (proj == 5) projection = new Stereographic(90.0, lonv, imageScale); break; default: System.out.println("unimplemented projection"); } this.ncfile.addAttribute(null, new Attribute("title", gini_GetEntityID(ent_id))); this.ncfile.addAttribute(null, new Attribute("summary", getPhysElemSummary(phys_elem, ent_id))); this.ncfile.addAttribute(null, new Attribute("id", gini_GetSectorID(sec_id))); this.ncfile.addAttribute(null, new Attribute("keywords_vocabulary", gini_GetPhysElemID(phys_elem, ent_id))); this.ncfile.addAttribute(null, new Attribute("cdm_data_type", FeatureType.GRID.toString())); this.ncfile.addAttribute(null, new Attribute(CF.FEATURE_TYPE, FeatureType.GRID.toString())); this.ncfile.addAttribute(null, new Attribute("standard_name_vocabulary", getPhysElemLongName(phys_elem, ent_id))); this.ncfile.addAttribute(null, new Attribute("creator_name", "UNIDATA")); this.ncfile.addAttribute(null, new Attribute("creator_url", "http://www.unidata.ucar.edu/")); this.ncfile.addAttribute(null, new Attribute("naming_authority", "UCAR/UCP")); this.ncfile.addAttribute(null, new Attribute("geospatial_lat_min", lat1)); this.ncfile.addAttribute(null, new Attribute("geospatial_lat_max", lat2)); this.ncfile.addAttribute(null, new Attribute("geospatial_lon_min", lon1)); this.ncfile.addAttribute(null, new Attribute("geospatial_lon_max", lon2)); //this.ncfile.addAttribute(null, new Attribute("geospatial_vertical_min", new Float(0.0))); //this.ncfile.addAttribute(null, new Attribute("geospatial_vertical_max", new Float(0.0))); /* * Get the image resolution. */ bos.position(41); /* jump to 42 bytes of PDB */ nv = bos.get(); /* Res [km] */ att = new Attribute("imageResolution", nv); this.ncfile.addAttribute(null, att); // if(proj == 1) // dyKm = nv.doubleValue()/dyKm; /* compression flag 43 byte */ nv = bos.get(); /* Res [km] */ att = new Attribute("compressionFlag", nv); this.ncfile.addAttribute(null, att); if (DataType.unsignedByteToShort(nv) == 128) { Z_type = 2; //out.println( "ReadNexrInfo:: This is a Z file "); } /* new 47 - 60 */ bos.position(46); nv = bos.get(); /* Cal indicator */ int navcal = DataType.unsignedByteToShort(nv); int[] calcods = null; if (navcal == 128) calcods = getCalibrationInfo(bos, phys_elem, ent_id); // only one data variable per gini file String vname = gini_GetPhysElemID(phys_elem, ent_id); Variable var = new Variable(ncfile, ncfile.getRootGroup(), null, vname); var.addAttribute(new Attribute(CDM.LONG_NAME, getPhysElemLongName(phys_elem, ent_id))); var.addAttribute(new Attribute(CDM.UNITS, getPhysElemUnits(phys_elem, ent_id))); // var.addAttribute( new Attribute(CDM.MISSING_VALUE, new Byte((byte) 0))); // ?? // get dimensions List<Dimension> dims = new ArrayList<>(); Dimension dimX = new Dimension("x", nx, true, false, false); Dimension dimY = new Dimension("y", ny, true, false, false); ncfile.addDimension(null, dimY); ncfile.addDimension(null, dimX); dims.add(dimT); dims.add(dimY); dims.add(dimX); var.setDimensions(dims); // size and beginning data position in file long begin = dataStart; if (debug) log.warn(" name= " + vname + " velems=" + var.getSize() + " begin= " + begin + "\n"); if (navcal == 128) { var.setDataType(DataType.FLOAT); var.setSPobject(new Vinfo(begin, nx, ny, calcods)); /* var.addAttribute(new Attribute("_Unsigned", "true")); int numer = calcods[0] - calcods[1]; int denom = calcods[2] - calcods[3]; float a = (numer*1.f) / (1.f*denom); float b = calcods[0] - a * calcods[2]; var.addAttribute( new Attribute("scale_factor", new Float(a))); var.addAttribute( new Attribute("add_offset", new Float(b))); */ } else { var.setDataType(DataType.BYTE); var.addAttribute(new Attribute(CDM.UNSIGNED, "true")); // var.addAttribute(new Attribute("_missing_value", new Short((short)255))); var.addAttribute(new Attribute(CDM.SCALE_FACTOR, (short) (1))); var.addAttribute(new Attribute(CDM.ADD_OFFSET, (short) (0))); var.setSPobject(new Vinfo(begin, nx, ny)); } String coordinates = "x y time"; var.addAttribute(new Attribute(_Coordinate.Axes, coordinates)); ncfile.addVariable(null, var); // add coordinate information. we need: // nx, ny, dx, dy, // latin, lov, la1, lo1 // we have to project in order to find the origin ProjectionPoint start = projection.latLonToProj(new LatLonPointImpl(lat1, lon1)); if (debug) log.warn("start at proj coord " + start); double startx = start.getX(); double starty = start.getY(); // create coordinate variables Variable xaxis = new Variable(ncfile, null, null, "x"); xaxis.setDataType(DataType.DOUBLE); xaxis.setDimensions("x"); xaxis.addAttribute(new Attribute(CDM.LONG_NAME, "projection x coordinate")); xaxis.addAttribute(new Attribute(CDM.UNITS, "km")); xaxis.addAttribute(new Attribute(_Coordinate.AxisType, "GeoX")); double[] data = new double[nx]; if (proj == 1) { double lon_1 = lon1; double lon_2 = lon2; if (lon1 < 0) lon_1 += 360.0; if (lon2 < 0) lon_2 += 360.0; double dx = (lon_2 - lon_1) / (nx - 1); for (int i = 0; i < data.length; i++) { double ln = lon1 + i * dx; ProjectionPoint pt = projection.latLonToProj(new LatLonPointImpl(lat1, ln)); data[i] = pt.getX(); // startx + i*dx; } } else { for (int i = 0; i < data.length; i++) data[i] = startx + i * dxKm; } dataA = Array.factory(DataType.DOUBLE.getPrimitiveClassType(), new int[]{nx}, data); xaxis.setCachedData(dataA, false); ncfile.addVariable(null, xaxis); Variable yaxis = new Variable(ncfile, null, null, "y"); yaxis.setDataType(DataType.DOUBLE); yaxis.setDimensions("y"); yaxis.addAttribute(new Attribute(CDM.LONG_NAME, "projection y coordinate")); yaxis.addAttribute(new Attribute(CDM.UNITS, "km")); yaxis.addAttribute(new Attribute(_Coordinate.AxisType, "GeoY")); data = new double[ny]; double endy = starty + dyKm * (data.length - 1); // apparently lat1,lon1 is always the lower ledt, but data is upper left if (proj == 1) { double dy = (lat2 - lat1) / (ny - 1); for (int i = 0; i < data.length; i++) { double la = lat2 - i * dy; ProjectionPoint pt = projection.latLonToProj(new LatLonPointImpl(la, lon1)); data[i] = pt.getY(); //endyy - i*dy; } } else { for (int i = 0; i < data.length; i++) data[i] = endy - i * dyKm; } dataA = Array.factory(DataType.DOUBLE.getPrimitiveClassType(), new int[]{ny}, data); yaxis.setCachedData(dataA, false); ncfile.addVariable(null, yaxis); // coordinate transform variable Variable ct = new Variable(ncfile, null, null, projection.getClassName()); ct.setDataType(DataType.CHAR); ct.setDimensions(""); for (Parameter p : projection.getProjectionParameters()) { ct.addAttribute(new Attribute(p)); } ct.addAttribute(new Attribute(_Coordinate.TransformType, "Projection")); ct.addAttribute(new Attribute(_Coordinate.Axes, "x y ")); // fake data dataA = Array.factory(DataType.CHAR.getPrimitiveClassType(), new int[]{}); dataA.setChar(dataA.getIndex(), ' '); ct.setCachedData(dataA, false); ncfile.addVariable(null, ct); ncfile.addAttribute(null, new Attribute(CDM.CONVENTIONS, _Coordinate.Convention)); // finish ncfile.finish(); } int[] getCalibrationInfo(ByteBuffer bos, int phys_elem, int ent_id) { bos.position(46); byte nv = bos.get(); /* Cal indicator */ int navcal = DataType.unsignedByteToShort(nv); int[] calcods = null; if (navcal == 128) { /* Unidata Cal block found; unpack values */ int scale = 10000; int jscale = 100000000; byte[] unsb = new byte[8]; bos.get(unsb); bos.position(55); nv = bos.get(); int calcod = DataType.unsignedByteToShort(nv); if (calcod > 0) { calcods = new int[5 * calcod + 1]; calcods[0] = calcod; for (int i = 0; i < calcod; i++) { bos.position(56 + i * 16); int minb = bos.getInt() / 10000; /* min brightness values */ int maxb = bos.getInt() / 10000; /* max brightness values */ int mind = bos.getInt(); /* min data values */ int maxd = bos.getInt(); /* max data values */ int idscal = 1; while (mind % idscal == 0 && maxd % idscal == 0) { idscal *= 10; } idscal /= 10; if (idscal < jscale) jscale = idscal; calcods[1 + i * 5] = mind; calcods[2 + i * 5] = maxd; calcods[3 + i * 5] = minb; calcods[4 + i * 5] = maxb; calcods[5 + i * 5] = 0; } if (jscale > scale) jscale = scale; scale /= jscale; if (gini_GetPhysElemID(phys_elem, ent_id).contains("Precipitation")) { if (scale < 100) { jscale /= (100 / scale); scale = 100; } } for (int i = 0; i < calcod; i++) { calcods[1 + i * 5] /= jscale; calcods[2 + i * 5] /= jscale; calcods[5 + i * 5] = scale; } } } return calcods; } int gini_GetCompressType() { return Z_type; } // Return the string of Sector for the GINI image file String gini_GetSectorID(int ent_id) { String name; switch (ent_id) { case 0: name = "Northern Hemisphere Composite"; break; case 1: name = "East CONUS"; break; case 2: name = "West CONUS"; break; case 3: name = "Alaska Regional"; break; case 4: name = "Alaska National"; break; case 5: name = "Hawaii Regional"; break; case 6: name = "Hawaii National"; break; case 7: name = "Puerto Rico Regional"; break; case 8: name = "Puerto Rico National"; break; case 9: name = "Supernational"; break; case 10: name = "NH Composite - Meteosat/GOES E/ GOES W/GMS"; break; case 11: name = "Central CONUS"; break; case 12: name = "East Floater"; break; case 13: name = "West Floater"; break; case 14: name = "Central Floater"; break; case 15: name = "Polar Floater"; break; default: name = "Unknown-ID"; } return name; } // Return the channel ID for the GINI image file String gini_GetEntityID(int ent_id) { String name; switch (ent_id) { case 2: name = "Miscellaneous"; break; case 3: name = "JERS"; break; case 4: name ="ERS/QuikSCAT/Scatterometer"; break; case 5: name = "POES/NPOESS"; break; case 6: name = "Composite"; break; case 7: name = "DMSP satellite Image"; break; case 8: name = "GMS satellite Image"; break; case 9: name = "METEOSAT satellite Image"; break; case 10: name = "GOES-7 satellite Image"; break; case 11: name = "GOES-8 satellite Image"; break; case 12: name = "GOES-9 satellite Image"; break; case 13: name = "GOES-10 satellite Image"; break; case 14: name = "GOES-11 satellite Image"; break; case 15: name = "GOES-12 satellite Image"; break; case 16: name = "GOES-13 satellite Image"; break; case 17: name = "GOES-14 satellite Image"; break; case 18: name = "GOES-15 satellite Image"; break; case 19: // GOES-R name = "GOES-16 satellite Image"; break; case 99: // special snowflake GEMPAK Composite Images generated by Unidata name = "RADAR-MOSIAC Composite Image"; break; default: name = "Unknown"; } return name; } // Return the channel ID for the GINI image file String gini_GetPhysElemID(int phys_elem, int ent_id) { String name; switch (phys_elem) { case 1: name = "VIS"; break; case 3: name = "IR_WV"; break; case 2: case 4: case 5: case 6: case 7: name = "IR"; break; case 13: name = "LI"; break; case 14: name = "PW"; break; case 15: name = "SFC_T"; break; case 16: name = "LI"; break; case 17: name = "PW"; break; case 18: name = "SFC_T"; break; case 19: name = "CAPE"; break; case 20: name = "T"; break; case 21: name = "WINDEX"; break; case 22: name = "DMPI"; break; case 23: name = "MDPI"; break; case 25: if (ent_id == 99) name = "HHC"; else name = "Volcano_imagery"; break; case 26: name = "EchoTops"; break; case 27: if (ent_id == 99) name = "Reflectivity"; else name = "CTP"; break; case 28: if (ent_id == 99) name = "Reflectivity"; else name = "Cloud_Amount"; break; case 29: name = "VIL"; break; case 30: case 31: name = "Precipitation"; break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: case 58: name = "sounder_imagery"; break; case 59: name = "VIS_sounder"; break; default: name = "Unknown"; } return name; } // ?? String getPhysElemUnits(int phys_elem, int ent_id) { switch (phys_elem) { case 1: case 3: case 2: case 4: case 5: case 6: case 7: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 25: case 43: case 48: case 50: case 51: case 52: case 55: case 57: case 59: return "N/A"; case 26: return "kft"; case 27: if (ent_id == 99) return "dBz"; else return "N/A"; case 28: if (ent_id == 99) return "dBz"; else return "N/A"; case 29: return "kg m-2"; case 30: return "IN"; case 31: return "IN"; default: return "Unknown"; } } // Return the channel ID for the GINI image file String getPhysElemLongName(int phys_elem, int ent_id) { switch (phys_elem) { case 1: return "Imager Visible"; case 2: return "Imager 3.9 micron IR"; case 3: return "Imager 6.7/6.5 micron IR (WV)"; case 4: return "Imager 11 micron IR"; case 5: return "Imager 12 micron IR"; case 6: return "Imager 13 micron IR"; case 7: return "Imager 1.3 micron IR"; case 13: return "Lifted Index LI"; case 14: return "Precipitable Water PW"; case 15: return "Surface Skin Temperature"; case 16: return "Lifted Index LI"; case 17: return "Precipitable Water PW"; case 18: return "Surface Skin Temperature"; case 19: return "Convective Available Potential Energy"; case 20: return "land-sea Temperature"; case 21: return "Wind Index"; case 22: return "Dry Microburst Potential Index"; case 23: return "Microburst Potential Index"; case 24: return "Derived Convective Inhibition"; case 25: if (ent_id == 99) return "1km National Hybrid Hydrometeor Classification Composite (Unidata)"; else return "Volcano_imagery"; case 26: if (ent_id == 99) return "1 km National Echo Tops Composite (Unidata)"; else return "4 km National Echo Tops"; case 27: if (ent_id == 99) return "1 km National Base Reflectivity Composite (Unidata)"; else return "Cloud Top Pressure or Height"; case 28: if (ent_id == 99) return "1 km National Reflectivity Composite (Unidata)"; else return "Cloud Amount"; case 29: if (ent_id == 99) return "1 km National Vertically Integrated Liquid Water (Unidata)"; else return "4 km National Vertically Integrated Liquid Water"; case 30: if (ent_id == 99) return "1 km National 1-hour Precipitation (Unidata)"; else return "Surface wind speeds over oceans and Great Lakes"; case 31: if (ent_id == 99) return "4 km National Storm Total Precipitation (Unidata)"; else return "Surface Wetness"; case 32: return "Ice concentrations"; case 33: return "Ice type"; case 34: return "Ice edge"; case 35: return "Cloud water content"; case 36: return "Surface type"; case 37: return "Snow indicator"; case 38: return "Snow/water content"; case 39: return "Derived volcano imagery"; case 41: return "Sounder 14.71 micron imagery"; case 42: return "Sounder 14.37 micron imagery"; case 43: return "Sounder 14.06 micron imagery"; case 44: return "Sounder 13.64 micron imagery"; case 45: return "Sounder 13.37 micron imagery"; case 46: return "Sounder 12.66 micron imagery"; case 47: return "Sounder 12.02 micron imagery"; case 48: return "11.03 micron sounder image"; case 49: return "Sounder 11.03 micron imagery"; case 50: return "7.43 micron sounder image"; case 51: return "7.02 micron sounder image"; case 52: return "6.51 micron sounder image"; case 53: return "Sounder 4.57 micron imagery"; case 54: return "Sounder 4.52 micron imagery"; case 55: return "4.45 micron sounder image"; case 56: return "Sounder 4.13 micron imagery"; case 57: return "3.98 micron sounder image"; case 58: return "Sounder 3.74 micron imagery"; case 59: return "VIS sounder image "; default: return "unknown physical element " + phys_elem; } } String getPhysElemSummary(int phys_elem, int ent_id) { switch (phys_elem) { case 1: return "Satellite Product Imager Visible"; case 2: return "Satellite Product Imager 3.9 micron IR"; case 3: return "Satellite Product Imager 6.7/6.5 micron IR (WV)"; case 4: return "Satellite Product Imager 11 micron IR"; case 5: return "Satellite Product Imager 12 micron IR"; case 6: return "Satellite Product Imager 13 micron IR"; case 7: return "Satellite Product Imager 1.3 micron IR"; case 13: return "Imager Based Derived Lifted Index LI"; case 14: return "Imager Based Derived Precipitable Water PW"; case 15: return "Imager Based Derived Surface Skin Temperature"; case 16: return "Sounder Based Derived Lifted Index LI"; case 17: return "Sounder Based Derived Precipitable Water PW"; case 18: return "Sounder Based Derived Surface Skin Temperature"; case 19: return "Derived Convective Available Potential Energy CAPE"; case 20: return "Derived land-sea Temperature"; case 21: return "Derived Wind Index WINDEX"; case 22: return "Derived Dry Microburst Potential Index DMPI"; case 23: return "Derived Microburst Day Potential Index MDPI"; case 43: return "Satellite Product 14.06 micron sounder image"; case 48: return "Satellite Product 11.03 micron sounder image"; case 50: return "Satellite Product 7.43 micron sounder image"; case 51: return "Satellite Product 7.02 micron sounder image"; case 52: return "Satellite Product 6.51 micron sounder image"; case 55: return "Satellite Product 4.45 micron sounder image"; case 57: return "Satellite Product 3.98 micron sounder image"; case 59: return "Satellite Product VIS sounder visible image "; case 25: if (ent_id == 99) return "National Hybrid Hydrometeor Classification Composite at Resolution 1 km"; else return "Satellite Derived Volcano_imagery"; case 26: if (ent_id == 99) return "Nexrad Level 3 National Echo Tops at Resolution 1 km"; else return "Nexrad Level 3 National Echo Tops at Resolution 4 km"; case 27: if (ent_id == 99) return "Nexrad Level 3 Base Reflectivity National Composition at Resolution 1 km"; else return "Gridded Cloud Top Pressure or Height"; case 28: if (ent_id == 99) return "Nexrad Level 3 National 248 nm Base Composite Reflectivity at Resolution 2 km"; else return "Gridded Cloud Amount"; case 29: if (ent_id == 99) return "Nexrad Level 3 National Vertically Integrated Liquid Water at Resolution 1 km"; else return "Nexrad Level 3 National Vertically Integrated Liquid Water at Resolution 4 km"; case 30: return "Nexrad Level 3 1 Hour Precipitation National Composition at Resolution 2 km"; case 31: return "Nexrad Level 3 Storm Total Precipitation National Composition at Resolution 4 km"; default: return "unknown"; } } // Read a scaled, 3-byte integer from file and convert to double private double readScaledInt(ByteBuffer buf) { // Get the first two bytes short s1 = buf.getShort(); // And the last one as unsigned short s2 = DataType.unsignedByteToShort(buf.get()); // Get the sign bit, converting from 0 or 2 to +/- 1. int posneg = 1 - ((s1 & 0x8000) >> 14); // Combine the first two bytes (without sign bit) with the last byte. // Multiply by proper factor for +/- int nn = (((s1 & 0x7FFF) << 8) | s2) * posneg; return (double) nn / 10000.0; } //////////////////////////////////////////////////////////////////////////////////////////////////////// // variable info for reading/writing static class Vinfo { long begin; // offset of start of data from start of file int nx; int ny; int[] levels; Vinfo(long begin, int x, int y) { this.begin = begin; this.nx = x; this.ny = y; } Vinfo(long begin, int x, int y, int[] levels) { this.begin = begin; this.nx = x; this.ny = y; this.levels = levels; } } }