/* * Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata * * Portions of this software were developed by the Unidata Program at the * University Corporation for Atmospheric Research. * * Access and use of this software shall impose the following obligations * and understandings on the user. The user is granted the right, without * any fee or cost, to use, copy, modify, alter, enhance and distribute * this software, and any derivative works thereof, and its supporting * documentation for any purpose whatsoever, provided that this entire * notice appears in all copies of the software, derivative works and * supporting documentation. Further, UCAR requests that the user credit * UCAR/Unidata in any publications that result from the use of this * software or in any product that includes this software. The names UCAR * and/or Unidata, however, may not be used in any advertising or publicity * to endorse or promote any products or commercial entity unless specific * written permission is obtained from UCAR/Unidata. The user also * understands that UCAR/Unidata is not obligated to provide the user with * any support, consulting, training or assistance of any kind with regard * to the use, operation and performance of this software nor to provide * the user with any updates, revisions, new versions or "bug fixes." * * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. */ package ucar.nc2.iosp.misc; import ucar.nc2.constants.AxisType; import ucar.nc2.constants.CDM; import ucar.nc2.constants.CF; import ucar.nc2.constants._Coordinate; import ucar.unidata.io.RandomAccessFile; import ucar.nc2.units.DateFormatter; import ucar.nc2.iosp.AbstractIOServiceProvider; import ucar.nc2.*; import ucar.nc2.util.CancelTask; import ucar.ma2.*; import java.io.IOException; import java.io.EOFException; import java.util.*; import java.nio.ByteBuffer; /** * NMC Office Note 29 * * @author caron * @since Feb 22, 2008 */ public class NmcObsLegacy extends AbstractIOServiceProvider { static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NmcObsLegacy.class); private List<Station> stations = new ArrayList<>(); private List<Report> reports = new ArrayList<>(); //private Map<String, List<Report>> map = new HashMap<String, List<Report>>(); //private List<String> stations; // private int nobs = 0, nstations = 0; private Calendar cal = null; private DateFormatter dateFormatter = new DateFormatter(); private Date refDate; // from the header private List<StructureCode> catStructures = new ArrayList<>(10); private boolean showObs = false, showSkip = false, showOverflow = false, showData = false, showHeader = false, showTime = false; private boolean checkType = false, checkPositions = false; public boolean isValidFile(RandomAccessFile raf) throws IOException { raf.seek(0); if (raf.length() < 60) return false; byte[] h = raf.readBytes(60); // 32 - 56 are X's for (int i = 32; i < 56; i++) if (h[i] != (byte) 'X') return false; try { short hour = Short.parseShort(new String(h, 0, 2, CDM.utf8Charset)); short minute = Short.parseShort(new String(h, 2, 2, CDM.utf8Charset)); short year = Short.parseShort(new String(h, 4, 2, CDM.utf8Charset)); short month = Short.parseShort(new String(h, 6, 2, CDM.utf8Charset)); short day = Short.parseShort(new String(h, 8, 2, CDM.utf8Charset)); if ((hour < 0) || (hour > 24)) return false; if ((minute < 0) || (minute > 60)) return false; if ((year < 0) || (year > 100)) return false; if ((month < 0) || (month > 12)) return false; if ((day < 0) || (day > 31)) return false; } catch (Exception e) { return false; } return true; } public String getFileTypeId() { return "NMCon29"; } public String getFileTypeDescription() { return "NMC Office Note 29"; } public void open(RandomAccessFile raf, NetcdfFile ncfile, CancelTask cancelTask) throws IOException { super.open(raf, ncfile, cancelTask); init(); ncfile.addAttribute(null, new Attribute(CDM.HISTORY, "Direct read of NMC ON29 by CDM")); ncfile.addAttribute(null, new Attribute(CDM.CONVENTIONS, "Unidata")); ncfile.addAttribute(null, new Attribute(CF.FEATURE_TYPE, CF.FeatureType.timeSeriesProfile.toString())); try { ncfile.addDimension(null, new Dimension("station", stations.size())); Structure station = makeStationStructure(); ncfile.addVariable(null, station); ncfile.addDimension(null, new Dimension("report", reports.size())); Structure reportIndexVar = makeReportIndexStructure(); ncfile.addVariable(null, reportIndexVar); Structure reportVar = makeReportStructure(); ncfile.addVariable(null, reportVar); } catch (InvalidRangeException e) { log.error("open ON29 File", e); throw new IllegalStateException(e.getMessage()); } } public Array readData(Variable v, Section section) throws IOException, InvalidRangeException { if (v.getShortName().equals("station")) return readStation(v, section); else if (v.getShortName().equals("report")) return readReport(v, section); else if (v.getShortName().equals("reportIndex")) return readReportIndex(v, section); throw new IllegalArgumentException("Unknown variable name= "+v.getShortName()); } /////////////////////////////////////////////////////////////////////////////////// private Structure makeStationStructure() throws IOException, InvalidRangeException { Structure station = new Structure(ncfile, null, null, "station"); station.setDimensions("station"); station.addAttribute(new Attribute(CDM.LONG_NAME, "unique stations within this file")); int pos = 0; Variable v = station.addMemberVariable(new Variable(ncfile, null, station, "stationName", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{6}); v.addAttribute(new Attribute(CDM.LONG_NAME, "name of station")); v.addAttribute(new Attribute("standard_name", "station_name")); v.setSPobject(new Vinfo(pos)); pos += 6; v = station.addMemberVariable(new Variable(ncfile, null, station, "lat", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, CDM.LAT_UNITS)); v.addAttribute(new Attribute(CDM.LONG_NAME, "geographic latitude")); v.addAttribute(new Attribute("accuracy", "degree/100")); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lat.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = station.addMemberVariable(new Variable(ncfile, null, station, "lon", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, CDM.LON_UNITS)); v.addAttribute(new Attribute(CDM.LONG_NAME, "geographic longitude")); v.addAttribute(new Attribute("accuracy", "degree/100")); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Lon.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = station.addMemberVariable(new Variable(ncfile, null, station, "elev", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "meters")); v.addAttribute(new Attribute(CDM.LONG_NAME, "station elevation above MSL")); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Height.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = station.addMemberVariable(new Variable(ncfile, null, station, "nrecords", DataType.INT, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "number of records")); v.addAttribute(new Attribute("standard_name", "npts")); v.setSPobject(new Vinfo(pos)); return station; } private Structure makeReportIndexStructure() throws InvalidRangeException, IOException { Structure reportIndex = new Structure(ncfile, null, null, "reportIndex"); reportIndex.setDimensions("report"); reportIndex.addAttribute(new Attribute(CDM.LONG_NAME, "index on report - in memory")); int pos = 0; Variable v = reportIndex.addMemberVariable(new Variable(ncfile, null, reportIndex, "stationName", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{6}); v.addAttribute(new Attribute(CDM.LONG_NAME, "name of station")); v.addAttribute(new Attribute("standard_name", "station_name")); v.setSPobject(new Vinfo(pos)); pos += 6; v = reportIndex.addMemberVariable(new Variable(ncfile, null, reportIndex, "time", DataType.INT, "")); v.addAttribute(new Attribute(CDM.UNITS, "secs since 1970-01-01 00:00")); v.addAttribute(new Attribute(CDM.LONG_NAME, "observation time")); v.setSPobject(new Vinfo(pos)); return reportIndex; } private Structure makeReportStructure() throws InvalidRangeException, IOException { Structure report = new Structure(ncfile, null, null, "report"); report.setDimensions("report"); report.addAttribute(new Attribute(CDM.LONG_NAME, "ON29 observation report")); int pos = 0; Variable v = report.addMemberVariable(new Variable(ncfile, null, report, "time", DataType.INT, "")); v.addAttribute(new Attribute(CDM.UNITS, "secs since 1970-01-01 00:00")); v.addAttribute(new Attribute(CDM.LONG_NAME, "observation time")); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Time.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = report.addMemberVariable(new Variable(ncfile, null, report, "timeISO", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{20}); v.addAttribute(new Attribute(CDM.LONG_NAME, "ISO formatted date/time")); v.setSPobject(new Vinfo(pos)); pos += 20; v = report.addMemberVariable(new Variable(ncfile, null, report, "reportType", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "report type from Table R.1")); v.setSPobject(new Vinfo(pos)); pos += 2; // only for ON29 v = report.addMemberVariable(new Variable(ncfile, null, report, "instType", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "instrument type from Table R.2")); v.setSPobject(new Vinfo(pos)); pos += 2; v = report.addMemberVariable(new Variable(ncfile, null, report, "reserved", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{7}); v.addAttribute(new Attribute(CDM.LONG_NAME, "reserved characters")); v.setSPobject(new Vinfo(pos)); pos += 7; List<Record> records = firstReport.readData(); // for the moment, we will use the first report as the exemplar pos = makeInnerSequence(report, records, 1, pos); pos = makeInnerSequence(report, records, 2, pos); pos = makeInnerSequence(report, records, 3, pos); pos = makeInnerSequence(report, records, 4, pos); pos = makeInnerSequence(report, records, 5, pos); pos = makeInnerSequence(report, records, 7, pos); pos = makeInnerSequence(report, records, 8, pos); pos = makeInnerSequence(report, records, 51, pos); makeInnerSequence(report, records, 52, pos); report.calcElementSize(); // recalc since we added new members return report; } private int makeInnerSequence(Structure reportVar, List<Record> records, int code, int obs_pos) throws InvalidRangeException { for (Record record : records) { if (record.code == code) { Entry first = record.entries[0]; Structure s = first.makeStructure(reportVar); s.setSPobject(new Vinfo(obs_pos)); obs_pos += 4; reportVar.addMemberVariable(s); catStructures.add(new StructureCode(s, code)); break; } } return obs_pos; } private static class Vinfo { int offset; Vinfo(int offset) { this.offset = offset; } } private static class StructureCode { Structure s; int code; StructureCode(Structure s, int code) { this.s = s; this.code = code; } } ///////////////////////////////////////////////////////////// private Array readStation(Variable v, Section section) throws IOException, InvalidRangeException { Structure s = (Structure) v; StructureMembers members = s.makeStructureMembers(); for (Variable v2 : s.getVariables()) { Vinfo vinfo = (Vinfo) v2.getSPobject(); StructureMembers.Member m = members.findMember(v2.getShortName()); if (vinfo != null) { m.setDataParam(vinfo.offset); //m.setVariableInfo( vinfo.size); } } int size = (int) section.computeSize(); ArrayStructureBB abb = new ArrayStructureBB(members, new int[]{size}); ByteBuffer bb = abb.getByteBuffer(); Range r = section.getRange(0); for (int i = r.first(); i <= r.last(); i += r.stride()) { Station station = stations.get(i); bb.put(station.r.stationId.getBytes(CDM.utf8Charset)); bb.putFloat(station.r.lat); bb.putFloat(station.r.lon); bb.putFloat(station.r.elevMeters); bb.putInt(station.nreports); } return abb; } public Array readReportIndex(Variable v, Section section) throws IOException, InvalidRangeException { //coverity[FB.BC_UNCONFIRMED_CAST] Structure s = (Structure) v; StructureMembers members = s.makeStructureMembers(); for (Variable v2 : s.getVariables()) { Vinfo vinfo = (Vinfo) v2.getSPobject(); StructureMembers.Member m = members.findMember(v2.getShortName()); m.setDataParam(vinfo.offset); } int size = (int) section.computeSize(); ArrayStructureBB abb = new ArrayStructureBB(members, new int[]{size}); ByteBuffer bb = abb.getByteBuffer(); Range r = section.getRange(0); for (int i = r.first(); i <= r.last(); i += r.stride()) { Report report = reports.get(i); report.loadIndexData(bb); } return abb; } public Array readReport(Variable v, Section section) throws IOException, InvalidRangeException { //coverity[FB.BC_UNCONFIRMED_CAST] Structure s = (Structure) v; StructureMembers members = s.makeStructureMembers(); for (Variable v2 : s.getVariables()) { Vinfo vinfo = (Vinfo) v2.getSPobject(); StructureMembers.Member m = members.findMember(v2.getShortName()); m.setDataParam(vinfo.offset); } int size = (int) section.computeSize(); ArrayStructureBB abb = new ArrayStructureBB(members, new int[]{size}); ByteBuffer bb = abb.getByteBuffer(); Range r = section.getRange(0); for (int i = r.first(); i <= r.last(); i += r.stride()) { Report report = reports.get(i); report.loadStructureData(abb, bb); } return abb; } /* private class ReportIterator implements StructureDataIterator { List<Report> reports; Iterator<Report> iter; StructureMembers members; ReportIterator(List<Report> reports) { this.reports = reports; iter = reports.iterator(); members = reportVar.makeStructureMembers(); for (Variable v2 : reportVar.getVariables()) { Vinfo vinfo = (Vinfo) v2.getSPobject(); StructureMembers.Member m = members.findMember(v2.getShortName()); m.setDataParam(vinfo.offset); } } public boolean hasNext() throws IOException { return iter.hasNext(); } public StructureData next() throws IOException { Report r = iter.next(); // LOOK should optimize - read 10 at a time or something ??? ArrayStructureBB abb = new ArrayStructureBB(members, new int[]{1}); ByteBuffer bb = abb.getByteBuffer(); bb.position(0); r.loadStructureData(abb, bb); return abb.getStructureData(0); } public void setBufferSize(int bytes) { } public StructureDataIterator reset() { iter = reports.iterator(); return this; } } */ private Report firstReport = null; private void init() throws IOException { int badPos = 0; int badType = 0; short firstType = -1; raf.seek(0); readHeader(raf); // read through all the reports, construct unique stations Map<String, Station> map = new HashMap<>(); while (true) { Report report = new Report(); if (!report.readId(raf)) break; if (firstReport == null) { firstReport = report; firstType = firstReport.reportType; } if (checkType && (report.reportType != firstType)) { System.out.println(report.stationId + " type: " + report.reportType + " not " + firstType); badType++; } Station stn = map.get(report.stationId); if (stn == null) { stn = new Station(report); map.put(report.stationId, stn); stations.add(stn); } else { stn.nreports++; if (checkPositions) { Report first = reports.get(0); if (first.lat != report.lat) { System.out.println(report.stationId + " lat: " + first.lat + " !=" + report.lat); badPos++; } if (first.lon != report.lon) System.out.println(report.stationId + " lon: " + first.lon + " !=" + report.lon); if (first.elevMeters != report.elevMeters) System.out.println(report.stationId + " elev: " + first.elevMeters + " !=" + report.elevMeters); } } reports.add(report); } Collections.sort(stations); if (checkPositions) System.out.println("\nnon matching lats= " + badPos); if (checkType) System.out.println("\nnon matching reportTypes= " + badType); //System.out.println(firstReport); //firstReport.show(); //firstReport.readData(); /* Set<String> keys = map.keySet(); if (showTimes || readData || checkSort) { int unsorted = 0; for (String key : keys) { List<Report> reports = map.get(key); if (showTimes) System.out.print("Station " + key + ": "); if (summarizeData) System.out.println("Station " + key + " :"); Report last = null; for (Report r : reports) { if ((last != null) && last.date.after(r.date)) { System.out.println("***NOT ORDERED " + key + " last=" + dateFormatter.toDateTimeStringISO(last.date) + "(" + last.filePos + ")" + " next =" + dateFormatter.toDateTimeStringISO(r.date) + "(" + r.filePos + ")"); unsorted++; } last = r; if (showTimes) System.out.print(dateFormatter.toDateTimeStringISO(r.date) + " "); if (readData || summarizeData) { List<Record> cats = r.readData(); if (summarizeData) { System.out.print(" " + r.obsTime + ": ("); for (Record cat : cats) System.out.print(cat.code + "/" + cat.nlevels + " "); System.out.println(")"); } } } if (showTimes) System.out.println(); } if (checkSort) System.out.println("\nunsorted= " + unsorted); } nstations = keys.size(); */ // System.out.println("\nnreports= " + reports.size() + " nstations= " + stations.size()); } private static class Station implements Comparable<Station> { String name; Report r; int nreports; Station(Report r) { this.name = r.stationId; this.r = r; this.nreports = 1; } public int compareTo(Station o) { return name.compareTo(o.name); } } private class Report { float lat, lon, elevMeters; String stationId; byte[] reserved = new byte[7]; short reportType, instType, obsTime; int reportLen; long filePos; Date date; boolean readId(RandomAccessFile raf) throws IOException { filePos = raf.getFilePointer(); byte[] reportId = raf.readBytes(40); String latS = new String(reportId, 0, 5, CDM.utf8Charset); if (latS.equals("END R")) { raf.skipBytes(-40); endRecord(raf); filePos = raf.getFilePointer(); reportId = raf.readBytes(40); latS = new String(reportId, 0, 5, CDM.utf8Charset); } if (latS.equals("ENDOF")) { raf.skipBytes(-40); if (!endFile(raf)) return false; filePos = raf.getFilePointer(); reportId = raf.readBytes(40); latS = new String(reportId, 0, 5, CDM.utf8Charset); } //System.out.println("ReportId start at " + start); try { lat = (float) (.01 * Float.parseFloat(latS)); lon = (float) (360.0 - .01 * Float.parseFloat(new String(reportId, 5, 5, CDM.utf8Charset))); stationId = new String(reportId, 10, 6, CDM.utf8Charset); obsTime = Short.parseShort(new String(reportId, 16, 4, CDM.utf8Charset)); System.arraycopy(reportId, 20, reserved, 0, 7); reportType = Short.parseShort(new String(reportId, 27, 3, CDM.utf8Charset)); elevMeters = Float.parseFloat(new String(reportId, 30, 5, CDM.utf8Charset)); instType = Short.parseShort(new String(reportId, 35, 2, CDM.utf8Charset)); reportLen = 10 * Integer.parseInt(new String(reportId, 37, 3, CDM.utf8Charset)); cal.setTime(refDate); int hour = cal.get(Calendar.HOUR_OF_DAY); if (obsTime / 100 > hour + 4) // if greater than 4 hours from reference time cal.add(Calendar.DAY_OF_MONTH, -1); // subtract a day LOOK cal.set(Calendar.HOUR_OF_DAY, obsTime / 100); cal.set(Calendar.MINUTE, 6 * (obsTime % 100)); date = cal.getTime(); if (showObs) System.out.println(this); else if (showTime) System.out.print(" time=" + obsTime + " date= " + dateFormatter.toDateTimeString(date)); //nobs++; raf.skipBytes(reportLen - 40); return reportLen < 30000; } catch (IOException e) { throw new IOException("BAD reportId=" + new String(reportId, CDM.utf8Charset) + " starts at " + filePos); } } public String toString() { return "Report " + " stationId=" + stationId + " lat=" + lat + " lon=" + lon + " obsTime=" + obsTime + " date= " + dateFormatter.toDateTimeStringISO(date) + " reportType=" + reportType + " elevMeters=" + elevMeters + " instType=" + instType + " reserved=" + new String(reserved, CDM.utf8Charset) + " start=" + filePos + " reportLen=" + reportLen; } // heres where the data for this Report is read into memory List<Record> readData() throws IOException { List<Record> records = new ArrayList<>(); raf.seek(filePos + 40); byte[] b = raf.readBytes(reportLen - 40); if (showData) System.out.println("\n" + new String(b, CDM.utf8Charset)); if (showData) System.out.println(this); int offset = 0; while (true) { Record record = new Record(); offset = record.read(b, offset); records.add(record); if (record.next >= reportLen / 10) break; } return records; } void show(RandomAccessFile raf) throws IOException { raf.seek(filePos); byte[] b = raf.readBytes(40); System.out.println(new String(b, CDM.utf8Charset)); } void loadIndexData(ByteBuffer bb) throws IOException { bb.put(stationId.getBytes(CDM.utf8Charset)); bb.putInt((int) (date.getTime() / 1000)); } void loadStructureData(ArrayStructureBB abb, ByteBuffer bb) throws IOException { bb.putInt((int) (date.getTime() / 1000)); bb.put(dateFormatter.toDateTimeStringISO(date).getBytes(CDM.utf8Charset)); bb.putShort(reportType); bb.putShort(instType); bb.put(reserved); List<Record> records = readData(); for (StructureCode sc : catStructures) loadInnerSequence(abb, bb, records, sc.s, sc.code); } private void loadInnerSequence(ArrayStructureBB abb, ByteBuffer bb, List<Record> records, Structure useStructure, int code) { for (Record record : records) { if (record.code == code) { CatIterator iter = new CatIterator(record.entries, useStructure); ArraySequence seq = new ArraySequence(iter.members, iter, record.entries.length); int index = abb.addObjectToHeap(seq); bb.putInt(index); return; } } // need an empty one CatIterator iter = new CatIterator(new Entry[0], useStructure); ArraySequence seq = new ArraySequence(iter.members, iter, -1); // ?? int index = abb.addObjectToHeap(seq); bb.putInt(index); } private class CatIterator implements StructureDataIterator { Entry[] entries; int count = 0; StructureMembers members; CatIterator(Entry[] entries, Structure useStructure) { this.entries = entries; members = useStructure.makeStructureMembers(); for (Variable v2 : useStructure.getVariables()) { Vinfo vinfo = (Vinfo) v2.getSPobject(); StructureMembers.Member m = members.findMember(v2.getShortName()); m.setDataParam(vinfo.offset); } } @Override public boolean hasNext() throws IOException { return count < entries.length; } @Override public StructureData next() throws IOException { Entry entry = entries[count++]; // LOOK should read 10 at a time or something ??? ArrayStructureBB abb = new ArrayStructureBB(members, new int[]{1}); ByteBuffer bb = abb.getByteBuffer(); bb.position(0); entry.loadStructureData(bb); return abb.getStructureData(0); } @Override public void setBufferSize(int bytes) { } @Override public StructureDataIterator reset() { count = 0; return this; } @Override public int getCurrentRecno() { return count - 1; } @Override public void finish() { // ignored } } } // a record has a variable number of entries, which are all of one "category" type private class Record { int code, next, nlevels, nbytes; Entry[] entries; int read(byte[] b, int offset) throws IOException { code = Integer.parseInt(new String(b, offset, 2, CDM.utf8Charset)); next = Integer.parseInt(new String(b, offset + 2, 3, CDM.utf8Charset)); nlevels = Integer.parseInt(new String(b, offset + 5, 2, CDM.utf8Charset)); nbytes = readIntWithOverflow(b, offset + 7, 3); if (showData) System.out.println("\n" + this); offset += 10; if (code == 1) { if (showData) System.out.println(catNames[1] + ":"); entries = new Cat01[nlevels]; for (int i = 0; i < nlevels; i++) { entries[i] = new Cat01(b, offset, i); if (showData) System.out.println(" " + i + ": " + entries[i]); offset += 22; } } else if (code == 2) { if (showData) System.out.println(catNames[2] + ":"); entries = new Cat02[nlevels]; for (int i = 0; i < nlevels; i++) { entries[i] = new Cat02(b, offset); if (showData) System.out.println(" " + i + ": " + entries[i]); offset += 15; } } else if (code == 3) { if (showData) System.out.println(catNames[3] + ":"); entries = new Cat03[nlevels]; for (int i = 0; i < nlevels; i++) { entries[i] = new Cat03(b, offset); if (showData) System.out.println(" " + i + ": " + entries[i]); offset += 13; } } else if (code == 4) { if (showData) System.out.println(catNames[4] + ":"); entries = new Cat04[nlevels]; for (int i = 0; i < nlevels; i++) { entries[i] = new Cat04(b, offset); if (showData) System.out.println(" " + i + ": " + entries[i]); offset += 13; } } else if (code == 5) { if (showData) System.out.println(catNames[5] + ":"); entries = new Cat05[nlevels]; for (int i = 0; i < nlevels; i++) { entries[i] = new Cat05(b, offset); if (showData) System.out.println(" " + i + ": " + entries[i]); offset += 22; } } else if (code == 7) { if (showData) System.out.println(catNames[7] + ":"); entries = new Cat07[nlevels]; for (int i = 0; i < nlevels; i++) { entries[i] = new Cat07(b, offset); if (showData) System.out.println(" " + i + ": " + entries[i]); offset += 10; } } else if (code == 8) { if (showData) System.out.println(catNames[8] + ":"); entries = new Cat08[nlevels]; for (int i = 0; i < nlevels; i++) { entries[i] = new Cat08(b, offset); if (showData) System.out.println(" " + i + ": " + entries[i]); offset += 10; } } else if (code == 51) { if (showData) System.out.println(catNames[10] + ":"); entries = new Cat51[nlevels]; for (int i = 0; i < nlevels; i++) { entries[i] = new Cat51(b, offset); if (showData) System.out.println(" " + i + ": " + entries[i]); offset += 60; } } else if (code == 52) { if (showData) System.out.println(catNames[10] + ":"); entries = new Cat52[nlevels]; for (int i = 0; i < nlevels; i++) { entries[i] = new Cat52(b, offset); if (showData) System.out.println(" " + i + ": " + entries[i]); offset += 40; } } else { throw new UnsupportedOperationException("code= " + code); } // must be multiple of 10 int skip = offset % 10; if (skip > 0) offset += (10 - skip); return offset; } public String toString() { return "Category/Group " + " code=" + code + " next= " + next + " nlevels=" + nlevels + " nbytes=" + nbytes; } } private int readIntWithOverflow(byte[] b, int offset, int len) { String s = new String(b, offset, len, CDM.utf8Charset); try { return Integer.parseInt(s); } catch (Exception e) { if (showOverflow) System.out.println("OVERFLOW=" + s); return 0; } } private String[] catNames = new String[]{"", "Category 01: mandatory constant-pressure data", "Category 02: temperature/dewpoint at variable pressure-levels ", "Category 03: wind at variable pressure-levels ", "Category 04: wind at variable height-levels ", "Category 05: tropopause data", "", "Category 07: cloud cover", "Category 08: additional data", "", "", "Category 51: surface Data", "Category 52: ship surface Data"}; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private static float[] mandPressureLevel = new float[]{1000, 850, 700, 500, 400, 300, 250, 200, 150, 100, 70, 50, 30, 20, 10, 7, 5, 3, 2, 1}; private abstract class Entry { abstract Structure makeStructure(Structure parent) throws InvalidRangeException; abstract void loadStructureData(ByteBuffer bb); } private class Cat01 extends Entry { short windDir, windSpeed; float geopot, press, temp, dewp; byte[] quality = new byte[4]; Cat01(byte[] b, int offset, int level) throws IOException { press = mandPressureLevel[level]; geopot = Float.parseFloat(new String(b, offset, 5, CDM.utf8Charset)); temp = .1f * Float.parseFloat(new String(b, offset + 5, 4, CDM.utf8Charset)); dewp = .1f * Float.parseFloat(new String(b, offset + 9, 3, CDM.utf8Charset)); windDir = Short.parseShort(new String(b, offset + 12, 3, CDM.utf8Charset)); windSpeed = Short.parseShort(new String(b, offset + 15, 3, CDM.utf8Charset)); System.arraycopy(b, offset + 18, quality, 0, 4); } public String toString() { return "Cat01: press= " + press + " geopot=" + geopot + " temp= " + temp + " dewp=" + dewp + " windDir=" + windDir + " windSpeed=" + windSpeed + " qs=" + new String(quality, CDM.utf8Charset); } Structure makeStructure(Structure parent) throws InvalidRangeException { Sequence seq = new Sequence(ncfile, null, parent, "mandatoryLevels"); seq.addAttribute(new Attribute(CDM.LONG_NAME, catNames[1])); int pos = 0; Variable v = seq.addMemberVariable(new Variable(ncfile, null, parent, "pressure", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "mbars")); v.addAttribute(new Attribute(CDM.LONG_NAME, "pressure level")); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Pressure.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "geopotential", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "meter")); v.addAttribute(new Attribute(CDM.LONG_NAME, "geopotential")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 99999.0f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "temperature", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "celsius")); v.addAttribute(new Attribute(CDM.LONG_NAME, "temperature")); v.addAttribute(new Attribute("accuracy", "celsius/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 999.9f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "dewpoint", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "celsius")); v.addAttribute(new Attribute(CDM.LONG_NAME, "dewpoint depression")); v.addAttribute(new Attribute("accuracy", "celsius/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 99.9f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "windDir", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "degrees")); v.addAttribute(new Attribute(CDM.LONG_NAME, "wind direction")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "windSpeed", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "knots")); v.addAttribute(new Attribute(CDM.LONG_NAME, "wind speed")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "qualityFlags", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{4}); v.addAttribute(new Attribute(CDM.LONG_NAME, "quality marks: 0=geopot, 1=temp, 2=dewpoint, 3=wind")); v.setSPobject(new Vinfo(pos)); return seq; } void loadStructureData(ByteBuffer bb) { bb.putFloat(press); bb.putFloat(geopot); bb.putFloat(temp); bb.putFloat(dewp); bb.putShort(windDir); bb.putShort(windSpeed); bb.put(quality); } } private class Cat02 extends Entry { float press, temp, dewp; byte[] quality = new byte[3]; String qs; Cat02(byte[] b, int offset) throws IOException { press = .1f * Float.parseFloat(new String(b, offset, 5, CDM.utf8Charset)); temp = .1f * Float.parseFloat(new String(b, offset + 5, 4, CDM.utf8Charset)); dewp = .1f * Float.parseFloat(new String(b, offset + 9, 3, CDM.utf8Charset)); System.arraycopy(b, offset + 12, quality, 0, 3); qs = new String(quality, CDM.utf8Charset); } public String toString() { return "Cat02: press=" + press + " temp= " + temp + " dewp=" + dewp + " qs=" + qs; } Structure makeStructure(Structure parent) throws InvalidRangeException { Sequence seq = new Sequence(ncfile, null, parent, "tempPressureLevels"); seq.addAttribute(new Attribute(CDM.LONG_NAME, catNames[2])); int pos = 0; Variable v = seq.addMemberVariable(new Variable(ncfile, null, parent, "pressure", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "mbars")); v.addAttribute(new Attribute(CDM.LONG_NAME, "pressure level")); v.addAttribute(new Attribute("accuracy", "mbar/10")); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Pressure.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "temperature", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "celsius")); v.addAttribute(new Attribute(CDM.LONG_NAME, "temperature")); v.addAttribute(new Attribute("accuracy", "celsius/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 999.9f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "dewpoint", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "celsius")); v.addAttribute(new Attribute(CDM.LONG_NAME, "dewpoint depression")); v.addAttribute(new Attribute("accuracy", "celsius/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 99.9f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "qualityFlags", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{3}); v.addAttribute(new Attribute(CDM.LONG_NAME, "quality marks: 0=pressure, 1=temp, 2=dewpoint")); v.setSPobject(new Vinfo(pos)); return seq; } void loadStructureData(ByteBuffer bb) { bb.putFloat(press); bb.putFloat(temp); bb.putFloat(dewp); bb.put(quality); } } private class Cat03 extends Entry { float press; short windDir, windSpeed; byte[] quality; String qs; Cat03(byte[] b, int offset) throws IOException { press = .1f * Float.parseFloat(new String(b, offset, 5, CDM.utf8Charset)); windDir = Short.parseShort(new String(b, offset + 5, 3, CDM.utf8Charset)); windSpeed = Short.parseShort(new String(b, offset + 8, 3, CDM.utf8Charset)); quality = new byte[2]; System.arraycopy(b, offset + 11, quality, 0, 2); qs = new String(quality, CDM.utf8Charset); } public String toString() { return "Cat03: press=" + press + " windDir=" + windDir + " windSpeed=" + windSpeed + " qs=" + qs; } Structure makeStructure(Structure parent) throws InvalidRangeException { Sequence seq = new Sequence(ncfile, null, parent, "windPressureLevels"); seq.addAttribute(new Attribute(CDM.LONG_NAME, catNames[3])); int pos = 0; Variable v = seq.addMemberVariable(new Variable(ncfile, null, parent, "pressure", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "mbars")); v.addAttribute(new Attribute(CDM.LONG_NAME, "pressure level")); v.addAttribute(new Attribute("accuracy", "mbar/10")); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Pressure.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "windDir", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "degrees")); v.addAttribute(new Attribute(CDM.LONG_NAME, "wind direction")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "windSpeed", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "knots")); v.addAttribute(new Attribute(CDM.LONG_NAME, "wind speed")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "qualityFlags", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "quality marks: 0=pressure, 1=wind")); v.setSPobject(new Vinfo(pos)); return seq; } void loadStructureData(ByteBuffer bb) { bb.putFloat(press); bb.putShort(windDir); bb.putShort(windSpeed); bb.put(quality); } } private class Cat04 extends Entry { float geopot; short windDir, windSpeed; byte[] quality; String qs; Cat04(byte[] b, int offset) throws IOException { geopot = Float.parseFloat(new String(b, offset, 5, CDM.utf8Charset)); windDir = Short.parseShort(new String(b, offset + 5, 3, CDM.utf8Charset)); windSpeed = Short.parseShort(new String(b, offset + 8, 3, CDM.utf8Charset)); quality = new byte[2]; System.arraycopy(b, offset + 11, quality, 0, 2); qs = new String(quality, CDM.utf8Charset); } public String toString() { return "Cat04: geopot=" + geopot + " windDir=" + windDir + " windSpeed=" + windSpeed + " qs=" + qs; } Structure makeStructure(Structure parent) throws InvalidRangeException { Sequence seq = new Sequence(ncfile, null, parent, "windHeightLevels"); seq.addAttribute(new Attribute(CDM.LONG_NAME, catNames[4])); int pos = 0; Variable v = seq.addMemberVariable(new Variable(ncfile, null, parent, "geopotential", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "meter")); v.addAttribute(new Attribute(CDM.LONG_NAME, "geopotential")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 99999.0f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "windDir", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "degrees")); v.addAttribute(new Attribute(CDM.LONG_NAME, "wind direction")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "windSpeed", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "knots")); v.addAttribute(new Attribute(CDM.LONG_NAME, "wind speed")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "qualityFlags", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "quality marks: 0=geopot, 1=wind")); v.setSPobject(new Vinfo(pos)); return seq; } void loadStructureData(ByteBuffer bb) { bb.putFloat(geopot); bb.putShort(windDir); bb.putShort(windSpeed); bb.put(quality); } } private class Cat05 extends Entry { float press, temp, dewp; short windDir, windSpeed; byte[] quality; String qs; Cat05(byte[] b, int offset) throws IOException { press = .1f * Float.parseFloat(new String(b, offset, 5, CDM.utf8Charset)); temp = .1f * Float.parseFloat(new String(b, offset + 5, 4, CDM.utf8Charset)); dewp = .1f * Float.parseFloat(new String(b, offset + 9, 3, CDM.utf8Charset)); windDir = Short.parseShort(new String(b, offset + 12, 3, CDM.utf8Charset)); windSpeed = Short.parseShort(new String(b, offset + 15, 3, CDM.utf8Charset)); quality = new byte[4]; System.arraycopy(b, offset + 18, quality, 0, 4); qs = new String(quality, CDM.utf8Charset); } public String toString() { return "Cat05: press= " + press + " temp= " + temp + " dewp=" + dewp + " windDir=" + windDir + " windSpeed=" + windSpeed + " qs=" + qs; } Structure makeStructure(Structure parent) throws InvalidRangeException { Sequence seq = new Sequence(ncfile, null, parent, "tropopause"); seq.addAttribute(new Attribute(CDM.LONG_NAME, catNames[5])); int pos = 0; Variable v = seq.addMemberVariable(new Variable(ncfile, null, parent, "pressure", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "mbars")); v.addAttribute(new Attribute(CDM.LONG_NAME, "pressure level")); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Pressure.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "temperature", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "celsius")); v.addAttribute(new Attribute(CDM.LONG_NAME, "temperature")); v.addAttribute(new Attribute("accuracy", "celsius/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 999.9f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "dewpoint", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "celsius")); v.addAttribute(new Attribute(CDM.LONG_NAME, "dewpoint depression")); v.addAttribute(new Attribute("accuracy", "celsius/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 99.9f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "windDir", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "degrees")); v.addAttribute(new Attribute(CDM.LONG_NAME, "wind direction")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "windSpeed", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "knots")); v.addAttribute(new Attribute(CDM.LONG_NAME, "wind speed")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "qualityFlags", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{4}); v.addAttribute(new Attribute(CDM.LONG_NAME, "quality marks: 0=pressure, 1=temp, 2=dewpoint, 3=wind")); v.setSPobject(new Vinfo(pos)); return seq; } void loadStructureData(ByteBuffer bb) { bb.putFloat(press); bb.putFloat(temp); bb.putFloat(dewp); bb.putShort(windDir); bb.putShort(windSpeed); bb.put(quality); } } private class Cat07 extends Entry { float press; short percentClouds; byte[] quality; String qs; Cat07(byte[] b, int offset) throws IOException { press = .1f * Float.parseFloat(new String(b, offset, 5, CDM.utf8Charset)); percentClouds = Short.parseShort(new String(b, offset + 5, 3, CDM.utf8Charset)); quality = new byte[2]; System.arraycopy(b, offset + 8, quality, 0, 2); qs = new String(quality, CDM.utf8Charset); } public String toString() { return "Cat07: press=" + press + " percentClouds=" + percentClouds + " qs=" + qs; } Structure makeStructure(Structure parent) throws InvalidRangeException { Sequence seq = new Sequence(ncfile, null, parent, "clouds"); seq.addAttribute(new Attribute(CDM.LONG_NAME, catNames[7])); int pos = 0; Variable v = seq.addMemberVariable(new Variable(ncfile, null, parent, "pressure", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "mbars")); v.addAttribute(new Attribute(CDM.LONG_NAME, "pressure level")); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Pressure.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "percentClouds", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "amount of cloudiness (%)")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "qualityFlags", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "quality marks: 0=pressure, 1=percentClouds")); v.setSPobject(new Vinfo(pos)); return seq; } void loadStructureData(ByteBuffer bb) { bb.putFloat(press); bb.putShort(percentClouds); bb.put(quality); } } private class Cat08 extends Entry { int data; short table101code; byte[] quality; String qs; Cat08(byte[] b, int offset) throws IOException { data = Integer.parseInt(new String(b, offset, 5, CDM.utf8Charset)); table101code = Short.parseShort(new String(b, offset + 5, 3, CDM.utf8Charset)); quality = new byte[2]; System.arraycopy(b, offset + 8, quality, 0, 2); qs = new String(quality, CDM.utf8Charset); } public String toString() { return "Cat08: data=" + data + " table101code=" + table101code + " qs=" + qs; } Structure makeStructure(Structure parent) throws InvalidRangeException { Sequence seq = new Sequence(ncfile, null, parent, "otherData"); seq.addAttribute(new Attribute(CDM.LONG_NAME, catNames[8])); int pos = 0; Variable v = seq.addMemberVariable(new Variable(ncfile, null, parent, "data", DataType.INT, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "additional data specified in table 101.1")); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Pressure.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "table101code", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "code figure from table 101")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "indicatorFlags", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "quality marks: 0=data, 1=form")); v.setSPobject(new Vinfo(pos)); return seq; } void loadStructureData(ByteBuffer bb) { bb.putInt(data); bb.putShort(table101code); bb.put(quality); } } private class Cat51 extends Entry { short windDir, windSpeed; float pressSeaLevel, pressStation, geopot, press, temp, dewp, maxTemp, minTemp, pressureTendency; byte[] quality = new byte[4]; byte pastWeatherW2, pressureTendencyChar; byte[] horizVis = new byte[3]; byte[] presentWeather = new byte[3]; byte[] pastWeatherW1 = new byte[2]; byte[] fracCloudN = new byte[2]; byte[] fracCloudNh = new byte[2]; byte[] cloudCl = new byte[2]; byte[] cloudBaseHeight = new byte[2]; byte[] cloudCm = new byte[2]; byte[] cloudCh = new byte[2]; Cat51(byte[] b, int offset) throws IOException { pressSeaLevel = Float.parseFloat(new String(b, offset, 5, CDM.utf8Charset)); pressStation = Float.parseFloat(new String(b, offset + 5, 5, CDM.utf8Charset)); windDir = Short.parseShort(new String(b, offset + 10, 3, CDM.utf8Charset)); windSpeed = Short.parseShort(new String(b, offset + 13, 3, CDM.utf8Charset)); temp = .1f * Float.parseFloat(new String(b, offset + 16, 4, CDM.utf8Charset)); dewp = .1f * Float.parseFloat(new String(b, offset + 20, 3, CDM.utf8Charset)); maxTemp = .1f * Float.parseFloat(new String(b, offset + 23, 4, CDM.utf8Charset)); minTemp = .1f * Float.parseFloat(new String(b, offset + 27, 4, CDM.utf8Charset)); System.arraycopy(b, offset + 31, quality, 0, 4); pastWeatherW2 = b[offset + 35]; System.arraycopy(b, offset + 36, horizVis, 0, 3); System.arraycopy(b, offset + 39, presentWeather, 0, 3); System.arraycopy(b, offset + 42, pastWeatherW1, 0, 2); System.arraycopy(b, offset + 44, fracCloudN, 0, 2); System.arraycopy(b, offset + 46, fracCloudNh, 0, 2); System.arraycopy(b, offset + 48, cloudCl, 0, 2); System.arraycopy(b, offset + 50, cloudBaseHeight, 0, 2); System.arraycopy(b, offset + 52, cloudCm, 0, 2); System.arraycopy(b, offset + 54, cloudCh, 0, 2); pressureTendencyChar = b[offset + 56]; pressureTendency = .1f * Float.parseFloat(new String(b, offset + 57, 3, CDM.utf8Charset)); } public String toString() { return "Cat51: press= " + press + " geopot=" + geopot + " temp= " + temp + " dewp=" + dewp + " windDir=" + windDir + " windSpeed=" + windSpeed + " qs=" + new String(quality, CDM.utf8Charset) + " pressureTendency=" + pressureTendency; } Structure makeStructure(Structure parent) throws InvalidRangeException { Sequence seq = new Sequence(ncfile, null, parent, "surfaceData"); seq.addAttribute(new Attribute(CDM.LONG_NAME, catNames[11])); int pos = 0; Variable v = seq.addMemberVariable(new Variable(ncfile, null, parent, "pressureSeaLevel", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "mbars")); v.addAttribute(new Attribute(CDM.LONG_NAME, "sea level pressure")); v.addAttribute(new Attribute("accuracy", "mbars/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 9999.9f)); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Pressure.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "pressure", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "mbars")); v.addAttribute(new Attribute(CDM.LONG_NAME, "station pressure")); v.addAttribute(new Attribute("accuracy", "mbars/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 9999.9f)); v.addAttribute(new Attribute(_Coordinate.AxisType, AxisType.Pressure.toString())); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "windDir", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "degrees")); v.addAttribute(new Attribute(CDM.LONG_NAME, "wind direction")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "windSpeed", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "knots")); v.addAttribute(new Attribute(CDM.LONG_NAME, "wind speed")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "temperature", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "celsius")); v.addAttribute(new Attribute(CDM.LONG_NAME, "air temperature")); v.addAttribute(new Attribute("accuracy", "celsius/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 999.9f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "dewpoint", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "celsius")); v.addAttribute(new Attribute(CDM.LONG_NAME, "dewpoint depression")); v.addAttribute(new Attribute("accuracy", "celsius/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 99.9f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "temperatureMax", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "celsius")); v.addAttribute(new Attribute(CDM.LONG_NAME, "maximum temperature")); v.addAttribute(new Attribute("accuracy", "celsius/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 999.9f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "temperatureMin", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "celsius")); v.addAttribute(new Attribute(CDM.LONG_NAME, "minimum temperature")); v.addAttribute(new Attribute("accuracy", "celsius/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 999.9f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "qualityFlags", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{4}); v.addAttribute(new Attribute(CDM.LONG_NAME, "quality marks: 0=pressureSeaLevel, 1=pressure, 2=wind, 3=temperature")); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "pastWeatherW2", DataType.CHAR, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "past weather (W2): WMO table 4561")); v.setSPobject(new Vinfo(pos)); pos += 1; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "horizViz", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{3}); v.addAttribute(new Attribute(CDM.LONG_NAME, "horizontal visibility: WMO table 4300")); v.setSPobject(new Vinfo(pos)); pos += 3; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "presentWeatherWW", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{3}); v.addAttribute(new Attribute(CDM.LONG_NAME, "present weather (WW): WMO table 4677")); v.setSPobject(new Vinfo(pos)); pos += 3; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "pastWeatherW1", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "past weather (WW): WMO table 4561")); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "cloudFractionN", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "cloud fraction (N): WMO table 2700")); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "cloudFractionNh", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "cloud fraction (Nh): WMO table 2700")); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "cloudFractionCL", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "cloud fraction (CL): WMO table 0513")); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "cloudHeightCL", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "cloud base height above ground (h): WMO table 1600")); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "cloudFractionCM", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "cloud fraction (CM): WMO table 0515")); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "cloudFractionCH", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "cloud fraction (CH): WMO table 0509")); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "pressureTendencyCharacteristic", DataType.CHAR, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "pressure tendency characteristic for 3 hours previous to obs time: WMO table 0200")); v.setSPobject(new Vinfo(pos)); pos += 1; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "pressureTendency", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "mbars")); v.addAttribute(new Attribute(CDM.LONG_NAME, "pressure tendency magnitude")); v.addAttribute(new Attribute("accuracy", "mbars/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 99.9f)); v.setSPobject(new Vinfo(pos)); return seq; } void loadStructureData(ByteBuffer bb) { bb.putFloat(pressSeaLevel); bb.putFloat(pressStation); bb.putShort(windDir); bb.putShort(windSpeed); bb.putFloat(temp); bb.putFloat(dewp); bb.putFloat(maxTemp); bb.putFloat(minTemp); bb.put(quality); bb.put(pastWeatherW2); bb.put(horizVis); bb.put(presentWeather); bb.put(pastWeatherW1); bb.put(fracCloudN); bb.put(fracCloudNh); bb.put(cloudCl); bb.put(cloudBaseHeight); bb.put(cloudCm); bb.put(cloudCh); bb.put(pressureTendencyChar); bb.putFloat(pressureTendency); } } private class Cat52 extends Entry { short snowDepth, wavePeriod, waveHeight, waveSwellPeriod, waveSwellHeight; float precip6hours, precip24hours, sst, waterEquiv; byte precipDuration, shipCourse; byte[] waveDirection = new byte[2]; byte[] special = new byte[2]; byte[] special2 = new byte[2]; byte[] shipSpeed = new byte[2]; Cat52(byte[] b, int offset) throws IOException { precip6hours = .01f * Float.parseFloat(new String(b, offset, 4, CDM.utf8Charset)); snowDepth = Short.parseShort(new String(b, offset + 4, 3, CDM.utf8Charset)); precip24hours = .01f * Float.parseFloat(new String(b, offset + 7, 4, CDM.utf8Charset)); precipDuration = b[offset + 11]; wavePeriod = Short.parseShort(new String(b, offset + 12, 2, CDM.utf8Charset)); waveHeight = Short.parseShort(new String(b, offset + 14, 2, CDM.utf8Charset)); System.arraycopy(b, offset + 16, waveDirection, 0, 2); waveSwellPeriod = Short.parseShort(new String(b, offset + 18, 2, CDM.utf8Charset)); waveSwellHeight = Short.parseShort(new String(b, offset + 20, 2, CDM.utf8Charset)); sst = .1f * Float.parseFloat(new String(b, offset + 22, 4, CDM.utf8Charset)); System.arraycopy(b, offset + 26, special, 0, 2); System.arraycopy(b, offset + 28, special2, 0, 2); shipCourse = b[offset + 30]; System.arraycopy(b, offset + 31, shipSpeed, 0, 2); waterEquiv = .001f * Float.parseFloat(new String(b, offset + 33, 7, CDM.utf8Charset)); } void loadStructureData(ByteBuffer bb) { bb.putFloat(precip6hours); bb.putShort(snowDepth); bb.putFloat(precip24hours); bb.put(precipDuration); bb.putShort(wavePeriod); bb.putShort(waveHeight); bb.put(waveDirection); bb.putShort(waveSwellPeriod); bb.putShort(waveSwellHeight); bb.putFloat(sst); bb.put(special); bb.put(special2); bb.put(shipCourse); bb.put(shipSpeed); bb.putFloat(waterEquiv); } public String toString() { return "Cat52: precip6hours= " + precip6hours + " precip24hours=" + precip24hours + " sst= " + sst + " waterEquiv=" + waterEquiv + " snowDepth=" + snowDepth + " wavePeriod=" + wavePeriod + " waveHeight=" + waveHeight + " waveSwellPeriod=" + waveSwellPeriod + " waveSwellHeight=" + waveSwellHeight; } Structure makeStructure(Structure parent) throws InvalidRangeException { Sequence seq = new Sequence(ncfile, null, parent, "surfaceData2"); seq.addAttribute(new Attribute(CDM.LONG_NAME, catNames[12])); int pos = 0; Variable v = seq.addMemberVariable(new Variable(ncfile, null, parent, "precip6hours", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "inch")); v.addAttribute(new Attribute(CDM.LONG_NAME, "precipitation past 6 hours")); v.addAttribute(new Attribute("accuracy", "inch/100")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 99.99f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "snowDepth", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "inch")); v.addAttribute(new Attribute(CDM.LONG_NAME, "total depth of snow on ground")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 999)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "precip24hours", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "inch")); v.addAttribute(new Attribute(CDM.LONG_NAME, "precipitation past 24 hours")); v.addAttribute(new Attribute("accuracy", "inch/100")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 99.99f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "precipDuration", DataType.BYTE, "")); v.addAttribute(new Attribute(CDM.UNITS, "6 hours")); v.addAttribute(new Attribute(CDM.LONG_NAME, "duration of precipitation observation")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 9)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "wavePeriod", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "second")); v.addAttribute(new Attribute(CDM.LONG_NAME, "period of waves")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 99)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "waveHeight", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "meter/2")); v.addAttribute(new Attribute(CDM.LONG_NAME, "height of waves")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 99)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "swellWaveDir", DataType.CHAR, "")); v.setDimensionsAnonymous(new int[]{2}); v.addAttribute(new Attribute(CDM.LONG_NAME, "direction from which swell waves are moving: WMO table 0877")); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "swellWavePeriod", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "second")); v.addAttribute(new Attribute(CDM.LONG_NAME, "period of swell waves")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 99)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "swellWaveHeight", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "meter/2")); v.addAttribute(new Attribute(CDM.LONG_NAME, "height of waves")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, (short) 99)); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "sst", DataType.FLOAT, "")); v.addAttribute(new Attribute(CDM.UNITS, "celsius")); v.addAttribute(new Attribute(CDM.LONG_NAME, "sea surface temperature")); v.addAttribute(new Attribute("accuracy", "celsius/10")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 999.9f)); v.setSPobject(new Vinfo(pos)); pos += 4; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "special", DataType.CHAR, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "special phenomena - general")); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "specialDetail", DataType.CHAR, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "special phenomena - detailed")); v.setSPobject(new Vinfo(pos)); pos += 2; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "shipCourse", DataType.CHAR, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "ships course: WMO table 0700")); v.setSPobject(new Vinfo(pos)); pos += 1; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "shipSpeed", DataType.CHAR, "")); v.addAttribute(new Attribute(CDM.LONG_NAME, "ships average speed: WMO table 4451")); v.setSPobject(new Vinfo(pos)); pos += 1; v = seq.addMemberVariable(new Variable(ncfile, null, parent, "waterEquiv", DataType.SHORT, "")); v.addAttribute(new Attribute(CDM.UNITS, "inch")); v.addAttribute(new Attribute(CDM.LONG_NAME, "water equivalent of snow and/or ice")); v.addAttribute(new Attribute("accuracy", "inch/100")); v.addAttribute(new Attribute(CDM.MISSING_VALUE, 99999.99f)); v.setSPobject(new Vinfo(pos)); return seq; } } private boolean endRecord(RandomAccessFile raf) throws IOException { if (showSkip) System.out.print(" endRecord start at " + raf.getFilePointer()); int skipped = 0; String endRecord = raf.readString(10); // new String(raf.readBytes(10), CDM.utf8Charset); while (endRecord.equals("END RECORD")) { endRecord = raf.readString(10); // new String(raf.readBytes(10)); skipped++; } if (showSkip) System.out.println(" last 10 chars= " + endRecord + " skipped= " + skipped); return true; } private boolean endFile(RandomAccessFile raf) throws IOException { if (showSkip) System.out.println(" endFile start at " + raf.getFilePointer()); String endRecord = raf.readString(10); // new String(raf.readBytes(10)); while (endRecord.equals("ENDOF FILE")) { endRecord = raf.readString(10); // new String(raf.readBytes(10)); } try { while (raf.read() != (int) 'X') ; //find where X's start while (raf.read() == (int) 'X') ; //skip X's till you run out raf.skipBytes(-1); // go back one readHeader(raf); return true; } catch (EOFException e) { return false; } } private void readHeader(RandomAccessFile raf) throws IOException { byte[] h = raf.readBytes(60); // 12 00 070101 short hour = Short.parseShort( new String(h, 0, 2, CDM.utf8Charset)); short minute = Short.parseShort(new String(h, 2, 2, CDM.utf8Charset)); short year = Short.parseShort(new String(h, 4, 2, CDM.utf8Charset)); short month = Short.parseShort(new String(h, 6, 2, CDM.utf8Charset)); short day = Short.parseShort(new String(h, 8, 2, CDM.utf8Charset)); int fullyear = (year > 30) ? 1900 + year : 2000 + year; if (cal == null) { cal = Calendar.getInstance(); cal.setTimeZone(TimeZone.getTimeZone("UTC")); } cal.clear(); cal.set(fullyear, month - 1, day, hour, minute); refDate = cal.getTime(); if (showHeader) System.out.println("\nhead=" + new String(h, CDM.utf8Charset) + " date= " + dateFormatter.toDateTimeString(refDate)); int b, count = 0; while ((b = raf.read()) == (int) 'X') count++; char c = (char) b; if (showSkip) System.out.println(" b=" + b + " c=" + c + " at " + raf.getFilePointer() + " skipped= " + count); raf.skipBytes(-1); // go back one } static public void main(String args[]) throws IOException, InvalidRangeException { String filename = "C:/data/cadis/tempting"; //String filename = "C:/data/cadis/Y94179"; //String filename = "C:/data/cadis/Y94132"; NmcObsLegacy iosp = new NmcObsLegacy(); RandomAccessFile raf = new RandomAccessFile(filename, "r"); NetcdfFile ncfile = new NetcdfFileSubclass(iosp, filename); iosp.open(raf, ncfile, null); System.out.println("\n" + ncfile); Variable v = ncfile.findVariable("station"); Array data = v.read(new Section().appendRange(0, 1)); System.out.println(NCdumpW.toString(data, "station", null)); v = ncfile.findVariable("report"); data = v.read(new Section().appendRange(0, 0)); System.out.println(NCdumpW.toString(data, "report", null)); v = ncfile.findVariable("reportIndex"); data = v.read(); System.out.println(NCdumpW.toString(data, "reportIndex", null)); iosp.close(); } }