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, ¤tNumDims));
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