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.BufferedReader;
20 import java.io.ByteArrayInputStream;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileNotFoundException;
24 import java.io.FileReader;
25 import java.io.IOException;
26 import java.io.PrintWriter;
27 import java.io.RandomAccessFile;
28 import java.io.StringWriter;
29 import java.io.UnsupportedEncodingException;
30 import java.text.DateFormat;
31 import java.text.NumberFormat;
32 import java.text.SimpleDateFormat;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Calendar;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.Date;
39 import java.util.HashMap;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Scanner;
44 import java.util.Set;
45 import java.util.TreeSet;
46 import java.util.regex.Pattern;
47
48 import org.apache.commons.cli.BasicParser;
49 import org.apache.commons.cli.CommandLine;
50 import org.apache.commons.cli.HelpFormatter;
51 import org.apache.commons.cli.MissingOptionException;
52 import org.apache.commons.cli.Option;
53 import org.apache.commons.cli.OptionBuilder;
54 import org.apache.commons.cli.Options;
55 import org.apache.commons.cli.ParseException;
56 import org.apache.log4j.Logger;
57
58 import ucar.ma2.DataType;
59 import ucar.nc2.Attribute;
60 import ucar.nc2.units.DateFormatter;
61 import au.csiro.netcdf.NcDefineAttributes;
62 import au.csiro.netcdf.NcDefineDimension;
63 import au.csiro.netcdf.NcDefineVariable;
64 import au.csiro.netcdf.NcWriteVariable;
65 import au.csiro.netcdf.cli.CommandLineOptionsComparator;
66 import au.csiro.netcdf.util.CSVTokenizer;
67 import au.csiro.netcdf.util.NetCDFUtils;
68
69
70
71
72
73
74
75
76
77
78
79
80 public class MdbsyScenarioAConverter
81 {
82
83 private static String COLLECTION = "Collection-MDBSY_Climate";
84 private static String SCENARIO = "";
85 private static String MODEL = "";
86 private static String CASE = "";
87
88
89
90
91 private static String LOCATION_LOOKUP_FILE = "";
92
93
94
95
96 private static String METADATA_FILE = "";
97
98
99
100
101 private static String INPUT_CSV_DIRECTORY = "";
102
103
104
105
106 private static String OUTPUT_NETCDF_DIRECTORY = "";
107
108
109
110
111 private static final String NETCDF_FILE_EXTENSION = ".nc";
112
113
114
115
116 private static final String ENCODING = "UTF-8";
117
118
119 private static final String[] variableNames = new String[] { "SILO_rainfall", "SILO_minTemp", "SILO_maxTemp",
120 "SILO_incomingSolarRadiation", "SILO_actualVapourPressure", "APET", "PPET", "relativeHumidity", "Evappt" };
121 private static final String[] variableUnits = new String[] { "mm", "degrees_celcius", "degrees_celcius", "MJ",
122 "hpa", "mm", "mm", "TBA", "mm" };
123 private static final String[] variableLongNames = new String[] { "SILO_rainfall", "SILO_minTemp", "SILO_maxTemp",
124 "SILO_incomingSolarRadiation", "SILO_actualVapourPressure", "APET", "PPET", "relativeHumidity", "P-TET" };
125 private static final String[] variableFillValues = new String[] { "-9999.0f", "-9999.0f", "-9999.0f", "-9999.0f",
126 "-9999.0f", "-9999.0f", "-9999.0f", "-9999.0f", "-9999.0f" };
127 private static final String[] variableMissingValues = variableFillValues;
128 private static final String[] variableMinValues = new String[] { "0.0f", "-20.0f", "-20.0f", "0.0f",
129 "0.0f", "0.0f", "0.0f", "0.0f", "0.0f" };
130 private static final String[] variableMaxValues = new String[] { "500.0f", "100.0f", "100.0f", "100.0f",
131 "100.0f", "100.0f", "100.0f", "100.0f", "500.0f" };
132 private static final DataType[] variableDataTypes = new DataType[] { DataType.FLOAT, DataType.FLOAT,
133 DataType.FLOAT, DataType.FLOAT, DataType.FLOAT, DataType.FLOAT, DataType.FLOAT, DataType.FLOAT,
134 DataType.FLOAT };
135 static final int numVariables = variableNames.length;
136 private static final String GRID_MAPPING = "crs";
137
138
139 private static final String TIME = "time";
140 private static final String TIME_RANGE = "0-43097";
141 private static final int TIME_SIZE = 43098;
142 private static final String TIME_LONG_NAME = "reference time";
143 private static final String TIME_STANDARD_NAME = "time";
144 private static final String TIME_UNITS = "days since 1889-01-01 0:0:0";
145 private static final String TIME_CALENDAR = "Gregorian";
146 private static final String TIME_AXIS = "T";
147 private static final String TIME_BOUNDS = "time_bnds";
148
149
150 private static final String LONG = "longitude";
151 private static final int LONG_COLUMN_INDEX = 1;
152 private static final String LONG_RANGE = "0-296";
153 private static final int LONG_SIZE = 297;
154 private static final String LONG_UNITS = "degrees_east";
155 private static final String LONG_STANDARD_NAME = "longitude";
156 private static final String LONG_AXIS = "X";
157 private static final String LONG_LONG_NAME = "longitude";
158
159
160 private static final String LAT = "latitude";
161 private static final int LAT_COLUMN_INDEX = 2;
162 private static final String LAT_RANGE = "0-278";
163 private static final int LAT_SIZE = 279;
164 private static final String LAT_UNITS = "degrees_north";
165 private static final String LAT_STANDARD_NAME = "latitude";
166 private static final String LAT_AXIS = "Y";
167 private static final String LAT_LONG_NAME = "latitude";
168
169
170 private static final String NV = "nv";
171 private static final String NV_RANGE = "0-1";
172 private static final int NV_SIZE = 2;
173
174
175 private static String ELEVATION = "elev";
176 private static int ELEVATION_COLUMN_INDEX = 3;
177 private static final String ELEVATION_UNITS = "m";
178 private static final String ELEVATION_STANDARD_NAME = "altitude";
179 private static final String ELEVATION_LONG_NAME = "Elevation above sea level";
180 private static final float ELEVATION_MISSING_VALUE = -999.99f;
181
182
183 private static String CATCHMENT_ID = "catchmentId";
184 private static int CATCHMENT_ID_COLUMN_INDEX = 4;
185 private static final String CATCHMENT_ID_LONG_NAME = "MDB Catchment Id";
186 private static final float CATCHMENT_ID_MISSING_VALUE = 0f;
187
188
189 private static String REPORTING_REGION = "repRegionId";
190 private static int REPORTING_REGION_ID_COLUMN_INDEX = 5;
191 private static String REPORTING_REGION_LONG_NAME = "MDB Reporting Region Id";
192 private static final float REPORTING_REGION_MISSING_VALUE = 0f;
193
194
195 private static String CRS_GRID_MAPPING_NAME = "latitude_longitude";
196 private static float CRS_LONGITUDE_OF_PRIME_MERIDIAN = 0.0f;
197 private static float CRS_SEMI_MAJOR_AXIS = 6378137.0f;
198 private static float CRS_INVERSE_FLATTENING = 298.257222101f;
199
200
201 private static final int CELL_ID_COLUMN_INDEX = 0;
202
203
204
205
206 private static final String EPOC_DATE_STRING = "1889-01-01";
207 private static final DateFormatter isoDateTimeFormat = new DateFormatter();
208 private static final int DATE_COLUMN_INDEX = 0;
209
210
211 private static int ROW_CHUNK_SIZE = 1000;
212
213
214 private static final int NUM_CSV_COLUMNS = 1 + numVariables;
215
216
217
218
219 private static final Logger LOG = Logger.getLogger(MdbsyScenarioAConverter.class.getName());
220
221
222 private ConversionUtils convUtils = new ConversionUtils();
223
224
225
226
227 @SuppressWarnings("static-access")
228 public static void main(String[] args) throws Exception
229 {
230 Options options = new Options();
231 try
232 {
233 Option inputDirectory = OptionBuilder.withArgName("dir").hasArg().withDescription(
234 " 1: the directory containing the csv files for the scenario.").isRequired(true).withLongOpt(
235 "inputDirectory").create("i");
236
237 Option lookupFile = OptionBuilder.withArgName("file").hasArg()
238 .withDescription(" 2: the cellId lookup file.").isRequired(true).withLongOpt("lookupFile").create(
239 "f");
240
241 Option outputDirectory = OptionBuilder.withArgName("dir").hasArg().withDescription(
242 " 3: the output directory.").isRequired(true).withLongOpt("outputDirectory").create("o");
243
244 Option scenario = OptionBuilder.withArgName("text").hasArg().withDescription(
245 " 4: the modelling scenario used to name the netCDF file.").isRequired(true).withLongOpt("scenario")
246 .create("s");
247
248 Option metadataFile = OptionBuilder.withArgName("file").hasArg().withDescription(
249 " 5: a file of global metadata attributes to be added to the netCDF file.").isRequired(true)
250 .withLongOpt("metadataFile").create("m");
251
252 Option numRows = OptionBuilder.withArgName("int").hasArg().withDescription(
253 " 6: the number of rows to read at a time.").isRequired(true).withLongOpt("rows").create("r");
254
255 Option byDecade = OptionBuilder.withDescription(
256 " 7: OPTIONAL, set if we want to convert into netCDF files by decade.").isRequired(false)
257 .withLongOpt("decade").create("d");
258
259 Option byLatitude = OptionBuilder.withDescription(
260 " 8: OPTIONAL, set if we want to convert into netCDF files by degrees of latitude.").isRequired(
261 false).withLongOpt("latitude").create("l");
262
263 Option startLat = OptionBuilder.withArgName("latitude").hasArg().withDescription(
264 " 9: OPTIONAL, The latitude at which to start processing. For byDecade, this option will "
265 + "assume already existing netCDF files.").isRequired(false).create("startLat");
266
267 Option endLat = OptionBuilder.withArgName("latitude").hasArg().withDescription(
268 "10: OPTIONAL, The latitude at which to end processing (inclusive).").isRequired(false).create(
269 "endLat");
270
271 options.addOption(inputDirectory);
272 options.addOption(lookupFile);
273 options.addOption(outputDirectory);
274 options.addOption(scenario);
275 options.addOption(metadataFile);
276 options.addOption(numRows);
277 options.addOption(byDecade);
278 options.addOption(byLatitude);
279 options.addOption(startLat);
280 options.addOption(endLat);
281
282
283 CommandLine parsedCommandLine = new BasicParser().parse(options, args);
284
285 INPUT_CSV_DIRECTORY = (parsedCommandLine.hasOption("inputDirectory")) ? parsedCommandLine
286 .getOptionValue("inputDirectory") : "";
287 LOCATION_LOOKUP_FILE = (parsedCommandLine.hasOption("lookupFile")) ? parsedCommandLine
288 .getOptionValue("lookupFile") : "";
289 OUTPUT_NETCDF_DIRECTORY = (parsedCommandLine.hasOption("outputDirectory")) ? parsedCommandLine
290 .getOptionValue("outputDirectory") : "";
291 SCENARIO = (parsedCommandLine.hasOption("scenario")) ? parsedCommandLine.getOptionValue("scenario") : "";
292 METADATA_FILE = (parsedCommandLine.hasOption("metadataFile")) ? parsedCommandLine
293 .getOptionValue("metadataFile") : "";
294 MODEL = "";
295 CASE = "";
296 ROW_CHUNK_SIZE = (parsedCommandLine.hasOption("rows")) ? Integer.valueOf(parsedCommandLine
297 .getOptionValue("rows")) : ROW_CHUNK_SIZE;
298 String startingLatitude = (parsedCommandLine.hasOption("startLat")) ? parsedCommandLine
299 .getOptionValue("startLat") : "";
300 String endingLatitude = (parsedCommandLine.hasOption("endLat")) ? parsedCommandLine
301 .getOptionValue("endLat") : "";
302
303 boolean doSplitByDecade = parsedCommandLine.hasOption("decade");
304 boolean doSplitByLatitude = parsedCommandLine.hasOption("latitude");
305
306 MdbsyScenarioAConverter converter = new MdbsyScenarioAConverter();
307
308 if (doSplitByDecade)
309 {
310 long start = System.currentTimeMillis();
311 converter.splitByDecade(startingLatitude, endingLatitude);
312 long end = System.currentTimeMillis() - start;
313 LOG.warn("Split by decade in: " + end + " ms.");
314 }
315
316 if (doSplitByLatitude)
317 {
318 long start = System.currentTimeMillis();
319 converter.splitByLatitude(startingLatitude, endingLatitude);
320 long end = System.currentTimeMillis() - start;
321 LOG.warn("Split by latitude in: " + end + " ms.");
322 }
323 }
324 catch (MissingOptionException moe)
325 {
326 LOG.error(moe.getMessage());
327
328 StringWriter sw = new StringWriter();
329 HelpFormatter formatter = new HelpFormatter();
330 formatter.setOptionComparator(new CommandLineOptionsComparator());
331 formatter.printHelp(new PrintWriter(sw), 80, "-", "header", options, 0, 1, "footer");
332 System.out.println(sw.toString());
333 }
334 }
335
336
337
338
339
340
341 public void splitByLatitude(String startingLatitude, String endingLatitude) throws Exception
342 {
343
344 Map<String, List<CellData>> triplesGroupedByLatitude = this.getTriplesGroupedByLatitude(this.readLookupFile());
345
346
347 Set<String> latitudeKeySet = triplesGroupedByLatitude.keySet();
348 Set<String> limitedLatitudes = convUtils.getLimitedLatitudes(latitudeKeySet, startingLatitude, endingLatitude);
349 List<String> latitudeKeyList = new ArrayList<String>(limitedLatitudes);
350 Collections.sort(latitudeKeyList, new Comparator<String>()
351 {
352 @Override
353 public int compare(String o1, String o2)
354 {
355 return Integer.valueOf(o2).compareTo(Integer.valueOf(o1));
356 }
357 });
358
359
360 for (String latitudeKey : latitudeKeyList)
361 {
362 LOG.warn("Started work on latitude: " + latitudeKey + ".");
363 String latitudeDegree = latitudeKey;
364 List<CellData> triples = triplesGroupedByLatitude.get(latitudeDegree);
365
366
367 Set<String> sortedLatitudes = this.getSortedLatitudes(triples);
368 int blockLatSize = sortedLatitudes.size();
369 String blockLatRange = "0-" + String.valueOf(sortedLatitudes.size() - 1);
370
371 Set<String> sortedLongitudes = this.getSortedLongitudes(triples);
372 int blockLongSize = sortedLongitudes.size();
373 String blockLongRange = "0-" + String.valueOf(sortedLongitudes.size() - 1);
374
375
376 String filenames[] = new String[numVariables];
377 List<String> dates = this.getDatesAsDaysSinceEpoc(this.getDates());
378 for (int i = 0; i < filenames.length; i++)
379 {
380 filenames[i] = this.generateLatitudeFilename(latitudeDegree, variableNames[i]);
381 this.createVariableFile(filenames[i], Arrays.asList(LAT, LONG, TIME), i, TIME_SIZE, blockLatSize,
382 blockLongSize);
383 this.fillCoordinateVariables(filenames[i], sortedLongitudes, sortedLatitudes, dates, blockLatRange,
384 blockLongRange, TIME_RANGE);
385 this.fillSpatialVariables(filenames[i], triples);
386 this.fillDataForLatitude(filenames[i], i, sortedLongitudes, sortedLatitudes, dates, Arrays.asList(LAT,
387 LONG, TIME));
388 }
389
390
391
392 this.fillDataByLatitudeVariables(filenames, triples, Arrays.asList(LAT, LONG, TIME));
393 }
394 }
395
396
397
398
399
400
401
402
403 public void splitByDecade(String startingLatitude, String endingLatitude) throws Exception
404 {
405
406 List<CellData> triples = this.getAllTriples(this.readLookupFile());
407
408
409 Set<String> sortedLatitudes = this.getSortedLatitudes(triples);
410 String blockLatRange = "0-" + String.valueOf(sortedLatitudes.size() - 1);
411
412 Set<String> sortedLongitudes = this.getSortedLongitudes(triples);
413 String blockLongRange = "0-" + String.valueOf(sortedLongitudes.size() - 1);
414 String startingLongitude = new ArrayList<String>(sortedLongitudes).get(0);
415
416
417 Map<Integer, List<String>> datesByDecade = this.getDatesByDecade(this.getDates());
418
419
420 Set<Integer> decadeKeySet = datesByDecade.keySet();
421 List<Integer> decadeKeyList = new ArrayList<Integer>(decadeKeySet);
422 Collections.sort(decadeKeyList);
423 Map<Integer, String[]> allFilenames = new HashMap<Integer, String[]>();
424
425
426 int start = 0;
427 int end = start;
428 int filenameStart = 0;
429 for (Integer decadeKey : decadeKeyList)
430 {
431 LOG.warn("Started work on decade: " + decadeKey + "0s.");
432 System.gc();
433 LOG.warn(" Mem: " + getMemDisplay());
434 List<String> dates = datesByDecade.get(decadeKey);
435 end = end + dates.size();
436
437 int blockTimeSize = dates.size();
438 String blockTimeRange = "0-" + String.valueOf(dates.size() - 1);
439
440
441
442
443
444
445 String decadeStr = String.valueOf(decadeKey + "0");
446
447 String filenames[] = new String[numVariables];
448 dates = this.getDatesAsDaysSinceEpoc(dates);
449 for (int i = 0; i < filenames.length; i++)
450 {
451 filenames[i] = this.generateDecadeFilename(decadeStr, variableNames[i]);
452 if ("".equals(startingLatitude))
453 {
454 this.createVariableFile(filenames[i], Arrays.asList(TIME, LAT, LONG), i, blockTimeSize,
455 LAT_SIZE, LONG_SIZE);
456 this.fillCoordinateVariables(filenames[i], sortedLongitudes, sortedLatitudes, dates, blockLatRange,
457 blockLongRange, blockTimeRange);
458 this.fillSpatialVariables(filenames[i], triples);
459 }
460 }
461
462 allFilenames.put(decadeKey, filenames);
463 filenameStart += numVariables;
464 }
465
466 LOG.warn(" Latitude limits : " + startingLatitude + " to " + endingLatitude + ".");
467
468 int numBlocks = LAT_SIZE / ROW_CHUNK_SIZE + (LAT_SIZE % ROW_CHUNK_SIZE > 0 ? 1 : 0);
469 Calendar baseCal = convUtils.getCalendar(new Date());
470 baseCal.clear();
471 baseCal.set(1889, 0, 1);
472
473 Set<String> limitedLatitudes = convUtils.getLimitedLatitudes(sortedLatitudes, startingLatitude, endingLatitude);
474 for (int blockNum = 0; blockNum < numBlocks; blockNum++)
475 {
476 LOG.warn(" Processing block : " + blockNum + ".");
477 LOG.warn(" Mem: " + getMemDisplay());
478
479 List<String> lats = convUtils.getLatitudeBlock(limitedLatitudes, blockNum, ROW_CHUNK_SIZE);
480 if (lats.isEmpty())
481 {
482 LOG.warn(" Skipping block as it is outside the latitude range.");
483 }
484 else
485 {
486 LOG.warn(" Processing latitudes : " + lats + ".");
487
488
489 List<CellData> targetCells = convUtils.buildCsvFilenamesForLatitudes(lats, triples, INPUT_CSV_DIRECTORY, ".csv");
490
491
492 LOG.warn(" Reading data from CSVs...");
493 Map<Integer, Map<String, LongitudeRange>> latData = convUtils.readDataByLatitudes(targetCells, lats,
494 numVariables, TIME_SIZE, LONG_SIZE, variableFillValues, startingLongitude);
495
496
497 LOG.warn(" Writing data to netCDF...");
498 LOG.warn(" Mem: " + getMemDisplay());
499 convUtils.writeLatDataByDecade(latData, allFilenames, variableNames, baseCal.getTime());
500
501 LOG.warn(" Writing finished.");
502 }
503 }
504 }
505
506 public String getMemDisplay()
507 {
508 Runtime rt = Runtime.getRuntime();
509 NumberFormat numFmt = NumberFormat.getNumberInstance();
510 numFmt.setMaximumFractionDigits(0);
511 StringBuffer sb = new StringBuffer();
512 sb.append(numFmt.format(rt.totalMemory() / 1024.0));
513 sb.append(" Kb total, ");
514 sb.append(numFmt.format(rt.freeMemory() / 1024.0));
515 sb.append(" Kb free, ");
516 sb.append(numFmt.format(rt.maxMemory() / 1024.0));
517 sb.append(" Kb max.");
518 return sb.toString();
519 }
520
521
522
523
524
525
526
527 private void createVariableFile(String outputFileName, List<String> dimensionOrdering, int variableIndex, int blockTimeSize, int blockLatSize, int blockLongSize)
528 throws Exception
529 {
530 this.defineDimensions(outputFileName, dimensionOrdering, blockTimeSize, blockLatSize, blockLongSize);
531 this.defineCoordinateVariables(outputFileName);
532 this.defineSpatialVariables(outputFileName);
533
534
535 DateFormat utcDateTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
536 Attribute converter = new Attribute("History", utcDateTime.format(new Date()) + " Converted to netCDF by "
537 + "$Id: MdbsyScenarioAConverter.java 84 2010-08-25 05:56:46Z robertbridle $");
538 List<Attribute> globalAttrs = new ArrayList<Attribute>();
539 globalAttrs.add(converter);
540 if (METADATA_FILE.length() > 0)
541 {
542 globalAttrs.addAll(NetCDFUtils.readAttributesFromStream(new FileInputStream(METADATA_FILE)));
543 }
544 NcDefineAttributes ncDefineAttr = new NcDefineAttributes();
545 ncDefineAttr.execute(outputFileName, globalAttrs, false
546
547 String dimensions = "";
548 for (String dimension : dimensionOrdering)
549 {
550 dimensions += dimension + " ";
551 }
552 this.defineVariable(outputFileName, dimensions, variableIndex);
553 }
554
555
556
557
558
559
560
561
562
563 private void defineDimensions(String outputFileName, List<String> dimensionOrdering, int blockTimeSize, int blockLatSize, int blockLongSize)
564 throws IllegalArgumentException, ParseException, IOException
565 {
566 NcDefineDimension command = new NcDefineDimension();
567
568 for (String dimName : dimensionOrdering)
569 {
570 if (dimName.equals(TIME))
571 {
572 command.execute(outputFileName, TIME, blockTimeSize, false
573 }
574 else if (dimName.equals(LAT))
575 {
576 command.execute(outputFileName, LAT, blockLatSize, false
577 }
578 else if (dimName.equals(LONG))
579 {
580 command.execute(outputFileName, LONG, blockLongSize, false
581 }
582 }
583
584
585 command.execute(outputFileName, NV, NV_SIZE, false
586 }
587
588
589
590
591
592
593
594
595
596 private void defineCoordinateVariables(String outputFileName) throws IllegalArgumentException, ParseException,
597 IOException
598 {
599 NcDefineVariable command = new NcDefineVariable();
600
601 command.execute(new File(outputFileName), LAT, DataType.FLOAT, Arrays.asList(new Attribute("units", LAT_UNITS),
602 new Attribute("long_name", LAT_LONG_NAME), new Attribute("standard_name", LAT_STANDARD_NAME), new Attribute("axis", LAT_AXIS)), LAT,
603 false
604 command.execute(new File(outputFileName), LONG, DataType.FLOAT, Arrays.asList(
605 new Attribute("long_name", LONG_LONG_NAME), new Attribute("units", LONG_UNITS), new Attribute("standard_name", LONG_STANDARD_NAME), new Attribute(
606 "axis", LONG_AXIS)), LONG, false
607 command.execute(new File(outputFileName), TIME, DataType.INT, Arrays.asList(new Attribute("units", TIME_UNITS),
608 new Attribute("long_name", TIME_LONG_NAME), new Attribute("standard_name", TIME_STANDARD_NAME),
609 new Attribute("axis", TIME_AXIS), new Attribute("calendar", TIME_CALENDAR), new Attribute("bounds",
610 TIME_BOUNDS)), TIME, false
611 command.execute(new File(outputFileName), TIME_BOUNDS, DataType.INT, new ArrayList<Attribute>(), TIME + " "
612 + NV, false
613 command.execute(new File(outputFileName), GRID_MAPPING, DataType.INT, Arrays.asList(new Attribute(
614 "grid_mapping_name", CRS_GRID_MAPPING_NAME), new Attribute("longitude_of_prime_meridian",
615 CRS_LONGITUDE_OF_PRIME_MERIDIAN), new Attribute("semi_major_axis", CRS_SEMI_MAJOR_AXIS),
616 new Attribute("inverse_flattening", CRS_INVERSE_FLATTENING)), "",
617 false
618 }
619
620
621
622
623
624
625
626
627
628 private void defineSpatialVariables(String outputFileName) throws IOException
629 {
630 NcDefineVariable command = new NcDefineVariable();
631
632 String spatialDim = LAT + " " + LONG;
633 command.execute(new File(outputFileName), ELEVATION, DataType.FLOAT, Arrays.asList(new Attribute("units",
634 ELEVATION_UNITS), new Attribute("standard_name", ELEVATION_STANDARD_NAME), new Attribute("long_name",
635 ELEVATION_LONG_NAME), new Attribute("_FillValue", ELEVATION_MISSING_VALUE), new Attribute(
636 "grid_mapping", GRID_MAPPING)), spatialDim, false
637 command
638 .execute(new File(outputFileName), CATCHMENT_ID, DataType.FLOAT, Arrays.asList(new Attribute(
639 "long_name", CATCHMENT_ID_LONG_NAME), new Attribute("_FillValue", CATCHMENT_ID_MISSING_VALUE), new Attribute(
640 "grid_mapping", GRID_MAPPING)), spatialDim, false
641 command
642 .execute(new File(outputFileName), REPORTING_REGION, DataType.FLOAT, Arrays.asList(new Attribute(
643 "long_name", REPORTING_REGION_LONG_NAME), new Attribute("_FillValue", REPORTING_REGION_MISSING_VALUE), new Attribute(
644 "grid_mapping", GRID_MAPPING)), spatialDim, false
645 false
646 }
647
648
649
650
651
652
653
654
655
656 private void defineVariable(String outputFileName, String dimensions, int variableIndex)
657 throws IllegalArgumentException, IOException, ParseException
658 {
659 NcDefineVariable command = new NcDefineVariable();
660 command
661 .execute(new File(outputFileName), variableNames[variableIndex], variableDataTypes[variableIndex],
662 Arrays.asList(new Attribute("units", variableUnits[variableIndex]), new Attribute("long_name",
663 variableLongNames[variableIndex]), new Attribute("missing_value", Float
664 .valueOf(variableMissingValues[variableIndex])), new Attribute("_FillValue", Float
665 .valueOf(variableFillValues[variableIndex])), new Attribute("valid_min", Float
666 .valueOf(variableMinValues[variableIndex])), new Attribute("valid_max", Float
667 .valueOf(variableMaxValues[variableIndex])), new Attribute("grid_mapping",
668 GRID_MAPPING)), dimensions,
669 false
670 }
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689 private void fillCoordinateVariables(String outputFileName, Set<String> sortedLongitudes,
690 Set<String> sortedLatitudes, List<String> dates, String blockLatRange, String blockLongRange, String timeRange) throws UnsupportedEncodingException, IOException,
691 java.text.ParseException
692 {
693 NcWriteVariable command = new NcWriteVariable();
694
695
696
697
698 StringBuffer longitudeStringBuffer = new StringBuffer();
699 for (Iterator<String> iter = sortedLongitudes.iterator(); iter.hasNext();)
700 {
701 longitudeStringBuffer.append(iter.next()).append(System.getProperty("line.separator"));
702 }
703
704
705 command.execute(new File(outputFileName), LONG, blockLongRange, new ByteArrayInputStream(longitudeStringBuffer
706 .toString().getBytes(ENCODING)), false);
707
708
709
710
711 StringBuffer latitudeStringBuffer = new StringBuffer();
712 for (Iterator<String> iter = sortedLatitudes.iterator(); iter.hasNext();)
713 {
714 latitudeStringBuffer.append(iter.next()).append(System.getProperty("line.separator"));
715 }
716
717
718 command.execute(new File(outputFileName), LAT, blockLatRange, new ByteArrayInputStream(latitudeStringBuffer
719 .toString().getBytes(ENCODING)), false);
720
721
722
723 StringBuffer timeBoundsStringBuffer = new StringBuffer();
724 StringBuffer dateStringBuffer = new StringBuffer();
725 boolean first = true;
726 for (String date : dates)
727 {
728 dateStringBuffer.append(date).append(System.getProperty("line.separator"));
729 if (!first)
730 {
731 timeBoundsStringBuffer.append(date).append(System.getProperty("line.separator"));
732 }
733 timeBoundsStringBuffer.append(date).append(System.getProperty("line.separator"));
734 first = false;
735 }
736 int dayAfterLastDay = Integer.parseInt(dates.get(dates.size()-1)) +1;
737 String finalString = String.valueOf(dayAfterLastDay);
738 timeBoundsStringBuffer.append(finalString).append(System.getProperty("line.separator"));
739
740
741 command.execute(new File(outputFileName), TIME, timeRange, new ByteArrayInputStream(dateStringBuffer
742 .toString().getBytes(ENCODING)), false);
743
744
745 command.execute(new File(outputFileName), TIME_BOUNDS, timeRange+","+NV_RANGE, new ByteArrayInputStream(
746 timeBoundsStringBuffer.toString().getBytes(ENCODING)), false);
747
748 }
749
750
751
752
753
754
755
756
757
758
759
760 private void fillSpatialVariables(String outputFileName, List<CellData> sortedCellData) throws IOException
761 {
762 NcWriteVariable command = new NcWriteVariable();
763
764 Set<String> latitudes = getSortedLatitudes(sortedCellData);
765 Set<String> longitudes = getSortedLongitudes(sortedCellData);
766 String spatialRange = "0-" + (latitudes.size() - 1) + ",0-" + (longitudes.size() - 1);
767
768
769
770
771 StringBuffer dataBuffer = fillSpatialDataBuffer(sortedCellData, latitudes, longitudes,
772 CellDataVariable.ELEVATION, "-999.99");
773
774
775 command.execute(new File(outputFileName), ELEVATION, spatialRange, new ByteArrayInputStream(dataBuffer
776 .toString().getBytes(ENCODING)), false);
777
778
779
780
781 dataBuffer = fillSpatialDataBuffer(sortedCellData, latitudes, longitudes, CellDataVariable.CATCHMENTID, "0");
782
783
784 command.execute(new File(outputFileName), CATCHMENT_ID, spatialRange, new ByteArrayInputStream(dataBuffer
785 .toString().getBytes(ENCODING)), false);
786
787
788
789
790 dataBuffer = fillSpatialDataBuffer(sortedCellData, latitudes, longitudes, CellDataVariable.REPORTINGREGIONID,
791 "0");
792
793
794 command.execute(new File(outputFileName), REPORTING_REGION, spatialRange, new ByteArrayInputStream(dataBuffer
795 .toString().getBytes(ENCODING)), false);
796 }
797
798
799
800
801
802
803
804
805
806 private StringBuffer fillSpatialDataBuffer(List<CellData> sortedCellData, Set<String> latitudes,
807 Set<String> longitudes, CellDataVariable varNum, String fillValue)
808 {
809 StringBuffer dataBuffer = new StringBuffer();
810 int cellNum = 0;
811 for (String latVal : latitudes)
812 {
813 for (String longVal : longitudes)
814 {
815 if (cellNum < sortedCellData.size())
816 {
817 CellData cellData = sortedCellData.get(cellNum);
818 if (cellData.latitude.equals(latVal) && cellData.longitude.equals(longVal))
819 {
820 switch (varNum)
821 {
822 case ELEVATION:
823 dataBuffer.append(cellData.elevation);
824 break;
825 case CATCHMENTID:
826 dataBuffer.append(cellData.catchmentId);
827 break;
828 case REPORTINGREGIONID:
829 dataBuffer.append(cellData.reportingRegionId);
830 break;
831 }
832 cellNum++;
833 }
834 else
835 {
836 dataBuffer.append(fillValue);
837 }
838 }
839 else
840 {
841 dataBuffer.append(fillValue);
842 }
843
844 dataBuffer.append(System.getProperty("line.separator"));
845 }
846
847 }
848 return dataBuffer;
849 }
850
851
852
853
854
855
856
857
858
859
860
861
862 private void fillDataForLatitude(String filename, int varIndex, Set<String> sortedLongitudes, Set<String> sortedLatitudes,
863 List<String> dates, List<String> dimensionOrdering) throws IOException
864 {
865 StringBuffer fillBlock = new StringBuffer();
866 String fillLine = variableFillValues[varIndex] + "\n";
867 for (int i = 0; i < dates.size(); i++)
868 {
869 fillBlock.append(fillLine);
870 }
871
872 NcWriteVariable writeVarCmd = new NcWriteVariable();
873 for (String latitude : sortedLatitudes)
874 {
875 for (String longitude : sortedLongitudes)
876 {
877 String fillRange = this.constructFillRange(dimensionOrdering, latitude, longitude, dates.size());
878 writeVarCmd.execute(new File(filename), variableNames[varIndex], fillRange,
879 new ByteArrayInputStream(fillBlock.toString().getBytes(ENCODING)), false);
880 }
881 }
882 }
883
884
885
886
887
888
889
890
891
892 private void fillDataByLatitudeVariables(String[] variableFileNames, List<CellData> triples, List<String> dimensionOrdering)
893 throws IOException
894 {
895 NcWriteVariable command = new NcWriteVariable();
896
897 int counter = 0;
898 for (CellData triple : triples)
899 {
900 if (++counter % 1000 == 0)
901 {
902 LOG.warn(" Num files processed: " + counter);
903 }
904
905 String csvFileName = triple.getCellId();
906 String latitude = triple.getLatitude();
907 String longitude = triple.getLongitude();
908
909 String fillRange = this.constructFillRange(dimensionOrdering, latitude, longitude, TIME_SIZE);
910
911 try
912 {
913 StringBuffer[] valuesStringBuffer = new StringBuffer[numVariables];
914 for (int i = 0; i < valuesStringBuffer.length; i++)
915 {
916 valuesStringBuffer[i] = new StringBuffer();
917 }
918
919 Scanner s = null;
920 try
921 {
922 s = new Scanner(new BufferedReader(new FileReader(INPUT_CSV_DIRECTORY + csvFileName + ".csv")))
923 .useDelimiter(Pattern.compile("[\n,]"));
924
925
926 for (int i = 0; i < NUM_CSV_COLUMNS; i++)
927 {
928 s.next();
929 }
930
931 while (s.hasNext())
932 {
933 s.next();
934
935 for (int i = 0; i < numVariables; i++)
936 {
937 valuesStringBuffer[i].append(s.next());
938
939 if (i < numVariables - 1)
940 {
941 valuesStringBuffer[i].append(System.getProperty("line.separator"));
942 }
943 }
944 }
945 }
946 finally
947 {
948 if (s != null)
949 {
950 s.close();
951 }
952 }
953
954 try
955 {
956 for (int i = 0; i < numVariables; i++)
957 {
958 command.execute(new File(variableFileNames[i]), variableNames[i], fillRange,
959 new ByteArrayInputStream(valuesStringBuffer[i].toString().getBytes(ENCODING)), false);
960 }
961 }
962 catch (IllegalArgumentException iae)
963 {
964 LOG.error(iae);
965 LOG.error("fillRange (date, lat, long): " + fillRange);
966 }
967
968 }
969 catch (FileNotFoundException fnfe)
970 {
971 LOG.info(fnfe.getMessage());
972
973 }
974 }
975 }
976
977
978
979
980
981
982
983
984 private String constructFillRange(List<String> dimensionOrdering, String latitude, String longitude, int dateSize)
985 {
986 String time_range = "0-" + String.valueOf(dateSize - 1);
987 String latitude_range = "lookup(" + latitude + ")-lookup(" + latitude + ")";
988 String longitude_range = "lookup(" + longitude + ")-lookup(" + longitude + ")";
989
990 String fillRange = new String();
991 for (Iterator<String> iter = dimensionOrdering.iterator(); iter.hasNext();)
992 {
993 String dimension = iter.next();
994 if (dimension.equals(TIME))
995 {
996 fillRange += time_range;
997 }
998 else if (dimension.equals(LAT))
999 {
1000 fillRange += latitude_range;
1001 }
1002 else if (dimension.equals(LONG))
1003 {
1004 fillRange += longitude_range;
1005 }
1006 if (iter.hasNext())
1007 fillRange += ",";
1008 }
1009 return fillRange;
1010 }
1011
1012
1013
1014
1015
1016
1017
1018 private Map<String, List<CellData>> getTriplesGroupedByLatitude(String[][] locationLookupValues)
1019 {
1020 Map<String, List<CellData>> triplesGroupedByLatitude = new HashMap<String, List<CellData>>();
1021 for (int i = 0; i < locationLookupValues.length; i++)
1022 {
1023 String cellId = locationLookupValues[i][CELL_ID_COLUMN_INDEX];
1024 String longitude = locationLookupValues[i][LONG_COLUMN_INDEX];
1025 String latitude = locationLookupValues[i][LAT_COLUMN_INDEX];
1026 String elevation = locationLookupValues[i][ELEVATION_COLUMN_INDEX];
1027 String catchmentId = locationLookupValues[i][CATCHMENT_ID_COLUMN_INDEX];
1028 String reportingRegionId = locationLookupValues[i][REPORTING_REGION_ID_COLUMN_INDEX];
1029
1030 String latitudeDegree = String.valueOf(Float.valueOf(latitude).intValue());
1031
1032 List<CellData> triplesInLatitude = triplesGroupedByLatitude.get(latitudeDegree);
1033 if (triplesInLatitude == null)
1034 {
1035 triplesInLatitude = new ArrayList<CellData>();
1036 triplesGroupedByLatitude.put(latitudeDegree, triplesInLatitude);
1037 }
1038 final CellData cellData = new CellData(cellId, longitude, latitude);
1039 cellData.elevation = elevation;
1040 cellData.catchmentId = catchmentId;
1041 cellData.reportingRegionId = reportingRegionId;
1042 triplesInLatitude.add(cellData);
1043 }
1044
1045
1046 int count = 0;
1047 for (Iterator<String> iter = triplesGroupedByLatitude.keySet().iterator(); iter.hasNext();)
1048 {
1049 count += triplesGroupedByLatitude.get(iter.next()).size();
1050 }
1051 if (count != locationLookupValues.length)
1052 {
1053 LOG.error("latitudes where not grouped by latitude correctly");
1054 System.exit(-1);
1055 }
1056
1057 return triplesGroupedByLatitude;
1058 }
1059
1060
1061
1062
1063
1064
1065
1066 public Map<Integer, List<String>> getDatesByDecade(List<String> dates)
1067 {
1068 Map<Integer, List<String>> datesGroupedByDecade = new HashMap<Integer, List<String>>();
1069
1070 for (String date : dates)
1071 {
1072 int year = Integer.valueOf(date.substring(0, 4));
1073 int decade = year / 10;
1074
1075 List<String> datesInDecade = datesGroupedByDecade.get(decade);
1076 if (datesInDecade == null)
1077 {
1078 datesInDecade = new ArrayList<String>();
1079 datesGroupedByDecade.put(decade, datesInDecade);
1080 }
1081 datesInDecade.add(date);
1082 }
1083
1084
1085 int count = 0;
1086 for (Iterator<Integer> iter = datesGroupedByDecade.keySet().iterator(); iter.hasNext();)
1087 {
1088 count += datesGroupedByDecade.get(iter.next()).size();
1089 }
1090 if (count != dates.size())
1091 {
1092 LOG.error("dates where not grouped decade correctly");
1093 System.exit(-1);
1094 }
1095
1096 return datesGroupedByDecade;
1097 }
1098
1099
1100
1101
1102
1103
1104
1105 private List<CellData> getAllTriples(String[][] locationLookupValues)
1106 {
1107 List<CellData> allTriples = new ArrayList<CellData>();
1108 for (int i = 0; i < locationLookupValues.length; i++)
1109 {
1110 String cellId = locationLookupValues[i][CELL_ID_COLUMN_INDEX];
1111 String longitude = locationLookupValues[i][LONG_COLUMN_INDEX];
1112 String latitude = locationLookupValues[i][LAT_COLUMN_INDEX];
1113 String elevation = locationLookupValues[i][ELEVATION_COLUMN_INDEX];
1114 String catchmentId = locationLookupValues[i][CATCHMENT_ID_COLUMN_INDEX];
1115 String reportingRegionId = locationLookupValues[i][REPORTING_REGION_ID_COLUMN_INDEX];
1116
1117 CellData cellData = new CellData(cellId, longitude, latitude);
1118 cellData.elevation = elevation;
1119 cellData.catchmentId = catchmentId;
1120 cellData.reportingRegionId = reportingRegionId;
1121 allTriples.add(cellData);
1122 }
1123
1124
1125 if (allTriples.size() != locationLookupValues.length)
1126 {
1127 LOG.error("lookup values where not read correctly");
1128 System.exit(-1);
1129 }
1130
1131 return allTriples;
1132 }
1133
1134
1135
1136
1137
1138
1139 private String[][] readLookupFile() throws IOException
1140 {
1141 RandomAccessFile raf = new RandomAccessFile(new File(LOCATION_LOOKUP_FILE), "r");
1142 raf.readLine();
1143
1144 List<String[]> lineList = new ArrayList<String[]>();
1145 String line = raf.readLine();
1146 while (line != null)
1147 {
1148 String[] cols = new CSVTokenizer(line).getAllColumns();
1149 lineList.add(cols);
1150 line = raf.readLine();
1151 }
1152
1153 raf.close();
1154
1155 String[][] lineCols = lineList.toArray(new String[][]{});
1156 return lineCols;
1157 }
1158
1159
1160
1161
1162
1163
1164
1165 private String[][] transpose(String[][] gridValues)
1166 {
1167 String[][] transposed = new String[gridValues[0].length][gridValues.length];
1168 for (int rows = 0; rows < gridValues.length; rows++)
1169 {
1170 for (int cols = 0; cols < gridValues[0].length; cols++)
1171 {
1172 transposed[cols][rows] = gridValues[rows][cols];
1173 }
1174 }
1175 return transposed;
1176 }
1177
1178
1179
1180
1181
1182
1183
1184 private Set<String> getSortedLatitudes(List<CellData> triples)
1185 {
1186 Set<String> latitudes = new TreeSet<String>();
1187 for (CellData triple : triples)
1188 {
1189 latitudes.add(triple.getLatitude());
1190
1191
1192
1193
1194 }
1195 return latitudes;
1196 }
1197
1198
1199
1200
1201
1202
1203
1204 private Set<String> getSortedLongitudes(List<CellData> triples)
1205 {
1206 Set<String> longitudes = new TreeSet<String>();
1207 for (CellData triple : triples)
1208 {
1209 longitudes.add(triple.getLongitude());
1210
1211
1212
1213
1214 }
1215 return longitudes;
1216 }
1217
1218
1219
1220
1221
1222 private List<String> getDates() throws IOException
1223 {
1224 List<String> dates = new ArrayList<String>();
1225
1226 RandomAccessFile raf = null;
1227 try
1228 {
1229
1230 File dir = new File(INPUT_CSV_DIRECTORY);
1231 String[] children = dir.list();
1232 if (children != null && children.length > 0)
1233 {
1234 raf = new RandomAccessFile(new File(INPUT_CSV_DIRECTORY + children[0]), "r");
1235 raf.readLine();
1236
1237 List<String[]> lineList = new ArrayList<String[]>();
1238 String line = raf.readLine();
1239 while (line != null)
1240 {
1241 String[] cols = new CSVTokenizer(line).getAllColumns();
1242 lineList.add(cols);
1243 line = raf.readLine();
1244 }
1245 String[][] lineCols = lineList.toArray(new String[][]{});
1246 dates = Arrays.asList(transpose(lineCols)[DATE_COLUMN_INDEX]);
1247 }
1248 }
1249 finally
1250 {
1251 if (raf != null)
1252 raf.close();
1253 }
1254
1255 return dates;
1256 }
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266 private List<String> getDatesAsDaysSinceEpoc(List<String> dates) throws IOException, java.text.ParseException
1267 {
1268 return daysSinceSinceEpocAsString(dates, isoDateTimeFormat.dateOnlyFormat(EPOC_DATE_STRING));
1269 }
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280 private List<String> daysSinceSinceEpocAsString(List<String> dates, Date epoc) throws java.text.ParseException
1281 {
1282 List<String> convertedDates = new ArrayList<String>();
1283 for (String date : dates)
1284 {
1285 Date currentDate = isoDateTimeFormat.dateOnlyFormat(date);
1286 convertedDates.add(String.valueOf(daysBetween(epoc, currentDate)));
1287 }
1288 return convertedDates;
1289 }
1290
1291
1292
1293
1294
1295
1296
1297
1298 public int daysBetween(Date d1, Date d2)
1299 {
1300 return (int) ((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24));
1301 }
1302
1303 private String generateLatitudeFilename(String latitudeDegree, String variable)
1304 {
1305 String filename = OUTPUT_NETCDF_DIRECTORY + COLLECTION + "." + SCENARIO + ".";
1306 if (MODEL.length() > 0)
1307 {
1308 filename += MODEL + ".";
1309 }
1310 if (CASE.length() > 0)
1311 {
1312 filename += CASE + ".";
1313 }
1314 filename += "Latitude-";
1315 if (latitudeDegree.startsWith("-"))
1316 {
1317 filename += latitudeDegree.substring(1) + "S.";
1318 }
1319 else
1320 {
1321 filename += latitudeDegree + "N.";
1322 }
1323 filename += "Var-" + variable + NETCDF_FILE_EXTENSION;
1324 return filename;
1325 }
1326
1327 private String generateDecadeFilename(String decadeStr, String variable)
1328 {
1329 String filename = OUTPUT_NETCDF_DIRECTORY + COLLECTION + "." + SCENARIO + ".";
1330 if (MODEL.length() > 0)
1331 {
1332 filename += MODEL + ".";
1333 }
1334 if (CASE.length() > 0)
1335 {
1336 filename += CASE + ".";
1337 }
1338 filename += "Decade-" + decadeStr + ".Var-" + variable + NETCDF_FILE_EXTENSION;
1339 return filename;
1340 }
1341
1342
1343
1344
1345
1346
1347 @SuppressWarnings("unused")
1348 private void printLookupInMemory(Map<Integer, Map<String, LongitudeRange>> lookup)
1349 {
1350 for (Iterator<Integer> dateIter = lookup.keySet().iterator(); dateIter.hasNext();)
1351 {
1352 Integer dateKey = dateIter.next();
1353 System.out.println(dateKey);
1354
1355 for (Iterator<String> latIter = lookup.get(dateKey).keySet().iterator(); latIter.hasNext();)
1356 {
1357 String latitude = latIter.next();
1358 System.out.println("\t" + latitude);
1359
1360 LongitudeRange longitudeRange = lookup.get(dateKey).get(latitude);
1361 System.out.println("\t\t" + longitudeRange.getStartLongitudeRange() + "-"
1362 + longitudeRange.getEndLongitudeRange());
1363 }
1364 }
1365 }
1366
1367 public enum CellDataVariable
1368 {
1369 CELLID, LONGITUDE, LATITUDE, ELEVATION, CATCHMENTID, REPORTINGREGIONID
1370 }
1371
1372 }