1 // -*- c++ -*-
2 /*=========================================================================
3 
4   Program:   Visualization Toolkit
5   Module:    vtkNetCDFCFReader.cxx
6 
7   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
8   All rights reserved.
9   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
10 
11      This software is distributed WITHOUT ANY WARRANTY; without even
12      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13      PURPOSE.  See the above copyright notice for more information.
14 
15 =========================================================================*/
16 
17 /*-------------------------------------------------------------------------
18   Copyright 2008 Sandia Corporation.
19   Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
20   the U.S. Government retains certain rights in this software.
21 -------------------------------------------------------------------------*/
22 
23 #include "vtkNetCDFCFReader.h"
24 
25 #include "vtkCellArray.h"
26 #include "vtkDataArraySelection.h"
27 #include "vtkDoubleArray.h"
28 #include "vtkExtentTranslator.h"
29 #include "vtkImageData.h"
30 #include "vtkInformation.h"
31 #include "vtkInformationVector.h"
32 #include "vtkIntArray.h"
33 #include "vtkMath.h"
34 #include "vtkMergePoints.h"
35 #include "vtkNew.h"
36 #include "vtkObjectFactory.h"
37 #include "vtkPoints.h"
38 #include "vtkRectilinearGrid.h"
39 #include "vtkStdString.h"
40 #include "vtkStreamingDemandDrivenPipeline.h"
41 #include "vtkStringArray.h"
42 #include "vtkStructuredGrid.h"
43 #include "vtkUnstructuredGrid.h"
44 
45 #include "vtkSmartPointer.h"
46 #define VTK_CREATE(type, name) vtkSmartPointer<type> name = vtkSmartPointer<type>::New()
47 
48 #include <set>
49 #include <vtksys/RegularExpression.hxx>
50 #include <vtksys/SystemTools.hxx>
51 
52 #include <cstring>
53 
54 #include "vtk_netcdf.h"
55 
56 #define CALL_NETCDF_GENERIC(call, on_error)                                                        \
57   {                                                                                                \
58     int errorcode = call;                                                                          \
59     if (errorcode != NC_NOERR)                                                                     \
60     {                                                                                              \
61       const char* errorstring = nc_strerror(errorcode);                                            \
62       on_error;                                                                                    \
63     }                                                                                              \
64   }
65 
66 #define CALL_NETCDF(call)                                                                          \
67   CALL_NETCDF_GENERIC(call, vtkErrorMacro(<< "netCDF Error: " << errorstring); return 0;)
68 
69 #define CALL_NETCDF_GW(call)                                                                       \
70   CALL_NETCDF_GENERIC(call, vtkGenericWarningMacro(<< "netCDF Error: " << errorstring); return 0;)
71 
72 #include <algorithm>
73 
74 #include <cmath>
75 
76 //=============================================================================
77 // Convenience function for getting the text attribute on a variable.  Returns
78 // true if the attribute exists, false otherwise.
ReadTextAttribute(int ncFD,int varId,const char * name,vtkStdString & result)79 static bool ReadTextAttribute(int ncFD, int varId, const char* name, vtkStdString& result)
80 {
81   size_t length;
82   if (nc_inq_attlen(ncFD, varId, name, &length) != NC_NOERR)
83   {
84     return false;
85   }
86 
87   result.resize(length);
88   if (length > 0)
89   {
90     if (nc_get_att_text(ncFD, varId, name, &result.at(0)) != NC_NOERR)
91     {
92       return false;
93     }
94   }
95   else
96   {
97     // If length == 0, then there really is nothing to read. Do nothing
98   }
99 
100   // The line below seems weird, but it is here for a good reason.  In general,
101   // text attributes are not null terminated, so you have to add your own (which
102   // the vtkStdString will do for us).  However, sometimes a null terminating
103   // character is written in the attribute anyway.  In a C string this is no big
104   // deal.  But it means that the vtkStdString has a null character in it and it
105   // is technically different than its own C string.  This line corrects that
106   // regardless of whether the null string was written we will get the right
107   // string.
108   result = result.c_str();
109 
110   return true;
111 }
112 
113 //------------------------------------------------------------------------------
114 // Convenience function for getting the range of all values in all components of
115 // a vtkDoubleArray.
GetRangeOfAllComponents(vtkDoubleArray * array,double range[2])116 static void GetRangeOfAllComponents(vtkDoubleArray* array, double range[2])
117 {
118   range[0] = VTK_DOUBLE_MAX;
119   range[1] = VTK_DOUBLE_MIN;
120   for (int component = 0; component < array->GetNumberOfComponents(); component++)
121   {
122     double componentRange[2];
123     array->GetRange(componentRange, component);
124     if (componentRange[0] < range[0])
125       range[0] = componentRange[0];
126     if (componentRange[1] > range[1])
127       range[1] = componentRange[1];
128   }
129 }
130 
131 //=============================================================================
vtkDimensionInfo(int ncFD,int id)132 vtkNetCDFCFReader::vtkDimensionInfo::vtkDimensionInfo(int ncFD, int id)
133 {
134   this->DimId = id;
135 
136   this->Units = UNDEFINED_UNITS;
137   this->HasRegularSpacing = true;
138   this->Origin = 0.0;
139   this->Spacing = 1.0;
140 
141   this->LoadMetaData(ncFD);
142 }
143 
LoadMetaData(int ncFD)144 int vtkNetCDFCFReader::vtkDimensionInfo::LoadMetaData(int ncFD)
145 {
146   this->Units = UNDEFINED_UNITS;
147 
148   char name[NC_MAX_NAME + 1];
149   CALL_NETCDF_GW(nc_inq_dimname(ncFD, this->DimId, name));
150   this->Name = name;
151 
152   size_t dimLen;
153   CALL_NETCDF_GW(nc_inq_dimlen(ncFD, this->DimId, &dimLen));
154   this->Coordinates = vtkSmartPointer<vtkDoubleArray>::New();
155   this->Coordinates->SetName((this->Name + "_Coordinates").c_str());
156   this->Coordinates->SetNumberOfComponents(1);
157   this->Coordinates->SetNumberOfTuples(static_cast<vtkIdType>(dimLen));
158 
159   this->Bounds = vtkSmartPointer<vtkDoubleArray>::New();
160   this->Bounds->SetName((this->Name + "_Bounds").c_str());
161   this->Bounds->SetNumberOfComponents(1);
162   this->Bounds->SetNumberOfTuples(static_cast<vtkIdType>(dimLen + 1));
163 
164   this->SpecialVariables = vtkSmartPointer<vtkStringArray>::New();
165 
166   int varId;
167   int varNumDims;
168   int varDim;
169   // By convention if there is a single dimension variable with the same name as
170   // its dimension, then the data contains the coordinates for the dimension.
171   if ((nc_inq_varid(ncFD, name, &varId) == NC_NOERR) &&
172     (nc_inq_varndims(ncFD, varId, &varNumDims) == NC_NOERR) && (varNumDims == 1) &&
173     (nc_inq_vardimid(ncFD, varId, &varDim) == NC_NOERR) && (varDim == this->DimId))
174   {
175     this->SpecialVariables->InsertNextValue(name);
176 
177     // Read coordinates
178     CALL_NETCDF_GW(nc_get_var_double(ncFD, varId, this->Coordinates->GetPointer(0)));
179 
180     // Check to see if the spacing is regular.
181     this->Origin = this->Coordinates->GetValue(0);
182     this->Spacing =
183       (this->Coordinates->GetValue(static_cast<vtkIdType>(dimLen - 1)) - this->Origin) /
184       (dimLen - 1);
185     this->HasRegularSpacing = true; // Then check to see if it is false.
186     double tolerance = 0.01 * this->Spacing;
187     for (size_t i = 1; i < dimLen; i++)
188     {
189       double expectedValue = this->Origin + i * this->Spacing;
190       double actualValue = this->Coordinates->GetValue(static_cast<vtkIdType>(i));
191       if ((actualValue < expectedValue - tolerance) || (actualValue > expectedValue + tolerance))
192       {
193         this->HasRegularSpacing = false;
194         break;
195       }
196     }
197 
198     // Check units.
199     vtkStdString units;
200     if (ReadTextAttribute(ncFD, varId, "units", units))
201     {
202       units = vtksys::SystemTools::LowerCase(units);
203       // Time, latitude, and longitude dimensions are those with units that
204       // correspond to strings formatted with the Unidata udunits package.  I'm
205       // not sure if these checks are complete, but they matches all of the
206       // examples I have seen.
207       if ((units.find(" since ") != vtkStdString::npos) ||
208         (units.find(" after ") != vtkStdString::npos) || (units == "second") ||
209         (units == "seconds") || (units == "day") || (units == "days") || (units == "hour") ||
210         (units == "hours") || (units == "minute") || (units == "minutes") || (units == "s") ||
211         (units == "sec") || (units == "secs") || (units == "shake") || (units == "shakes") ||
212         (units == "sidereal_day") || (units == "sidereal_days") || (units == "sidereal_hour") ||
213         (units == "sidereal_hours") || (units == "sidereal_minute") ||
214         (units == "sidereal_minutes") || (units == "sidereal_second") ||
215         (units == "sidereal_seconds") || (units == "sidereal_year") ||
216         (units == "sidereal_years") || (units == "tropical_year") || (units == "tropical_years") ||
217         (units == "lunar_month") || (units == "lunar_months") || (units == "common_year") ||
218         (units == "common_years") || (units == "leap_year") || (units == "leap_years") ||
219         (units == "Julian_year") || (units == "Julian_years") || (units == "Gregorian_year") ||
220         (units == "Gregorian_years") || (units == "sidereal_month") ||
221         (units == "sidereal_months") || (units == "tropical_month") ||
222         (units == "tropical_months") || (units == "d") || (units == "min") || (units == "mins") ||
223         (units == "hrs") || (units == "h") || (units == "fortnight") || (units == "fortnights") ||
224         (units == "week") || (units == "jiffy") || (units == "jiffies") || (units == "year") ||
225         (units == "years") || (units == "yr") || (units == "yrs") || (units == "a") ||
226         (units == "eon") || (units == "eons") || (units == "month") || (units == "months"))
227       {
228         this->Units = TIME_UNITS;
229       }
230       else if (vtksys::RegularExpression("degrees?_?n").find(units))
231       {
232         this->Units = LATITUDE_UNITS;
233       }
234       else if (vtksys::RegularExpression("degrees?_?e").find(units))
235       {
236         this->Units = LONGITUDE_UNITS;
237       }
238     }
239 
240     // Check axis.
241     vtkStdString axis;
242     if (ReadTextAttribute(ncFD, varId, "axis", axis))
243     {
244       // The axis attribute is an alternate way of defining the coordinate type.
245       // The string can be "X", "Y", "Z", or "T" which mean longitude, latitude,
246       // vertical, and time, respectively.
247       if (axis == "X")
248       {
249         this->Units = LONGITUDE_UNITS;
250       }
251       else if (axis == "Y")
252       {
253         this->Units = LATITUDE_UNITS;
254       }
255       else if (axis == "Z")
256       {
257         this->Units = VERTICAL_UNITS;
258       }
259       else if (axis == "T")
260       {
261         this->Units = TIME_UNITS;
262       }
263     }
264 
265     // Check positive.
266     vtkStdString positive;
267     if (ReadTextAttribute(ncFD, varId, "positive", positive))
268     {
269       positive = vtksys::SystemTools::LowerCase(positive);
270       if (positive.find("down") != vtkStdString::npos)
271       {
272         // Flip the values of the coordinates.
273         for (vtkIdType i = 0; i < this->Coordinates->GetNumberOfTuples(); i++)
274         {
275           this->Coordinates->SetValue(i, -(this->Coordinates->GetValue(i)));
276         }
277         this->Spacing = -this->Spacing;
278       }
279     }
280 
281     // Create the bounds array, which is used in place of the coordinates when
282     // loading as cell data.  We will look for the bounds attribute on the
283     // description variable to see if cell bounds have been written out.  This
284     // code assumes that if such an attribute exists, it is the name of another
285     // variable that is of dimensions of size dimLen X 2.  There are no checks
286     // for this (other than the existence of the attribute), so if this is not
287     // the case then the code could fail.
288     vtkStdString boundsName;
289     if (ReadTextAttribute(ncFD, varId, "bounds", boundsName))
290     {
291       this->SpecialVariables->InsertNextValue(boundsName);
292 
293       int boundsVarId;
294       CALL_NETCDF_GW(nc_inq_varid(ncFD, boundsName.c_str(), &boundsVarId));
295 
296       // Read in the first bound value for each entry as a point bound.  If the
297       // cells are connected, the second bound value should equal the first
298       // bound value of the next entry anyway.
299       size_t start[2];
300       start[0] = start[1] = 0;
301       size_t count[2];
302       count[0] = dimLen;
303       count[1] = 1;
304       CALL_NETCDF_GW(
305         nc_get_vars_double(ncFD, boundsVarId, start, count, nullptr, this->Bounds->GetPointer(0)));
306 
307       // Read in the last value for the bounds array.  It will be the second
308       // bound in the last entry.  This will not be replicated unless the
309       // dimension is a longitudinal one that wraps all the way around.
310       start[0] = dimLen - 1;
311       start[1] = 1;
312       count[0] = 1;
313       count[1] = 1;
314       CALL_NETCDF_GW(nc_get_vars_double(ncFD, boundsVarId, start, count, nullptr,
315         this->Bounds->GetPointer(static_cast<vtkIdType>(dimLen))));
316     }
317     else
318     {
319       // Bounds not given.  Set them based on the coordinates.
320       this->Bounds->SetValue(0, this->Coordinates->GetValue(0) - 0.5 * this->Spacing);
321       for (vtkIdType i = 1; i < static_cast<vtkIdType>(dimLen); i++)
322       {
323         double v0 = this->Coordinates->GetValue(i - 1);
324         double v1 = this->Coordinates->GetValue(i);
325         this->Bounds->SetValue(i, 0.5 * (v0 + v1));
326       }
327       this->Bounds->SetValue(static_cast<vtkIdType>(dimLen),
328         this->Coordinates->GetValue(static_cast<vtkIdType>(dimLen - 1)) + 0.5 * this->Spacing);
329     }
330   }
331   else
332   {
333     // Fake coordinates
334     for (size_t i = 0; i < dimLen; i++)
335     {
336       this->Coordinates->SetValue(static_cast<vtkIdType>(i), static_cast<double>(i));
337       this->Bounds->SetValue(static_cast<vtkIdType>(i), static_cast<double>(i) - 0.5);
338     }
339     this->Bounds->SetValue(static_cast<vtkIdType>(dimLen), static_cast<double>(dimLen) - 0.5);
340     this->HasRegularSpacing = true;
341     this->Origin = 0.0;
342     this->Spacing = 1.0;
343   }
344 
345   return 1;
346 }
347 
348 //------------------------------------------------------------------------------
349 class vtkNetCDFCFReader::vtkDimensionInfoVector
350 {
351 public:
352   std::vector<vtkDimensionInfo> v;
353 };
354 
355 //=============================================================================
vtkDependentDimensionInfo(int ncFD,int varId,vtkNetCDFCFReader * parent)356 vtkNetCDFCFReader::vtkDependentDimensionInfo::vtkDependentDimensionInfo(
357   int ncFD, int varId, vtkNetCDFCFReader* parent)
358 {
359   if (this->LoadMetaData(ncFD, varId, parent))
360   {
361     this->Valid = true;
362   }
363   else
364   {
365     this->Valid = false;
366   }
367 }
368 
369 //------------------------------------------------------------------------------
LoadMetaData(int ncFD,int varId,vtkNetCDFCFReader * parent)370 int vtkNetCDFCFReader::vtkDependentDimensionInfo::LoadMetaData(
371   int ncFD, int varId, vtkNetCDFCFReader* parent)
372 {
373   int longitudeCoordVarId, latitudeCoordVarId;
374   int longitudeBoundsVarId, latitudeBoundsVarId;
375   longitudeCoordVarId = latitudeCoordVarId = -1;
376   longitudeBoundsVarId = latitudeBoundsVarId = -1;
377   this->GridDimensions = vtkSmartPointer<vtkIntArray>::New();
378   this->SpecialVariables = vtkSmartPointer<vtkStringArray>::New();
379 
380   // The grid dimensions are the dimensions on the variable.  Since multiple
381   // variables can be put on the same grid and this class identifies grids by
382   // their variables, I group all of the dimension combinations together for 2D
383   // coordinate lookup.  Technically, the CF specification allows you to specify
384   // different coordinate variables, but we do not support that because there is
385   // no easy way to differentiate grids with the same dimensions.  If different
386   // coordinates are needed, then duplicate dimensions should be created.
387   // Anyone who disagrees should write their own reader class.
388   int numGridDimensions;
389   CALL_NETCDF_GW(nc_inq_varndims(ncFD, varId, &numGridDimensions));
390 
391   if (numGridDimensions == 0)
392   {
393     // If a variable has no dimensions, there is no reason to have dependent
394     // dimension variables. Just exit here for safety.
395     return 0;
396   }
397 
398   this->GridDimensions->SetNumberOfTuples(numGridDimensions);
399   CALL_NETCDF_GW(nc_inq_vardimid(ncFD, varId, this->GridDimensions->GetPointer(0)));
400 
401   // Remove initial time dimension, which has no effect on data type.
402   if (parent->IsTimeDimension(ncFD, this->GridDimensions->GetValue(0)))
403   {
404     this->GridDimensions->RemoveTuple(0);
405     numGridDimensions--;
406     if (numGridDimensions == 0)
407     {
408       // If a variable has no dimensions (ignoring time), there is no reason
409       // to have dependent dimension variables. Just exit here for safety.
410       return 0;
411     }
412   }
413 
414   // Most coordinate variables are defined by a variable the same name as the
415   // dimension they describe.  Those are handled elsewhere.  This class handles
416   // dependent variables that define coordinates that are not the same name as
417   // any dimension.  This is only done when the coordinates cannot be expressed
418   // as a 1D table lookup from dimension index.  This occurs in only two places
419   // in the CF convention.  First, it happens for 2D coordinate variables with
420   // 4-sided cells.  This is basically when the grid is a 2D curvilinear grid.
421   // Each i,j topological point can be placed anywhere in space.  Second, it
422   // happens for multi-dimensional coordinate variables with p-sided cells.
423   // These are unstructured collections of polygons.
424 
425   vtkStdString coordinates;
426   if (!ReadTextAttribute(ncFD, varId, "coordinates", coordinates))
427     return 0;
428 
429   std::vector<std::string> coordName;
430   vtksys::SystemTools::Split(coordinates, coordName, ' ');
431 
432   int numAuxCoordDims = -1;
433 
434   for (std::vector<std::string>::iterator iter = coordName.begin(); iter != coordName.end(); ++iter)
435   {
436     int auxCoordVarId;
437     if (nc_inq_varid(ncFD, iter->c_str(), &auxCoordVarId) != NC_NOERR)
438       continue;
439 
440     // Make sure that the coordinate variables have the same dimensions and that
441     // those dimensions are the same as the last two dimensions on the grid.
442     // Not sure if that is enforced by the specification, but I am going to make
443     // that assumption.
444     int numDims;
445     CALL_NETCDF_GW(nc_inq_varndims(ncFD, auxCoordVarId, &numDims));
446     // I am only supporting either 1 or 2 dimensions in the coordinate
447     // variables.  See the comment below regarding identifying the
448     // CellsUnstructured flag.
449     if (numDims > 2)
450       continue;
451 
452     int auxCoordDims[2];
453     CALL_NETCDF_GW(nc_inq_vardimid(ncFD, auxCoordVarId, auxCoordDims));
454     int* gridDims = this->GridDimensions->GetPointer(numGridDimensions - numDims);
455     bool auxCoordDimsValid = true;
456     for (int dimId = 0; dimId < numDims; dimId++)
457     {
458       if (auxCoordDims[dimId] != gridDims[dimId])
459       {
460         auxCoordDimsValid = false;
461         break;
462       }
463     }
464     if (!auxCoordDimsValid)
465       continue;
466 
467     // The variable is no use to me unless it is identified as either longitude
468     // or latitude.
469     vtkStdString units;
470     if (!ReadTextAttribute(ncFD, auxCoordVarId, "units", units))
471       continue;
472     units = vtksys::SystemTools::LowerCase(units);
473     if (vtksys::RegularExpression("degrees?_?n").find(units))
474     {
475       latitudeCoordVarId = auxCoordVarId;
476     }
477     else if (vtksys::RegularExpression("degrees?_?e").find(units))
478     {
479       longitudeCoordVarId = auxCoordVarId;
480     }
481     else
482     {
483       continue;
484     }
485     this->SpecialVariables->InsertNextValue(*iter);
486 
487     if (numAuxCoordDims < 0)
488     {
489       // Record the number of dimensions in the coordinate arrays.
490       numAuxCoordDims = numDims;
491     }
492     else if (numAuxCoordDims != numDims)
493     {
494       // Number of dimensions in different coordinate arrays do not match.
495       return 0;
496     }
497   }
498 
499   if ((longitudeCoordVarId == -1) || (latitudeCoordVarId == -1))
500   {
501     // Did not find all coordinate variables.
502     return 0;
503   }
504 
505   // Technically, p-sided cells can be accessed with any number of dimensions.
506   // However, it makes little sense to have more than a 1D array of cell ids.
507   // Supporting more than this is a pain because it exceeds the dimensionality
508   // supported by vtkDataArray.  It also makes it exceedingly difficult to
509   // determine whether the topology is implicitly defined by 2D 4-sided cells or
510   // explicitly with p-sided cells.  Thus, I am only implementing p-sided cells
511   // with 1D coordinate variables.
512   if (numAuxCoordDims == 1)
513   {
514     this->CellsUnstructured = true;
515   }
516   else if (numAuxCoordDims == 2)
517   {
518     this->CellsUnstructured = false;
519   }
520   else
521   {
522     // Not supporting this.
523     return 0;
524   }
525 
526   vtkStdString bounds;
527   if (ReadTextAttribute(ncFD, longitudeCoordVarId, "bounds", bounds))
528   {
529     // The bounds is supposed to point to an array with numAuxCoordDims+1
530     // dimensions.  The first numAuxCoordDims should be the same as the coord
531     // arrays.  The last dimension has the number of vertices in each cell.
532     // Maybe I should check this, but I'm not.
533     CALL_NETCDF_GW(nc_inq_varid(ncFD, bounds.c_str(), &longitudeBoundsVarId));
534     this->SpecialVariables->InsertNextValue(bounds);
535   }
536   if (ReadTextAttribute(ncFD, latitudeCoordVarId, "bounds", bounds))
537   {
538     // The bounds is supposed to point to an array with numAuxCoordDims+1
539     // dimensions.  The first numAuxCoordDims should be the same as the coord
540     // arrays.  The last dimension has the number of vertices in each cell.
541     // Maybe I should check this, but I'm not.
542     CALL_NETCDF_GW(nc_inq_varid(ncFD, bounds.c_str(), &latitudeBoundsVarId));
543     this->SpecialVariables->InsertNextValue(bounds);
544   }
545 
546   this->HasBounds = ((longitudeBoundsVarId != -1) && (latitudeBoundsVarId != -1));
547 
548   // Load in all the longitude and latitude coordinates.  Maybe not the most
549   // efficient thing to do for large data, but it is just a 2D surface, so it
550   // should be OK for most things.
551   this->LongitudeCoordinates = vtkSmartPointer<vtkDoubleArray>::New();
552   this->LatitudeCoordinates = vtkSmartPointer<vtkDoubleArray>::New();
553   if (this->CellsUnstructured)
554   {
555     if (this->HasBounds)
556     {
557       int ok;
558       ok = this->LoadUnstructuredBoundsVariable(
559         ncFD, longitudeBoundsVarId, this->LongitudeCoordinates);
560       if (!ok)
561         return 0;
562       ok =
563         this->LoadUnstructuredBoundsVariable(ncFD, latitudeBoundsVarId, this->LatitudeCoordinates);
564       if (!ok)
565         return 0;
566     }
567     else
568     {
569       // Unstructured cells need bounds to define the topology.
570       return 0;
571     }
572   }
573   else
574   {
575     if (this->HasBounds)
576     {
577       int ok;
578       ok = this->LoadBoundsVariable(ncFD, longitudeBoundsVarId, this->LongitudeCoordinates);
579       if (!ok)
580         return 0;
581       ok = this->LoadBoundsVariable(ncFD, latitudeBoundsVarId, this->LatitudeCoordinates);
582       if (!ok)
583         return 0;
584     }
585     else
586     {
587       int ok;
588       ok = this->LoadCoordinateVariable(ncFD, longitudeCoordVarId, this->LongitudeCoordinates);
589       if (!ok)
590         return 0;
591       ok = this->LoadCoordinateVariable(ncFD, latitudeCoordVarId, this->LatitudeCoordinates);
592       if (!ok)
593         return 0;
594     }
595   }
596 
597   return 1;
598 }
599 
600 //------------------------------------------------------------------------------
LoadCoordinateVariable(int ncFD,int varId,vtkDoubleArray * coords)601 int vtkNetCDFCFReader::vtkDependentDimensionInfo::LoadCoordinateVariable(
602   int ncFD, int varId, vtkDoubleArray* coords)
603 {
604   int dimIds[2];
605   CALL_NETCDF_GW(nc_inq_vardimid(ncFD, varId, dimIds));
606 
607   size_t dimSizes[2];
608   for (int i = 0; i < 2; i++)
609   {
610     CALL_NETCDF_GW(nc_inq_dimlen(ncFD, dimIds[i], &dimSizes[i]));
611   }
612 
613   coords->SetNumberOfComponents(static_cast<int>(dimSizes[1]));
614   coords->SetNumberOfTuples(static_cast<vtkIdType>(dimSizes[0]));
615   CALL_NETCDF_GW(nc_get_var_double(ncFD, varId, coords->GetPointer(0)));
616 
617   return 1;
618 }
619 
620 //------------------------------------------------------------------------------
LoadBoundsVariable(int ncFD,int varId,vtkDoubleArray * coords)621 int vtkNetCDFCFReader::vtkDependentDimensionInfo::LoadBoundsVariable(
622   int ncFD, int varId, vtkDoubleArray* coords)
623 {
624   int dimIds[3];
625   CALL_NETCDF_GW(nc_inq_vardimid(ncFD, varId, dimIds));
626 
627   size_t dimSizes[3];
628   for (int i = 0; i < 3; i++)
629   {
630     CALL_NETCDF_GW(nc_inq_dimlen(ncFD, dimIds[i], &dimSizes[i]));
631   }
632 
633   if (dimSizes[2] != 4)
634   {
635     vtkGenericWarningMacro(<< "Expected 2D dependent coordinate bounds to have"
636                            << " 4 entries in final dimension.  Instead has " << dimSizes[2]);
637     return 0;
638   }
639 
640   // Bounds are stored as 4-tuples for every cell.  Tuple entries 0 and 1
641   // connect to the cell in the -i topological direction.  Tuple entries 0 and 3
642   // connect to the cell in the -j topological direction.
643   std::vector<double> boundsData(dimSizes[0] * dimSizes[1] * 4);
644   if (!boundsData.empty())
645   {
646     CALL_NETCDF_GW(nc_get_var_double(ncFD, varId, &boundsData.at(0)));
647   }
648 
649   // The coords array are the coords at the points.  There is one more point
650   // than cell in each topological direction.
651   int numComponents = static_cast<int>(dimSizes[1]);
652   vtkIdType numTuples = static_cast<vtkIdType>(dimSizes[0]);
653   coords->SetNumberOfComponents(numComponents + 1);
654   coords->SetNumberOfTuples(numTuples + 1);
655 
656   // Copy from the bounds data to the coordinates data.  Most values will
657   // be copied from the bound's 0'th tuple entry.  Values at the extremes
658   // will be copied from other entries.
659   for (vtkIdType j = 0; j < numTuples; j++)
660   {
661     for (int i = 0; i < numComponents; i++)
662     {
663       coords->SetComponent(j, i, boundsData[(j * numComponents + i) * 4 + 0]);
664     }
665     coords->SetComponent(j, numComponents, boundsData[((j + 1) * numComponents - 1) * 4 + 1]);
666   }
667   for (int i = 0; i < numComponents; i++)
668   {
669     coords->SetComponent(numTuples, i, boundsData[((numTuples - 1) * numComponents) * 4 + 2]);
670   }
671   coords->SetComponent(
672     numTuples, numComponents, boundsData[(numTuples * numComponents - 1) * 4 + 3]);
673 
674   return 1;
675 }
676 
677 //------------------------------------------------------------------------------
LoadUnstructuredBoundsVariable(int ncFD,int varId,vtkDoubleArray * coords)678 int vtkNetCDFCFReader::vtkDependentDimensionInfo::LoadUnstructuredBoundsVariable(
679   int ncFD, int varId, vtkDoubleArray* coords)
680 {
681   int dimIds[2];
682   CALL_NETCDF_GW(nc_inq_vardimid(ncFD, varId, dimIds));
683 
684   size_t dimSizes[2];
685   for (int i = 0; i < 2; i++)
686   {
687     CALL_NETCDF_GW(nc_inq_dimlen(ncFD, dimIds[i], &dimSizes[i]));
688   }
689 
690   int numVertPerCell = static_cast<int>(dimSizes[1]);
691   vtkIdType numCells = static_cast<vtkIdType>(dimSizes[0]);
692 
693   coords->SetNumberOfComponents(numVertPerCell);
694   coords->SetNumberOfTuples(numCells);
695   CALL_NETCDF_GW(nc_get_var_double(ncFD, varId, coords->GetPointer(0)));
696 
697   return 1;
698 }
699 
700 //------------------------------------------------------------------------------
701 class vtkNetCDFCFReader::vtkDependentDimensionInfoVector
702 {
703 public:
704   std::vector<vtkNetCDFCFReader::vtkDependentDimensionInfo> v;
705 };
706 
707 //=============================================================================
708 vtkStandardNewMacro(vtkNetCDFCFReader);
709 
710 //------------------------------------------------------------------------------
vtkNetCDFCFReader()711 vtkNetCDFCFReader::vtkNetCDFCFReader()
712 {
713   this->SphericalCoordinates = 1;
714   this->VerticalScale = 1.0;
715   this->VerticalBias = 0.0;
716   this->OutputType = -1;
717 
718   this->DimensionInfo = new vtkDimensionInfoVector;
719   this->DependentDimensionInfo = new vtkDependentDimensionInfoVector;
720 }
721 
~vtkNetCDFCFReader()722 vtkNetCDFCFReader::~vtkNetCDFCFReader()
723 {
724   delete this->DimensionInfo;
725   delete this->DependentDimensionInfo;
726 }
727 
PrintSelf(ostream & os,vtkIndent indent)728 void vtkNetCDFCFReader::PrintSelf(ostream& os, vtkIndent indent)
729 {
730   this->Superclass::PrintSelf(os, indent);
731 
732   os << indent << "SphericalCoordinates: " << this->SphericalCoordinates << endl;
733   os << indent << "VerticalScale: " << this->VerticalScale << endl;
734   os << indent << "VerticalBias: " << this->VerticalBias << endl;
735   os << indent << "OutputType: " << this->OutputType << endl;
736 }
737 
738 //------------------------------------------------------------------------------
CanReadFile(const char * filename)739 int vtkNetCDFCFReader::CanReadFile(const char* filename)
740 {
741   // We really just read basic arrays from netCDF files.  If the netCDF library
742   // says we can read it, then we can read it.
743   int ncFD;
744   int errorcode = nc_open(filename, NC_NOWRITE, &ncFD);
745   if (errorcode == NC_NOERR)
746   {
747     nc_close(ncFD);
748     return 1;
749   }
750   else
751   {
752     return 0;
753   }
754 }
755 
756 //------------------------------------------------------------------------------
SetOutputType(int type)757 void vtkNetCDFCFReader::SetOutputType(int type)
758 {
759   vtkDebugMacro(<< this->GetClassName() << " (" << this
760                 << "):"
761                    " setting OutputType to "
762                 << type);
763   if (this->OutputType != type)
764   {
765     bool typeValid = ((type == -1) || (type == VTK_IMAGE_DATA) || (type == VTK_RECTILINEAR_GRID) ||
766       (type == VTK_STRUCTURED_GRID) || (type == VTK_UNSTRUCTURED_GRID));
767     if (typeValid)
768     {
769       this->OutputType = type;
770       this->Modified();
771     }
772     else
773     {
774       vtkErrorMacro(<< "Invalid OutputType: " << type);
775     }
776   }
777 }
778 
779 //------------------------------------------------------------------------------
RequestDataObject(vtkInformation * vtkNotUsed (request),vtkInformationVector ** vtkNotUsed (inputVector),vtkInformationVector * outputVector)780 int vtkNetCDFCFReader::RequestDataObject(vtkInformation* vtkNotUsed(request),
781   vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector)
782 {
783   vtkInformation* outInfo = outputVector->GetInformationObject(0);
784   vtkDataObject* output = vtkDataObject::GetData(outInfo);
785 
786   // This is really too early to know the appropriate data type.  We need to
787   // have meta data and let the user select arrays.  We have to do part
788   // of the RequestInformation to get the appropriate meta data.
789   if (!this->UpdateMetaData())
790     return 0;
791 
792   // Check that the dataType is correct or automatically set it if it is set to
793   // -1.
794   int dataType = this->OutputType;
795 
796   int ncFD;
797   CALL_NETCDF(nc_open(this->FileName, NC_NOWRITE, &ncFD));
798 
799   int numArrays = this->VariableArraySelection->GetNumberOfArrays();
800   for (int arrayIndex = 0; arrayIndex < numArrays; arrayIndex++)
801   {
802     if (!this->VariableArraySelection->GetArraySetting(arrayIndex))
803       continue;
804 
805     const char* name = this->VariableArraySelection->GetArrayName(arrayIndex);
806     int varId;
807     CALL_NETCDF(nc_inq_varid(ncFD, name, &varId));
808 
809     int currentNumDims;
810     CALL_NETCDF(nc_inq_varndims(ncFD, varId, &currentNumDims));
811     if (currentNumDims < 1)
812       continue;
813     VTK_CREATE(vtkIntArray, currentDimensions);
814     currentDimensions->SetNumberOfComponents(1);
815     currentDimensions->SetNumberOfTuples(currentNumDims);
816     CALL_NETCDF(nc_inq_vardimid(ncFD, varId, currentDimensions->GetPointer(0)));
817 
818     // Remove initial time dimension, which has no effect on data type.
819     if (this->IsTimeDimension(ncFD, currentDimensions->GetValue(0)))
820     {
821       currentDimensions->RemoveTuple(0);
822       currentNumDims--;
823       if (currentNumDims < 1)
824         continue;
825     }
826 
827     CoordinateTypesEnum coordType = this->CoordinateType(currentDimensions);
828 
829     int preferredDataType;
830     switch (coordType)
831     {
832       case COORDS_UNIFORM_RECTILINEAR:
833         preferredDataType = VTK_IMAGE_DATA;
834         break;
835       case COORDS_NONUNIFORM_RECTILINEAR:
836         preferredDataType = VTK_RECTILINEAR_GRID;
837         break;
838       case COORDS_REGULAR_SPHERICAL:
839       case COORDS_2D_EUCLIDEAN:
840       case COORDS_2D_SPHERICAL:
841       case COORDS_EUCLIDEAN_4SIDED_CELLS:
842       case COORDS_SPHERICAL_4SIDED_CELLS:
843         preferredDataType = VTK_STRUCTURED_GRID;
844         break;
845       case COORDS_EUCLIDEAN_PSIDED_CELLS:
846       case COORDS_SPHERICAL_PSIDED_CELLS:
847         preferredDataType = VTK_UNSTRUCTURED_GRID;
848         break;
849       default:
850         vtkErrorMacro(<< "Internal error: unknown coordinate type.");
851         return 0;
852     }
853 
854     // Check the data type.
855     if (dataType == -1)
856     {
857       dataType = preferredDataType;
858     }
859     else
860     {
861       switch (dataType)
862       {
863         case VTK_IMAGE_DATA:
864           if (preferredDataType != VTK_IMAGE_DATA)
865           {
866             vtkWarningMacro("You have set the OutputType to a data type that"
867                             " cannot fully represent the topology of the data."
868                             " Some of the topology will be ignored.");
869           }
870           break;
871         case VTK_RECTILINEAR_GRID:
872           if ((preferredDataType != VTK_IMAGE_DATA) && (preferredDataType != VTK_RECTILINEAR_GRID))
873           {
874             vtkWarningMacro("You have set the OutputType to a data type that"
875                             " cannot fully represent the topology of the data."
876                             " Some of the topology will be ignored.");
877           }
878           break;
879         case VTK_STRUCTURED_GRID:
880           if ((preferredDataType != VTK_IMAGE_DATA) &&
881             (preferredDataType != VTK_RECTILINEAR_GRID) &&
882             (preferredDataType != VTK_STRUCTURED_GRID))
883           {
884             vtkWarningMacro("You have set the OutputType to a data type that"
885                             " cannot fully represent the topology of the data."
886                             " Some of the topology will be ignored.");
887           }
888           break;
889         case VTK_UNSTRUCTURED_GRID:
890           // Unstructured grid supports all topologies.
891           break;
892         default:
893           vtkErrorMacro(<< "Sanity check failed: bad internal type.");
894           return 0;
895       }
896     }
897 
898     // That's right, break.  We really only want to look at the dimensions of
899     // the first valid loaded variable.  If we got here, we found a variable.
900     // The loop is really only for the continues at the top.
901     break;
902   }
903 
904   CALL_NETCDF(nc_close(ncFD));
905 
906   if (dataType == -1)
907   {
908     // Either no variables are selected or only variables with no dimensions.
909     // In either case, an image data should be fine.
910     dataType = VTK_IMAGE_DATA;
911   }
912 
913   if (dataType == VTK_IMAGE_DATA)
914   {
915     if (!output || !output->IsA("vtkImageData"))
916     {
917       output = vtkImageData::New();
918       outInfo->Set(vtkDataObject::DATA_OBJECT(), output);
919       output->Delete(); // Not really deleted.
920     }
921   }
922   else if (dataType == VTK_RECTILINEAR_GRID)
923   {
924     if (!output || !output->IsA("vtkRectilinearGrid"))
925     {
926       output = vtkRectilinearGrid::New();
927       outInfo->Set(vtkDataObject::DATA_OBJECT(), output);
928       output->Delete(); // Not really deleted.
929     }
930   }
931   else if (dataType == VTK_STRUCTURED_GRID)
932   {
933     if (!output || !output->IsA("vtkStructuredGrid"))
934     {
935       output = vtkStructuredGrid::New();
936       outInfo->Set(vtkDataObject::DATA_OBJECT(), output);
937       output->Delete(); // Not really deleted.
938     }
939   }
940   else if (dataType == VTK_UNSTRUCTURED_GRID)
941   {
942     if (!output || !output->IsA("vtkUnstructuredGrid"))
943     {
944       output = vtkUnstructuredGrid::New();
945       outInfo->Set(vtkDataObject::DATA_OBJECT(), output);
946       output->Delete(); // Not really deleted.
947     }
948   }
949   else
950   {
951     vtkErrorMacro(<< "Sanity check failed: bad internal type.");
952     return 0;
953   }
954 
955   return 1;
956 }
957 
958 //------------------------------------------------------------------------------
RequestInformation(vtkInformation * request,vtkInformationVector ** inputVector,vtkInformationVector * outputVector)959 int vtkNetCDFCFReader::RequestInformation(
960   vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
961 {
962   // Let the superclass do the heavy lifting.
963   if (!this->Superclass::RequestInformation(request, inputVector, outputVector))
964   {
965     return 0;
966   }
967 
968   // Superclass understands structured data, but we have to handle unstructured
969   // "extents" (pieces).
970   vtkInformation* outInfo = outputVector->GetInformationObject(0);
971   vtkDataObject* output = vtkDataObject::GetData(outInfo);
972   if (output)
973   {
974     if (output->GetExtentType() != VTK_3D_EXTENT)
975     {
976       outInfo->Set(CAN_HANDLE_PIECE_REQUEST(), 1);
977     }
978     else
979     {
980       outInfo->Set(CAN_PRODUCE_SUB_EXTENT(), 1);
981     }
982   }
983   else
984   {
985     return 0;
986   }
987   return 1;
988 }
989 
990 //------------------------------------------------------------------------------
RequestData(vtkInformation * request,vtkInformationVector ** inputVector,vtkInformationVector * outputVector)991 int vtkNetCDFCFReader::RequestData(
992   vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
993 {
994   // If the output does not directly support 3D extents, then we have to make
995   // some from the piece information so the superclass knows what portion of
996   // arrays to load.
997   vtkDataObject* output = vtkDataObject::GetData(outputVector);
998   if (output)
999   {
1000     if (output->GetExtentType() == VTK_3D_EXTENT)
1001     {
1002       // Do nothing.  3D extents already set.
1003     }
1004     else if (output->GetExtentType() == VTK_PIECES_EXTENT)
1005     {
1006       int pieceNumber, numberOfPieces, ghostLevels;
1007       vtkInformation* outInfo = outputVector->GetInformationObject(0);
1008       pieceNumber = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
1009       numberOfPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());
1010       ghostLevels = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS());
1011 
1012       int extent[6];
1013       this->ExtentForDimensionsAndPiece(pieceNumber, numberOfPieces, ghostLevels, extent);
1014 
1015       // Store the update extent in the output's information object to make it
1016       // easy to find whenever loading data for this object.
1017       output->GetInformation()->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), extent, 6);
1018     }
1019     else
1020     {
1021       vtkWarningMacro(<< "Invalid extent type encountered.  Data arrays may"
1022                       << " be loaded incorrectly.");
1023     }
1024   }
1025   else // output == nullptr
1026   {
1027     vtkErrorMacro(<< "No output object.");
1028     return 0;
1029   }
1030 
1031   // Let the superclass do the heavy lifting.
1032   if (!this->Superclass::RequestData(request, inputVector, outputVector))
1033   {
1034     return 0;
1035   }
1036 
1037   // Add spacing information defined by the COARDS conventions.
1038 
1039   vtkImageData* imageOutput = vtkImageData::GetData(outputVector);
1040   if (imageOutput)
1041   {
1042     this->AddRectilinearCoordinates(imageOutput);
1043   }
1044 
1045   vtkRectilinearGrid* rectilinearOutput = vtkRectilinearGrid::GetData(outputVector);
1046   if (rectilinearOutput)
1047   {
1048     switch (this->CoordinateType(this->LoadingDimensions))
1049     {
1050       case COORDS_EUCLIDEAN_PSIDED_CELLS:
1051       case COORDS_SPHERICAL_PSIDED_CELLS:
1052         // There is no sensible way to store p-sided cells in a structured grid.
1053         // Just fake some coordinates (related to ParaView bug #11543).
1054         this->FakeRectilinearCoordinates(rectilinearOutput);
1055         break;
1056       case COORDS_UNIFORM_RECTILINEAR:
1057       case COORDS_NONUNIFORM_RECTILINEAR:
1058       case COORDS_REGULAR_SPHERICAL:
1059       case COORDS_2D_EUCLIDEAN:
1060       case COORDS_2D_SPHERICAL:
1061       case COORDS_EUCLIDEAN_4SIDED_CELLS:
1062       case COORDS_SPHERICAL_4SIDED_CELLS:
1063       default:
1064         this->AddRectilinearCoordinates(rectilinearOutput);
1065     }
1066   }
1067 
1068   vtkStructuredGrid* structuredOutput = vtkStructuredGrid::GetData(outputVector);
1069   if (structuredOutput)
1070   {
1071     switch (this->CoordinateType(this->LoadingDimensions))
1072     {
1073       case COORDS_UNIFORM_RECTILINEAR:
1074       case COORDS_NONUNIFORM_RECTILINEAR:
1075         this->Add1DRectilinearCoordinates(structuredOutput);
1076         break;
1077       case COORDS_REGULAR_SPHERICAL:
1078         this->Add1DSphericalCoordinates(structuredOutput);
1079         break;
1080       case COORDS_2D_EUCLIDEAN:
1081       case COORDS_EUCLIDEAN_4SIDED_CELLS:
1082         this->Add2DRectilinearCoordinates(structuredOutput);
1083         break;
1084       case COORDS_2D_SPHERICAL:
1085       case COORDS_SPHERICAL_4SIDED_CELLS:
1086         this->Add2DSphericalCoordinates(structuredOutput);
1087         break;
1088       case COORDS_EUCLIDEAN_PSIDED_CELLS:
1089       case COORDS_SPHERICAL_PSIDED_CELLS:
1090         // There is no sensible way to store p-sided cells in a structured grid.
1091         // Just fake some coordinates (ParaView bug #11543).
1092         this->FakeStructuredCoordinates(structuredOutput);
1093         break;
1094       default:
1095         vtkErrorMacro("Internal error: unknown coordinate type.");
1096         return 0;
1097     }
1098   }
1099 
1100   vtkUnstructuredGrid* unstructuredOutput = vtkUnstructuredGrid::GetData(outputVector);
1101   if (unstructuredOutput)
1102   {
1103     int extent[6];
1104     this->GetUpdateExtentForOutput(unstructuredOutput, extent);
1105 
1106     switch (this->CoordinateType(this->LoadingDimensions))
1107     {
1108       case COORDS_UNIFORM_RECTILINEAR:
1109       case COORDS_NONUNIFORM_RECTILINEAR:
1110         this->Add1DRectilinearCoordinates(unstructuredOutput, extent);
1111         break;
1112       case COORDS_REGULAR_SPHERICAL:
1113         this->Add1DSphericalCoordinates(unstructuredOutput, extent);
1114         break;
1115       case COORDS_2D_EUCLIDEAN:
1116       case COORDS_EUCLIDEAN_4SIDED_CELLS:
1117         this->Add2DRectilinearCoordinates(unstructuredOutput, extent);
1118         break;
1119       case COORDS_2D_SPHERICAL:
1120       case COORDS_SPHERICAL_4SIDED_CELLS:
1121         this->Add2DSphericalCoordinates(unstructuredOutput, extent);
1122         break;
1123       case COORDS_EUCLIDEAN_PSIDED_CELLS:
1124         this->AddUnstructuredRectilinearCoordinates(unstructuredOutput, extent);
1125         break;
1126       case COORDS_SPHERICAL_PSIDED_CELLS:
1127         this->AddUnstructuredSphericalCoordinates(unstructuredOutput, extent);
1128         break;
1129       default:
1130         vtkErrorMacro("Internal error: unknown coordinate type.");
1131         return 0;
1132     }
1133   }
1134 
1135   return 1;
1136 }
1137 
1138 //------------------------------------------------------------------------------
ExtentForDimensionsAndPiece(int pieceNumber,int numberOfPieces,int ghostLevels,int extent[6])1139 void vtkNetCDFCFReader::ExtentForDimensionsAndPiece(
1140   int pieceNumber, int numberOfPieces, int ghostLevels, int extent[6])
1141 {
1142   VTK_CREATE(vtkExtentTranslator, extentTranslator);
1143 
1144   extentTranslator->SetWholeExtent(this->WholeExtent);
1145   extentTranslator->SetPiece(pieceNumber);
1146   extentTranslator->SetNumberOfPieces(numberOfPieces);
1147   extentTranslator->SetGhostLevel(ghostLevels);
1148 
1149   extentTranslator->PieceToExtent();
1150 
1151   extentTranslator->GetExtent(extent);
1152 }
1153 
1154 //------------------------------------------------------------------------------
GetUpdateExtentForOutput(vtkDataSet * output,int extent[6])1155 void vtkNetCDFCFReader::GetUpdateExtentForOutput(vtkDataSet* output, int extent[6])
1156 {
1157   vtkInformation* info = output->GetInformation();
1158   if (info->Has(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT()))
1159   {
1160     info->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), extent);
1161   }
1162   else
1163   {
1164     return this->Superclass::GetUpdateExtentForOutput(output, extent);
1165   }
1166 }
1167 
1168 //------------------------------------------------------------------------------
AddRectilinearCoordinates(vtkImageData * imageOutput)1169 void vtkNetCDFCFReader::AddRectilinearCoordinates(vtkImageData* imageOutput)
1170 {
1171   double origin[3];
1172   origin[0] = origin[1] = origin[2] = 0.0;
1173   double spacing[3];
1174   spacing[0] = spacing[1] = spacing[2] = 1.0;
1175 
1176   int numDim = this->LoadingDimensions->GetNumberOfTuples();
1177   if (numDim >= 3)
1178     numDim = 3;
1179 
1180   for (int i = 0; i < numDim; i++)
1181   {
1182     // Remember that netCDF dimension ordering is backward from VTK.
1183     int dim = this->LoadingDimensions->GetValue(numDim - i - 1);
1184     vtkDimensionInfo* dimInfo = this->GetDimensionInfo(dim);
1185     origin[i] = dimInfo->GetOrigin();
1186     spacing[i] = dimInfo->GetSpacing();
1187   }
1188 
1189   imageOutput->SetOrigin(origin);
1190   imageOutput->SetSpacing(spacing);
1191 }
1192 
1193 //------------------------------------------------------------------------------
AddRectilinearCoordinates(vtkRectilinearGrid * rectilinearOutput)1194 void vtkNetCDFCFReader::AddRectilinearCoordinates(vtkRectilinearGrid* rectilinearOutput)
1195 {
1196   int extent[6];
1197   rectilinearOutput->GetExtent(extent);
1198 
1199   int numDim = this->LoadingDimensions->GetNumberOfTuples();
1200   for (int i = 0; i < 3; i++)
1201   {
1202     vtkSmartPointer<vtkDoubleArray> coords;
1203     if (i < numDim)
1204     {
1205       // Remember that netCDF dimension ordering is backward from VTK.
1206       int dim = this->LoadingDimensions->GetValue(numDim - i - 1);
1207       coords = this->GetDimensionInfo(dim)->GetCoordinates();
1208       int extLow = extent[2 * i];
1209       int extHi = extent[2 * i + 1];
1210       if ((extLow != 0) || (extHi != coords->GetNumberOfTuples() - 1))
1211       {
1212         // Getting a subset of this dimension.
1213         VTK_CREATE(vtkDoubleArray, newcoords);
1214         newcoords->SetNumberOfComponents(1);
1215         newcoords->SetNumberOfTuples(extHi - extLow + 1);
1216         memcpy(newcoords->GetPointer(0), coords->GetPointer(extLow),
1217           (extHi - extLow + 1) * sizeof(double));
1218         coords = newcoords;
1219       }
1220     }
1221     else
1222     {
1223       coords = vtkSmartPointer<vtkDoubleArray>::New();
1224       coords->SetNumberOfTuples(1);
1225       coords->SetComponent(0, 0, 0.0);
1226     }
1227     switch (i)
1228     {
1229       case 0:
1230         rectilinearOutput->SetXCoordinates(coords);
1231         break;
1232       case 1:
1233         rectilinearOutput->SetYCoordinates(coords);
1234         break;
1235       case 2:
1236         rectilinearOutput->SetZCoordinates(coords);
1237         break;
1238       default:
1239         vtkErrorMacro("Sanity check failed!");
1240         break;
1241     }
1242   }
1243 }
1244 
1245 //------------------------------------------------------------------------------
FakeRectilinearCoordinates(vtkRectilinearGrid * rectilinearOutput)1246 void vtkNetCDFCFReader::FakeRectilinearCoordinates(vtkRectilinearGrid* rectilinearOutput)
1247 {
1248   int dimensionSizes[3];
1249   rectilinearOutput->GetDimensions(dimensionSizes);
1250 
1251   for (int dim = 0; dim < 3; dim++)
1252   {
1253     vtkNew<vtkDoubleArray> coordinate;
1254     coordinate->SetNumberOfComponents(1);
1255     coordinate->SetNumberOfTuples(dimensionSizes[dim]);
1256 
1257     for (int index = 0; index < dimensionSizes[dim]; index++)
1258     {
1259       coordinate->SetComponent(index, 0, static_cast<double>(index));
1260     }
1261     switch (dim)
1262     {
1263       case 0:
1264         rectilinearOutput->SetXCoordinates(coordinate);
1265         break;
1266       case 1:
1267         rectilinearOutput->SetYCoordinates(coordinate);
1268         break;
1269       case 2:
1270         rectilinearOutput->SetZCoordinates(coordinate);
1271         break;
1272       default:
1273         vtkErrorMacro("Sanity check failed!");
1274         break;
1275     }
1276   }
1277 }
1278 
1279 //------------------------------------------------------------------------------
Add1DRectilinearCoordinates(vtkPoints * points,const int extent[6])1280 void vtkNetCDFCFReader::Add1DRectilinearCoordinates(vtkPoints* points, const int extent[6])
1281 {
1282   points->SetDataTypeToDouble();
1283   points->SetNumberOfPoints(
1284     (extent[1] - extent[0] + 1) * (extent[3] - extent[2] + 1) * (extent[5] - extent[4] + 1));
1285   vtkDataArray* pointData = points->GetData();
1286 
1287   int numDimNetCDF = this->LoadingDimensions->GetNumberOfTuples();
1288   for (int dimVTK = 0; dimVTK < 3; dimVTK++)
1289   {
1290     vtkSmartPointer<vtkDoubleArray> coords;
1291     if (dimVTK < numDimNetCDF)
1292     {
1293       // Remember that netCDF dimension ordering is backward from VTK.
1294       int dimNetCDF = this->LoadingDimensions->GetValue(numDimNetCDF - dimVTK - 1);
1295       coords = this->GetDimensionInfo(dimNetCDF)->GetCoordinates();
1296 
1297       int ijk[3];
1298       vtkIdType pointIdx = 0;
1299       for (ijk[2] = extent[2 * 2]; ijk[2] <= extent[2 * 2 + 1]; ijk[2]++)
1300       {
1301         for (ijk[1] = extent[1 * 2]; ijk[1] <= extent[1 * 2 + 1]; ijk[1]++)
1302         {
1303           for (ijk[0] = extent[0 * 2]; ijk[0] <= extent[0 * 2 + 1]; ijk[0]++)
1304           {
1305             pointData->SetComponent(pointIdx, dimVTK, coords->GetValue(ijk[dimVTK]));
1306             pointIdx++;
1307           }
1308         }
1309       }
1310     }
1311     else
1312     {
1313       int ijk[3];
1314       vtkIdType pointIdx = 0;
1315       for (ijk[2] = extent[2 * 2]; ijk[2] <= extent[2 * 2 + 1]; ijk[2]++)
1316       {
1317         for (ijk[1] = extent[1 * 2]; ijk[1] <= extent[1 * 2 + 1]; ijk[1]++)
1318         {
1319           for (ijk[0] = extent[0 * 2]; ijk[0] <= extent[0 * 2 + 1]; ijk[0]++)
1320           {
1321             pointData->SetComponent(pointIdx, dimVTK, 0.0);
1322             pointIdx++;
1323           }
1324         }
1325       }
1326     }
1327   }
1328 }
1329 
1330 //------------------------------------------------------------------------------
Add2DRectilinearCoordinates(vtkPoints * points,const int extent[6])1331 void vtkNetCDFCFReader::Add2DRectilinearCoordinates(vtkPoints* points, const int extent[6])
1332 {
1333   points->SetDataTypeToDouble();
1334   points->Allocate(
1335     (extent[1] - extent[0] + 1) * (extent[3] - extent[2] + 1) * (extent[5] - extent[4] + 1));
1336 
1337   vtkDependentDimensionInfo* info = this->FindDependentDimensionInfo(this->LoadingDimensions);
1338 
1339   vtkDoubleArray* longitudeCoordinates = info->GetLongitudeCoordinates();
1340   vtkDoubleArray* latitudeCoordinates = info->GetLatitudeCoordinates();
1341 
1342   vtkDoubleArray* verticalCoordinates = nullptr;
1343   if (this->LoadingDimensions->GetNumberOfTuples() == 3)
1344   {
1345     int vertDim = this->LoadingDimensions->GetValue(0);
1346     if (info->GetHasBounds())
1347     {
1348       verticalCoordinates = this->GetDimensionInfo(vertDim)->GetBounds();
1349     }
1350     else
1351     {
1352       verticalCoordinates = this->GetDimensionInfo(vertDim)->GetCoordinates();
1353     }
1354   }
1355 
1356   for (int k = extent[4]; k <= extent[5]; k++)
1357   {
1358     double h;
1359     if (verticalCoordinates)
1360     {
1361       h = verticalCoordinates->GetValue(k);
1362     }
1363     else
1364     {
1365       h = 0.0;
1366     }
1367     for (int j = extent[2]; j <= extent[3]; j++)
1368     {
1369       for (int i = extent[0]; i <= extent[1]; i++)
1370       {
1371         double lon = longitudeCoordinates->GetComponent(j, i);
1372         double lat = latitudeCoordinates->GetComponent(j, i);
1373         points->InsertNextPoint(lon, lat, h);
1374       }
1375     }
1376   }
1377 }
1378 
1379 //------------------------------------------------------------------------------
Add1DRectilinearCoordinates(vtkStructuredGrid * structuredOutput)1380 void vtkNetCDFCFReader::Add1DRectilinearCoordinates(vtkStructuredGrid* structuredOutput)
1381 {
1382   int extent[6];
1383   structuredOutput->GetExtent(extent);
1384 
1385   VTK_CREATE(vtkPoints, points);
1386   this->Add1DRectilinearCoordinates(points, extent);
1387   structuredOutput->SetPoints(points);
1388 }
1389 
1390 //------------------------------------------------------------------------------
Add2DRectilinearCoordinates(vtkStructuredGrid * structuredOutput)1391 void vtkNetCDFCFReader::Add2DRectilinearCoordinates(vtkStructuredGrid* structuredOutput)
1392 {
1393   int extent[6];
1394   structuredOutput->GetExtent(extent);
1395 
1396   VTK_CREATE(vtkPoints, points);
1397   this->Add2DRectilinearCoordinates(points, extent);
1398   structuredOutput->SetPoints(points);
1399 }
1400 
1401 //------------------------------------------------------------------------------
FakeStructuredCoordinates(vtkStructuredGrid * structuredOutput)1402 void vtkNetCDFCFReader::FakeStructuredCoordinates(vtkStructuredGrid* structuredOutput)
1403 {
1404   int extent[6];
1405   structuredOutput->GetExtent(extent);
1406 
1407   vtkNew<vtkPoints> points;
1408   points->SetDataTypeToDouble();
1409   points->Allocate(
1410     (extent[1] - extent[0] + 1) * (extent[3] - extent[2] + 1) * (extent[5] - extent[4] + 1));
1411 
1412   for (int kIndex = extent[4]; kIndex <= extent[5]; kIndex++)
1413   {
1414     for (int jIndex = extent[2]; jIndex <= extent[3]; jIndex++)
1415     {
1416       for (int iIndex = extent[0]; iIndex <= extent[1]; iIndex++)
1417       {
1418         points->InsertNextPoint(iIndex, jIndex, kIndex);
1419       }
1420     }
1421   }
1422 
1423   structuredOutput->SetPoints(points);
1424 }
1425 
1426 //------------------------------------------------------------------------------
Add1DRectilinearCoordinates(vtkUnstructuredGrid * unstructuredOutput,const int extent[6])1427 void vtkNetCDFCFReader::Add1DRectilinearCoordinates(
1428   vtkUnstructuredGrid* unstructuredOutput, const int extent[6])
1429 {
1430   VTK_CREATE(vtkPoints, points);
1431   this->Add1DRectilinearCoordinates(points, extent);
1432   unstructuredOutput->SetPoints(points);
1433 
1434   this->AddStructuredCells(unstructuredOutput, extent);
1435 }
1436 
1437 //------------------------------------------------------------------------------
Add2DRectilinearCoordinates(vtkUnstructuredGrid * unstructuredOutput,const int extent[6])1438 void vtkNetCDFCFReader::Add2DRectilinearCoordinates(
1439   vtkUnstructuredGrid* unstructuredOutput, const int extent[6])
1440 {
1441   VTK_CREATE(vtkPoints, points);
1442   this->Add2DRectilinearCoordinates(points, extent);
1443   unstructuredOutput->SetPoints(points);
1444 
1445   this->AddStructuredCells(unstructuredOutput, extent);
1446 }
1447 
1448 //------------------------------------------------------------------------------
Add1DSphericalCoordinates(vtkPoints * points,const int extent[6])1449 void vtkNetCDFCFReader::Add1DSphericalCoordinates(vtkPoints* points, const int extent[6])
1450 {
1451   points->SetDataTypeToDouble();
1452   points->Allocate(
1453     (extent[1] - extent[0] + 1) * (extent[3] - extent[2] + 1) * (extent[5] - extent[4] + 1));
1454 
1455   vtkDoubleArray* coordArrays[3];
1456   for (vtkIdType i = 0; i < this->LoadingDimensions->GetNumberOfTuples(); i++)
1457   {
1458     int dim = this->LoadingDimensions->GetValue(i);
1459     coordArrays[i] = this->GetDimensionInfo(dim)->GetBounds();
1460   }
1461 
1462   int longitudeDim, latitudeDim, verticalDim;
1463   this->IdentifySphericalCoordinates(
1464     this->LoadingDimensions, longitudeDim, latitudeDim, verticalDim);
1465 
1466   if ((longitudeDim < 0) || (latitudeDim < 0))
1467   {
1468     vtkErrorMacro(<< "Internal error: treating non spherical coordinates as if"
1469                   << " they were spherical.");
1470     return;
1471   }
1472 
1473   // Check the height scale and bias.
1474   double vertScale = this->VerticalScale;
1475   double vertBias = this->VerticalBias;
1476   if (verticalDim >= 0)
1477   {
1478     double* verticalRange = coordArrays[verticalDim]->GetRange();
1479     if ((verticalRange[0] * vertScale + vertBias < 0) ||
1480       (verticalRange[1] * vertScale + vertBias < 0))
1481     {
1482       vertBias = -std::min(verticalRange[0], verticalRange[1]) * vertScale;
1483     }
1484   }
1485   else
1486   {
1487     if (vertScale + vertBias <= 0)
1488     {
1489       vertScale = 1.0;
1490       vertBias = 0.0;
1491     }
1492   }
1493 
1494   int ijk[3];
1495   for (ijk[0] = extent[4]; ijk[0] <= extent[5]; ijk[0]++)
1496   {
1497     for (ijk[1] = extent[2]; ijk[1] <= extent[3]; ijk[1]++)
1498     {
1499       for (ijk[2] = extent[0]; ijk[2] <= extent[1]; ijk[2]++)
1500       {
1501         double lon, lat, h;
1502         if (verticalDim >= 0)
1503         {
1504           lon = coordArrays[longitudeDim]->GetValue(ijk[longitudeDim]);
1505           lat = coordArrays[latitudeDim]->GetValue(ijk[latitudeDim]);
1506           h = coordArrays[verticalDim]->GetValue(ijk[verticalDim]);
1507         }
1508         else
1509         {
1510           lon = coordArrays[longitudeDim]->GetValue(ijk[longitudeDim + 1]);
1511           lat = coordArrays[latitudeDim]->GetValue(ijk[latitudeDim + 1]);
1512           h = 1.0;
1513         }
1514         lon = vtkMath::RadiansFromDegrees(lon);
1515         lat = vtkMath::RadiansFromDegrees(lat);
1516         h = h * vertScale + vertBias;
1517 
1518         double cartesianCoord[3];
1519         cartesianCoord[0] = h * cos(lon) * cos(lat);
1520         cartesianCoord[1] = h * sin(lon) * cos(lat);
1521         cartesianCoord[2] = h * sin(lat);
1522         points->InsertNextPoint(cartesianCoord);
1523       }
1524     }
1525   }
1526 }
1527 
1528 //------------------------------------------------------------------------------
Add2DSphericalCoordinates(vtkPoints * points,const int extent[6])1529 void vtkNetCDFCFReader::Add2DSphericalCoordinates(vtkPoints* points, const int extent[6])
1530 {
1531   points->SetDataTypeToDouble();
1532   points->Allocate(
1533     (extent[1] - extent[0] + 1) * (extent[3] - extent[2] + 1) * (extent[5] - extent[4] + 1));
1534 
1535   vtkDependentDimensionInfo* info = this->FindDependentDimensionInfo(this->LoadingDimensions);
1536 
1537   vtkDoubleArray* longitudeCoordinates = info->GetLongitudeCoordinates();
1538   vtkDoubleArray* latitudeCoordinates = info->GetLatitudeCoordinates();
1539 
1540   vtkDoubleArray* verticalCoordinates = nullptr;
1541   if (this->LoadingDimensions->GetNumberOfTuples() == 3)
1542   {
1543     int vertDim = this->LoadingDimensions->GetValue(0);
1544     if (info->GetHasBounds())
1545     {
1546       verticalCoordinates = this->GetDimensionInfo(vertDim)->GetBounds();
1547     }
1548     else
1549     {
1550       verticalCoordinates = this->GetDimensionInfo(vertDim)->GetCoordinates();
1551     }
1552   }
1553 
1554   // Check the height scale and bias.
1555   double vertScale = this->VerticalScale;
1556   double vertBias = this->VerticalBias;
1557   if (verticalCoordinates)
1558   {
1559     double* verticalRange = verticalCoordinates->GetRange();
1560     if ((verticalRange[0] * vertScale + vertBias < 0) ||
1561       (verticalRange[1] * vertScale + vertBias < 0))
1562     {
1563       vertBias = -std::min(verticalRange[0], verticalRange[1]) * vertScale;
1564     }
1565   }
1566   else
1567   {
1568     if (vertScale + vertBias <= 0)
1569     {
1570       vertScale = 1.0;
1571       vertBias = 0.0;
1572     }
1573   }
1574 
1575   for (int k = extent[4]; k <= extent[5]; k++)
1576   {
1577     double h;
1578     if (verticalCoordinates)
1579     {
1580       h = verticalCoordinates->GetValue(k) * vertScale + vertBias;
1581     }
1582     else
1583     {
1584       h = vertScale + vertBias;
1585     }
1586     for (int j = extent[2]; j <= extent[3]; j++)
1587     {
1588       for (int i = extent[0]; i <= extent[1]; i++)
1589       {
1590         double lon = longitudeCoordinates->GetComponent(j, i);
1591         double lat = latitudeCoordinates->GetComponent(j, i);
1592         lon = vtkMath::RadiansFromDegrees(lon);
1593         lat = vtkMath::RadiansFromDegrees(lat);
1594 
1595         double cartesianCoord[3];
1596         cartesianCoord[0] = h * cos(lon) * cos(lat);
1597         cartesianCoord[1] = h * sin(lon) * cos(lat);
1598         cartesianCoord[2] = h * sin(lat);
1599         points->InsertNextPoint(cartesianCoord);
1600       }
1601     }
1602   }
1603 }
1604 
1605 //------------------------------------------------------------------------------
Add1DSphericalCoordinates(vtkStructuredGrid * structuredOutput)1606 void vtkNetCDFCFReader::Add1DSphericalCoordinates(vtkStructuredGrid* structuredOutput)
1607 {
1608   int extent[6];
1609   structuredOutput->GetExtent(extent);
1610 
1611   VTK_CREATE(vtkPoints, points);
1612   this->Add1DSphericalCoordinates(points, extent);
1613   structuredOutput->SetPoints(points);
1614 }
1615 
1616 //------------------------------------------------------------------------------
Add2DSphericalCoordinates(vtkStructuredGrid * structuredOutput)1617 void vtkNetCDFCFReader::Add2DSphericalCoordinates(vtkStructuredGrid* structuredOutput)
1618 {
1619   int extent[6];
1620   structuredOutput->GetExtent(extent);
1621 
1622   VTK_CREATE(vtkPoints, points);
1623   this->Add2DSphericalCoordinates(points, extent);
1624   structuredOutput->SetPoints(points);
1625 }
1626 
1627 //------------------------------------------------------------------------------
Add1DSphericalCoordinates(vtkUnstructuredGrid * unstructuredOutput,const int extent[6])1628 void vtkNetCDFCFReader::Add1DSphericalCoordinates(
1629   vtkUnstructuredGrid* unstructuredOutput, const int extent[6])
1630 {
1631   VTK_CREATE(vtkPoints, points);
1632   this->Add1DSphericalCoordinates(points, extent);
1633   unstructuredOutput->SetPoints(points);
1634 
1635   this->AddStructuredCells(unstructuredOutput, extent);
1636 }
1637 
1638 //------------------------------------------------------------------------------
Add2DSphericalCoordinates(vtkUnstructuredGrid * unstructuredOutput,const int extent[6])1639 void vtkNetCDFCFReader::Add2DSphericalCoordinates(
1640   vtkUnstructuredGrid* unstructuredOutput, const int extent[6])
1641 {
1642   VTK_CREATE(vtkPoints, points);
1643   this->Add2DSphericalCoordinates(points, extent);
1644   unstructuredOutput->SetPoints(points);
1645 
1646   this->AddStructuredCells(unstructuredOutput, extent);
1647 }
1648 
1649 //------------------------------------------------------------------------------
AddStructuredCells(vtkUnstructuredGrid * unstructuredOutput,const int extent[6])1650 void vtkNetCDFCFReader::AddStructuredCells(
1651   vtkUnstructuredGrid* unstructuredOutput, const int extent[6])
1652 {
1653   vtkIdType numPoints[3];
1654   numPoints[0] = extent[1] - extent[0] + 1;
1655   numPoints[1] = extent[3] - extent[2] + 1;
1656   numPoints[2] = extent[5] - extent[4] + 1;
1657 
1658   vtkIdType numCells[3];
1659   numCells[0] = numPoints[0] - 1;
1660   numCells[1] = numPoints[1] - 1;
1661   numCells[2] = numPoints[2] - 1;
1662 
1663   vtkIdType nextPointRow = numPoints[0];
1664   vtkIdType nextPointSlab = nextPointRow * numPoints[1];
1665 
1666   bool extentIs2D = (numCells[2] < 1);
1667 
1668   if (extentIs2D)
1669   {
1670     vtkIdType totalNumCells = numCells[0] * numCells[1];
1671     unstructuredOutput->Allocate(totalNumCells);
1672     vtkCellArray* cells = unstructuredOutput->GetCells();
1673     cells->AllocateEstimate(totalNumCells, 4);
1674 
1675     for (int j = 0; j < numCells[1]; j++)
1676     {
1677       vtkIdType rowStart = j * nextPointRow;
1678       for (int i = 0; i < numCells[0]; i++)
1679       {
1680         vtkIdType lowCellPoint = rowStart + i;
1681 
1682         vtkIdType pointIds[4];
1683         pointIds[0] = lowCellPoint;
1684         pointIds[1] = lowCellPoint + 1;
1685         pointIds[2] = lowCellPoint + nextPointRow + 1;
1686         pointIds[3] = lowCellPoint + nextPointRow;
1687 
1688         unstructuredOutput->InsertNextCell(VTK_QUAD, 4, pointIds);
1689       }
1690     }
1691   }
1692   else // !extentIs2D
1693   {
1694     vtkIdType totalNumCells = numCells[0] * numCells[1] * numCells[2];
1695     unstructuredOutput->Allocate(totalNumCells);
1696     vtkCellArray* cells = unstructuredOutput->GetCells();
1697     cells->AllocateEstimate(totalNumCells, 8);
1698 
1699     for (int k = 0; k < numCells[2]; k++)
1700     {
1701       vtkIdType slabStart = k * nextPointSlab;
1702       for (int j = 0; j < numCells[1]; j++)
1703       {
1704         vtkIdType rowStart = slabStart + j * nextPointRow;
1705         for (int i = 0; i < numCells[0]; i++)
1706         {
1707           vtkIdType lowCellPoint = rowStart + i;
1708 
1709           // This code is assuming that all axis are scaling up.  If that is
1710           // not the case, this will probably make inverted hexahedra.
1711           vtkIdType pointIds[8];
1712           pointIds[0] = lowCellPoint;
1713           pointIds[1] = lowCellPoint + 1;
1714           pointIds[2] = lowCellPoint + nextPointRow + 1;
1715           pointIds[3] = lowCellPoint + nextPointRow;
1716           pointIds[4] = lowCellPoint + nextPointSlab;
1717           pointIds[5] = lowCellPoint + nextPointSlab + 1;
1718           pointIds[6] = lowCellPoint + nextPointSlab + nextPointRow + 1;
1719           pointIds[7] = lowCellPoint + nextPointSlab + nextPointRow;
1720 
1721           unstructuredOutput->InsertNextCell(VTK_HEXAHEDRON, 8, pointIds);
1722         }
1723       }
1724     }
1725   }
1726 }
1727 
1728 //------------------------------------------------------------------------------
AddUnstructuredRectilinearCoordinates(vtkUnstructuredGrid * unstructuredOutput,const int extent[6])1729 void vtkNetCDFCFReader::AddUnstructuredRectilinearCoordinates(
1730   vtkUnstructuredGrid* unstructuredOutput, const int extent[6])
1731 {
1732   vtkDependentDimensionInfo* info = this->FindDependentDimensionInfo(this->LoadingDimensions);
1733 
1734   vtkDoubleArray* longitudeCoordinates = info->GetLongitudeCoordinates();
1735   vtkDoubleArray* latitudeCoordinates = info->GetLatitudeCoordinates();
1736 
1737   int numPointsPerCell = longitudeCoordinates->GetNumberOfComponents();
1738   vtkIdType totalNumCells = longitudeCoordinates->GetNumberOfTuples();
1739 
1740   double bounds[6];
1741   GetRangeOfAllComponents(longitudeCoordinates, bounds + 0);
1742   GetRangeOfAllComponents(latitudeCoordinates, bounds + 2);
1743   bounds[4] = bounds[5] = 0.0;
1744 
1745   VTK_CREATE(vtkPoints, points);
1746   points->SetDataTypeToDouble();
1747   points->Allocate(totalNumCells);
1748 
1749   VTK_CREATE(vtkMergePoints, locator);
1750   locator->InitPointInsertion(points, bounds);
1751 
1752   // Make space in output unstructured grid.
1753   unstructuredOutput->Allocate(extent[1] - extent[0]);
1754   vtkCellArray* cells = unstructuredOutput->GetCells();
1755   cells->AllocateEstimate(extent[1] - extent[0], numPointsPerCell);
1756 
1757   std::vector<vtkIdType> cellPoints(numPointsPerCell);
1758 
1759   // This is a rather lame way to break up cells amongst processes.  It will be
1760   // slow and ghost cells are totally screwed up.
1761   for (int cellId = extent[0]; cellId < extent[1]; cellId++)
1762   {
1763     for (int cellPointId = 0; cellPointId < numPointsPerCell; cellPointId++)
1764     {
1765       double coord[3];
1766       coord[0] = longitudeCoordinates->GetComponent(cellId, cellPointId);
1767       coord[1] = latitudeCoordinates->GetComponent(cellId, cellPointId);
1768       coord[2] = 0.0;
1769 
1770       vtkIdType pointId;
1771       locator->InsertUniquePoint(coord, pointId);
1772 
1773       cellPoints[cellPointId] = pointId;
1774     }
1775     unstructuredOutput->InsertNextCell(VTK_POLYGON, numPointsPerCell, &cellPoints.at(0));
1776   }
1777 
1778   points->Squeeze();
1779   unstructuredOutput->SetPoints(points);
1780 }
1781 
1782 //------------------------------------------------------------------------------
AddUnstructuredSphericalCoordinates(vtkUnstructuredGrid * unstructuredOutput,const int extent[6])1783 void vtkNetCDFCFReader::AddUnstructuredSphericalCoordinates(
1784   vtkUnstructuredGrid* unstructuredOutput, const int extent[6])
1785 {
1786   // First load the data as rectilinear coordinates, and then convert them
1787   // to spherical coordinates.  Not only does this reuse code, but it also
1788   // probably makes the locator more efficient this way.
1789   this->AddUnstructuredRectilinearCoordinates(unstructuredOutput, extent);
1790 
1791   double height = 1.0 * this->VerticalScale + this->VerticalBias;
1792   if (height <= 0.0)
1793   {
1794     height = 1.0;
1795   }
1796 
1797   vtkPoints* points = unstructuredOutput->GetPoints();
1798   vtkIdType numPoints = points->GetNumberOfPoints();
1799   for (vtkIdType pointId = 0; pointId < numPoints; pointId++)
1800   {
1801     double lonLat[3];
1802     points->GetPoint(pointId, lonLat);
1803     double lon = vtkMath::RadiansFromDegrees(lonLat[0]);
1804     double lat = vtkMath::RadiansFromDegrees(lonLat[1]);
1805 
1806     double cartesianCoord[3];
1807     cartesianCoord[0] = height * cos(lon) * cos(lat);
1808     cartesianCoord[1] = height * sin(lon) * cos(lat);
1809     cartesianCoord[2] = height * sin(lat);
1810     points->SetPoint(pointId, cartesianCoord);
1811   }
1812 }
1813 
1814 //------------------------------------------------------------------------------
ReadMetaData(int ncFD)1815 int vtkNetCDFCFReader::ReadMetaData(int ncFD)
1816 {
1817   vtkDebugMacro("ReadMetaData");
1818 
1819   int numDimensions;
1820   CALL_NETCDF(nc_inq_ndims(ncFD, &numDimensions));
1821   this->DimensionInfo->v.resize(numDimensions);
1822 
1823   std::set<vtkStdString> specialVariables;
1824 
1825   for (int i = 0; i < numDimensions; i++)
1826   {
1827     this->DimensionInfo->v[i] = vtkDimensionInfo(ncFD, i);
1828 
1829     // Record any special variables for this dimension.
1830     vtkStringArray* dimensionVariables = this->DimensionInfo->v[i].GetSpecialVariables();
1831     for (vtkIdType j = 0; j < dimensionVariables->GetNumberOfValues(); j++)
1832     {
1833       specialVariables.insert(dimensionVariables->GetValue(j));
1834     }
1835   }
1836 
1837   int numVariables;
1838   CALL_NETCDF(nc_inq_nvars(ncFD, &numVariables));
1839 
1840   // Check all variables for special 2D coordinates.
1841   for (int i = 0; i < numVariables; i++)
1842   {
1843     vtkDependentDimensionInfo info(ncFD, i, this);
1844     if (!info.GetValid())
1845       continue;
1846     if (this->FindDependentDimensionInfo(info.GetGridDimensions()) != nullptr)
1847     {
1848       continue;
1849     }
1850 
1851     this->DependentDimensionInfo->v.push_back(info);
1852 
1853     // Record any special variables.
1854     vtkStringArray* dimensionVariables = info.GetSpecialVariables();
1855     for (vtkIdType j = 0; j < dimensionVariables->GetNumberOfValues(); j++)
1856     {
1857       specialVariables.insert(dimensionVariables->GetValue(j));
1858     }
1859   }
1860 
1861   // Look at all variables and record them so that the user can select which
1862   // ones he wants.  This oddness of adding and removing from
1863   // VariableArraySelection is to preserve any current settings for variables.
1864   typedef std::set<vtkStdString> stringSet;
1865   stringSet variablesToAdd;
1866   stringSet variablesToRemove;
1867 
1868   // Initialize variablesToRemove with all the variables.  Then remove them from
1869   // the list as we find them.
1870   for (int i = 0; i < this->VariableArraySelection->GetNumberOfArrays(); i++)
1871   {
1872     variablesToRemove.insert(this->VariableArraySelection->GetArrayName(i));
1873   }
1874 
1875   for (int i = 0; i < numVariables; i++)
1876   {
1877     char name[NC_MAX_NAME + 1];
1878     CALL_NETCDF(nc_inq_varname(ncFD, i, name));
1879     if (specialVariables.find(name) == specialVariables.end())
1880     {
1881       if (variablesToRemove.find(name) == variablesToRemove.end())
1882       {
1883         // Variable not already here.  Insert it in the variables to add.
1884         variablesToAdd.insert(name);
1885       }
1886       else
1887       {
1888         // Variable already exists.  Leave it be.  Remove it from the
1889         // variablesToRemove list.
1890         variablesToRemove.erase(name);
1891       }
1892     }
1893   }
1894 
1895   // Add and remove variables.  This will be a no-op if the variables have not
1896   // changed.
1897   for (stringSet::iterator removeItr = variablesToRemove.begin();
1898        removeItr != variablesToRemove.end(); ++removeItr)
1899   {
1900     this->VariableArraySelection->RemoveArrayByName(removeItr->c_str());
1901   }
1902   for (stringSet::iterator addItr = variablesToAdd.begin(); addItr != variablesToAdd.end();
1903        ++addItr)
1904   {
1905     this->VariableArraySelection->AddArray(addItr->c_str());
1906   }
1907 
1908   return 1;
1909 }
1910 
1911 //------------------------------------------------------------------------------
IsTimeDimension(int vtkNotUsed (ncFD),int dimId)1912 int vtkNetCDFCFReader::IsTimeDimension(int vtkNotUsed(ncFD), int dimId)
1913 {
1914   return (this->GetDimensionInfo(dimId)->GetUnits() == vtkDimensionInfo::TIME_UNITS);
1915 }
1916 
1917 //------------------------------------------------------------------------------
GetTimeValues(int vtkNotUsed (ncFD),int dimId)1918 vtkSmartPointer<vtkDoubleArray> vtkNetCDFCFReader::GetTimeValues(int vtkNotUsed(ncFD), int dimId)
1919 {
1920   return this->GetDimensionInfo(dimId)->GetCoordinates();
1921 }
1922 
1923 //------------------------------------------------------------------------------
GetDimensionInfo(int dimension)1924 inline vtkNetCDFCFReader::vtkDimensionInfo* vtkNetCDFCFReader::GetDimensionInfo(int dimension)
1925 {
1926   return &(this->DimensionInfo->v.at(dimension));
1927 }
1928 
1929 //------------------------------------------------------------------------------
FindDependentDimensionInfo(vtkIntArray * dims)1930 vtkNetCDFCFReader::vtkDependentDimensionInfo* vtkNetCDFCFReader::FindDependentDimensionInfo(
1931   vtkIntArray* dims)
1932 {
1933   for (size_t i = 0; i < this->DependentDimensionInfo->v.size(); i++)
1934   {
1935     vtkIntArray* dependentDims = this->DependentDimensionInfo->v[i].GetGridDimensions();
1936     if (dims->GetNumberOfTuples() == dependentDims->GetNumberOfTuples())
1937     {
1938       bool same = true;
1939       for (vtkIdType j = 0; j < dims->GetNumberOfTuples(); j++)
1940       {
1941         if (dims->GetValue(j) != dependentDims->GetValue(j))
1942         {
1943           same = false;
1944           break;
1945         }
1946       }
1947       if (same)
1948         return &(this->DependentDimensionInfo->v[i]);
1949     }
1950   }
1951   return nullptr;
1952 }
1953 
1954 //------------------------------------------------------------------------------
IdentifySphericalCoordinates(vtkIntArray * dimensions,int & longitudeDim,int & latitudeDim,int & verticalDim)1955 void vtkNetCDFCFReader::IdentifySphericalCoordinates(
1956   vtkIntArray* dimensions, int& longitudeDim, int& latitudeDim, int& verticalDim)
1957 {
1958   longitudeDim = latitudeDim = verticalDim = -1;
1959   for (int i = 0; i < dimensions->GetNumberOfTuples(); i++)
1960   {
1961     switch (this->GetDimensionInfo(dimensions->GetValue(i))->GetUnits())
1962     {
1963       case vtkDimensionInfo::LONGITUDE_UNITS:
1964         longitudeDim = i;
1965         break;
1966       case vtkDimensionInfo::LATITUDE_UNITS:
1967         latitudeDim = i;
1968         break;
1969       case vtkDimensionInfo::UNDEFINED_UNITS:
1970       case vtkDimensionInfo::TIME_UNITS:
1971       case vtkDimensionInfo::VERTICAL_UNITS:
1972       default:
1973         verticalDim = i;
1974         break;
1975     }
1976   }
1977 }
1978 
1979 //------------------------------------------------------------------------------
CoordinateType(vtkIntArray * dimensions)1980 vtkNetCDFCFReader::CoordinateTypesEnum vtkNetCDFCFReader::CoordinateType(vtkIntArray* dimensions)
1981 {
1982   vtkDependentDimensionInfo* dependentDimInfo = this->FindDependentDimensionInfo(dimensions);
1983 
1984   // Check to see if using p-sided cells.
1985   if (dependentDimInfo && dependentDimInfo->GetCellsUnstructured())
1986   {
1987     if (this->SphericalCoordinates)
1988     {
1989       return COORDS_SPHERICAL_PSIDED_CELLS;
1990     }
1991     else
1992     {
1993       return COORDS_EUCLIDEAN_PSIDED_CELLS;
1994     }
1995   }
1996 
1997   // Check to see if using 4-sided cells.
1998   if (dependentDimInfo && !dependentDimInfo->GetCellsUnstructured() &&
1999     dependentDimInfo->GetHasBounds())
2000   {
2001     if (this->SphericalCoordinates)
2002     {
2003       return COORDS_SPHERICAL_4SIDED_CELLS;
2004     }
2005     else
2006     {
2007       return COORDS_EUCLIDEAN_4SIDED_CELLS;
2008     }
2009   }
2010 
2011   // Check to see if using 2D coordinate lookup.
2012   if (dependentDimInfo && !dependentDimInfo->GetCellsUnstructured() &&
2013     !dependentDimInfo->GetHasBounds())
2014   {
2015     if (this->SphericalCoordinates)
2016     {
2017       return COORDS_2D_SPHERICAL;
2018     }
2019     else
2020     {
2021       return COORDS_2D_EUCLIDEAN;
2022     }
2023   }
2024 
2025   // Check to see if we should (otherwise) be using spherical coordinates.
2026   if (this->SphericalCoordinates)
2027   {
2028     int longitudeDim, latitudeDim, verticalDim;
2029     this->IdentifySphericalCoordinates(dimensions, longitudeDim, latitudeDim, verticalDim);
2030     if ((longitudeDim != -1) && (latitudeDim != -1) &&
2031       ((dimensions->GetNumberOfTuples() == 2) || (verticalDim != -1)))
2032     {
2033       return COORDS_REGULAR_SPHERICAL;
2034     }
2035   }
2036 
2037   // Check to see if any dimension as irregular spacing.
2038   for (int i = 0; i < dimensions->GetNumberOfTuples(); i++)
2039   {
2040     int dimId = dimensions->GetValue(i);
2041     if (!this->GetDimensionInfo(dimId)->GetHasRegularSpacing())
2042     {
2043       return COORDS_NONUNIFORM_RECTILINEAR;
2044     }
2045   }
2046 
2047   // All dimensions appear to be uniform rectilinear.
2048   return COORDS_UNIFORM_RECTILINEAR;
2049 }
2050 
2051 //------------------------------------------------------------------------------
DimensionsAreForPointData(vtkIntArray * dimensions)2052 bool vtkNetCDFCFReader::DimensionsAreForPointData(vtkIntArray* dimensions)
2053 {
2054   switch (this->CoordinateType(dimensions))
2055   {
2056     case COORDS_UNIFORM_RECTILINEAR:
2057       return true;
2058     case COORDS_NONUNIFORM_RECTILINEAR:
2059       return true;
2060     case COORDS_REGULAR_SPHERICAL:
2061       return false;
2062     case COORDS_2D_EUCLIDEAN:
2063       return true;
2064     case COORDS_2D_SPHERICAL:
2065       return true;
2066     case COORDS_EUCLIDEAN_4SIDED_CELLS:
2067       return false;
2068     case COORDS_SPHERICAL_4SIDED_CELLS:
2069       return false;
2070     case COORDS_EUCLIDEAN_PSIDED_CELLS:
2071       return false;
2072     case COORDS_SPHERICAL_PSIDED_CELLS:
2073       return false;
2074     default:
2075       vtkErrorMacro("Internal error: unknown coordinate type.");
2076       return true;
2077   }
2078 }
2079