001    /**
002     * 
003     */
004    package org.wdssii.core;
005    
006    import java.io.File;
007    import java.io.FileWriter;
008    import java.io.IOException;
009    import java.io.PrintWriter;
010    import java.text.SimpleDateFormat;
011    import java.util.Date;
012    import java.util.Iterator;
013    import java.util.Map.Entry;
014    
015    import org.apache.commons.logging.Log;
016    import org.apache.commons.logging.LogFactory;
017    
018    import ucar.ma2.ArrayFloat;
019    import ucar.nc2.Attribute;
020    import ucar.nc2.Dimension;
021    import ucar.nc2.NetcdfFileWriteable;
022    
023    /**
024     * @author lakshman
025     * 
026     */
027    public class NetcdfDataEncoder extends DataEncoder {
028            private static Log log = LogFactory.getLog(NetcdfDataEncoder.class);
029    
030            protected NetcdfDataEncoder(){
031            }
032    
033            private String compress(String fileName, String relativeFileName) throws IOException {
034                    if ( this.isCompressionEnabled() ){
035                            try{
036                                    new File( fileName + ".gz" ).delete();
037                            } catch (Exception e){
038                                    // ok if file doesn't exist
039                            }
040                            Runtime.getRuntime().exec("gzip " + fileName);
041                            relativeFileName += ".gz";
042                    }               
043                    return relativeFileName;
044            }
045    
046            @Override
047            protected String[] getParams(String relativeFileName){
048                    return new String[] { "netcdf", "{indexlocation}", relativeFileName };
049            }
050            
051            @Override
052            protected void writeAndNotify(PolarGrid pg, String outputDir, String[] subtypes) {
053                    boolean sparse = shouldWriteSparse(pg.getValues(), DataType.MissingData);
054                    NetcdfFileWriteable ncfile = null;
055                    try {
056                            // filename
057                            String formattedDate = getFormattedDate(pg);
058                            String relativeFileName = getRelativeFileName(pg, outputDir,
059                                            formattedDate, subtypes);
060                            String fileName = outputDir + '/' + relativeFileName;
061                            if (log.isDebugEnabled()) {
062                                    log.debug("Writing to " + fileName);
063                            }
064                            // create sparse/regular grid
065                            NetcdfSparseGrid sparseGrid = null;
066                            NetcdfRegularGrid netcdfGrid = null;
067                            if ( sparse ){
068                                    sparseGrid = new NetcdfSparseGrid(pg.getValues(), DataType.MissingData);
069                            } else {
070                                    netcdfGrid = new NetcdfRegularGrid(pg.getValues());
071                            }
072                            int numRadials = pg.getNumRadials();
073                            int numGates = pg.getNumGates();
074                            
075                            // create netcdf file and set up dimensions
076                            ncfile = NetcdfFileWriteable.createNew(fileName, false);
077                            Dimension azdim = ncfile.addDimension("Azimuth", numRadials);
078                            Dimension rndim = ncfile.addDimension("Gate", numGates);
079    
080                            ncfile.addVariable("Azimuth", ucar.ma2.DataType.FLOAT,
081                                            new Dimension[] { azdim });
082                            ncfile.addVariableAttribute("Azimuth", new Attribute("Units","Degrees"));
083                            ncfile.addVariable("BeamWidth", ucar.ma2.DataType.FLOAT,
084                                            new Dimension[] { azdim });
085                            ncfile.addVariableAttribute("BeamWidth", new Attribute("Units","Degrees"));
086                            ncfile.addVariable("AzimuthalSpacing", ucar.ma2.DataType.FLOAT,
087                                            new Dimension[] { azdim });
088                            ncfile.addVariableAttribute("AzimuthalSpacing", new Attribute("Units","Degrees"));
089                            ncfile.addVariable("GateWidth", ucar.ma2.DataType.FLOAT,
090                                            new Dimension[] { azdim });
091                            ncfile.addVariableAttribute("GateWidth", new Attribute("Units","Meters"));
092                            ncfile.addVariable("NyquistVelocity", ucar.ma2.DataType.FLOAT,
093                                            new Dimension[] { azdim });
094                            ncfile.addVariableAttribute("NyquistVelocity", new Attribute("Units","MetersPerSecond"));
095                            
096                            Dimension pxdim = null;
097                            if ( sparse ){
098                                    int numPixels = (int) (sparseGrid.getValues().getSize());
099                                    pxdim = ncfile.addDimension("pixel", numPixels);
100                                    ncfile.addVariable(pg.getTypeName(), ucar.ma2.DataType.FLOAT,
101                                            new Dimension[] { pxdim });
102                                    ncfile.addVariable("pixel_x", ucar.ma2.DataType.SHORT,
103                                            new Dimension[] { pxdim });
104                                    ncfile.addVariable("pixel_y", ucar.ma2.DataType.SHORT,
105                                            new Dimension[] { pxdim });
106                                    ncfile.addVariable("pixel_count", ucar.ma2.DataType.INT,
107                                            new Dimension[] { pxdim });
108                                    ncfile.addVariableAttribute(pg.getTypeName(),new Attribute("BackgroundValue", DataType.MissingData));
109                            } else {
110                                    ncfile.addVariable(pg.getTypeName(), ucar.ma2.DataType.FLOAT,
111                                                    new Dimension[] { azdim,rndim });
112                            }
113                            
114                            ncfile.addVariableAttribute(pg.getTypeName(), new Attribute("Units",pg.getUnit()));
115                            // follow netcdf best practices convention
116                            ncfile.addVariableAttribute(pg.getTypeName(), new Attribute("units",pg.getUnit()));
117                            ncfile.addVariableAttribute(pg.getTypeName(), new Attribute("missing_value",DataType.MissingData));
118                            
119                            ncfile.addGlobalAttribute("Elevation", pg.getElevation());
120                            ncfile.addGlobalAttribute("ElevationUnits", "Degrees");
121                            ncfile.addGlobalAttribute("RangeToFirstGate", pg.getRangeToFirstGateKms() * 1000 );
122                            ncfile.addGlobalAttribute("RangeToFirstGateUnits", "Meters" );
123                            
124                            if (sparse){
125                                    addGlobalAttributes(pg, ncfile, "SparseRadialSet");
126                            } else {
127                                    addGlobalAttributes(pg, ncfile, "RadialSet");
128                            }
129                            ncfile.create();
130    
131                            // write the variables
132                            ArrayFloat az = new ArrayFloat(new int[] { numRadials });
133                            ArrayFloat bw = new ArrayFloat(new int[] { numRadials });
134                            ArrayFloat as = new ArrayFloat(new int[] { numRadials });
135                            ArrayFloat gw = new ArrayFloat(new int[] { numRadials });
136                            ArrayFloat ny = new ArrayFloat(new int[] { numRadials });
137                            ucar.ma2.Index azindex = az.getIndex();
138                            for (int i = 0; i < numRadials; ++i) {
139                                    azindex.set(i);
140                                    az.set(azindex, pg.getAngularResolution() * i);
141                                    bw.set(azindex, pg.getBeamWidth());
142                                    as.set(azindex, pg.getAngularResolution());
143                                    gw.set(azindex, pg.getGateWidthKms() * 1000); // meters in netcdf file
144                                    ny.set(azindex, pg.getNyquist());
145                            }
146                            ncfile.write("Azimuth", az);
147                            ncfile.write("BeamWidth", bw);
148                            ncfile.write("AzimuthalSpacing", as);
149                            ncfile.write("GateWidth", gw);
150                            ncfile.write("NyquistVelocity", ny);
151                            
152                            if (sparse){
153                                    ncfile.write(pg.getTypeName(), sparseGrid.getValues());
154                                    ncfile.write("pixel_x", sparseGrid.getPixelx());
155                                    ncfile.write("pixel_y", sparseGrid.getPixely());
156                                    ncfile.write("pixel_count", sparseGrid.getPixelcount());
157                            } else {
158                                    ncfile.write(pg.getTypeName(), netcdfGrid.getValues());
159                            }
160                            // close the file. Set to null so that finally doesn't try to close
161                            // again
162                            ncfile.close();
163                            ncfile = null;
164    
165                            // invoke gzip on the file
166                            relativeFileName = compress(fileName, relativeFileName);
167                            
168                            // create index
169                            notifyRecord(pg, outputDir, subtypes, formattedDate,
170                                            relativeFileName);
171                            
172                            if (log.isInfoEnabled()){
173                                    log.info(relativeFileName + " written");
174                            }
175                    } catch (Exception e) {
176                            log.error("Writing failed: ", e);
177                    } finally {
178                            if (ncfile != null) {
179                                    try {
180                                            if (ncfile != null){
181                                                    ncfile.close();
182                                            }
183                                    } catch (IOException e) {
184                                    }
185                            }
186                    }
187            }
188    
189            protected void writeAndNotify(LatLonGrid llg, String outputDir, String[] subtypes) {
190                    boolean sparse = shouldWriteSparse(llg.getValues(), DataType.MissingData);
191                    NetcdfFileWriteable ncfile = null;
192                    try {
193                            // filename
194                            String formattedDate = getFormattedDate(llg);
195                            String relativeFileName = getRelativeFileName(llg, outputDir,
196                                            formattedDate, subtypes);
197                            String fileName = outputDir + '/' + relativeFileName;
198                            if (log.isDebugEnabled()) {
199                                    log.debug("Writing to " + fileName);
200                            }
201                            // create sparse/regular grid
202                            NetcdfSparseGrid sparseGrid = null;
203                            NetcdfRegularGrid netcdfGrid = null;
204                            if ( sparse ){
205                                    sparseGrid = new NetcdfSparseGrid(llg.getValues(), DataType.MissingData);
206                            } else {
207                                    netcdfGrid = new NetcdfRegularGrid(llg.getValues());
208                            }
209                            int numLat = llg.getNumLat();
210                            int numLon = llg.getNumLon();
211                            
212                            // create netcdf file and set up dimensions
213                            ncfile = NetcdfFileWriteable.createNew(fileName, false);
214                            Dimension azdim = ncfile.addDimension("Lat", numLat);
215                            Dimension rndim = ncfile.addDimension("Lon", numLon);
216                            
217                            Dimension pxdim = null;
218                            if ( sparse ){
219                                    int numPixels = (int) (sparseGrid.getValues().getSize());
220                                    pxdim = ncfile.addDimension("pixel", numPixels);
221                                    ncfile.addVariable(llg.getTypeName(), ucar.ma2.DataType.FLOAT,
222                                            new Dimension[] { pxdim });
223                                    ncfile.addVariable("pixel_x", ucar.ma2.DataType.SHORT,
224                                            new Dimension[] { pxdim });
225                                    ncfile.addVariable("pixel_y", ucar.ma2.DataType.SHORT,
226                                            new Dimension[] { pxdim });
227                                    ncfile.addVariable("pixel_count", ucar.ma2.DataType.INT,
228                                            new Dimension[] { pxdim });
229                                    ncfile.addVariableAttribute(llg.getTypeName(),new Attribute("BackgroundValue", DataType.MissingData));
230                            } else {
231                                    ncfile.addVariable(llg.getTypeName(), ucar.ma2.DataType.FLOAT,
232                                                    new Dimension[] { azdim,rndim });
233                            }
234                            
235                            ncfile.addVariableAttribute(llg.getTypeName(), new Attribute("Units",llg.getUnit()));
236                            // follow netcdf best practices convention
237                            ncfile.addVariableAttribute(llg.getTypeName(), new Attribute("units",llg.getUnit()));
238                            ncfile.addVariableAttribute(llg.getTypeName(), new Attribute("missing_value",DataType.MissingData));
239    
240                            ncfile.addGlobalAttribute("LatGridSpacing", llg.getDeltaLat() );
241                            ncfile.addGlobalAttribute("LatGridSpacingUnits", "Degrees");
242                            ncfile.addGlobalAttribute("LonGridSpacing", llg.getDeltaLon() );
243                            ncfile.addGlobalAttribute("LonGridSpacingUnits", "Degrees");
244                            
245                            if (sparse){
246                                    addGlobalAttributes(llg, ncfile, "SparseLatLonGrid");
247                            } else {
248                                    addGlobalAttributes(llg, ncfile, "LatLonGrid");
249                            }
250                            ncfile.create();
251    
252                            // write the variables
253                            if (sparse){
254                                    ncfile.write(llg.getTypeName(), sparseGrid.getValues());
255                                    ncfile.write("pixel_x", sparseGrid.getPixelx());
256                                    ncfile.write("pixel_y", sparseGrid.getPixely());
257                                    ncfile.write("pixel_count", sparseGrid.getPixelcount());
258                            } else {
259                                    ncfile.write(llg.getTypeName(), netcdfGrid.getValues());
260                            }
261                            // close the file. Set to null so that finally doesn't try to close
262                            // again
263                            ncfile.close();
264                            ncfile = null;
265    
266                            // invoke gzip on the file
267                            relativeFileName = compress(fileName, relativeFileName);
268                            
269                            // create index
270                            notifyRecord(llg, outputDir, subtypes, formattedDate,
271                                            relativeFileName);
272                            
273                            if (log.isInfoEnabled()){
274                                    log.info(relativeFileName + " written");
275                            }
276                    } catch (Exception e) {
277                            log.error("Writing failed: ", e);
278                    } finally {
279                            if (ncfile != null) {
280                                    try {
281                                            if (ncfile != null){
282                                                    ncfile.close();
283                                            }
284                                    } catch (IOException e) {
285                                    }
286                            }
287                    }
288            }
289            
290            private static void addGlobalAttributes(DataType dt,
291                            NetcdfFileWriteable ncfile, String dataType) {
292                    ncfile.addGlobalAttribute("TypeName", dt.getTypeName());
293                    ncfile.addGlobalAttribute("DataType", dataType);
294                    ncfile.addGlobalAttribute("Latitude", dt.getLocation().getLatitude());
295                    ncfile.addGlobalAttribute("Longitude", dt.getLocation().getLongitude());
296                    ncfile.addGlobalAttribute("Height", dt.getLocation().getHeightKms() * 1000);
297                    ncfile.addGlobalAttribute("MissingData", DataType.MissingData);
298                    ncfile.addGlobalAttribute("RangeFolded", DataType.RangeFolded);
299    
300                    long tm_long = dt.getTime().getTime();
301                    long tm = tm_long / 1000;
302                    double frac = (tm_long - tm * 1000) / 1000.0;
303                    ncfile.addGlobalAttribute("Time", (int) tm); // FIXME: netcdf 3 does
304                    // not support long
305                    ncfile.addGlobalAttribute("FractionalTime", frac);
306                    Iterator<Entry<String, String>> attributes = dt.getAttributes()
307                                    .entrySet().iterator();
308                    Iterator<String> attributeKeys = dt.getAttributes().keySet().iterator();
309                    StringBuilder attributeNames = new StringBuilder();
310                    while (attributeKeys.hasNext()) {
311                            attributeNames.append(attributeKeys.next()).append(' ');
312                    }
313                    ncfile.addGlobalAttribute("attributes", attributeNames.toString());
314                    while (attributes.hasNext()) {
315                            Entry<String, String> entry = attributes.next();
316                            ncfile.addGlobalAttribute(entry.getKey() + "-value", entry
317                                            .getValue());
318                    }
319            }
320    
321            private static String getRelativeFileName(DataType dt, String outputDir,
322                            String formattedDate, String[] subtypes) throws IOException {
323                    String subtype = (subtypes.length > 0) ? subtypes[0] : "";
324    
325                    StringBuilder dirName = new StringBuilder();
326                    dirName.append(dt.getTypeName()).append("/");
327                    dirName.append(subtype).append("/");
328    
329                    new File(outputDir + '/' + dirName.toString()).mkdirs();
330    
331                    String fileName = dirName.append(formattedDate).append(".netcdf")
332                                    .toString();
333                    return fileName;
334            }
335            
336            @Override
337            protected void writeAndNotify(DataTable dt, String outputDir,
338                            String[] subtypes) {
339                    throw new UnsupportedOperationException("DataTable netcdf format not defined");
340            }
341    }