/* * Copyright 1998-2011 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.wmo; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.input.SAXBuilder; import ucar.unidata.util.StringUtil2; import java.io.IOException; import java.io.InputStream; import java.util.*; /** * Read and process WMO common code and flag tables. * * <pre> * see manual on codes I.2 —Co Tab — 1 COMMON CODE TABLE C–1: Identification of originating/generating centre GRIB-1, BUFR-3 COMMON CODE TABLE C–2: Radiosonde/sounding system used COMMON CODE TABLE C–3: Instrument make and type for water temperature profile measurement with fall rate equation coefficients COMMON CODE TABLE C–4: Water temperature profile recorder types COMMON CODE TABLE C–5: Satellite identifier COMMON CODE TABLE C–6: List of units for TDCFs COMMON CODE TABLE C–7: Tracking techniques/status of system used COMMON CODE TABLE C–8: Satellite instruments COMMON CODE TABLE C–11: Originating/generating centres GRIB-2, BUFR-4 COMMON CODE TABLE C–12: Sub-centres of originating centres defined by entries in Common code tables C–1 or C–11 COMMON CODE TABLE C–13: Data sub-categories of categories defined by entries in BUFR Table A COMMON CODE TABLE C–14: Atmospheric chemical or physical constituent type * </pre> * @author caron * @since 3/29/11 */ public class CommonCodeTable implements Comparable<CommonCodeTable> { static private final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CommonCodeTable.class); static private final Map<Integer, CommonCodeTable> tableMap = new HashMap<>(); static private final String version = "_20141105_en"; ////////////////////////////////////////////////////////////////////////// /* <Common_C13_20130508_en> <No>35</No> <CodeFigure_DataCategories>2</CodeFigure_DataCategories> <Name_DataCategories_en>Vertical soundings (other than satellite)</Name_DataCategories_en> <CodeFigure_InternationalDataSubcategories>21</CodeFigure_InternationalDataSubcategories> <Name_InternationalDataSubcategories_en>Profiles of atmospheric constituents concentrations</Name_InternationalDataSubcategories_en> <Status>Operational</Status> </Common_C13_20130508_en> <Common_C01_20141105_en> <No>6</No> <CodeFigureForF1F2>04</CodeFigureForF1F2> <CodeFigureForF3F3F3>004</CodeFigureForF3F3F3> <Octet5GRIB1_Octet6BUFR3>4</Octet5GRIB1_Octet6BUFR3> <OriginatingGeneratingCentres_en>Moscow</OriginatingGeneratingCentres_en> <Status>Operational</Status> </Common_C01_20141105_en> */ public enum Table { // code code2 value C1("Centers-GRIB1,BUFR3", 1, 1, new String[]{"CodeFigureForF1F2", "CodeFigureForF3F3F3", "OriginatingGeneratingCentres_en"}), // C2("Radiosondes", 2, 1, new String[]{"CodeFigureForBUFR", null, "RadiosondeSoundingSystemUsed_en"}), C3("Water temperature profile instrument", 3, 1, new String[]{"CodeFigureForBUFR", null, "InstrumentMakeAndType_en"}), C4("Water temperature profile recorder", 4, 1, new String[]{"CodeFigureForBUFR", null, "Meaning_en"}), C5("Satellite identifier", 5, 1, new String[]{"CodeFigureForBUFR", null, "SatelliteName_en"}), C7("Satellite tracking", 7, 1, new String[]{"CodeFigureForBUFR", null, "TrackingTechniquesStatusOfSystemUsed_en"}), C8("Satellite instruments", 8, 1, new String[]{"Code", null, "InstrumentLongName_en", "InstrumentShortName_en"}), C11("Centers-GRIB2,BUFR4", 11,1, new String[]{"GRIB2_BUFR4", null, "OriginatingGeneratingCentre_en"}), // code value code2 value2 C12("Subcenters", 12,2, new String[]{"CodeFigure_OriginatingCentres", "Name_OriginatingCentres_en", "CodeFigure_SubCentres", "Name_SubCentres_en"}), C13("Data sub-categories", 13,2, new String[]{"CodeFigure_DataCategories", "Name_DataCategories_en", "CodeFigure_InternationalDataSubcategories", "Name_InternationalDataSubcategories_en"}), C14("Atmospheric chemical or physical constituent type",14,2, new String[]{"CodeFigure", "ChemicalFormula", null, "Meaning_en"}), ; String name; String[] elems; // type 1: 0 = code, 1 = code2 (may be null), 2 = value // type 2: 0 = code1, 1 = comment , 2 = code3 (may be null), 3 = value int num, type; // type 1 = 1 code, type 2 = 2 codes String num0; Table(String name, int num, int type, String[] elems) { this.name = name; this.num = num; this.type = type; this.elems = elems; Formatter f = new Formatter(); f.format("%02d", num); num0 = f.toString(); } public String getResourceName() { return "/resources/wmo/Common_C" + this.num0 + version +".xml"; } public String getRootElemName() { return "Common_C" + this.num0 + version; } public int getTableNo() { return num; } public int getTableType() { return type; } String[] getElemNames() { return elems; } public String getName() { return name; } } /** * Center name, from table C-1 or C-11 * * @param center_id center id * @param edition grib edition * @return center name, or "unknown" */ static public String getCenterName(int center_id, int edition) { String result = (edition == 1) ? getTableValue(1, center_id) : getTableValue(11, center_id); if (result != null) return result; if (center_id == 0) return "WMO standard table"; return "Unknown center=" + center_id; } /** * Center name, from table C-1 or C-11 * * @param center_id center id * @param edition bufr edition * @return center name, or "unknown" */ static public String getCenterNameBufr(int center_id, int edition) { String result = (edition < 4) ? getTableValue(1, center_id) : getTableValue(11, center_id); if (result != null) return result; if (center_id == 0) return "WMO standard table"; return "Unknown center=" + center_id; } /** * Subcenter name, from table C-12 * * @param center_id center id * @param subcenter_id subcenter id * @return subcenter name, or null if not found */ static public String getSubCenterName(int center_id, int subcenter_id) { return getTableValue(12, center_id, subcenter_id); } /** * data subcategory name, from table C-13 * * @param cat data category * @param subcat data subcategory * @return subcategory name, or null if not found */ static public String getDataSubcategoy(int cat, int subcat) { return getTableValue(13, cat, subcat); } public static CommonCodeTable getTable(int tableNo) { CommonCodeTable cct = tableMap.get(tableNo); if (cct == null) { Table want = null; for (Table t : Table.values()) if (t.num == tableNo) { want = t; break; } if (want == null) throw new IllegalStateException("Unknown wmo common code table number= "+tableNo); try { cct = readCommonCodes(want); tableMap.put(tableNo, cct); } catch (IOException e) { throw new IllegalStateException("Cant open wmo common code table "+want); } } return cct; } public static String getTableValue(int tableNo, int code) { CommonCodeTable cct = getTable(tableNo); if (cct == null) { logger.error("WMO common table {} is not implemented", tableNo); return null; } TableEntry te = cct.get(code); if (te == null) return null; return te.value; } public static String getTableValue(int tableNo, int code, int code2) { CommonCodeTable cct = getTable(tableNo); TableEntry te = cct.get(code, code2); if (te == null) return null; return te.value; } static private CommonCodeTable readCommonCodes(Table version) throws IOException { InputStream ios = null; try { Class c = CommonCodeTable.class; ios = c.getResourceAsStream(version.getResourceName()); if (ios == null) { throw new IllegalStateException("CommonCodeTable cant open " + version.getResourceName()); } org.jdom2.Document doc; try { SAXBuilder builder = new SAXBuilder(); doc = builder.build(ios); } catch (JDOMException e) { throw new IOException(e.getMessage()); } Element root = doc.getRootElement(); String previousValue = null; CommonCodeTable ct = new CommonCodeTable(version.name(), version.getTableType()); String[] elems = version.getElemNames(); List<Element> featList = root.getChildren(version.getRootElemName()); for (Element elem : featList) { if (version.type == 1) { String line = elem.getChildTextNormalize("No"); String code = elem.getChildTextNormalize(elems[0]); String code2 = (elems[1] != null) ? elem.getChildTextNormalize(elems[1]) : null; String value = elem.getChildTextNormalize(elems[2]); //if ((value == null || value.length() == 0) && elems[3] != null) value = elem.getChildTextNormalize(elems[3]); WTF ? Element statusElem = elem.getChild("Status"); String status = (statusElem == null) ? null : statusElem.getTextNormalize(); if (value != null && value.equals(")")) value = previousValue; ct.add(line, code, code2, value, status); previousValue = value; } else { String line = elem.getChildTextNormalize("No"); String code = elem.getChildTextNormalize(elems[0]); String value = elem.getChildTextNormalize(elems[1]); String code2 = (elems[2] == null) ? null : elem.getChildTextNormalize(elems[2]); String value2 = elem.getChildTextNormalize(elems[3]); Element statusElem = elem.getChild("Status"); String status = (statusElem == null) ? null : statusElem.getTextNormalize(); ct.add(line, code, value, code2, value2, status); } } ios.close(); return ct; } finally { if (ios != null) ios.close(); } } //////////////////////////////////////////////////////////////////////////////////// public final String tableName; public final int type; public final List<TableEntry> entries = new ArrayList<TableEntry>(); private Map<Integer, String> map = null; CommonCodeTable(String name, int type) { this.tableName = name; this.type = type; } void add(String line, String code, String code2, String value, String status) { entries.add(new TableEntry(line, code, code2, value, status)); } void add(String line, String code, String value, String code2, String value2, String status) { entries.add(new TableEntry(line, code, value, code2, value2, status)); } // look replace with hash or array TableEntry get(int code) { for (TableEntry p : entries) { if (p.code == code) return p; } return null; } TableEntry get(int code, int code2) { for (TableEntry p : entries) { if ((p.code == code) && (p.code2 == code2))return p; } return null; } public String getTableName() { return tableName; } public Map<Integer, String> getMap() { if (map == null) { map = new HashMap<Integer, String>(entries.size() * 2); for (TableEntry p : entries) { map.put(p.code, p.value); } } return map; } @Override public int compareTo(CommonCodeTable o) { return tableName.compareTo(o.tableName); } String state() { Set<Integer> set = new HashSet<Integer>(); int max = 0; int dups = 0; for (TableEntry entry : entries) { if (entry.comment != null) continue; if (entry.code > max) max = entry.code; if (set.contains(entry.code)) dups++; else set.add(entry.code); } return "density= "+entries.size() + "/" + max+"; dups= "+dups; } public class TableEntry implements Comparable<TableEntry> { public int line, code, code2; public String value, status, comment; int parse(String s) { if (s == null) return -1; try { return Integer.parseInt(s); } catch (NumberFormatException e2) { return -2; } } TableEntry(String line, String code1, String code2, String value, String status) { this.line = Integer.parseInt(line); this.code = parse(code1); if (this.code < 0) this.code = parse(code2); if (this.code < 1) comment = value; else { this.value = filter(value); this.status = status; } } TableEntry(String line, String code1, String value1, String code2, String value2, String status) { this.line = Integer.parseInt(line); this.code = parse(code1); this.code2 = parse(code2); this.value = filter(value2); this.comment = value1; this.status = status; } @Override public int compareTo(TableEntry o) { if (type == 1) return Integer.compare(code, o.code); else { int diff = Integer.compare(code, o.code); return (diff == 0) ? Integer.compare(code2, o.code2) : diff; } } @Override public String toString() { if (comment != null) return "TableEntry{" + ", line=" + line + ", comment=" + comment + '}'; else return "TableEntry{" + ", line=" + line + ", code=" + code + ", value='" + value + '\'' + ", status='" + status + '\'' + '}'; } } private static final char badDash = 173; private String filter(String s) { if (s == null) return null; return StringUtil2.replace(s, badDash, "-"); } public static void main(String arg[]) throws IOException { CommonCodeTable ct = readCommonCodes(Table.C1); for (TableEntry entry : ct.entries) System.out.printf("%s%n", entry); System.out.printf("%n%s%n", ct.state()); } } /* previous version ////////////////////////////////////////////////////////////////////////// /* <Exp_CommonTableC01_E> <No>11</No> <CodeFigureForF1F2>09</CodeFigureForF1F2> <CodeFigureForF3F3F3>009</CodeFigureForF3F3F3> <Octet5GRIB1_Octet6BUFR3>9</Octet5GRIB1_Octet6BUFR3> <OriginatingGeneratingCentres_E>US National Weather Service - Other</OriginatingGeneratingCentres_E> <Status>Operational</Status> </Exp_CommonTableC01_E> <Exp_CommonTableC02_E> <No>1</No> <DateOfAssignment_E>Not applicable</DateOfAssignment_E> <CodeFigureForrara>00</CodeFigureForrara> <CodeFigureForBUFR>0</CodeFigureForBUFR> <RadiosondeSoundingSystemUsed_E>Reserved</RadiosondeSoundingSystemUsed_E> <Status>Operational</Status> </Exp_CommonTableC02_E> <Exp_CommonTableC03_E> <No>3</No> <CodeFigureForIXIIXIX>011</CodeFigureForIXIIXIX> <CodeFigureForBUFR>11</CodeFigureForBUFR> <InstrumentMakeAndType_E>Sippican T-5</InstrumentMakeAndType_E> <EquationCoefficients_a>6.828</EquationCoefficients_a> <EquationCoefficients_b>-1.82</EquationCoefficients_b> <Status>Operational</Status> </Exp_CommonTableC03_E> <Exp_CommonTableC04_E> <No>3</No> <CodeFigureForXRXR>03</CodeFigureForXRXR> <CodeFigureForBUFR>3</CodeFigureForBUFR> <Meaning_E>Sippican MK-9</Meaning_E> <Status>Operational</Status> </Exp_CommonTableC04_E> <Exp_CommonTableC05_E> <No>4</No> <CodeFigureForI6I6I6>002</CodeFigureForI6I6I6> <CodeFigureForBUFR>2</CodeFigureForBUFR> <CodeFigureForGRIB2>2</CodeFigureForGRIB2> <SatelliteName_E>ERS 2</SatelliteName_E> <Status>Operational</Status> </Exp_CommonTableC05_E> <Exp_CommonTableC07_E> <No>4</No> <CodeFigureForsasa>03</CodeFigureForsasa> <CodeFigureForBUFR>3</CodeFigureForBUFR> <TrackingTechniquesStatusOfSystemUsed_E>Automatic with auxiliary ranging</TrackingTechniquesStatusOfSystemUsed_E> <Status>Operational</Status> </Exp_CommonTableC07_E> <Exp_CommonTableC07_E> <No>5</No> <CodeFigureForsasa>04</CodeFigureForsasa> <CodeFigureForBUFR>4</CodeFigureForBUFR> <TrackingTechniquesStatusOfSystemUsed_E>Not used</TrackingTechniquesStatusOfSystemUsed_E> <Status>Operational</Status> </Exp_CommonTableC07_E> <Exp_CommonTableC08_E> <No>3</No> <Code>12</Code> <Agency_E>BNSC</Agency_E> <Type_E>Radiometer</Type_E> <InstrumentShortName_E>ATSR-2</InstrumentShortName_E> <InstrumentLongName_E>Along track scanning radiometer - 2</InstrumentLongName_E> <Status>Operational</Status> </Exp_CommonTableC08_E> <Exp_CommonTableC11_E> <No>16</No> <CREX2>00013</CREX2> <GRIB2_BUFR4>13</GRIB2_BUFR4> <OriginatingGeneratingCentre_E>)</OriginatingGeneratingCentre_E> <Status>Operational</Status> </Exp_CommonTableC11_E> <Exp_CommonTableC12_E> <No>8</No> <CodeFigure_OriginatingCentres>2</CodeFigure_OriginatingCentres> <Name_OriginatingCentres_E>Melbourne</Name_OriginatingCentres_E> <CodeFigure_SubCentres>219</CodeFigure_SubCentres> <Name_SubCentres_E>Townsville</Name_SubCentres_E> <Status>Operational</Status> </Exp_CommonTableC12_E> <Exp_CommonTableC13_E> <No>46</No> <CodeFigure_DataCategories>003</CodeFigure_DataCategories> <Name_DataCategories_E>Vertical soundings (satellite)</Name_DataCategories_E> <CodeFigure_InternationalDataSubcategories>030</CodeFigure_InternationalDataSubcategories> <Name_InternationalDataSubcategories_E>Hyperspectral temperature/humidity sounding</Name_InternationalDataSubcategories_E> <Status>Validation</Status> </Exp_CommonTableC13_E> <Exp_CommonTableC13_E> */ /* <CommonCodeTable_C1_Nov11_en> <No>1</No> <CodeFigureForF1F2>00</CodeFigureForF1F2> <CodeFigureForF3F3F3>000</CodeFigureForF3F3F3> <Octet5GRIB1_Octet6BUFR3>0</Octet5GRIB1_Octet6BUFR3> <OriginatingGeneratingCentres_en>WMO Secretariat</OriginatingGeneratingCentres_en> <Status>Operational</Status> </CommonCodeTable_C1_Nov11_en> < public enum Table { // code code2 value C1("Centers-GRIB1,BUFR3", 1, 1, new String[]{"CodeFigureForF1F2", "CodeFigureForF3F3F3", "OriginatingGeneratingCentres_en"}), C2("Radiosondes", 2, 1, new String[]{"CodeFigureForBUFR", null, "RadiosondeSoundingSystemUsed_en"}), C3("Water temperature profile instrument", 3, 1, new String[]{"CodeFigureForBUFR", null, "InstrumentMakeAndType_en"}), C4("Water temperature profile recorder", 4, 1, new String[]{"CodeFigureForBUFR", null, "Meaning_en"}), C5("Satellite identifier", 5, 1, new String[]{"CodeFigureForBUFR", null, "SatelliteName_en"}), C7("Satellite tracking", 7, 1, new String[]{"CodeFigureForBUFR", null, "TrackingTechniquesStatusOfSystemUsed_en"}), C8("Satellite instruments", 8, 1, new String[]{"Code", null, "InstrumentLongName_en", "InstrumentShortName_en"}), C11("Centers-GRIB2,BUFR4", 11,1, new String[]{"GRIB2_BUFR4", null, "OriginatingGeneratingCentre_en"}), // code value code2 value2 C12("Subcenters", 12,2, new String[]{"CodeFigure_OriginatingCentres", "Name_OriginatingCentres_en", "CodeFigure_SubCentres", "Name_SubCentres_en"}), C13("Data sub-categories", 13,2, new String[]{"CodeFigure_DataCategories", "Name_DataCategories_en", "CodeFigure_InternationalDataSubcategories", "Name_InternationalDataSubcategories_en"}), ; */