001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2023
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see http://www.gnu.org/licenses.
027 */
028
029package edu.wisc.ssec.mcidasv.data.hydra;
030
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033import ucar.nc2.*;
034import ucar.nc2.ncml.NcMLReader;
035import ucar.ma2.*;
036
037import java.util.HashMap;
038import java.util.List;
039import java.util.ArrayList;
040import java.util.Iterator;
041import java.net.URL;
042import java.io.InputStream;
043import java.io.ByteArrayInputStream;
044import java.util.Map;
045
046import org.jdom2.input.SAXBuilder;
047import org.jdom2.output.XMLOutputter;
048import org.jdom2.Document;
049import org.jdom2.Element;
050
051
052public class NetCDFFile implements MultiDimensionReader {
053   private static final Logger logger = LoggerFactory.getLogger(NetCDFFile.class);
054   private final Map<String, Variable> varMap = new HashMap<>();
055   private final Map<String, String[]> varDimNames = new HashMap<>();
056   private final Map<String, int[]> varDimLengths = new HashMap<>();
057   private final Map<String, Class> varDataType = new HashMap<>();
058   private final Map<String, String> varUnits = new HashMap<>();
059
060   NetcdfFile ncfile = null;
061
062   public static NetCDFFile makeUnion(String filename, String other) throws Exception {
063     Object obj = new Object();
064     URL url = obj.getClass().getResource("/edu/wisc/ssec/mcidasv/data/hydra/resources/union.ncml");
065     SAXBuilder builder = new SAXBuilder(false);
066     Document doc = null;
067
068     try {
069       doc = builder.build(url);
070     } catch (Exception e) {
071       logger.error("Problem creating SAXBuilder", e);
072     }
073     Element root = doc.getRootElement();
074
075     List list = root.getChildren();
076
077     list = ((Element)list.get(1)).getChildren();
078
079     org.jdom2.Attribute attr1 = (((Element)list.get(0)).getAttributes()).get(0);
080     attr1.setValue(filename);
081
082     org.jdom2.Attribute attr2 = (((Element)list.get(1)).getAttributes()).get(0);
083     attr2.setValue(other);
084
085     XMLOutputter xmlOut = new XMLOutputter();
086     String newStr = xmlOut.outputString(doc);
087       logger.trace("union string:\n{}", newStr);
088     ByteArrayInputStream is = new ByteArrayInputStream(newStr.getBytes());
089     return new NetCDFFile(is);
090   }
091
092   public NetCDFFile(InputStream is) throws Exception {
093     ncfile = NcMLReader.readNcML(is, null);
094     init();
095   }
096
097   public NetCDFFile(String filename) throws Exception {
098     if (filename.endsWith(".ncml")) {
099       java.io.FileReader rdr = new java.io.FileReader(filename);
100       ncfile = NcMLReader.readNcML(rdr, null);
101     }
102     else {
103       ncfile = NetcdfFile.open(filename);
104     }
105     init();
106   }
107     
108   public NetCDFFile(String filename, org.jdom2.Element root) throws Exception {
109          ncfile = NcMLReader.readNcML(filename, root, null);
110          init();
111   }
112   
113   private void init() throws Exception {
114     Iterator varIter = ncfile.getVariables().iterator();
115     while(varIter.hasNext()) {
116       Variable var = (Variable) varIter.next();
117
118       if (var instanceof Structure) {
119         analyzeStructure((Structure) var);
120         continue;
121       }
122
123       int rank = var.getRank();
124       String varName = var.getFullName();
125       varMap.put(varName, var);
126       Iterator dimIter = var.getDimensions().iterator();
127       String[] dimNames = new String[rank];
128       int[] dimLengths = new int[rank];
129       int cnt = 0;
130       while(dimIter.hasNext()) {
131         Dimension dim = (Dimension) dimIter.next();
132         String dim_name = dim.getShortName();
133         if (dim_name == null) dim_name = "dim"+cnt;
134         dimNames[cnt] = dim_name;
135         dimLengths[cnt] = dim.getLength();
136         cnt++;
137       }
138       varDimNames.put(varName, dimNames);
139       varDimLengths.put(varName, dimLengths);
140       varDataType.put(varName, var.getDataType().getPrimitiveClassType());
141       
142       Attribute attr = var.findAttribute("units");
143       if (attr != null) {
144         String unitStr = attr.getStringValue();
145         varUnits.put(varName, unitStr);
146       }
147     }
148   }
149
150   void analyzeStructure(Structure var) throws Exception {
151           if ((var.getShape()).length == 0) {
152                   return;
153           }
154           String varName = var.getFullName();
155           String[] dimNames = new String[2];
156           int[] dimLengths = new int[2];
157           int cnt = 0;
158           dimLengths[0] = (var.getShape())[0];
159           dimNames[0] = "dim" + cnt;
160
161           cnt++;
162           StructureData sData = var.readStructure(0);
163           List memList = sData.getMembers();
164           dimLengths[1] = memList.size();
165           dimNames[1] = "dim" + cnt;
166
167           varDimNames.put(varName, dimNames);
168           varDimLengths.put(varName, dimLengths);
169           varMap.put(varName, var);
170
171           StructureMembers sMembers = sData.getStructureMembers();
172           Object obj = sData.getScalarObject(sMembers.getMember(0));
173           varDataType.put(varName, obj.getClass());
174   }
175
176   public Class getArrayType(String array_name) {
177           return varDataType.get(array_name);
178   }
179
180   public String[] getDimensionNames(String array_name) {
181     return varDimNames.get(array_name);
182   }
183
184   public int[] getDimensionLengths(String array_name) {
185     return varDimLengths.get(array_name);
186   }
187
188   public String getArrayUnitString(String array_name) {
189     return varUnits.get(array_name);
190   }
191
192   public int getDimensionLength(String dimName) {
193     Dimension dim = ncfile.findDimension(dimName);
194     return (dim != null) ? dim.getLength() : -1;
195   }
196
197   public float[] getFloatArray(String array_name, int[] start, int[] count, int[] stride) throws Exception {
198     return (float[]) readArray(array_name, start, count, stride);
199   }
200
201   public int[] getIntArray(String array_name, int[] start, int[] count, int[] stride) throws Exception {
202     return (int[]) readArray(array_name, start, count, stride);
203   }
204
205   public double[] getDoubleArray(String array_name, int[] start, int[] count, int[] stride) throws Exception {
206     return (double[]) readArray(array_name, start, count, stride);
207   }
208
209   public short[] getShortArray(String array_name, int[] start, int[] count, int[] stride) throws Exception {
210     return (short[]) readArray(array_name, start, count, stride);
211   }
212
213   public byte[] getByteArray(String array_name, int[] start, int[] count, int[] stride) throws Exception {
214     return (byte[]) readArray(array_name, start, count, stride);
215   }
216
217   public Object getArray(String array_name, int[] start, int[] count, int[] stride) throws Exception {
218     return readArray(array_name, start, count, stride);
219   }
220
221   protected synchronized Object readArray(String array_name, int[] start, int[] count, int[] stride) throws Exception {
222     Variable var = varMap.get(array_name);
223     if (var instanceof Structure) {
224       Array array = Array.factory(getArrayType(array_name), count);
225       Index2D idx = new Index2D(count);
226       for (int i=0; i<count[0]; i++) {
227         StructureData sData = ((Structure)var).readStructure(start[0]+i);
228         StructureMembers sMembers = sData.getStructureMembers();
229         for (int j=0; j<count[1]; j++) {
230           Object obj = sData.getScalarObject(sMembers.getMember(start[1]+j));
231           idx.set(i,j);
232           array.setObject(idx, obj);
233         }
234       }
235       return array.copyTo1DJavaArray();
236     }
237     else {
238       List<Range> rangeList = new ArrayList<>(start.length);
239       for (int i=0;i<start.length;i++) {
240         Range rng = new Range(start[i], start[i]+(count[i]-1)*stride[i], stride[i]);
241         rangeList.add(i, rng);
242       }
243       Array array = var.read(rangeList);
244       return array.copyTo1DJavaArray();
245     }
246   }
247
248   public HDFArray getGlobalAttribute(String attr_name) throws Exception {
249     throw new Exception("NetCDFFile.getGlobalAttributes: Unimplemented");
250   }
251
252   public HDFArray getArrayAttribute(String array_name, String attr_name) throws Exception {
253     Object array = null;
254     DataType dataType = null;
255
256     Variable var = varMap.get(array_name);
257     if (var != null) {
258        Attribute attr = var.findAttribute(attr_name);
259        if (attr != null) {
260           Array attrVals = attr.getValues();
261           dataType = attr.getDataType();
262           array = attrVals.copyTo1DJavaArray();
263        }
264     }
265
266     if (array == null) {
267        return null;
268     }
269     
270     HDFArray harray = null;
271
272     if (dataType.getPrimitiveClassType() == Float.TYPE) {
273       harray = HDFArray.make((float[])array);
274     }
275     else if (dataType.getPrimitiveClassType() == Double.TYPE) {
276       harray = HDFArray.make((double[])array);
277     }
278     else if (dataType == DataType.STRING) {
279       harray = HDFArray.make((String[])array);
280     }
281     else if (dataType.getPrimitiveClassType() == Short.TYPE) {
282       harray = HDFArray.make((short[])array);
283     }
284     else if (dataType.getPrimitiveClassType() == Integer.TYPE) {
285       harray = HDFArray.make((int[])array);
286     }
287     return harray;
288   }
289
290   public void close() throws Exception {
291     ncfile.close();
292   }
293
294   public Map<String, Variable> getVarMap() {
295     return varMap;
296   }
297
298   public boolean hasArray(String name) {
299       return varMap.get(name) != null;
300   }
301
302   public boolean hasDimension(String name) {
303       return ncfile.findDimension(name) != null;
304   }
305
306   public NetcdfFile getNetCDFFile() {
307           return ncfile;
308   }
309   
310   public static void main(String[] args) throws Exception {
311     NetCDFFile ncfile = new NetCDFFile(args[0]);
312     ncfile.close();
313   }
314}