1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package au.csiro.netcdf.wron;
18
19 import java.io.BufferedWriter;
20 import java.io.File;
21 import java.io.FileWriter;
22 import java.io.IOException;
23 import java.io.PrintWriter;
24 import java.io.RandomAccessFile;
25 import java.io.StringWriter;
26 import java.util.ArrayList;
27 import java.util.Date;
28 import java.util.List;
29
30 import org.apache.commons.cli.BasicParser;
31 import org.apache.commons.cli.CommandLine;
32 import org.apache.commons.cli.HelpFormatter;
33 import org.apache.commons.cli.MissingOptionException;
34 import org.apache.commons.cli.Option;
35 import org.apache.commons.cli.OptionBuilder;
36 import org.apache.commons.cli.Options;
37 import org.apache.commons.cli.ParseException;
38 import org.apache.log4j.Logger;
39
40 import ucar.ma2.Array;
41 import ucar.ma2.InvalidRangeException;
42 import ucar.nc2.Attribute;
43 import ucar.nc2.NetcdfFile;
44 import ucar.nc2.Variable;
45 import ucar.nc2.units.DateFormatter;
46 import au.csiro.netcdf.cli.CommandLineOptionsComparator;
47 import au.csiro.netcdf.util.CSVTokenizer;
48 import au.csiro.netcdf.util.Util;
49
50
51
52
53
54
55
56
57
58
59 public class MdbsyNetCDF2CSVConverter
60 {
61
62
63
64 private static final Logger LOG = Logger.getLogger(MdbsyNetCDF2CSVConverter.class.getName());
65
66
67
68
69 private static final String LATITUDE_VARIABLE_NAME = "lat";
70
71
72
73
74 private static final String LONGITUDE_VARIABLE_NAME = "long";
75
76
77
78
79 private static final String TIME_VARIABLE_NAME = "time";
80
81
82
83
84 private static final Long MILLISECONDS_IN_A_DAY = new Long("86400000");
85
86
87
88
89
90
91
92
93
94 public static final String INPUT_DIR = "inputDir";
95
96
97
98
99 public static final String OUTPUT_DIR = "outputDir";
100
101
102
103
104 public static final String FILENAME_PATTERN = "pattern";
105
106
107
108
109 private static final int VARIABLE_COLUMN = 0;
110
111
112
113
114
115 private static final int RANK_THREE = 3;
116
117
118
119
120 private static final Object CSV_FILE_EXTENSION = ".csv";
121
122
123
124
125 private static final Object NETCDF_FILE_EXTENSION = ".nc";
126
127
128
129
130 private static String DATE_COLUMN_TEXT = null;
131
132
133
134
135 private static final String UNITS = "units";
136
137
138
139
140
141
142
143
144
145
146 @SuppressWarnings("static-access")
147 public static void main(String[] args) throws ParseException, IOException, InvalidRangeException
148 {
149 Options options = new Options();
150 try
151 {
152 Option inputDirectoryName = OptionBuilder.withArgName("dir").hasArg().withDescription(
153 "1. the directory path containing the netCDF files (split by latitude) to be converted into csv.")
154 .isRequired(true).withLongOpt(INPUT_DIR).create("i");
155
156 Option outputDirectoryName = OptionBuilder.withArgName("dir").hasArg().withDescription(
157 "2: the directory path to place the new csv files.").isRequired(true).withLongOpt(OUTPUT_DIR)
158 .create("o");
159
160 options.addOption(inputDirectoryName);
161 options.addOption(outputDirectoryName);
162
163
164 CommandLine parsedCommandLine = new BasicParser().parse(options, args);
165
166 String inputDir = parsedCommandLine.getOptionValue(INPUT_DIR);
167 String outputDir = parsedCommandLine.getOptionValue(OUTPUT_DIR);
168
169 MdbsyNetCDF2CSVConverter converter = new MdbsyNetCDF2CSVConverter();
170
171 long start = System.currentTimeMillis();
172 converter.execute(inputDir, outputDir);
173 long end = System.currentTimeMillis() - start;
174 LOG.warn("Successfully converted all netcdf files to csv files in: " + end + " ms.");
175 }
176 catch (MissingOptionException moe)
177 {
178 LOG.error(moe.getMessage());
179
180
181 String header = "Recreate ";
182 String footer = "\nExample: --inputDir \"C:\\input\" --outputDir \"C:\\output\" \n"
183 + "Will convert the netCDF files (which are split by latitude) in the input directory to csv files in the output directory.";
184 StringWriter sw = new StringWriter();
185 HelpFormatter formatter = new HelpFormatter();
186 formatter.setOptionComparator(new CommandLineOptionsComparator());
187 final int lineWidth = 80;
188 formatter.printHelp(new PrintWriter(sw), lineWidth, "-", header, options, 0, 1, footer);
189 System.out.println(sw.toString());
190 }
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204
205 public void execute(String inputDir, String outputDir) throws IOException, InvalidRangeException
206 {
207
208
209
210
211 System.out.println("Starting intial convert of NetCDF 2 CSV.");
212 LOG.info("Starting intial convert of NetCDF 2 CSV.");
213 writeCSVFiles(inputDir, outputDir);
214 System.out.println("Finished intial convert of NetCDF 2 CSV.");
215 LOG.info("Finished intial convert of NetCDF 2 CSV.");
216
217
218
219
220
221
222 System.out.println("Started transposition of CSV.");
223 LOG.info("Started transposition of CSV.");
224 transposeCSVFiles(outputDir);
225 System.out.println("Finished transposition of CSV.");
226 LOG.info("Finished transposition of CSV.");
227 }
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244 private void writeCSVFiles(String inputDir, String outputDir) throws IOException, InvalidRangeException
245 {
246 File dir = new File(inputDir);
247 File[] files = dir.listFiles();
248 if (files == null)
249 {
250 System.out.println("The input directory does not exist: " + inputDir);
251 LOG.error("The input directory does not exist: " + inputDir);
252 return;
253 }
254
255 for (int fileIndex = 0; fileIndex < files.length; fileIndex++)
256 {
257 if (files[fileIndex].isFile()
258 && NETCDF_FILE_EXTENSION.equals(Util.getFileExtension(files[fileIndex].getName())))
259 {
260 NetcdfFile nc = null;
261 try
262 {
263 nc = NetcdfFile.open(files[fileIndex].getAbsolutePath());
264
265 Array latitudes = getLatitudeValues(nc);
266 Array longitudes = getLongitudeValues(nc);
267
268 LOG.info("latitude coordinate variable size: " + latitudes.getSize());
269 LOG.info("longitude coordinate variable size: " + longitudes.getSize());
270
271 for (int latIndex = 0; latIndex < latitudes.getSize(); latIndex++)
272 {
273 for (int longIndex = 0; longIndex < longitudes.getSize(); longIndex++)
274 {
275 String fileName = createFileNameBasedOnLatLong(nc, latIndex, longIndex);
276 String filePath = outputDir + System.getProperty("file.separator") + fileName + ".csv";
277
278 LOG.info("\tWorking on file: " + filePath);
279
280
281 if (Util.fileExists(filePath))
282 {
283 File file = new File(filePath);
284 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file, true
285
286
287 )));
288
289 List<String> variablesAlreadyWrittenOut = getVariablesAlreadyWrittenOut(file);
290 List<String> variablesThatCanBeWrittenOut = getVariablesThatCanBeWrittenOut(nc);
291
292 variablesThatCanBeWrittenOut.removeAll(variablesAlreadyWrittenOut);
293
294 try
295 {
296 writeOutVariable(nc, out, variablesThatCanBeWrittenOut, latIndex, longIndex);
297 }
298 finally
299 {
300 if (out != null)
301 out.close();
302 }
303 }
304 else
305
306 {
307 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(new File(filePath),
308 false
309
310 List<String> variablesThatCanBeWrittenOut = getVariablesThatCanBeWrittenOut(nc);
311
312 try
313 {
314 out.println(writeOutDateColumn(nc));
315
316
317 writeOutVariable(nc, out, variablesThatCanBeWrittenOut, latIndex, longIndex);
318 }
319 finally
320 {
321 if (out != null)
322 out.close();
323 }
324 }
325 }
326 }
327 }
328 catch (java.io.FileNotFoundException fnfe)
329 {
330 System.out.println("file not found= " + files[fileIndex]);
331 LOG.error(fnfe);
332 }
333 finally
334 {
335 if (nc != null)
336 nc.close();
337 }
338 }
339 }
340 }
341
342
343
344
345
346
347
348
349 private void writeOutVariable(NetcdfFile nc, PrintWriter out, List<String> variablesThatCanBeWrittenOut,
350 int latIndex, int longIndex) throws IOException, InvalidRangeException
351 {
352 for (String variableName : variablesThatCanBeWrittenOut)
353 {
354 Array subsection = getVariableAcrossTime(nc, variableName, latIndex, longIndex);
355 out.println(variableName + "," + subsection.toString().replaceAll(" ", ","));
356 }
357 }
358
359
360
361
362
363
364
365
366 private void transposeCSVFiles(String outputDir) throws IOException
367 {
368 File dir = new File(outputDir);
369 File[] files = dir.listFiles();
370 if (files == null)
371 {
372 System.out.println("The output directory does not exist: " + outputDir);
373 LOG.error("The output directory does not exist: " + outputDir);
374 return;
375 }
376
377 for (int fileIndex = 0; fileIndex < files.length; fileIndex++)
378 {
379 if (files[fileIndex].isFile()
380 && CSV_FILE_EXTENSION.equals(Util.getFileExtension(files[fileIndex].getName())))
381 {
382 String[][] matrix = readLookupFile(files[fileIndex]);
383
384
385 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(files[fileIndex], false
386
387
388 )));
389
390
391 String[][] transposed = transpose(matrix);
392
393 try
394 {
395 for (int row = 0; row < transposed.length; row++)
396 {
397 for (int col = 0; col < transposed[0].length; col++)
398 {
399 out.print(transposed[row][col]);
400 if (col < (transposed[0].length - 1))
401 {
402 out.print(", ");
403 }
404 }
405 out.println();
406 }
407 }
408 finally
409 {
410 if (out != null)
411 out.close();
412 }
413 }
414 }
415 }
416
417
418
419
420
421
422
423
424
425
426 private List<String> getVariablesThatCanBeWrittenOut(NetcdfFile nc)
427 {
428 List<String> variablesThatCanBeWrittenOut = new ArrayList<String>();
429
430 for (Variable variable : nc.getVariables())
431 {
432 if (variable.getRank() == RANK_THREE)
433 {
434 variablesThatCanBeWrittenOut.add(variable.getName().trim());
435 }
436 }
437
438 return variablesThatCanBeWrittenOut;
439 }
440
441
442
443
444
445
446
447
448
449 private List<String> getVariablesAlreadyWrittenOut(File file) throws IOException
450 {
451 List<String> variablesAlreadyWrittenOut = new ArrayList<String>();
452
453 String[][] matrix = readLookupFile(file);
454 for (int rows = 0; rows < matrix.length; rows++)
455 {
456 variablesAlreadyWrittenOut.add(matrix[rows][VARIABLE_COLUMN].trim());
457 }
458
459 return variablesAlreadyWrittenOut;
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475 private String createFileNameBasedOnLatLong(NetcdfFile nc, int latIndex, int longIndex) throws IOException,
476 InvalidRangeException
477 {
478 return "region_" + nc.readSection("lat(" + latIndex + ":" + latIndex + ")").toString().trim() + "_"
479 + nc.readSection("long(" + longIndex + ":" + longIndex + ")").toString().trim();
480 }
481
482
483
484
485
486
487
488
489
490 public static String[][] readLookupFile(File file) throws IOException
491 {
492 RandomAccessFile raf = new RandomAccessFile(file, "r");
493 List<String[]> lineList = new ArrayList<String[]>();
494
495 try
496 {
497 String line = raf.readLine();
498 while (line != null)
499 {
500 String[] cols = new CSVTokenizer(line).getAllColumns();
501 lineList.add(cols);
502 line = raf.readLine();
503 }
504 }
505 finally
506 {
507 raf.close();
508 }
509
510 String[][] lineCols = lineList.toArray(new String[][] {});
511 return lineCols;
512 }
513
514
515
516
517
518
519
520 private String[][] transpose(String[][] values)
521 {
522 if (values.length == 0)
523 {
524 return new String[0][0];
525 }
526
527 String[][] transposed = new String[values[0].length][values.length];
528 for (int row = 0; row < values.length; row++)
529 {
530 for (int col = 0; col < values[0].length; col++)
531 {
532 transposed[col][row] = values[row][col];
533 }
534 }
535 return transposed;
536 }
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553 private Array getVariableAcrossTime(NetcdfFile nc, String variableName, int latIndex, int longIndex)
554 throws IOException, InvalidRangeException
555 {
556 try
557 {
558 return nc.readSection(variableName + "(" + latIndex + ":" + latIndex + "," + longIndex + ":" + longIndex
559 + ",:)");
560 }
561 catch (InvalidRangeException ire)
562 {
563 ire.printStackTrace();
564 LOG.error("Could not read section: " + variableName + "(" + latIndex + ":" + latIndex + "," + longIndex
565 + ":" + longIndex + ",:)", ire);
566 }
567 return null;
568 }
569
570
571
572
573
574
575
576
577
578
579 private Array getLongitudeValues(NetcdfFile nc) throws IOException, IllegalArgumentException
580 {
581 List<Variable> variables = nc.getVariables();
582 if (variables != null)
583 {
584 for (Variable v : variables)
585 {
586 if (v.isCoordinateVariable() && LONGITUDE_VARIABLE_NAME.equals(v.getName()))
587 {
588 return v.read();
589 }
590 }
591 }
592 LOG.error("Could not find coordinate variable: " + LONGITUDE_VARIABLE_NAME);
593 throw new IllegalStateException("Could not find coordinate variable: " + LONGITUDE_VARIABLE_NAME);
594 }
595
596
597
598
599
600
601
602
603
604
605 private Array getLatitudeValues(NetcdfFile nc) throws IOException, IllegalArgumentException
606 {
607 List<Variable> variables = nc.getVariables();
608 if (variables != null)
609 {
610 for (Variable v : variables)
611 {
612 if (v.isCoordinateVariable() && LATITUDE_VARIABLE_NAME.equals(v.getName()))
613 {
614 return v.read();
615 }
616 }
617 }
618 LOG.error("Could not find coordinate variable: " + LATITUDE_VARIABLE_NAME);
619 throw new IllegalStateException("Could not find coordinate variable: " + LATITUDE_VARIABLE_NAME);
620 }
621
622
623
624
625
626
627
628
629
630 private static Variable getTimeVariable(NetcdfFile nc) throws IllegalArgumentException
631 {
632 List<Variable> variables = nc.getVariables();
633 if (variables != null)
634 {
635 for (Variable v : variables)
636 {
637 if (v.isCoordinateVariable() && TIME_VARIABLE_NAME.equals(v.getName()))
638 {
639 return v;
640 }
641 }
642 }
643 LOG.error("Could not find coordinate variable: " + TIME_VARIABLE_NAME);
644 throw new IllegalStateException("Could not find coordinate variable: " + TIME_VARIABLE_NAME);
645 }
646
647
648
649
650
651
652
653
654 private static String writeOutDateColumn(NetcdfFile nc)
655 {
656
657 if (DATE_COLUMN_TEXT == null)
658 {
659 StringBuffer strBuf = new StringBuffer();
660
661 DateFormatter dateFormatter = new DateFormatter();
662 Date epocDate;
663 try
664 {
665 Variable time = getTimeVariable(nc);
666
667 String timeUnits = getVariablesUnits(time);
668 String epocString = timeUnits.substring(timeUnits.indexOf("days since") + 11);
669
670
671
672
673 epocDate = dateFormatter.dateOnlyFormat(epocString);
674 strBuf.append("Date,");
675
676 long timeVariableSize = time.getSize();
677 for (int i = 0; i < timeVariableSize; i++)
678 {
679 strBuf.append(dateFormatter.toDateOnlyString(
680 new Date(epocDate.getTime() + (i * MILLISECONDS_IN_A_DAY))).trim());
681
682 if (i < (timeVariableSize - 1))
683 {
684 strBuf.append(",");
685 }
686 }
687
688 DATE_COLUMN_TEXT = strBuf.toString();
689 }
690 catch (java.text.ParseException pe)
691 {
692 pe.printStackTrace();
693 LOG.error("Error parsing date", pe);
694 }
695 }
696 return DATE_COLUMN_TEXT;
697 }
698
699
700
701
702
703
704
705
706 private static String getVariablesUnits(Variable variable)
707 {
708 List<Attribute> attributes = variable.getAttributes();
709 if (attributes != null)
710 {
711 for (Attribute attribute : attributes)
712 {
713 if (UNITS.equals(attribute.getName()))
714 {
715 return attribute.getStringValue();
716 }
717 }
718 }
719 LOG
720 .error("Could not find the value for the variable: " + variable.getName() + " -\"" + UNITS
721 + "\" attribute.");
722 throw new IllegalStateException("Could not find the value for the variable: " + variable.getName() + " -\""
723 + UNITS + "\" attribute.");
724 }
725 }