1 /***********************************************************************/
2 /* Open Visualization Data Explorer */
3 /* (C) Copyright IBM Corp. 1989,1999 */
4 /* ALL RIGHTS RESERVED */
5 /* This code licensed under the */
6 /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */
7 /***********************************************************************/
8 #include <dxconfig.h>
9
10
11
12 #include <stdio.h>
13 #include <dx/dx.h>
14 #include "import.h"
15
16 #if defined(HAVE_LIBNETCDF)
17
18 #undef int8
19 #undef uint8
20 #undef int16
21 #undef uint16
22 #undef int32
23 #undef uint32
24 #undef float64
25
26 #if (defined(WIN32) && defined(NDEBUG)) || defined(_MT)
27 #define _HDFDLL_
28 #elif defined(_DEBUG)
29 #undef _DEBUG
30 #endif
31
32 #include <netcdf.h>
33 #include <ctype.h>
34 #include <string.h>
35 #include <math.h>
36
37
38 #define DEBUG 0
39 #define REALCODE 0 /* if 0, sends fake data type info for QueryInfo */
40 #define TESTQUERY 0 /* if 1, send QueryInfo object back for Import */
41
42 #define VOID char /* prefer void, but char if your compiler chokes */
43 #define STR(x) (Object)DXNewString(x)
44
45 #define MAX_FILENAME_LEN MAX_NC_NAME
46 #define MAX_VARNAME_LEN MAX_NC_NAME
47 #define MAXNAME MAX_NC_NAME
48 #define MAXHAND 8
49 #define MAXATTRSTR 16
50
51 /* attribute names in netCDF file */
52 #define SERIESATTRIB "series"
53 #define SERIESPOSATTRIB "seriespositions"
54 #define FIELDATTRIB "field"
55 #define GROUPATTRIB "group"
56 #define TOPOATTRIB "connections"
57 #define GEOMATTRIB "positions"
58 #define OTOPOATTRIB "topology" /* "connections" */
59 #define OGEOMATTRIB "geometry" /* "positions" */
60 #define COMPATTRIB "component" /* general purpose component */
61 #define ATTRATTRIB "attribute" /* general purpose attribute */
62 #define OATTRATTRIB "attributes" /* alternate spelling */
63 #define MAX_DXATTRIBUTES 9 /* number of variable dx attributes */
64
65 /* component names in Group/Field */
66 #define TOPCOMPNAME "connections"
67 #define GEOCOMPNAME "positions"
68 #define DATCOMPNAME "data"
69
70
71 #define MAXCOMPNAME 128
72 #define MAXRANK 16 /* number of dimensions in array */
73 #define MAXSHAPE 128 /* max dim of individual data item */
74 #define INVALID_VARID -2
75 #define INVALID_DIMID -2
76
77 /* flags for build_poscon */
78 #define I_NONE 0
79 #define I_COMPACT_P 1
80 #define I_PRODUCT_P 2
81 #define I_IRREGULAR_P 3
82 #define I_REGULAR_P 4
83 #define I_COMPACT_C 5
84 #define I_PRODUCT_C 6
85 #define I_IRREGULAR_C 7
86 #define I_REGULAR_C 8
87
88 /* flags for Object_Import */
89 #define IMPORT_UIQUERY 0
90 #define IMPORT_DATA 1
91 #define IMPORT_VARINFO 2
92
93 #ifndef DXD_NON_UNIX_ENV_SEPARATOR
94 #define PATH_SEP_CHAR ':'
95 #else
96 #define PATH_SEP_CHAR ';'
97 #endif
98
99 /* for collecting series groups, general groups and composite fields.
100 */
101 struct varinfo {
102 Class class;
103 char name[MAXCOMPNAME];
104 int cdfid;
105 char cdfname[MAX_FILENAME_LEN];
106 int varid;
107 double position;
108 int *startframe;
109 int *endframe;
110 int *deltaframe;
111 int recdim;
112 struct varinfo *child;
113 struct varinfo *next;
114 };
115 typedef struct varinfo *Varinfo;
116
117
118 /* for assembling arrays correctly.
119 */
120 struct arrayinfo {
121 int cdfhandle; /* for netCDF variable currently being read */
122 int varid;
123 int recdim; /* dim ID for unlimited (record) dimension */
124 int data_isrec; /* whether data uses record dimension */
125 char stringattr[MAXNAME];
126 int data_ndims; /* data array shape */
127 int datacounts[MAXRANK];
128 int array_ndims; /* current array shape */
129 int arraycounts[MAXRANK];
130 int arrayrank; /* current array individual items */
131 int arrayshape[MAXSHAPE];
132 int arraytype;
133 Varinfo vp; /* for getting start/end/delta/current info */
134 };
135 typedef struct arrayinfo *Arrayinfo;
136
137 static char *dxattributes[] = {
138 "field",
139 "group",
140 "connections",
141 "positions",
142 "topology",
143 "geometry",
144 "component",
145 "attribute",
146 "attributes"
147 };
148
149 static char *regularname[] = {
150 "point", /* rank 0 */
151 "lines", /* rank 1 */
152 "quads", /* rank 2 */
153 "cubes", /* rank 3 */
154 "cubes4D", /* rank 4 */
155 "cubes5D", /* rank 5 */
156 "cubes6D", /* rank 6 */
157 "cubes7D", /* rank 7 */
158 "cubes8D", /* rank 8 */
159 "cubes9D", /* rank 9 */
160 };
161
162 /* these prototypes should be moved to import.h - they are publics */
163 Object _dxfQueryImportInfo(char *filename);
164 static Object EndImport(Object o);
165
166 /* private functions */
167 static Object QueryNetCDFInfo(char *filename);
168 static Object Object_Import(char *filename, char **varlist, int *starttime,
169 int *endtime, int *deltatime, int importflag);
170
171 static Object import_field(int cdfhandle, Varinfo vp);
172 static Object query_field(int cdfhandle, Varinfo vp);
173 static Varinfo query_var(int cdfhandle, char **varlist, int *starttime,
174 int *endtime, int *deltatime);
175
176 static int open_netcdf_file(char *filename);
177 static void close_netcdf_file(int cdfhandle);
178 static Varinfo match_vp(Varinfo vp, int varid, char **s, char *stringattr);
179 static void vp_delete(Varinfo vp);
180
181 static Object build_field(int, int);
182 static Object build_series(int, Varinfo vp);
183 static Object build_poscon(Arrayinfo ap, int flag, int dim);
184 static Object build_data(Arrayinfo, int);
185 static Object build_array(Arrayinfo);
186 static Object build_regpos(Arrayinfo, int);
187 static Object build_regpos1D(Arrayinfo, int, int);
188 static Object build_regcon(Arrayinfo);
189 static Object build_regcon1D(Arrayinfo, int);
190
191 static char *getattr(int hand, int varid, char *attrname,
192 char *stringattr,int maxlen);
193 static char *getNattr(int hand, int varid, int i, char *attrname, int n,
194 char *stringattr);
195 static Error setattr(Arrayinfo ap, Field f, char *compname);
196 static char *isattr(int hand, int varid, char *attrname);
197 static Error setuserattr(Arrayinfo ap, Field f, char *compname);
198 static Error getglobalattr(Object o,Varinfo vp);
199
200 static Error check_serieslength(Arrayinfo ap);
201 static int get_serieslength(Arrayinfo);
202 static int is_seriesvariable(int hand, int varid, int recdim);
203 static Error get_seriesvalue(Arrayinfo, float **);
204
205 static Error variablename(int hand, int varid, char *cbuf);
206 static int setrank(char *);
207 static char *parseit(char *in, int *n, char *out[]);
208
209 /* globals defined in xdr_float.c. xdr_float() increments these counts
210 * if IEEE defined but not-supported-on-the-i860-without-a-trap numbers
211 * are encountered. they are all set to zero.
212 */
213 #if !defined(DXD_STANDARD_IEEE)
214 extern int dx_denorm_fp_count;
215 extern int dx_bad_fp_count;
216 #endif
217
218 /*
219 * public entry points:
220 */
221
222 /*
223 * inquire about the fields in a file
224 *
225 */
_dxfQueryImportInfo(char * filename)226 Object _dxfQueryImportInfo(char *filename)
227 {
228 /* only handle netCDF for now */
229 return QueryNetCDFInfo(filename);
230 }
231
232
233 /*
234 * create a field or group object. new interface.
235 *
236 * input: netCDF dataset name, dependant variable name list, int *starttime,
237 * int *endtime, int *deltatime
238 * output: group or field
239 *
240 */
DXImportNetCDF(char * filename,char ** variable,int * starttime,int * endtime,int * deltatime)241 Object DXImportNetCDF(char *filename, char **variable, int *starttime,
242 int *endtime, int *deltatime)
243 {
244 return Object_Import(filename, variable, starttime, endtime,
245 deltatime, IMPORT_DATA);
246 }
247
248
249 /*
250 * do any processing at end of import, e.g. partitioning or statistics
251 */
EndImport(Object o)252 static Object EndImport(Object o)
253 {
254 return o;
255 }
256
257
258 static Object
QueryNetCDFInfo(char * filename)259 QueryNetCDFInfo(char *filename)
260 {
261 return Object_Import(filename, NULL, NULL, NULL, NULL, IMPORT_UIQUERY);
262 }
263
264
265 static Object
Object_Import(char * filename,char ** varlist,int * starttime,int * endtime,int * deltatime,int importflag)266 Object_Import(char *filename, char **varlist, int *starttime, int *endtime,
267 int *deltatime, int importflag)
268 {
269 int cdfhandle; /* file descriptor */
270 Varinfo vp = NULL; /* number and types of fields to import */
271 Object o = NULL; /* output objects */
272
273
274 /* open the file, handle default dirs, socket connections, etc.
275 * if return < 0, error code has already been set.
276 */
277 if((cdfhandle = open_netcdf_file(filename)) < 0)
278 return NULL;
279
280 #if !defined(DXD_STANDARD_IEEE)
281
282 /* zero out the counts, and look at the end to see if we encountered
283 * any bad floating point values.
284 */
285 dx_denorm_fp_count = 0;
286 dx_bad_fp_count = 0;
287
288 #endif
289
290 /* fill in the varinfo struct, and return NULL if no
291 * valid fields are found. sets error code before return.
292 */
293 if(!(vp = query_var(cdfhandle, varlist, starttime, endtime, deltatime)))
294 return NULL;
295
296
297 /* if query, put information about the field into a string object.
298 * if import, import the data and construct the object.
299 */
300 switch(importflag) {
301 case IMPORT_UIQUERY:
302 o = query_field(cdfhandle, vp);
303 break;
304
305 case IMPORT_DATA:
306 o = import_field(cdfhandle, vp);
307 if( !o )
308 DXWarning("Nothing has been imported. Did you use a OpenDX-compliant netCDF file convention?");
309 else
310 getglobalattr(o,vp);
311 break;
312
313 case IMPORT_VARINFO:
314 close_netcdf_file(cdfhandle);
315 return (Object)vp;
316
317 default:
318 DXSetError(ERROR_INTERNAL, "bad flag");
319 }
320
321
322 /* close the file, sever socket connections, etc.
323 */
324 close_netcdf_file(cdfhandle);
325
326 vp_delete(vp);
327
328 #if !defined(DXD_STANDARD_IEEE)
329
330 if(dx_denorm_fp_count > 0)
331 DXWarning("%d denormalized floating point values rounded to zero",
332 dx_denorm_fp_count);
333 if(dx_bad_fp_count > 0)
334 DXWarning("%d NaN or Infinity floating point values changed to zero",
335 dx_bad_fp_count);
336 #endif
337
338 /* hook for pre-processing the imported data, if needed */
339 o = EndImport(o);
340 return o;
341
342 }
343
344 /* open the file. when we start doing distributed things, here is
345 * where the socket connections should be added.
346 * look for name.nc instead of name.cdf name not found lumba 3Apr96
347 */
348 static int
open_netcdf_file(char * filename)349 open_netcdf_file(char *filename)
350 {
351 int cdfhandle;
352 int foundfile = 0;
353 char *fname = NULL, *foundname = NULL, *cp;
354 char *datadir = NULL;
355 int ll;
356
357 /* netCDF library options: on error, don't abort and don't print */
358 ncopts = 0;
359
360 #define XTRA 8 /* space for trailing /, .nc, trailing 0 and some slop */
361
362 datadir = (char *)getenv("DXDATA");
363 ll = strlen(filename) + XTRA;
364 if (datadir != NULL)
365 ll += strlen(datadir);
366 fname = (char *)DXAllocateLocalZero(ll);
367 if (!fname)
368 goto error;
369
370 /* try name, name.nc, dir/name and dir/name.nc */
371 if((cdfhandle = ncopen(filename, NC_NOWRITE)) < 0) {
372 if (ncerr == 0 && !foundfile) {
373 foundfile++;
374 foundname = (char *)DXAllocateLocalZero(strlen(filename)+1);
375 if (!foundname)
376 goto error;
377 strcpy(foundname, filename);
378 }
379
380
381 strcpy(fname, filename);
382 strcat(fname, ".nc");
383 if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0) {
384 DXMessage("opening file '%s'", fname);
385 goto file_ok;
386 }
387 if (ncerr == 0 && !foundfile) {
388 foundfile++;
389 foundname = (char *)DXAllocateLocalZero(strlen(fname)+1);
390 if (!foundname)
391 goto error;
392 strcpy(foundname, fname);
393 }
394
395 if (!datadir)
396 goto e1;
397
398 while (datadir) {
399
400 strcpy(fname, datadir);
401 if((cp = strchr(fname, PATH_SEP_CHAR)) != NULL)
402 *cp = '\0';
403 strcat(fname, "/");
404 strcat(fname, filename);
405 if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0) {
406 DXMessage("opening file '%s'", fname);
407 goto file_ok;
408 }
409 if (ncerr == 0 && !foundfile) {
410 foundfile++;
411 foundname = (char *)DXAllocateLocalZero(strlen(fname)+1);
412 if (!foundname)
413 goto error;
414 strcpy(foundname, fname);
415 }
416
417 strcat(fname, ".nc");
418 if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0) {
419 DXMessage("opening file '%s'", fname);
420 goto file_ok;
421 }
422 if (ncerr == 0 && !foundfile) {
423 foundfile++;
424 foundname = (char *)DXAllocateLocalZero(strlen(fname)+1);
425 if (!foundname)
426 goto error;
427 strcpy(foundname, fname);
428 }
429
430 datadir = strchr(datadir, PATH_SEP_CHAR);
431 if (datadir)
432 datadir++;
433 }
434
435 e1:
436 if (!foundfile)
437 DXSetError(ERROR_BAD_PARAMETER, "can't open file '%s'", filename);
438 else
439 DXSetError(ERROR_BAD_PARAMETER, "'%s' not a netCDF file", foundname);
440
441 error:
442 DXFree((Pointer)fname);
443 DXFree((Pointer)foundname);
444 return -1; /* bad filehandle */
445
446 }
447
448 file_ok:
449 DXFree((Pointer)fname);
450 DXFree((Pointer)foundname);
451 return cdfhandle;
452 }
453
454
455 /* close the file. when we start doing distributed things, here is
456 * where the socket connections should be closed cleanly.
457 */
458 static void
close_netcdf_file(int cdfhandle)459 close_netcdf_file(int cdfhandle)
460 {
461 ncclose(cdfhandle);
462 }
463
464
465 /* see if this netcdf file can be opened. don't set error, but return
466 * OK or ERROR.
467 */
468 ImportStatReturn
_dxfstat_netcdf_file(char * filename)469 _dxfstat_netcdf_file(char *filename)
470 {
471 int cdfhandle;
472 char *fname = NULL, *cp;
473 char *datadir = NULL;
474
475 /* netCDF library options: on error, don't abort and don't print */
476 ncopts = 0;
477
478 /* try name, name.cdf, dir/name and dir/name.cdf */
479 if((cdfhandle = ncopen(filename, NC_NOWRITE)) >= 0)
480 goto file_ok;
481
482 /* XTRA defined above:
483 * space for trailing /, .nc, trailing 0 and some slop
484 */
485
486 datadir = (char *)getenv("DXDATA");
487 if (!datadir)
488 goto error;
489
490 fname = (char *)DXAllocateLocalZero((datadir ? strlen(datadir) : 0) +
491 strlen(filename) + XTRA);
492 if (!fname)
493 goto error;
494
495 strcpy(fname, filename);
496 strcat(fname, ".nc");
497 if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0)
498 {
499 DXFree((Pointer)fname);
500 return IMPORT_STAT_NOT_FOUND;
501 }
502
503 while (datadir) {
504
505 strcpy(fname, datadir);
506 if((cp = strchr(fname, PATH_SEP_CHAR)) != NULL)
507 *cp = '\0';
508 strcat(fname, "/");
509 strcat(fname, filename);
510 if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0)
511 goto file_ok;
512
513 strcat(fname, ".nc");
514 if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0)
515 goto file_ok;
516
517 datadir = strchr(datadir, PATH_SEP_CHAR);
518 if (datadir)
519 datadir++;
520 }
521
522 error:
523 DXFree((Pointer)fname);
524 return IMPORT_STAT_ERROR;
525
526 file_ok:
527 DXFree((Pointer)fname);
528 ncclose(cdfhandle);
529 return IMPORT_STAT_FOUND;
530 }
531
532 /* find out how many netcdf variables match the spec, and construct an
533 * information block for each field. if the caller has given a specific
534 * variable or variable list, restrict it to those; if input list is null,
535 * count all fields and series attrs in the file.
536 * varlist must be null terminated.
537 */
538 static Varinfo
query_var(int cdfhandle,char ** varlist,int * starttime,int * endtime,int * deltatime)539 query_var(int cdfhandle, char **varlist, int *starttime, int *endtime,
540 int *deltatime)
541 {
542 char stringattr[MAXNAME], *cp, *s[MAXATTRSTR], **lp, *attrtext=NULL;
543 char *subv[2];
544 int ndims, nvars, ngatts, recdim;
545 int i, j, k, matched;
546 int count = 0;
547 int numspec = 0, *found = NULL;
548 double position;
549 Varinfo vp = NULL; /* root of tree, to be returned */
550 Varinfo vp1, vp2, vp3; /* temp node pointers */
551
552
553 /* count the number of netCDF variables in the file
554 */
555 if ((ncinquire(cdfhandle, &ndims, &nvars, &ngatts, &recdim) < 0)
556 || nvars <= 0) {
557 DXSetError(ERROR_MISSING_DATA, "no netCDF variables found in file");
558 return NULL;
559 }
560
561
562 /* count the number of fields specified by the user */
563 if (varlist != NULL && varlist[0] != NULL) {
564 for(lp = varlist; *lp; lp++)
565 numspec++;
566
567 if ((found = (int *)DXAllocateLocalZero(sizeof(int) * numspec)) == NULL)
568 return NULL;
569 }
570
571 /* check for any global series attributes.
572 */
573 for (k = 0; k < ngatts; k++) {
574 /* If a continue has been encountered in the previous iteratation */
575 if(attrtext) DXFree((Pointer) attrtext);
576
577 if(!(attrtext=getNattr(cdfhandle, NC_GLOBAL, k, SERIESATTRIB,
578 strlen(SERIESATTRIB), stringattr)))
579 continue;
580
581 j = MAXATTRSTR;
582 cp = parseit(attrtext, &j, s);
583 if(j <= 0) {
584 DXSetError(ERROR_DATA_INVALID, "bad attribute for series");
585 goto error;
586 }
587
588 matched = 0;
589 if(varlist == NULL || varlist[0] == NULL)
590 matched = 1;
591
592 else for(lp = varlist; *lp; lp++) {
593 if(strcmp(s[0], *lp))
594 continue;
595
596 matched = 1;
597 found[lp - varlist]++;
598 break;
599 }
600
601 if(!matched)
602 continue;
603
604 /* we are going to process this netCDF variable - add it to the tree
605 * we are building. if match_vp() returns non-null, it found a
606 * match, and has already added it as a compositefield member.
607 *
608 * vp = root of tree
609 * vp1 = new series members
610 * vp2 = new series parent node
611 * vp3 = temp node for finding end of next->next->next chain.
612 */
613 if (match_vp(vp, NC_GLOBAL, s, attrtext) != NULL)
614 continue;
615
616 /* new series object
617 */
618 vp2 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
619 if(!vp2)
620 goto error;
621 vp2->class = CLASS_SERIES;
622 strcpy(vp2->name, s[0]);
623 vp2->varid = INVALID_VARID;
624 vp2->startframe = starttime;
625 vp2->endframe = endtime;
626 vp2->deltaframe = deltatime;
627 vp2->recdim = INVALID_DIMID;
628 vp2->cdfid = cdfhandle;
629
630 /* multiple files */
631 if(s[1] && !strcmp(s[1], "files")) {
632 i = -1;
633 while(1) {
634 i++;
635 j = MAXATTRSTR;
636 cp = parseit(cp, &j, s);
637 if(j <= 0)
638 break;
639
640 /* if any of start, end, delta or current are set,
641 * restrict the input to only those in the list.
642 */
643 if (starttime && i < *starttime)
644 continue;
645
646 if (endtime && i > *endtime)
647 continue;
648
649 if (deltatime) {
650 if ((starttime && (i - *starttime) % *deltatime)
651 || (!starttime && i % *deltatime))
652 continue;
653 }
654
655 subv[1] = NULL;
656 switch(j) {
657 case 1:
658 /* only filename, no fieldname or series pos */
659 subv[0] = vp2->name;
660 position = i;
661 break;
662
663 case 2:
664 /* filename plus either fieldname or position */
665 if(isdigit(s[1][0])) {
666 subv[0] = vp2->name;
667 position = atof(s[1]);
668 } else {
669 subv[0] = s[1];
670 position = i;
671 }
672 break;
673
674 case 3:
675 /* filename, fieldname, position */
676 subv[0] = s[1];
677 position = atof(s[2]);
678 break;
679
680 default:
681 /* more than 3 parms? */
682 DXSetError(ERROR_BAD_PARAMETER,
683 "bad attribute for series '%s'",
684 vp2->name);
685 goto error;
686 }
687
688 /* new series member */
689 vp1 = (Varinfo)Object_Import(s[0], subv, starttime, endtime,
690 deltatime, IMPORT_VARINFO);
691 if(!vp1)
692 goto error;
693
694 strcpy(vp1->cdfname, s[0]);
695 vp1->cdfid = -1;
696 vp1->position = position;
697
698 /* first series member? make it the first child */
699 if (vp2->child == NULL)
700 vp2->child = vp1;
701
702 else { /* add to end of series existing list */
703 vp3 = vp2->child;
704 while (vp3->next != NULL)
705 vp3 = vp3->next;
706
707 vp3->next = vp1;
708 }
709 count++;
710 }
711
712 } else {
713 /* series using variables in this file */
714 i = -1;
715 while(1) {
716 i++;
717 j = MAXATTRSTR;
718 cp = parseit(cp, &j, s);
719 if(j <= 0)
720 break;
721
722 /* if any of start, end, delta or current are set,
723 * restrict the input to only those in the list.
724 */
725 if (starttime && i < *starttime)
726 continue;
727
728 if (endtime && i > *endtime)
729 continue;
730
731 if (deltatime) {
732 if ((starttime && (i - *starttime) % *deltatime)
733 || (!starttime && i % *deltatime))
734 continue;
735 }
736
737 /* new series member */
738 vp1 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
739 if(!vp1)
740 goto error;
741 vp1->class = CLASS_FIELD;
742 vp1->varid = ncvarid(cdfhandle, s[0]);
743 if(vp1->varid < 0) {
744 DXSetError(ERROR_BAD_PARAMETER,
745 "field %s not found in series %s",
746 s[0], vp2->name);
747 DXFree((Pointer)vp1);
748 goto error;
749 }
750 vp1->cdfid = cdfhandle;
751 vp1->recdim = INVALID_DIMID;
752 strcpy(vp1->name, s[0]);
753
754 if(s[1])
755 vp1->position = atof(s[1]);
756 else
757 vp1->position = i;
758
759 /* first series member? make it the first child */
760 if (vp2->child == NULL)
761 vp2->child = vp1;
762
763 else { /* add to end of series existing list */
764 vp3 = vp2->child;
765 while (vp3->next != NULL)
766 vp3 = vp3->next;
767
768 vp3->next = vp1;
769 }
770 count++;
771 }
772 }
773
774 /* vp is root of tree.
775 * vp1 is new group object if needed.
776 * vp2 is series object constructed during this pass
777 * vp3 is temp to add object to end of next->next->next list.
778 */
779
780 /* if first object, make it the root */
781 if(!vp)
782 vp = vp2;
783
784 else if(vp->class != CLASS_GROUP) { /* new group needed */
785 vp1 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
786 if(!vp1)
787 goto error;
788 vp1->class = CLASS_GROUP;
789 vp1->varid = INVALID_VARID;
790 vp1->recdim = INVALID_DIMID;
791 vp1->cdfid = cdfhandle;
792 vp1->child = vp;
793 vp->next = vp2;
794 vp = vp1;
795
796 } else { /* group already exists. add to end of list */
797 vp3 = vp->child;
798 while (vp3->next != NULL)
799 vp3 = vp3->next;
800 vp3->next = vp2;
801 }
802 count++;
803 DXFree((Pointer) attrtext);
804 attrtext=NULL;
805 }
806 if(attrtext) {
807 DXFree((Pointer) attrtext);
808 attrtext=NULL;
809 }
810
811
812 /* check each variable looking for 'field' attribute.
813 */
814 for(i=0; i<nvars; i++) {
815
816 if(!getattr(cdfhandle, i, FIELDATTRIB, stringattr, MAXNAME))
817 continue;
818
819 j = MAXATTRSTR;
820 cp = parseit(stringattr, &j, s);
821 if(j <= 0)
822 continue;
823
824 matched = 0;
825 if(varlist == NULL || varlist[0] == NULL)
826 matched = 1;
827
828 else for(lp = varlist; *lp; lp++) {
829 if(strcmp(s[0], *lp))
830 continue;
831
832 matched = 1;
833 found[lp - varlist]++;
834 break;
835 }
836
837 if(!matched)
838 continue;
839
840 /* if field name matches an existing one, match_vp returns non-null,
841 * and has already added it as a member of a composite field.
842 */
843 if (match_vp(vp, i, s, stringattr))
844 continue;
845
846 /* new field/implicit series */
847 vp2 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
848 if(!vp2)
849 goto error;
850
851 /* check here for series keyword */
852 if(j > 2 && !strcmp(s[2], "series")) {
853 vp2->class = CLASS_SERIES;
854 strcpy(vp2->name, s[0]);
855 vp2->varid = i;
856 vp2->cdfid = cdfhandle;
857 vp2->startframe = starttime;
858 vp2->endframe = endtime;
859 vp2->deltaframe = deltatime;
860 vp2->recdim = recdim;
861 } else {
862 vp2->class = CLASS_FIELD;
863 strcpy(vp2->name, s[0]);
864 vp2->varid = i;
865 vp2->cdfid = cdfhandle;
866 vp2->recdim = INVALID_DIMID;
867 }
868
869 /* vp is root of tree.
870 * vp1 is new group node if needed.
871 * vp2 is new field/series
872 * vp3 is temp to add object to end of next->next->next list.
873 */
874
875 /* if first object, make it root */
876 if(!vp)
877 vp = vp2;
878
879 else if(vp->class != CLASS_GROUP) { /* create new group */
880 vp1 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
881 if(!vp1)
882 goto error;
883 vp1->class = CLASS_GROUP;
884 vp1->varid = INVALID_VARID;
885 vp1->recdim = INVALID_DIMID;
886 vp1->cdfid = cdfhandle;
887 vp1->child = vp;
888 vp->next = vp2;
889 vp = vp1;
890
891 } else { /* group already exists. add object to end */
892 vp3 = vp->child;
893 while (vp3->next != NULL)
894 vp3 = vp3->next;
895
896 vp3->next = vp2;
897 }
898 count++;
899 }
900
901
902 /* if no field attributes found, import netCDF variables as reg grids */
903 if (count <= 0) {
904 if (numspec == 0)
905 DXWarning("no 'field' attributes; importing all netCDF variables");
906 else
907 DXWarning("no 'field' attributes matched list; trying netCDF variable names");
908
909 /* treat all variables as having a 'field' attribute.
910 */
911 for(i=0; i<nvars; i++) {
912
913 if(!variablename(cdfhandle, i, stringattr))
914 goto error;
915
916 matched = 0;
917 if(varlist == NULL || varlist[0] == NULL)
918 matched = 1;
919
920 else for(lp = varlist; *lp; lp++) {
921 if(strcmp(stringattr, *lp))
922 continue;
923
924 matched = 1;
925 found[lp - varlist]++;
926 break;
927 }
928
929 if (!strcmp(stringattr,"")) /* dimension=0 so skip this variable */
930 matched = 0;
931
932 if(!matched)
933 continue;
934
935
936 /* this can be simpler than above, since netCDF variable names
937 * must be unique. therefore, we can't have identical names
938 * so there can't be composite fields. also, we don't recognize
939 * series (although if recdim is used, we could, i suppose).
940 */
941 vp2 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
942 if(!vp2)
943 goto error;
944
945 vp2->class = CLASS_FIELD;
946 strcpy(vp2->name, stringattr);
947 vp2->varid = i;
948 vp2->cdfid = cdfhandle;
949 vp2->recdim = INVALID_DIMID;
950
951 /* vp is root of tree.
952 * vp1 is new group node if needed.
953 * vp2 is new node to be added.
954 * vp3 is temp to add node at end of next->next->next list
955 */
956
957 /* first object */
958 if(!vp)
959 vp = vp2;
960
961 else if(vp->class != CLASS_GROUP) { /* create a group */
962 vp1 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
963 if(!vp1)
964 goto error;
965 vp1->class = CLASS_GROUP;
966 vp1->varid = INVALID_VARID;
967 vp1->recdim = INVALID_DIMID;
968 vp1->cdfid = cdfhandle;
969 vp1->child = vp;
970 vp->next = vp2;
971 vp = vp1;
972
973 } else { /* group already exists. add to end of list */
974 vp3 = vp->child;
975 while (vp3->next)
976 vp3 = vp3->next;
977 vp3->next = vp2;
978 }
979 count++;
980 }
981
982 }
983
984 /* if STILL 0, user specified list and didn't match variables */
985 if (count <= 0) {
986 DXSetError(ERROR_BAD_PARAMETER, "no matching fields found in file");
987 goto error;
988 }
989
990 /* warn about field names which didn't match anything in the file */
991 if (found) {
992 for (i=0; i<numspec; i++)
993 if (found[i] == 0)
994 DXWarning("field '%s' not found in file", varlist[i]);
995
996 DXFree((Pointer)found);
997 }
998
999
1000 return vp;
1001
1002 error:
1003 if(attrtext)
1004 DXFree((Pointer)attrtext);
1005 if (found)
1006 DXFree((Pointer)found);
1007 if(vp)
1008 vp_delete(vp);
1009 return NULL;
1010 }
1011
1012
1013
1014 /* if this routine finds a match, it returns non-null. if the field
1015 * isn't already found, it returns null.
1016 */
1017 static Varinfo
match_vp(Varinfo vp,int varid,char ** s,char * stringattr)1018 match_vp(Varinfo vp, int varid, char **s, char *stringattr)
1019 {
1020 Varinfo vp1, vp2, vp3;
1021
1022
1023 /* search down through vp, looking for name matches. if hit,
1024 * this becomes a composite field.
1025 * (if varid already in the file, skip the field because it's there as
1026 * part of a series?)
1027 */
1028 for(vp1 = vp; vp1; vp1 = vp1->next) {
1029 #if 0
1030 /* already in tree, skip it this time? */
1031 if(vp1->varid == varid)
1032 return vp;
1033 #endif
1034
1035 if(!strcmp(vp1->name, s[0])) {
1036 /* make a composite field node - this name matches.
1037 * vp1 is original node. vp2 is created and the vp1 info is
1038 * copied into it. then vp1 is changed to a composite field node.
1039 */
1040 if(!vp1->child) {
1041 /* test here: class should == FIELD */
1042 vp2 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
1043 if(!vp2)
1044 return NULL;
1045 vp2->class = vp1->class;
1046 vp2->varid = vp1->varid;
1047 vp2->cdfid = vp1->cdfid;
1048 vp2->recdim = vp1->recdim;
1049 vp1->class = CLASS_COMPOSITEFIELD;
1050 vp1->varid = INVALID_VARID;
1051 vp1->recdim = INVALID_DIMID;
1052 vp1->child = vp2;
1053 }
1054
1055 /* new node for this field */
1056 vp2 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
1057 if(!vp2)
1058 return NULL;
1059 vp2->class = CLASS_FIELD;
1060 vp2->varid = varid;
1061 vp2->recdim = INVALID_DIMID;
1062 vp2->cdfid = vp1->cdfid;
1063
1064 /* add to end of composite field list */
1065 vp3 = vp1->child;
1066 while (vp3->next != NULL)
1067 vp3 = vp3->next;
1068
1069 vp3->next = vp2;
1070 return vp;
1071 }
1072
1073 if(vp1->child)
1074 return match_vp(vp1->child, varid, s, stringattr);
1075
1076
1077 }
1078
1079 return NULL;
1080 }
1081
1082
1083 /* build a string which describes a field, put it into a string object
1084 * and return it.
1085 */
1086 static Object
query_field(int cdfhandle,Varinfo vp)1087 query_field(int cdfhandle, Varinfo vp)
1088 {
1089 char stringattr[MAXNAME], *cp, *s[MAXATTRSTR];
1090 int i, rank;
1091 Varinfo nvp;
1092
1093 Object o = NULL;
1094 Object newo = NULL;
1095 char *sp1 = NULL;
1096
1097
1098 switch(vp->class) {
1099 case CLASS_GROUP:
1100 if(!vp->child)
1101 DXErrorReturn(ERROR_INTERNAL, "inconsistant data structs");
1102
1103 for(nvp = vp->child; nvp; nvp = nvp->next) {
1104
1105 newo = query_field(cdfhandle, nvp);
1106 if(!newo) {
1107 DXDelete(o);
1108 return NULL;
1109 }
1110 if(!o) {
1111 o = STR("Group: ");
1112 if(!o) {
1113 DXDelete(newo);
1114 return NULL;
1115 }
1116 }
1117 sp1 = DXAllocate(strlen(DXGetString((String)o)) +
1118 strlen(DXGetString((String)newo)));
1119 strcpy(sp1, DXGetString((String)o));
1120 strcat(sp1, DXGetString((String)newo));
1121 strcat(sp1, ";");
1122 DXDelete(o);
1123 DXDelete(newo);
1124 o = STR(sp1);
1125 }
1126 break;
1127
1128 case CLASS_SERIES:
1129 if(!vp->child) {
1130 /* series object all in one N+1 D netcdf variable */
1131 o = STR("Series: ");
1132
1133 newo = query_field(cdfhandle, vp);
1134 if(!newo) {
1135 DXDelete(o);
1136 return NULL;
1137 }
1138
1139 sp1 = DXAllocate(strlen(DXGetString((String)o)) +
1140 strlen(DXGetString((String)newo)));
1141 strcpy(sp1, DXGetString((String)o));
1142 strcat(sp1, DXGetString((String)newo));
1143 DXDelete(o);
1144 DXDelete(newo);
1145 o = STR(sp1);
1146 break;
1147 }
1148
1149 for(nvp = vp->child; nvp; nvp = nvp->next) {
1150
1151 newo = query_field(cdfhandle, nvp);
1152 if(!newo) {
1153 DXDelete(o);
1154 return NULL;
1155 }
1156 if(!o) {
1157 o = STR("Series: ");
1158 if(!o) {
1159 DXDelete(newo);
1160 return NULL;
1161 }
1162 }
1163 sp1 = DXAllocate(strlen(DXGetString((String)o)) +
1164 strlen(DXGetString((String)newo)));
1165 strcpy(sp1, DXGetString((String)o));
1166 strcat(sp1, DXGetString((String)newo));
1167 sprintf(sp1+strlen(sp1), ", position %g;", nvp->position);
1168 DXDelete(o);
1169 DXDelete(newo);
1170 o = STR(sp1);
1171 }
1172 break;
1173
1174 case CLASS_COMPOSITEFIELD:
1175 if(!vp->child)
1176 DXErrorReturn(ERROR_INTERNAL, "inconsistant data structs");
1177
1178 for(nvp = vp->child; nvp; nvp = nvp->next) {
1179
1180 newo = query_field(cdfhandle, nvp);
1181 if(!newo) {
1182 DXDelete(o);
1183 return NULL;
1184 }
1185 if(!o) {
1186 o = STR("Composite Field: ");
1187 if(!o) {
1188 DXDelete(newo);
1189 return NULL;
1190 }
1191 }
1192 sp1 = DXAllocate(strlen(DXGetString((String)o)) +
1193 strlen(DXGetString((String)newo)));
1194 strcpy(sp1, DXGetString((String)o));
1195 strcat(sp1, DXGetString((String)newo));
1196 strcat(sp1, ";");
1197 DXDelete(o);
1198 DXDelete(newo);
1199 o = STR(sp1);
1200 }
1201 break;
1202
1203 case CLASS_FIELD:
1204 if(vp->cdfid < 0 || vp->cdfid != cdfhandle) {
1205 if(vp->cdfname[0] == '\0')
1206 return NULL;
1207 vp->cdfid = open_netcdf_file(vp->cdfname);
1208 if(vp->cdfid < 0)
1209 return NULL;
1210 }
1211 getattr(vp->cdfid, vp->varid, FIELDATTRIB, stringattr, MAXNAME);
1212
1213 cp = stringattr;
1214 i = MAXATTRSTR;
1215 cp = parseit(cp, &i, s);
1216
1217 if(i <= 0)
1218 return NULL;
1219
1220 /* should already be set in varinfo --
1221 * strcpy(varname, s[0]);
1222 */
1223 rank = setrank(s[1]);
1224 /* etc etc */
1225
1226
1227 #if REALCODE
1228 sp1 = (char *)DXAllocate(strlen(vp->name) + sizeof("name:") + 2);
1229 strcpy(sp1, "name:");
1230 strcat(sp1, vp->name);
1231 strcat(sp1, ";");
1232 #else
1233 /* misc whitespace added for testing */
1234 sp1 = (char *)DXAllocate(1024);
1235 strcpy(sp1, "name:");
1236 strcat(sp1, vp->name);
1237 strcat(sp1, ";contype:tetras;");
1238 strcat(sp1, "datatype: float;\tdatacat:real;");
1239 strcat(sp1, "datarank:1; datashape:3;");
1240 strcat(sp1, " datacount:37000;\n");
1241 strcat(sp1, "metahistory:original data;");
1242 strcat(sp1, "metadesc:3-D seismic data from mars;");
1243 #endif /* !REALCODE */
1244
1245 o = STR(sp1);
1246 DXFree(sp1);
1247 if(cdfhandle != vp->cdfid) {
1248 close_netcdf_file(vp->cdfid);
1249 vp->cdfid = -1;
1250 }
1251 break;
1252
1253 default:
1254 o = NULL; /* ??? */
1255 break;
1256 }
1257
1258 return o;
1259 }
1260
1261
1262
1263
1264 /* build an object based on the varinfo structures, and return it
1265 */
1266 static Object
import_field(int cdfhandle,Varinfo vp)1267 import_field(int cdfhandle, Varinfo vp)
1268 {
1269 int i = 0;
1270 Varinfo nvp;
1271
1272 Object o = NULL;
1273 Object newo = NULL;
1274
1275
1276 switch(vp->class) {
1277 case CLASS_GROUP:
1278 if(!vp->child)
1279 DXErrorReturn(ERROR_INTERNAL, "inconsistant data structs");
1280
1281 for(nvp = vp->child; nvp; nvp = nvp->next) {
1282
1283 newo = import_field(cdfhandle, nvp);
1284 if(!newo) {
1285 DXDelete(o);
1286 return NULL;
1287 }
1288 if(!o) {
1289 o = (Object)DXNewGroup();
1290 if(!o) {
1291 DXDelete(newo);
1292 return NULL;
1293 }
1294 }
1295 DXSetMember((Group)o, nvp->name, newo);
1296 }
1297 break;
1298
1299 case CLASS_SERIES:
1300 if(!vp->child) {
1301 /* series bundled as 1 netcdf variable, using unlimited dim */
1302 o = build_series(cdfhandle, vp);
1303 break;
1304 }
1305
1306 for(nvp = vp->child; nvp; nvp = nvp->next) {
1307
1308 newo = import_field(cdfhandle, nvp);
1309 if(!newo) {
1310 DXDelete(o);
1311 return NULL;
1312 }
1313 if(!o) {
1314 o = (Object)DXNewSeries();
1315 if(!o) {
1316 DXDelete(newo);
1317 return NULL;
1318 }
1319 i = 0;
1320 }
1321 DXSetSeriesMember((Series)o, i++, nvp->position, newo);
1322 }
1323 break;
1324
1325 case CLASS_COMPOSITEFIELD:
1326 if(!vp->child)
1327 DXErrorReturn(ERROR_INTERNAL, "inconsistant data structs");
1328
1329 for(nvp = vp->child; nvp; nvp = nvp->next) {
1330
1331 newo = import_field(cdfhandle, nvp);
1332 if(!newo) {
1333 DXDelete(o);
1334 return NULL;
1335 }
1336 if(!o) {
1337 o = (Object)DXNewCompositeField();
1338 if(!o) {
1339 DXDelete(newo);
1340 return NULL;
1341 }
1342 }
1343 DXSetMember((Group)o, NULL, newo);
1344 }
1345 break;
1346
1347 case CLASS_FIELD:
1348
1349 if(vp->cdfid < 0 || vp->cdfid != cdfhandle) {
1350 if(vp->cdfname[0] == '\0')
1351 return NULL;
1352 vp->cdfid = open_netcdf_file(vp->cdfname);
1353 if(vp->cdfid < 0)
1354 return NULL;
1355 }
1356
1357 o = build_field(vp->cdfid, vp->varid);
1358
1359 if(cdfhandle != vp->cdfid) {
1360 close_netcdf_file(vp->cdfid);
1361 vp->cdfid = -1;
1362 }
1363
1364 if (o && vp->name)
1365 DXSetAttribute(o, "name", (Object)DXNewString(vp->name));
1366
1367 break;
1368
1369 default:
1370 DXSetError(ERROR_INTERNAL, "bad class in varinfo struct");
1371 DXDelete(o);
1372 return NULL;
1373 }
1374
1375 return o;
1376 }
1377
1378
1379 /* delete a varinfo tree
1380 */
vp_delete(Varinfo vp)1381 static void vp_delete(Varinfo vp)
1382 {
1383 Varinfo vp1, vp2;
1384
1385 for(vp2 = vp; vp2; ) {
1386 if(vp2->child)
1387 vp_delete(vp2->child);
1388
1389 vp1 = vp2;
1390 vp2 = vp2->next;
1391 DXFree((Pointer)vp1);
1392
1393 }
1394 }
1395
1396
1397 /* debug - print a varinfo tree
1398 */
vp_print(Varinfo vp)1399 static void vp_print(Varinfo vp)
1400 {
1401 Varinfo vp1, vp2;
1402
1403 for(vp2 = vp; vp2; ) {
1404 if(vp2->child)
1405 vp_print(vp2->child);
1406
1407 vp1 = vp2;
1408 vp2 = vp2->next;
1409 printf("%08x: class = %d, varid = %d, name = %s\n",
1410 (unsigned int) vp1, vp1->class, vp1->varid, vp1->name);
1411 printf(" next = %08x, child = %08x\n", (unsigned int) vp1->next, (unsigned int) vp1->child);
1412
1413 }
1414 }
1415
1416 /*
1417 * use netCDF routines to retrieve the named attribute, verify it is a
1418 * string (char), and null terminate it.
1419 */
1420 static char *
getattr(int hand,int varid,char * attrname,char * stringattr,int maxlen)1421 getattr(int hand, int varid, char *attrname, char *stringattr,int maxlen)
1422 {
1423 nc_type datatype;
1424 int len;
1425 char *tmpattr;
1426
1427 if(ncattinq(hand, varid, attrname, &datatype, &len) < 0)
1428 return NULL;
1429
1430 if(datatype != NC_CHAR || len <= 0)
1431 return NULL;
1432
1433 tmpattr=(char *)DXAllocate(len+1);
1434 ncattget(hand, varid, attrname, tmpattr);
1435 tmpattr[len] = '\0';
1436
1437 strncpy(stringattr,tmpattr,maxlen);
1438 stringattr[maxlen-1]='\0';
1439
1440 if(strncmp(stringattr,tmpattr,maxlen))
1441 DXWarning("String attribute truncated: `%s' -> `%s'",
1442 tmpattr,stringattr);
1443
1444 DXFree((Pointer) tmpattr);
1445 return stringattr;
1446 }
1447 /*
1448 * use netCDF routines to retrieve the Nth attribute of a variable and
1449 * compare the first len characters for a match. if matched, get the
1450 * complete value of the attribute, verify it is a string (char), and
1451 * null terminate it.
1452 * The attribute is returned as a newly allocated string. It is the
1453 * caller's responsibility the deallocate it when it is no longer
1454 * needed.
1455 */
1456 static char *
getNattr(int hand,int varid,int n,char * attrname,int len,char * stringattr)1457 getNattr(int hand, int varid, int n, char *attrname, int len, char *stringattr)
1458 {
1459 nc_type datatype;
1460 int i, alen;
1461 char *attrtext=NULL;
1462
1463 for(i = n; ; i++) {
1464 if(ncattname(hand, varid, i, stringattr) < 0)
1465 return NULL;
1466
1467 if(strncmp(stringattr, attrname, len))
1468 continue;
1469
1470 /* reset attribute name to the one retrieved from netcdf */
1471 attrname = stringattr;
1472 break;
1473 }
1474
1475 if(ncattinq(hand, varid, attrname, &datatype, &alen) < 0)
1476 return NULL;
1477
1478 if(datatype != NC_CHAR || alen <= 0)
1479 return NULL;
1480
1481 /* does this work? we are actually using the same buffer for the
1482 * input attribute name and for the returned attribute value.
1483 ncattget(hand, varid, attrname, stringattr);
1484 */
1485 /* No it does not. Make sure the target string has sufficient length */
1486 attrtext=DXAllocate(alen+1);
1487 ncattget(hand, varid, attrname, attrtext);
1488 attrtext[alen] = '\0';
1489
1490 return attrtext;
1491 }
1492
1493 /*
1494 * test just to see if there is such a named attribute for this variable.
1495 */
1496 static char *
isattr(int hand,int varid,char * attrname)1497 isattr(int hand, int varid, char *attrname)
1498 {
1499 nc_type datatype;
1500 int len;
1501
1502 if(ncattinq(hand, varid, attrname, &datatype, &len) < 0)
1503 return NULL;
1504
1505 return attrname;
1506
1507 }
1508
1509 /*
1510 * see if this variable has any ADX attributes, and use them to set
1511 * the component attributes.
1512 */
setattr(Arrayinfo ap,Field f,char * compname)1513 static Error setattr(Arrayinfo ap, Field f, char *compname)
1514 {
1515 char *cp, *s[MAXATTRSTR];
1516 char savename[MAXCOMPNAME];
1517 int j;
1518
1519 /* save the component name now, because it may get overwritten
1520 * further down
1521 */
1522 strcpy(savename, compname);
1523
1524 /* see if there is an 'attribute' attribute on this variable
1525 */
1526 if(!getattr(ap->cdfhandle, ap->varid, ATTRATTRIB, ap->stringattr,MAXNAME)
1527 && !getattr(ap->cdfhandle, ap->varid, OATTRATTRIB, ap->stringattr,MAXNAME))
1528 goto done;
1529
1530
1531 cp = ap->stringattr;
1532 while(1) {
1533 j = MAXATTRSTR;
1534 cp = parseit(cp, &j, s);
1535
1536 /* field:attribute = "a, b"; attribute required. exactly two
1537 * parameters must be present.
1538 * a = attribute to set.
1539 * b = string to set it to.
1540 */
1541 if(j <= 0)
1542 break;
1543 if(j != 2) {
1544 DXSetError(ERROR_BAD_PARAMETER,
1545 "bad attribute value on component '%s'", savename);
1546 return ERROR;
1547 }
1548
1549
1550 /* and add it to the field with the given component name */
1551 if(!DXSetComponentAttribute(f, savename, s[0], STR(s[1]))) {
1552 DXSetError(ERROR_BAD_PARAMETER,
1553 "bad attribute value: '%s' on component '%s'",
1554 s[0], savename);
1555 return ERROR;
1556
1557 }
1558 }
1559
1560 done:
1561 /* look for non DX specific attributes and make them component
1562 * attributes
1563 */
1564 setuserattr(ap,f,savename);
1565
1566 return OK;
1567 }
1568
1569 /* go through each attribute and for each one that is not a
1570 * DX netcdf attribute, create a DX object and make it a
1571 * component attribute
1572 */
setuserattr(Arrayinfo ap,Field f,char * compname)1573 static Error setuserattr(Arrayinfo ap, Field f, char *compname)
1574 {
1575 int i, attr_count=0;
1576 int alen;
1577 nc_type datatype;
1578 Type type;
1579 Array a;
1580 char *attr_string;
1581 void *attr_value;
1582
1583 while(ncattname(ap->cdfhandle,ap->varid,attr_count,ap->stringattr) !=-1) {
1584 attr_count++;
1585 /* is this a DX specific attribute, if yes ignore */
1586 for (i=0; i<MAX_DXATTRIBUTES; i++){
1587 if (!strcmp(dxattributes[i],ap->stringattr))
1588 break;
1589 }
1590 if (i < MAX_DXATTRIBUTES) /* skip the dx attributes */
1591 continue;
1592
1593 if(ncattinq(ap->cdfhandle, ap->varid, ap->stringattr, &datatype, &alen) < 0)
1594 return ERROR;
1595
1596 /* convert from netCDF data flags to SVS data flags */
1597 switch(datatype) {
1598 case NC_CHAR: type = TYPE_STRING; break;
1599 case NC_SHORT: type = TYPE_SHORT; break;
1600 case NC_LONG: type = TYPE_INT; break;
1601 case NC_FLOAT: type = TYPE_FLOAT; break;
1602 case NC_DOUBLE: type = TYPE_DOUBLE; break;
1603 default: type = TYPE_UBYTE; break;
1604 /* we need a TYPE_BYTE */
1605 }
1606
1607 if (type == TYPE_STRING){
1608 attr_string = (char *)DXAllocate(alen+1);
1609 if (ncattget(ap->cdfhandle, ap->varid,ap->stringattr,attr_string) < 0)
1610 return ERROR;
1611 attr_string[alen] = '\0';
1612 DXSetComponentAttribute(f, compname, ap->stringattr, STR(attr_string));
1613 DXFree((Pointer)attr_string);
1614 }
1615 else{
1616 a = DXNewArray(type,CATEGORY_REAL,0);
1617 a = DXAddArrayData(a,0,alen,NULL);
1618 attr_value = DXGetArrayData(a);
1619 if (ncattget(ap->cdfhandle, ap->varid,ap->stringattr,attr_value) < 0)
1620 return ERROR;
1621 DXSetComponentAttribute(f,compname, ap->stringattr, (Object)a);
1622 }
1623 }
1624 return OK;
1625 }
1626
1627 /* go through each attribute and for each one that is not a
1628 * DX netcdf attribute, create a DX object and make it a
1629 * component attribute
1630 */
getglobalattr(Object o,Varinfo vp)1631 static Error getglobalattr(Object o, Varinfo vp)
1632 {
1633 int attr_count=0;
1634 int alen;
1635 nc_type datatype;
1636 Type type;
1637 Array a;
1638 char stringattr[MAXNAME];
1639 char *attr_string;
1640 void *attr_value;
1641
1642 if( !o ) {
1643 if (DXGetError() == ERROR_NONE)
1644 DXErrorReturn(ERROR_INTERNAL,"getglobalattr called without a proper object");
1645 }
1646
1647 while(ncattname(vp->cdfid,NC_GLOBAL,attr_count,stringattr) !=-1) {
1648 attr_count++;
1649 /* is this a DX specific attribute, if yes ignore */
1650 if (!strcmp(SERIESATTRIB,stringattr))
1651 continue;
1652
1653 if(ncattinq(vp->cdfid, NC_GLOBAL, stringattr, &datatype, &alen) < 0)
1654 return ERROR;
1655
1656 /* convert from netCDF data flags to SVS data flags */
1657 switch(datatype) {
1658 case NC_CHAR: type = TYPE_STRING; break;
1659 case NC_SHORT: type = TYPE_SHORT; break;
1660 case NC_LONG: type = TYPE_INT; break;
1661 case NC_FLOAT: type = TYPE_FLOAT; break;
1662 case NC_DOUBLE: type = TYPE_DOUBLE; break;
1663 default: type = TYPE_UBYTE; break;
1664 /* we need a TYPE_BYTE */
1665 }
1666
1667 if (type == TYPE_STRING){
1668 attr_string = (char *)DXAllocate(alen+1);
1669 if (ncattget(vp->cdfid, NC_GLOBAL,stringattr,attr_string) < 0)
1670 return ERROR;
1671 attr_string[alen] = '\0';
1672 DXSetAttribute(o, stringattr, STR(attr_string));
1673 DXFree((Pointer)attr_string);
1674 }
1675 else{
1676 a = DXNewArray(type,CATEGORY_REAL,0);
1677 a = DXAddArrayData(a,0,alen,NULL);
1678 attr_value = DXGetArrayData(a);
1679 if (ncattget(vp->cdfid, NC_GLOBAL,stringattr,attr_value) < 0)
1680 return ERROR;
1681 DXSetAttribute(o, stringattr, (Object)a);
1682 }
1683 }
1684 return OK;
1685 }
1686
1687 /* this is where a single netcdf variable is an entire series, with the
1688 * unlimited record dimension interpreted as the series value, and the
1689 * data as numdims-1.
1690 */
build_series(int hand,Varinfo vp)1691 static Object build_series(int hand, Varinfo vp)
1692 {
1693 int arraytype = I_NONE;
1694 int series = 0, varseries;
1695 int nopos = 0, nocon = 0;
1696 struct arrayinfo a, *ap = &a;
1697 char *cp, *s[MAXATTRSTR];
1698 int i, j, k;
1699 int varid = vp->varid;
1700 int recdim = vp->recdim;
1701 int nterms;
1702 float value, *valuelist = NULL;
1703 Field f = NULL;
1704 Object indata, arr, terms[MAXRANK], *t = NULL;
1705 Object eltype = NULL;
1706 Object o = NULL;
1707
1708
1709 /* set up the Arrayinfo struct
1710 */
1711 ap->cdfhandle = hand;
1712 ap->varid = varid;
1713 ap->recdim = recdim;
1714 ap->vp = vp;
1715
1716
1717 /* get the attribute value.
1718 */
1719 if(!getattr(hand, varid, FIELDATTRIB, ap->stringattr,MAXNAME))
1720 return NULL;
1721
1722 cp = ap->stringattr;
1723 j = MAXATTRSTR;
1724 cp = parseit(cp, &j, s);
1725 if(j <= 0)
1726 return NULL;
1727
1728
1729 if (!check_serieslength(ap))
1730 return NULL;
1731
1732 series = get_serieslength(ap);
1733 if(series <= 0)
1734 DXErrorReturn(ERROR_DATA_INVALID, "no members in series dimension");
1735
1736
1737 o = (Object)DXNewSeries();
1738
1739
1740 /*
1741 * data component
1742 */
1743
1744 /* decide if it's scalar, vector or matrix data */
1745 ap->arrayrank = setrank(s[1]);
1746
1747 varseries = is_seriesvariable(ap->cdfhandle, ap->varid, ap->recdim);
1748 ap->data_isrec = varseries;
1749
1750 /* this routine reads a netCDF variable into an Array.
1751 * in this case, it's reading in the data component.
1752 */
1753 if(varseries) {
1754 indata = build_data(ap, recdim);
1755 if(!get_seriesvalue(ap, &valuelist))
1756 goto error;
1757 } else
1758 indata = build_data(ap, INVALID_DIMID);
1759
1760 if(!indata)
1761 goto error;
1762
1763 for(i=0; i<series; i++) {
1764 f = DXNewField();
1765 if(!f)
1766 goto error;
1767
1768 arr = varseries ? DXGetSeriesMember((Series)indata, i, &value)
1769 : indata;
1770
1771 if(!DXSetComponentValue(f, DATCOMPNAME, arr))
1772 goto error;
1773
1774 /* look here for more attributes on the data component - to set
1775 * data component attributes.
1776 */
1777 if(!setattr(ap, f, DATCOMPNAME))
1778 goto error;
1779
1780
1781 if(varseries && valuelist)
1782 value = valuelist[i];
1783 #if 0
1784 else
1785 value = i;
1786 #endif
1787
1788 o = (Object)DXSetSeriesMember((Series)o, i, value, (Object)f);
1789
1790 }
1791
1792 /* if the data was a series-in-a-variable, delete the temporary group
1793 * holding the data arrays.
1794 */
1795 if(varseries) {
1796 DXDelete(indata);
1797 DXFree((Pointer)valuelist);
1798 }
1799
1800 /*
1801 * positions component
1802 */
1803
1804 ap->arrayrank = 1; /* unless they explicitly say 'scalar' */
1805 varseries = 0;
1806
1807 /* look for a positions (old points) component. if the data variable
1808 * doesn't have a 'positions' attribute, assume a regularly spaced grid.
1809 */
1810 if(!getattr(hand, varid, GEOMATTRIB, ap->stringattr,MAXNAME)
1811 && !getattr(hand, varid, OGEOMATTRIB, ap->stringattr,MAXNAME)) {
1812 nterms = 1;
1813 indata = build_poscon(ap, I_REGULAR_P, 0);
1814 terms[0] = indata;
1815
1816 } else {
1817 /* else they gave us a geometry. parse it.
1818 */
1819
1820 if(isattr(hand, varid, OGEOMATTRIB))
1821 DXWarning("'geometry' attribute obsolete, use 'positions' instead");
1822
1823 cp = ap->stringattr;
1824 nterms = 0;
1825 while(1) {
1826 j = MAXATTRSTR;
1827 cp = parseit(cp, &j, s);
1828 if(j <= 0)
1829 break;
1830
1831 /* positions are vectors unless specifically told scalar */
1832 if((j > 1 && !strcmp("scalar", s[1]))
1833 ||(j > 2 && !strcmp("scalar", s[2])))
1834 ap->arrayrank = 0;
1835
1836 if(!strcmp("none", s[0])) {
1837
1838 /* no positions - should this be ok? */
1839 nopos++;
1840
1841 } else if(!strcmp("regular", s[0])) {
1842
1843 /* 'regular' is same as default */
1844 arraytype = I_REGULAR_P;
1845
1846 } else if((j > 1 && !strcmp("product", s[1]))
1847 ||(j > 2 && !strcmp("product", s[2]))) {
1848
1849
1850 if((k = ncvarid(hand, s[0])) < 0) {
1851 DXSetError(ERROR_BAD_PARAMETER,"positions variable missing");
1852 goto error;
1853 }
1854 ap->varid = k;
1855 varseries = is_seriesvariable(ap->cdfhandle, ap->varid,
1856 ap->recdim);
1857
1858 /* regular grid, non-zero origin or non-unit spacing */
1859 if((j > 1 &&
1860 (!strcmp("regular", s[1]) || !strcmp("compact", s[1])))
1861 || (j > 2 &&
1862 (!strcmp("regular", s[2]) || !strcmp("compact", s[2])))) {
1863
1864 arraytype = I_PRODUCT_P;
1865
1866 } else {
1867
1868 arraytype = I_IRREGULAR_P;
1869 }
1870
1871
1872 } else {
1873 /* else positions isn't regular, or is grid
1874 * but not 0.0 origin or 1.0 deltas.
1875 */
1876 if((k = ncvarid(hand, s[0])) < 0) {
1877 DXSetError(ERROR_BAD_PARAMETER,"positions variable missing");
1878 goto error;
1879 }
1880 ap->varid = k;
1881 varseries = is_seriesvariable(ap->cdfhandle, ap->varid,
1882 ap->recdim);
1883
1884 /* regular grid, non-zero origin or non-unit spacing */
1885 if((j > 1 &&
1886 (!strcmp("regular", s[1]) || !strcmp("compact", s[1])))
1887 || (j > 2 &&
1888 (!strcmp("regular", s[2]) || !strcmp("compact", s[2])))) {
1889
1890 if(j > 2 && !strcmp("product", s[2]))
1891 arraytype = I_PRODUCT_P;
1892 else
1893 arraytype = I_COMPACT_P;
1894
1895 } else {
1896
1897 arraytype = I_IRREGULAR_P;
1898 }
1899
1900 }
1901
1902 if(nopos)
1903 break;
1904
1905 indata = build_poscon(ap, arraytype, nterms);
1906 terms[nterms] = indata;
1907 nterms++;
1908 }
1909 }
1910
1911 /* if there is a positions component */
1912 if(!nopos) {
1913 if(!varseries) {
1914 if(nterms > 1) {
1915 indata = (Object)DXNewProductArrayV(nterms, (Array *)terms);
1916 if(!indata) {
1917 DXSetError(ERROR_BAD_PARAMETER, "bad positions attribute");
1918 goto error;
1919 }
1920 }
1921
1922 for(i=0; i < series; i++) {
1923
1924 f = (Field)DXGetEnumeratedMember((Group)o, i, NULL);
1925
1926 /* and add it to field */
1927 f = DXSetComponentValue(f, GEOCOMPNAME, indata);
1928 f = DXSetComponentAttribute(f, DATCOMPNAME,
1929 "dep", STR(GEOCOMPNAME));
1930 if(!f)
1931 goto error;
1932
1933 /* and check for attributes if another variable was used */
1934 if(ap->varid != varid && !setattr(ap, f, GEOCOMPNAME))
1935 goto error;
1936
1937 }
1938
1939 } else {
1940
1941 if(nterms > 1) {
1942 t = (Object *)DXAllocateLocalZero(series * nterms * sizeof(Array));
1943 if(!t)
1944 goto error;
1945
1946 for(k=0; k < nterms; k++)
1947 for(j=0; j < series; j++)
1948 t[j * nterms + k] =
1949 DXGetEnumeratedMember((Group)terms[k], j, NULL);
1950
1951 }
1952
1953 for(j=0; j < series; j++) {
1954
1955 f = (Field)DXGetEnumeratedMember((Group)o, j, NULL);
1956
1957 if(nterms > 1) {
1958 arr = (Object)DXNewProductArrayV(nterms,
1959 (Array *)&t[j * nterms]);
1960 if(!arr) {
1961 DXSetError(ERROR_BAD_PARAMETER,
1962 "bad positions attribute");
1963 goto error;
1964 }
1965 } else
1966 arr = DXGetEnumeratedMember((Group)indata, j, NULL);
1967
1968 /* and add it to field */
1969 f = DXSetComponentValue(f, GEOCOMPNAME, arr);
1970 f = DXSetComponentAttribute(f, DATCOMPNAME,
1971 "dep", STR(GEOCOMPNAME));
1972 if(!f)
1973 goto error;
1974
1975 /* and check for attributes if another variable was used */
1976 if(ap->varid != varid && !setattr(ap, f, GEOCOMPNAME))
1977 goto error;
1978 }
1979
1980 if(t)
1981 DXFree((Pointer)t);
1982 }
1983
1984 /* delete anything which is a series-in-a-single-netcdf-variable,
1985 * because a temporary Group object was constructed to hold it.
1986 */
1987 for(j=0; j<nterms; j++)
1988 if(DXGetObjectClass(terms[j]) == CLASS_GROUP)
1989 DXDelete(terms[j]);
1990 }
1991
1992
1993 /*
1994 * connections component
1995 */
1996 varseries = 0;
1997
1998 /* now look for the 'connections' information. if there isn't
1999 * anything, assume it's a regular grid. if there is an attribute
2000 * for 'connections' (currently 'topology' also still accepted in the
2001 * netCDF file), parse it. if it's 'none', there isn't a topology
2002 * component. if it's 'regular', set it to a regular grid.
2003 * otherwise, it should be the name of another netCDF variable
2004 * which contains the connections information.
2005 */
2006 if(!getattr(hand, varid, TOPOATTRIB, ap->stringattr,MAXNAME)
2007 && !getattr(hand, varid, OTOPOATTRIB, ap->stringattr,MAXNAME)) {
2008 nterms = 1;
2009 indata = build_poscon(ap, I_REGULAR_C, 0);
2010 eltype = STR(regularname[ap->data_ndims]);
2011 terms[0] = indata;
2012
2013 } else {
2014
2015 /* a connections attribute. parse it */
2016 if(isattr(hand, varid, OTOPOATTRIB))
2017 DXWarning("'topology' attribute obsolete, use 'connections' instead");
2018
2019 cp = ap->stringattr;
2020 nterms = 0;
2021 while(1) {
2022 j = MAXATTRSTR;
2023 cp = parseit(cp, &j, s);
2024 if(j <= 0)
2025 break;
2026
2027 /* no topology */
2028 if(!strcmp("none", s[0])) {
2029 nocon++;
2030
2031 } else if(!strcmp("regular", s[0])) {
2032
2033 /* also accept this as regular grid */
2034 arraytype = I_REGULAR_C;
2035 eltype = STR(regularname[ap->data_ndims]);
2036
2037 } else if(j > 1 && !strcmp("product", s[1])) {
2038
2039 if((k = ncvarid(hand, s[0])) < 0) {
2040 DXSetError(ERROR_BAD_PARAMETER,
2041 "connections variable missing");
2042 goto error;
2043 }
2044 ap->varid = k;
2045 varseries = is_seriesvariable(ap->cdfhandle, ap->varid,
2046 ap->recdim);
2047
2048 arraytype = I_PRODUCT_C;
2049
2050 /* if irregular, they must specify an element type */
2051 if(j < 3 || !s[2] || !s[2][0]) {
2052 DXSetError(ERROR_BAD_PARAMETER,
2053 "'element type' must be specified");
2054 goto error;
2055 } else
2056 eltype = STR(s[2]);
2057 } else {
2058
2059 /* irregular - look for the other netCDF variable */
2060 if((k = ncvarid(hand, s[0])) < 0) {
2061 DXSetError(ERROR_BAD_PARAMETER,
2062 "connections variable missing");
2063 goto error;
2064 }
2065 ap->varid = k;
2066 varseries = is_seriesvariable(ap->cdfhandle, ap->varid,
2067 ap->recdim);
2068
2069 arraytype = I_IRREGULAR_C;
2070
2071 /* if irregular, they must specify an element type */
2072 if(j < 2 || !s[1] || !s[1][0]) {
2073 DXSetError(ERROR_BAD_PARAMETER,
2074 "'element type' must be specified");
2075 goto error;
2076 } else
2077 eltype = STR(s[1]);
2078 }
2079
2080 if(nocon)
2081 break;
2082
2083 indata = build_poscon(ap, arraytype, nterms);
2084 terms[nterms] = indata;
2085 nterms++;
2086 }
2087
2088 }
2089
2090
2091 /* if there is a connections component */
2092 if(!nocon) {
2093 if(!varseries) {
2094 if(nterms > 1) {
2095 indata = (Object)DXNewMeshArrayV(nterms, (Array *)terms);
2096 if(!indata) {
2097 DXSetError(ERROR_BAD_PARAMETER, "bad connections attribute");
2098 goto error;
2099 }
2100 }
2101
2102 for(j=0; j < series; j++) {
2103
2104 f = (Field)DXGetEnumeratedMember((Group)o, j, NULL);
2105
2106 /* and add it to field */
2107 f = DXSetComponentValue(f, TOPCOMPNAME, indata);
2108 f = DXSetComponentAttribute(f, TOPCOMPNAME,
2109 "element type", eltype);
2110 f = DXSetComponentAttribute(f, TOPCOMPNAME,
2111 "ref", STR(GEOCOMPNAME));
2112 if(!f)
2113 goto error;
2114
2115
2116 /* and check for attributes if another variable was used */
2117 if(ap->varid != varid && !setattr(ap, f, TOPCOMPNAME))
2118 goto error;
2119
2120 }
2121
2122 } else {
2123
2124 if(nterms > 1) {
2125 t = (Object *)DXAllocateLocalZero(series * nterms * sizeof(Array));
2126 if(!t)
2127 goto error;
2128
2129 for(k=0; k < nterms; k++)
2130 for(j=0; j < series; j++)
2131 t[j * nterms + k] =
2132 DXGetEnumeratedMember((Group)terms[k], j, NULL);
2133
2134 }
2135
2136 for(j=0; j < series; j++) {
2137
2138 f = (Field)DXGetEnumeratedMember((Group)o, j, NULL);
2139
2140 if(nterms > 1) {
2141 arr = (Object)DXNewMeshArrayV(nterms,
2142 (Array *)&t[j * nterms]);
2143 if(!arr) {
2144 DXSetError(ERROR_BAD_PARAMETER,
2145 "bad connections attribute");
2146 goto error;
2147 }
2148 } else
2149 arr = DXGetEnumeratedMember((Group)indata, j, NULL);
2150
2151 /* and add it to field */
2152 f = DXSetComponentValue(f, TOPCOMPNAME, arr);
2153 f = DXSetComponentAttribute(f, TOPCOMPNAME,
2154 "element type", eltype);
2155 f = DXSetComponentAttribute(f, TOPCOMPNAME,
2156 "ref", STR(GEOCOMPNAME));
2157 if(!f)
2158 goto error;
2159
2160
2161 /* and check for attributes if another variable was used */
2162 if(ap->varid != varid && !setattr(ap, f, TOPCOMPNAME))
2163 goto error;
2164 }
2165
2166 if(t)
2167 DXFree((Pointer)t);
2168 }
2169
2170 /* delete anything which is a series-in-a-single-netcdf-variable,
2171 * because a temporary Group object was constructed to hold it.
2172 */
2173 for(j=0; j<nterms; j++)
2174 if(DXGetObjectClass(terms[j]) == CLASS_GROUP)
2175 DXDelete(terms[j]);
2176
2177 }
2178
2179
2180
2181 /*
2182 * additional components
2183 */
2184
2185 /* are there additional components that should be added?
2186 */
2187 if(getattr(hand, varid, COMPATTRIB, ap->stringattr,MAXNAME)) {
2188
2189 cp = ap->stringattr;
2190 while(1) {
2191 j = MAXATTRSTR;
2192 cp = parseit(cp, &j, s);
2193
2194 /* field:component = "a, b, c"; attribute required. all three
2195 * parameters must be present for additional components.
2196 * a = name of variable.
2197 * b = component name
2198 * c = rank (scalar, vector, matrix, tensor)
2199 */
2200 if(j <= 0)
2201 break;
2202
2203 if(j < 3) {
2204 DXSetError(ERROR_BAD_PARAMETER,
2205 "varname, compname, shape required");
2206 goto error;
2207 }
2208
2209 if((k = ncvarid(hand, s[0])) < 0) {
2210 DXSetError(ERROR_BAD_PARAMETER,
2211 "component variable '%s' not found", s[0]);
2212 goto error;
2213 }
2214
2215 ap->varid = k;
2216 ap->arrayrank = setrank(s[2]);
2217 varseries = is_seriesvariable(ap->cdfhandle, ap->varid, ap->recdim);
2218
2219 /* actually read the data into an Array */
2220 if(!(indata = build_array(ap)))
2221 goto error;
2222
2223 for(i=0; i<series; i++) {
2224
2225 f = (Field)DXGetEnumeratedMember((Group)o, i, NULL);
2226
2227 /* and add it to the field with the given component name */
2228 arr = varseries ? DXGetEnumeratedMember((Group)indata, i, NULL)
2229 : indata;
2230
2231 if(!DXSetComponentValue(f, s[1], arr))
2232 goto error;
2233
2234 /* and check for attributes */
2235 if(!setattr(ap, f, s[1]))
2236 goto error;
2237
2238 }
2239
2240 if(varseries)
2241 DXDelete(indata);
2242 }
2243
2244 }
2245
2246 #ifdef DO_ENDFIELD
2247 for(i=0; i<series; i++)
2248 DXEndField((Field)DXGetSeriesMember((Series)o, i, &value));
2249 #endif
2250
2251 return o;
2252
2253 error:
2254 DXDelete((Object)f);
2255 DXDelete(o);
2256 DXFree((Pointer)t);
2257 return NULL;
2258
2259 }
2260
2261
2262 /* verify that start and end are not out of range.
2263 */
check_serieslength(Arrayinfo ap)2264 static Error check_serieslength(Arrayinfo ap)
2265 {
2266 long size;
2267
2268 if(ncdiminq(ap->cdfhandle, /* file descriptor */
2269 ap->recdim, /* record dimension id */
2270 (char *)0, /* dimension name */
2271 &size) < 0) /* record size */
2272 DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2273
2274 if (ap->vp->startframe && *ap->vp->startframe < 0) {
2275 DXSetError(ERROR_BAD_PARAMETER, "start must be a non-negative integer");
2276 return ERROR;
2277 }
2278
2279 if (ap->vp->endframe && *ap->vp->endframe > size) {
2280 DXSetError(ERROR_BAD_PARAMETER, "end is larger than series members");
2281 return ERROR;
2282 }
2283
2284 return OK;
2285 }
2286
2287 /* given the dimension number of the unlimited dimension, find out how
2288 * long it is.
2289 */
get_serieslength(Arrayinfo ap)2290 static int get_serieslength(Arrayinfo ap)
2291 {
2292 int cnt;
2293 long size;
2294 int start, end, delta;
2295
2296 if(ncdiminq(ap->cdfhandle, /* file descriptor */
2297 ap->recdim, /* record dimension id */
2298 (char *)0, /* dimension name */
2299 &size) < 0) /* record size */
2300 DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2301
2302 start = ap->vp->startframe ? *ap->vp->startframe : 0;
2303 end = ap->vp->endframe ? *ap->vp->endframe : size-1;
2304 delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
2305
2306 if(delta == 0)
2307 return 0;
2308
2309 cnt = 0;
2310 for (size=start; size<=end; size+=delta)
2311 cnt++;
2312
2313 return (cnt);
2314 }
2315
2316 /* given a variable and the record dimension id, return whether that variable
2317 * uses that id.
2318 */
is_seriesvariable(int hand,int varid,int recdim)2319 static int is_seriesvariable(int hand, int varid, int recdim)
2320 {
2321 nc_type datatype;
2322 int natts, ndims;
2323 int dim[MAX_VAR_DIMS];
2324
2325 /* inquire about the variable - it's size, shape and type */
2326 if(ncvarinq(hand, /* file descriptor */
2327 varid, /* variable ID */
2328 (char *)0, /* variable name */
2329 &datatype, /* data item type */
2330 &ndims, /* number of dimensions */
2331 dim, /* array of dimension ID's */
2332 &natts) < 0) /* number of attributes */
2333 DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2334
2335
2336 /* if the first variable is the unlimited dimension, this is a series.
2337 */
2338 return (dim[0] == recdim);
2339 }
2340
2341
2342 /* given a variable which uses the record dimension, and an index,
2343 * find out what number is associated with that record number.
2344 */
get_seriesvalue(Arrayinfo ap,float ** valuelist)2345 static Error get_seriesvalue(Arrayinfo ap, float **valuelist)
2346 {
2347 #if 0
2348 return (float)index;
2349 #else
2350 /* bother. what makes sense for this is: to have a separate netCDF
2351 * variable, a 1D array using the unlimited dimension, which contains
2352 * the series value for each dimension. but how to you specify it?
2353 * as a 'var1:series = seriesvar' attribute on the data field?
2354 */
2355 nc_type datatype;
2356 int varid;
2357 int i, j, natts, ndims;
2358 int dim[MAX_VAR_DIMS];
2359 long size, start;
2360 int delta;
2361 char *s[MAXATTRSTR];
2362
2363
2364 /* value list comes in as NULL. if there is no series positions
2365 * arrray, just return. else try to read in the variable, allocate
2366 * space for the array, and return with valuelist pointing to it.
2367 */
2368 if(!getattr(ap->cdfhandle, ap->varid, SERIESPOSATTRIB, ap->stringattr,MAXNAME))
2369 return OK;
2370
2371 j = MAXATTRSTR;
2372 parseit(ap->stringattr, &j, s);
2373
2374 if(j != 1)
2375 DXErrorReturn(ERROR_DATA_INVALID, "bad seriesposition attribute");
2376
2377 if((varid = ncvarid(ap->cdfhandle, s[0])) < 0) {
2378 DXSetError(ERROR_BAD_PARAMETER,
2379 "seriesposition variable '%s' not found", s[0]);
2380 return ERROR;
2381 }
2382
2383
2384 /* inquire about the variable - it's size, shape and type */
2385 if(ncvarinq(ap->cdfhandle, /* file descriptor */
2386 varid, /* variable ID */
2387 (char *)0, /* variable name */
2388 &datatype, /* data item type */
2389 &ndims, /* number of dimensions */
2390 dim, /* array of dimension ID's */
2391 &natts) < 0) /* number of attributes */
2392 DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2393
2394 if(dim[0] != ap->recdim)
2395 DXErrorReturn(ERROR_DATA_INVALID,
2396 "seriespositions array must use unlimited record dimension");
2397
2398 if(datatype != NC_FLOAT)
2399 DXErrorReturn(ERROR_DATA_INVALID,
2400 "seriespositions array must be type float");
2401
2402 if(ndims != 1)
2403 DXErrorReturn(ERROR_DATA_INVALID, "seriespositions array must be 1D");
2404
2405 size = get_serieslength(ap);
2406 start = ap->vp->startframe ? *ap->vp->startframe : 0;
2407 delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
2408
2409 if(!(*valuelist = (float *)DXAllocateLocal(sizeof(float) * size)))
2410 return ERROR;
2411
2412 if(delta == 1) {
2413 if(ncvarget(ap->cdfhandle, /* file descriptor */
2414 varid, /* variable ID */
2415 &start, /* array-origin for each dim */
2416 &size, /* array-counts along each dim */
2417 (void *)*valuelist) < 0) /* where values are returned */
2418 goto error;
2419 } else {
2420 for(i=0; i<size; i++) {
2421 if(ncvarget1(ap->cdfhandle, varid, &start,
2422 (void *)&valuelist[i]) < 0)
2423 goto error;
2424
2425 start += delta;
2426 }
2427 }
2428 return OK;
2429
2430 error:
2431 DXFree((Pointer) *valuelist);
2432 DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2433
2434 #endif
2435 }
2436
2437 /*
2438 * read a variable into global memory, and build up a field.
2439 */
build_field(int hand,int varid)2440 static Object build_field(int hand, int varid)
2441 {
2442 Array indata, terms[MAXRANK];
2443 int nopos = 0, nocon = 0;
2444 struct arrayinfo a, *ap = &a;
2445 int arraytype = I_NONE;
2446 char *cp, *s[MAXATTRSTR];
2447 int i, j, k;
2448 Field f = NULL;
2449 Object eltype = NULL;
2450
2451
2452 /* set up the Arrayinfo struct
2453 */
2454 ap->cdfhandle = hand;
2455 ap->varid = varid;
2456 ap->recdim = INVALID_DIMID;
2457
2458
2459 /* get the attribute value.
2460 */
2461 if(getattr(hand, varid, FIELDATTRIB, ap->stringattr,MAXNAME)) {
2462
2463 cp = ap->stringattr;
2464 j = MAXATTRSTR;
2465 cp = parseit(cp, &j, s);
2466 if(j <= 0)
2467 return NULL;
2468 } else {
2469 if (!variablename(hand, varid, ap->stringattr))
2470 return NULL;
2471
2472 s[0] = ap->stringattr;
2473 s[1] = NULL;
2474 s[2] = NULL;
2475 j = 1;
2476 }
2477
2478
2479
2480 /*
2481 * data component
2482 */
2483
2484 /* decide if it's scalar, vector or matrix data */
2485 ap->arrayrank = setrank(s[1]);
2486
2487 /* this routine reads a netCDF variable into an Array.
2488 * in this case, it's reading in the data component.
2489 */
2490 if(!(indata = (Array)build_data(ap, INVALID_DIMID)))
2491 goto error;
2492
2493 f = DXNewField();
2494 if(!f)
2495 goto error;
2496
2497 if(!DXSetComponentValue(f, DATCOMPNAME, (Object)indata))
2498 goto error;
2499
2500 /* look here for more attributes on the data component - to set
2501 * data component attributes.
2502 */
2503 if(!setattr(ap, f, DATCOMPNAME))
2504 goto error;
2505
2506 /*
2507 * positions component
2508 */
2509
2510 ap->arrayrank = 1; /* unless they explicitly say 'scalar' */
2511
2512 /* look for a positions (old points) component. if the data variable
2513 * doesn't have a 'positions' attribute, assume a regularly spaced grid.
2514 */
2515 if(!getattr(hand, varid, GEOMATTRIB, ap->stringattr,MAXNAME)
2516 && !getattr(hand, varid, OGEOMATTRIB, ap->stringattr,MAXNAME)) {
2517 i = 1;
2518 indata = (Array)build_poscon(ap, I_REGULAR_P, 0);
2519
2520 } else {
2521 /* else they gave us a geometry. parse it.
2522 */
2523
2524 if(isattr(hand, varid, OGEOMATTRIB))
2525 DXWarning("'geometry' attribute obsolete, use 'positions' instead");
2526
2527 cp = ap->stringattr;
2528 i = 0;
2529 while(1) {
2530 j = MAXATTRSTR;
2531 cp = parseit(cp, &j, s);
2532 if(j <= 0)
2533 break;
2534
2535 /* positions are vectors unless specifically told scalar */
2536 if((j > 1 && !strcmp("scalar", s[1]))
2537 ||(j > 2 && !strcmp("scalar", s[2])))
2538 ap->arrayrank = 0;
2539
2540 if(!strcmp("none", s[0])) {
2541
2542 /* no positions - should this be ok? */
2543 nopos++;
2544
2545 } else if(!strcmp("regular", s[0])) {
2546
2547 /* 'regular' is same as default */
2548 arraytype = I_REGULAR_P;
2549
2550 } else if((j > 1 && !strcmp("product", s[1]))
2551 ||(j > 2 && !strcmp("product", s[2]))) {
2552
2553
2554 if((k = ncvarid(hand, s[0])) < 0) {
2555 DXSetError(ERROR_BAD_PARAMETER,"positions variable missing");
2556 goto error;
2557 }
2558 ap->varid = k;
2559
2560 /* regular grid, non-zero origin or non-unit spacing */
2561 if((j > 1 &&
2562 (!strcmp("regular", s[1]) || !strcmp("compact", s[1])))
2563 || (j > 2 &&
2564 (!strcmp("regular", s[2]) || !strcmp("compact", s[2])))) {
2565
2566 arraytype = I_PRODUCT_P;
2567
2568 } else {
2569
2570 arraytype = I_IRREGULAR_P;
2571 }
2572
2573
2574 } else {
2575 /* else positions isn't regular, or is grid
2576 * but not 0.0 origin or 1.0 deltas.
2577 */
2578 if((k = ncvarid(hand, s[0])) < 0) {
2579 DXSetError(ERROR_BAD_PARAMETER,"positions variable missing");
2580 goto error;
2581 }
2582 ap->varid = k;
2583
2584 /* regular grid, non-zero origin or non-unit spacing */
2585 if((j > 1 &&
2586 (!strcmp("regular", s[1]) || !strcmp("compact", s[1])))
2587 || (j > 2 &&
2588 (!strcmp("regular", s[2]) || !strcmp("compact", s[2])))) {
2589
2590 if(j > 2 && !strcmp("product", s[2]))
2591 arraytype = I_PRODUCT_P;
2592 else
2593 arraytype = I_COMPACT_P;
2594
2595 } else {
2596
2597 arraytype = I_IRREGULAR_P;
2598 }
2599
2600 }
2601
2602 if(nopos)
2603 break;
2604
2605 indata = (Array)build_poscon(ap, arraytype, i);
2606 terms[i] = indata;
2607 i++;
2608 }
2609 }
2610
2611 /* if there is a positions component */
2612 if(!nopos) {
2613 if(i > 1) {
2614 indata = (Array)DXNewProductArrayV(i, terms);
2615 if(!indata) {
2616 DXSetError(ERROR_BAD_PARAMETER, "bad positions attribute");
2617 goto error;
2618 }
2619 }
2620
2621 /* and add it to field */
2622 f = DXSetComponentValue(f, GEOCOMPNAME, (Object)indata);
2623 f = DXSetComponentAttribute(f, DATCOMPNAME, "dep", STR(GEOCOMPNAME));
2624 if(!f)
2625 goto error;
2626
2627 /* and check for attributes if another variable was used */
2628 if(ap->varid != varid && !setattr(ap, f, GEOCOMPNAME))
2629 goto error;
2630
2631 }
2632
2633
2634 /*
2635 * connections component
2636 */
2637
2638 /* now look for the 'connections' information. if there isn't
2639 * anything, assume it's a regular grid. if there is an attribute
2640 * for 'connections' (currently 'topology' also still accepted in the
2641 * netCDF file), parse it. if it's 'none', there isn't a topology
2642 * component. if it's 'regular', set it to a regular grid.
2643 * otherwise, it should be the name of another netCDF variable
2644 * which contains the connections information.
2645 */
2646 if(!getattr(hand, varid, TOPOATTRIB, ap->stringattr,MAXNAME)
2647 && !getattr(hand, varid, OTOPOATTRIB, ap->stringattr,MAXNAME)) {
2648 i = 1;
2649 indata = (Array)build_poscon(ap, I_REGULAR_C, 0);
2650 eltype = STR(regularname[ap->data_ndims]);
2651
2652 } else {
2653
2654 /* a connections attribute. parse it */
2655 if(isattr(hand, varid, OTOPOATTRIB))
2656 DXWarning("'topology' attribute obsolete, use 'connections' instead");
2657
2658 cp = ap->stringattr;
2659 i = 0;
2660 while(1) {
2661 j = MAXATTRSTR;
2662 cp = parseit(cp, &j, s);
2663 if(j <= 0)
2664 break;
2665
2666 /* no topology */
2667 if(!strcmp("none", s[0])) {
2668 nocon++;
2669
2670 } else if(!strcmp("regular", s[0])) {
2671
2672 /* also accept this as regular grid */
2673 arraytype = I_REGULAR_C;
2674 eltype = STR(regularname[ap->data_ndims]);
2675
2676 } else if(j > 1 && !strcmp("product", s[1])) {
2677
2678 if((k = ncvarid(hand, s[0])) < 0) {
2679 DXSetError(ERROR_BAD_PARAMETER,
2680 "connections variable missing");
2681 goto error;
2682 }
2683 ap->varid = k;
2684
2685 arraytype = I_PRODUCT_C;
2686
2687 /* if irregular, they must specify an element type */
2688 if(j < 3 || !s[2] || !s[2][0]) {
2689 DXSetError(ERROR_BAD_PARAMETER,
2690 "'element type' must be specified");
2691 goto error;
2692 } else
2693 eltype = STR(s[2]);
2694 } else {
2695
2696 /* irregular - look for the other netCDF variable */
2697 if((k = ncvarid(hand, s[0])) < 0) {
2698 DXSetError(ERROR_BAD_PARAMETER,
2699 "connections variable missing");
2700 goto error;
2701 }
2702 ap->varid = k;
2703
2704 arraytype = I_IRREGULAR_C;
2705
2706 /* if irregular, they must specify an element type */
2707 if(j < 2 || !s[1] || !s[1][0]) {
2708 DXSetError(ERROR_BAD_PARAMETER,
2709 "'element type' must be specified");
2710 goto error;
2711 } else
2712 eltype = STR(s[1]);
2713 }
2714
2715 if(nocon)
2716 break;
2717
2718 indata = (Array)build_poscon(ap, arraytype, i);
2719 terms[i] = indata;
2720 i++;
2721 }
2722
2723 }
2724
2725 /* if there is a connections component */
2726 if(!nocon) {
2727 if(i > 1) {
2728 indata = (Array)DXNewMeshArrayV(i, terms);
2729 if(!indata) {
2730 DXSetError(ERROR_BAD_PARAMETER, "bad connections attribute");
2731 goto error;
2732 }
2733 }
2734
2735 /* and add it to field */
2736 f = DXSetComponentValue(f, TOPCOMPNAME, (Object)indata);
2737 f = DXSetComponentAttribute(f, TOPCOMPNAME, "element type", eltype);
2738 f = DXSetComponentAttribute(f, TOPCOMPNAME, "ref", STR(GEOCOMPNAME));
2739 if(!f)
2740 goto error;
2741
2742 /* and check for attributes if another variable was used */
2743 if(ap->varid != varid && !setattr(ap, f, TOPCOMPNAME))
2744 goto error;
2745
2746 }
2747
2748
2749 /*
2750 * additional components
2751 */
2752
2753 /* are there additional components that should be added?
2754 */
2755 if(getattr(hand, varid, COMPATTRIB, ap->stringattr,MAXNAME)) {
2756
2757 cp = ap->stringattr;
2758 while(1) {
2759 j = MAXATTRSTR;
2760 cp = parseit(cp, &j, s);
2761
2762 /* field:component = "a, b, c"; attribute required. all three
2763 * parameters must be present for additional components.
2764 * a = name of variable.
2765 * b = component name
2766 * c = rank (scalar, vector, matrix, tensor)
2767 */
2768 if(j <= 0)
2769 break;
2770
2771 if(j < 3) {
2772 DXSetError(ERROR_BAD_PARAMETER,
2773 "varname, compname, shape required");
2774 goto error;
2775 }
2776
2777 if((k = ncvarid(hand, s[0])) < 0) {
2778 DXSetError(ERROR_BAD_PARAMETER,
2779 "component variable '%s' not found", s[0]);
2780 goto error;
2781 }
2782
2783 ap->varid = k;
2784 ap->arrayrank = setrank(s[2]);
2785
2786 /* actually read the data into an Array */
2787 if(!(indata = (Array)build_array(ap)))
2788 goto error;
2789
2790 /* and add it to the field with the given component name */
2791 if(!DXSetComponentValue(f, s[1], (Object)indata))
2792 goto error;
2793
2794 /* and check for attributes */
2795 if(!setattr(ap, f, s[1]))
2796 goto error;
2797 }
2798
2799 }
2800
2801 #ifdef DO_ENDFIELD
2802 f = DXEndField(f);
2803 #endif
2804 return (Object)f;
2805
2806 error:
2807 DXDelete((Object)f);
2808 return NULL;
2809 }
2810
2811
2812 /* single routine for deciding what routine to call to actually construct
2813 * either a positions or connections array. (it's called from more than
2814 * one place, so it's here to keep the calls to the routines from being
2815 * scattered thoughout the code)
2816 */
2817 static Object
build_poscon(Arrayinfo ap,int flag,int dim)2818 build_poscon(Arrayinfo ap, int flag, int dim)
2819 {
2820 Object indata = NULL;
2821
2822 switch(flag) {
2823
2824 case I_NONE:
2825 break;
2826
2827 case I_COMPACT_P:
2828 /* regular grid, non-zero origin or non-unit spacing */
2829 indata = build_regpos(ap, 0);
2830 break;
2831
2832 case I_REGULAR_P:
2833 /* origin at 0.0, deltas 1.0, regular grid */
2834 indata = build_regpos(ap, 1);
2835 break;
2836
2837 case I_REGULAR_C:
2838 /* N-dim connected grid */
2839 indata = build_regcon(ap);
2840 break;
2841
2842 case I_IRREGULAR_P:
2843 case I_IRREGULAR_C:
2844 /* explicit positions or connections list */
2845 indata = build_array(ap);
2846 break;
2847
2848 case I_PRODUCT_P:
2849 /* indata = build_regpos(ap, 0); */
2850 indata = build_regpos1D(ap, 0, dim);
2851 break;
2852
2853 case I_PRODUCT_C:
2854 indata = build_regcon1D(ap, dim);
2855 break;
2856
2857 default:
2858 DXSetError(ERROR_INTERNAL, "bad case in build_pos switch statement");
2859 break;
2860 }
2861
2862 return indata;
2863 }
2864
2865
2866
2867
2868 /* this code reads in a single variable into an array and also sets
2869 * up the AI struct for later. if recdim is valid, it is the id of
2870 * the variable used for the series dimension. it needs to be the
2871 * first variable if it is used.
2872 */
2873 static Object
build_data(Arrayinfo ap,int recdim)2874 build_data(Arrayinfo ap, int recdim)
2875 {
2876 nc_type datatype;
2877 int i, j, size, natts, series;
2878 int start, end, delta;
2879 int dim[MAX_VAR_DIMS];
2880 long dstart[MAX_VAR_DIMS];
2881 long long_datacounts[MAXRANK];
2882 VOID *dataval;
2883 Object o = NULL;
2884 Array adata = NULL;
2885 long *tempbuf;
2886 int k,n,ndims=1;
2887 int *data_int;
2888
2889 /* inquire about the variable - it's size, shape and type */
2890 if(ncvarinq(ap->cdfhandle, /* file descriptor */
2891 ap->varid, /* variable ID */
2892 (char *)0, /* variable name */
2893 &datatype, /* data item type */
2894 &ap->data_ndims, /* number of dimensions */
2895 dim, /* array of dimension ID's */
2896 &natts) < 0) /* number of attributes */
2897 DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2898
2899
2900 if(ap->data_ndims <= 0){
2901 DXSetError(ERROR_INTERNAL, "%s variable with no dimensions",
2902 ap->stringattr);
2903 return ERROR;
2904 }
2905
2906 /* figure out how big it is - total number of items, taking into
2907 * account that if it is an array of vectors or matricies, the
2908 * quickest varying dimension isn't part of the total shape.
2909 */
2910 size = 1;
2911 for (i=0; i<ap->data_ndims; i++) {
2912 ncdiminq (ap->cdfhandle, dim[i], (char *)0, &long_datacounts[i]);
2913 ap->datacounts[i] = long_datacounts[i];
2914 dstart[i] = 0;
2915 if(i < ap->data_ndims - ap->arrayrank)
2916 size *= ap->datacounts[i];
2917 }
2918
2919 /* convert from netCDF data flags to SVS data flags */
2920 switch(datatype) {
2921 case NC_CHAR: ap->arraytype = TYPE_UBYTE; break;
2922 case NC_SHORT: ap->arraytype = TYPE_SHORT; break;
2923 case NC_LONG: ap->arraytype = TYPE_INT; break;
2924 case NC_FLOAT: ap->arraytype = TYPE_FLOAT; break;
2925 case NC_DOUBLE: ap->arraytype = TYPE_DOUBLE; break;
2926 default: ap->arraytype = TYPE_UBYTE; break;
2927 /* we need a TYPE_BYTE */
2928 }
2929
2930
2931 /* if the first variable is the unlimited dimension, this is a series.
2932 */
2933 series = (dim[0] == recdim);
2934
2935 /* create a group to hold the output, and don't count the size of
2936 * the first dimension in the data.
2937 */
2938 if(series) {
2939 o = (Object)DXNewSeries();
2940 size /= ap->datacounts[0];
2941
2942 start = ap->vp->startframe ? *ap->vp->startframe : 0;
2943 end = ap->vp->endframe ? *ap->vp->endframe : ap->datacounts[0]-1;
2944 delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
2945
2946 if(delta == 0) {
2947 DXSetError(ERROR_DATA_INVALID, "delta is zero");
2948 goto error;
2949 }
2950
2951 ap->datacounts[0] = 1;
2952 long_datacounts[0] = 1;
2953 } else {
2954 start = 0;
2955 end = 0;
2956 delta = 1;
2957 }
2958
2959 for(i=start, j=0; i<=end; i+=delta, j++) {
2960
2961 /* variables are labeled as a vector/matrix/tensor. the outermost
2962 * (last) variable must vary fastest. if not scalar, pass the shape
2963 * of the vector or matrix into DXNewArray as shape.
2964 */
2965 adata = DXNewArrayV (ap->arraytype, CATEGORY_REAL, ap->arrayrank,
2966 &ap->datacounts[ap->data_ndims - ap->arrayrank]);
2967
2968 adata = DXAddArrayData (adata, 0, size, NULL);
2969
2970 if(series)
2971 dstart[0] = i;
2972
2973 /* if data is long, create a temp long buffer to copy
2974 netCDF data to then move to the SVS array buffer */
2975 if (ap->arraytype == TYPE_INT) {
2976 data_int = (int *)DXGetArrayData (adata);
2977 if(!data_int)
2978 goto error;
2979 for (k=0,n=ap->data_ndims - ap->arrayrank; k<ap->arrayrank; k++,n++)
2980 ndims *= ap->datacounts[n];
2981 tempbuf = (long *)DXAllocate(ndims * size * sizeof(long));
2982 if(ncvarget(ap->cdfhandle, /* netcdf file handle */
2983 ap->varid, /* variable id */
2984 dstart, /* array-origin for each dim */
2985 long_datacounts, /* array-counts along each dim */
2986 tempbuf) < 0) { /* memory pointer */
2987 DXSetError(ERROR_INTERNAL, "netCDF library error");
2988 goto error;
2989 }
2990 for (k=0; k<size*ndims; k++)
2991 data_int[k] = (int)tempbuf[k];
2992 DXFree((Pointer)tempbuf);
2993 }
2994 else {
2995
2996 dataval = DXGetArrayData (adata);
2997 if(!dataval)
2998 goto error;
2999
3000 /* do the memory copy from netCDF buffer to SVS array buffer */
3001 if(ncvarget(ap->cdfhandle, /* netcdf file handle */
3002 ap->varid, /* variable id */
3003 dstart, /* array-origin for each dim */
3004 long_datacounts, /* array-counts along each dim */
3005 dataval) < 0) { /* memory pointer */
3006 DXSetError(ERROR_INTERNAL, "netCDF library error");
3007 goto error;
3008 }
3009 }
3010
3011 if(series) {
3012 if (!DXSetSeriesMember((Series)o, j, (double)i, (Object)adata))
3013 goto error;
3014
3015 adata = NULL;
3016 }
3017 }
3018
3019 /* account for a vector or matrix at each location, and also series.
3020 */
3021 ap->data_ndims -= ap->arrayrank;
3022 if(series) {
3023 ap->data_ndims--;
3024 for(i=0; i < ap->data_ndims; i++){
3025 ap->datacounts[i] = ap->datacounts[i+1];
3026 long_datacounts[i] = long_datacounts[i+1];
3027 }
3028 }
3029
3030 /* if single array instead of group */
3031 if(!o)
3032 o = (Object)adata;
3033
3034 return(o);
3035
3036 error:
3037 DXDelete(o);
3038 DXDelete((Object)adata);
3039 return NULL;
3040 }
3041
3042
3043 /* this code reads in a single variable into an array, using an AI struct
3044 * in which the shape of the data has already been initialized.
3045 */
3046 static Object
build_array(Arrayinfo ap)3047 build_array(Arrayinfo ap)
3048 {
3049 nc_type datatype;
3050 int i, size, natts, series;
3051 int start, end, delta;
3052 int dim[MAX_VAR_DIMS];
3053 long dstart[MAX_VAR_DIMS];
3054 long long_arraycounts[MAXRANK];
3055 VOID *dataval;
3056 Object o = NULL;
3057 Array adata = NULL;
3058 long *tempbuf;
3059 int k,n,ndims=1;
3060 int *data_int;
3061
3062
3063 /* inquire about the variable - it's size, shape and type */
3064 if(ncvarinq(ap->cdfhandle, /* file descriptor */
3065 ap->varid, /* variable ID */
3066 (char *)0, /* variable name */
3067 &datatype, /* data item type */
3068 &ap->array_ndims, /* number of dimensions */
3069 dim, /* array of dimension ID's */
3070 &natts) < 0) /* number of attributes */
3071 DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
3072
3073
3074 if(ap->array_ndims < 0)
3075 DXErrorReturn(ERROR_INTERNAL, "netCDF variable with no dimensions");
3076
3077 /* figure out how big it is - total number of items, taking into
3078 * account that if it is an array of vectors or matricies, the
3079 * quickest varying dimension isn't part of the total shape.
3080 */
3081 size = 1;
3082 for (i=0; i<ap->array_ndims; i++) {
3083 ncdiminq (ap->cdfhandle, dim[i], (char *)0, &long_arraycounts[i]);
3084 ap->arraycounts[i] = long_arraycounts[i];
3085 dstart[i] = 0;
3086 if(i < ap->array_ndims - ap->arrayrank)
3087 size *= ap->arraycounts[i];
3088 }
3089
3090 /* convert from netCDF data flags to SVS data flags */
3091 switch(datatype) {
3092 case NC_CHAR: ap->arraytype = TYPE_UBYTE; break;
3093 case NC_SHORT: ap->arraytype = TYPE_SHORT; break;
3094 case NC_LONG: ap->arraytype = TYPE_INT; break;
3095 case NC_FLOAT: ap->arraytype = TYPE_FLOAT; break;
3096 case NC_DOUBLE: ap->arraytype = TYPE_DOUBLE; break;
3097 default: ap->arraytype = TYPE_UBYTE; break;
3098 /* we need a TYPE_BYTE */
3099 }
3100
3101
3102 /* if the first variable is the unlimited dimension, this is a series.
3103 */
3104 series = (dim[0] == ap->recdim);
3105
3106 /* create a group to hold the output, and don't count the size of
3107 * the first dimension in the data.
3108 */
3109 if(series) {
3110 o = (Object)DXNewGroup();
3111 size /= ap->arraycounts[0];
3112
3113 start = ap->vp->startframe ? *ap->vp->startframe : 0;
3114 end = ap->vp->endframe ? *ap->vp->endframe : ap->arraycounts[0]-1;
3115 delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
3116
3117 if(delta == 0) {
3118 DXSetError(ERROR_DATA_INVALID, "delta is zero");
3119 goto error;
3120 }
3121
3122 ap->arraycounts[0] = 1;
3123 long_arraycounts[0] =1;
3124 } else {
3125 start = 0;
3126 end = 0;
3127 delta = 1;
3128 }
3129
3130 for(i=start; i<=end; i+=delta) {
3131
3132 /* variables are labeled as a vector/matrix/tensor. the outermost
3133 * (last) variable must vary fastest. if not scalar, pass the shape
3134 * of the vector or matrix into DXNewArray as shape.
3135 */
3136 adata = DXNewArrayV (ap->arraytype, CATEGORY_REAL, ap->arrayrank,
3137 &ap->arraycounts[ap->array_ndims - ap->arrayrank]);
3138
3139
3140
3141 adata = DXAddArrayData (adata, 0, size, NULL);
3142
3143 if(series)
3144 dstart[0] = i;
3145
3146 /* if data is long, create a temp long buffer to copy
3147 netCDF data to then move to the SVS array buffer */
3148 if (ap->arraytype == TYPE_INT) {
3149 data_int = DXGetArrayData (adata);
3150 if(!data_int)
3151 goto error;
3152 for (k=0, n=ap->array_ndims - ap->arrayrank; k<ap->arrayrank;k++,n++)
3153 ndims *=ap->arraycounts[n];
3154 tempbuf = (long *)DXAllocate(size * ndims * sizeof(long));
3155 if(ncvarget(ap->cdfhandle, /* netcdf file handle */
3156 ap->varid, /* variable id */
3157 dstart, /* array-origin for each dim */
3158 long_arraycounts, /* array-counts along each dim */
3159 tempbuf) < 0) { /* memory pointer */
3160 DXSetError(ERROR_INTERNAL, "netCDF library error");
3161 goto error;
3162 }
3163 for (k=0; k<size*ndims; k++)
3164 data_int[k] = (int)tempbuf[k];
3165 DXFree((Pointer)tempbuf);
3166 }
3167 else {
3168 dataval = DXGetArrayData (adata);
3169 if(!dataval)
3170 goto error;
3171 /* do the memory copy from netCDF buffer to SVS array buffer */
3172 if(ncvarget(ap->cdfhandle, /* netcdf file handle */
3173 ap->varid, /* variable id */
3174 dstart, /* array-origin for each dim */
3175 long_arraycounts, /* array-counts along each dim */
3176 dataval) < 0) { /* memory pointer */
3177 DXSetError(ERROR_INTERNAL, "netCDF library error");
3178 goto error;
3179 }
3180 }
3181
3182 if(series) {
3183 if (!DXSetMember((Group)o, NULL, (Object)adata))
3184 goto error;
3185
3186 adata = NULL;
3187 }
3188
3189 }
3190
3191
3192 /* if single array instead of group */
3193 if(!o)
3194 o = (Object)adata;
3195
3196 return(o);
3197
3198 error:
3199 DXDelete(o);
3200 DXDelete((Object)adata);
3201 return NULL;
3202 }
3203
3204
3205 /*
3206 * construct a regular Ndim positions component -
3207 * either origins 0.0, deltas 1.0, or user specified origins & deltas
3208 */
build_regpos(Arrayinfo ap,int regular)3209 static Object build_regpos(Arrayinfo ap, int regular)
3210 {
3211 int i, j, k, loops;
3212 long index[3];
3213 float origins[MAX_VAR_DIMS], deltas[MAXRANK*MAXRANK];
3214 int start, delta;
3215 Array indata;
3216 Object o = NULL;
3217
3218 memset((char *)deltas, '\0', sizeof(deltas));
3219
3220 /* setup arrays for each dimension */
3221 if(regular) {
3222
3223 for (i=0; i<ap->data_ndims; i++) {
3224 origins[i] = 0.0;
3225 j = ap->data_ndims * i + i;
3226 deltas[j] = 1.0;
3227 }
3228
3229 /* actually make the positions Array */
3230 indata = DXMakeGridPositionsV(ap->data_ndims, ap->datacounts,
3231 origins, deltas);
3232
3233 return((Object)indata);
3234 }
3235
3236 /* not completely regular */
3237
3238 /* is single positions array shared, or is it a series? */
3239 if(!is_seriesvariable(ap->cdfhandle, ap->varid, ap->recdim)) {
3240
3241 for (i=0; i<ap->data_ndims; i++) {
3242
3243 /* origins */
3244 index[0] = i;
3245 index[1] = 0;
3246 if(ncvarget1(ap->cdfhandle, ap->varid, index, &origins[i]) < 0)
3247 DXErrorReturn(ERROR_INTERNAL,
3248 "netCDF library error getting origins");
3249
3250 /* deltas */
3251 index[1] = 1;
3252 j = ap->data_ndims * i + i;
3253 if(ncvarget1(ap->cdfhandle, ap->varid, index, &deltas[j]) < 0)
3254 DXErrorReturn(ERROR_INTERNAL,
3255 "netCDF library error getting deltas");
3256
3257 }
3258
3259 /* actually make the positions Array */
3260 indata = DXMakeGridPositionsV(ap->data_ndims, ap->datacounts,
3261 origins, deltas);
3262
3263 return((Object)indata);
3264 }
3265
3266 /* positions are also series */
3267 loops = get_serieslength(ap);
3268 start = ap->vp->startframe ? *ap->vp->startframe : 0;
3269 delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
3270
3271 for (j=0; j<loops; j++) {
3272
3273 memset((char *)deltas, '\0', sizeof(deltas));
3274
3275 for (i=0; i<ap->data_ndims; i++) {
3276
3277 /* origins */
3278 index[0] = start;
3279 index[1] = i;
3280 index[2] = 0;
3281 if(ncvarget1(ap->cdfhandle, ap->varid, index, &origins[i]) < 0)
3282 DXErrorReturn(ERROR_INTERNAL,
3283 "netCDF library error getting origins");
3284
3285 /* deltas */
3286 index[2] = 1;
3287 k = ap->data_ndims * i + i;
3288 if(ncvarget1(ap->cdfhandle, ap->varid, index, &deltas[k]) < 0)
3289 DXErrorReturn(ERROR_INTERNAL,
3290 "netCDF library error getting deltas");
3291
3292 }
3293
3294 /* make the positions Array for one timestep */
3295 indata = DXMakeGridPositionsV(ap->data_ndims, ap->datacounts,
3296 origins, deltas);
3297
3298 if(!o)
3299 o = (Object)DXNewGroup();
3300 o = (Object)DXSetMember((Group)o, NULL, (Object)indata);
3301 if(!o)
3302 return NULL;
3303
3304 start += delta;
3305 }
3306
3307 return o;
3308 }
3309
3310
3311
3312 /*
3313 * construct a single regular array -
3314 * either origin 0.0, delta 1.0, or user specified origin & deltas
3315 */
build_regpos1D(Arrayinfo ap,int regular,int dim)3316 static Object build_regpos1D(Arrayinfo ap, int regular, int dim)
3317 {
3318 int i, j, loops;
3319 long index[3];
3320 float origins[MAX_VAR_DIMS], deltas[MAXRANK*MAXRANK];
3321 int start, delta;
3322 Array indata;
3323 Object o = NULL;
3324
3325 memset((char *)deltas, '\0', sizeof(deltas));
3326
3327 /* setup an array for one dimension */
3328 if(regular) {
3329
3330 for (i=0; i<ap->data_ndims; i++)
3331 origins[0] = 0.0;
3332
3333 deltas[dim] = 1.0;
3334
3335 indata = (Array)DXNewRegularArray(TYPE_FLOAT, ap->data_ndims,
3336 ap->datacounts[dim],
3337 (Pointer)origins, (Pointer)deltas);
3338
3339 return((Object)indata);
3340 }
3341
3342 /* is the positions variable also a series, or constant? */
3343 if(!is_seriesvariable(ap->cdfhandle, ap->varid, ap->recdim)) {
3344
3345 /* origins */
3346 index[0] = 0;
3347 for (i=0; i<ap->data_ndims; i++) {
3348 index[1] = i;
3349 if(ncvarget1(ap->cdfhandle, ap->varid, index, &origins[i]) < 0)
3350 DXErrorReturn(ERROR_INTERNAL,
3351 "netCDF library error getting origins");
3352 }
3353
3354 /* deltas */
3355 index[0] = 1;
3356 for (i=0; i<ap->data_ndims; i++) {
3357 index[1] = i;
3358 if(ncvarget1(ap->cdfhandle, ap->varid, index, &deltas[i]) < 0)
3359 DXErrorReturn(ERROR_INTERNAL,
3360 "netCDF library error getting deltas");
3361 }
3362
3363
3364 indata = (Array)DXNewRegularArray(TYPE_FLOAT, ap->data_ndims,
3365 ap->datacounts[dim],
3366 (Pointer)origins, (Pointer)deltas);
3367
3368 return((Object)indata);
3369 }
3370
3371 /* positions are also series */
3372 loops = get_serieslength(ap);
3373 start = ap->vp->startframe ? *ap->vp->startframe : 0;
3374 delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
3375
3376 for (j=0; j<loops; j++) {
3377 for (i=0; i<ap->data_ndims; i++) {
3378
3379 /* origins */
3380 index[0] = start;
3381 index[1] = 0;
3382 for (i=0; i<ap->data_ndims; i++) {
3383 index[2] = i;
3384 if(ncvarget1(ap->cdfhandle, ap->varid, index, &origins[i]) < 0)
3385 DXErrorReturn(ERROR_INTERNAL,
3386 "netCDF library error getting origins");
3387 }
3388
3389 /* deltas */
3390 index[1] = 1;
3391 for (i=0; i<ap->data_ndims; i++) {
3392 index[2] = i;
3393 if(ncvarget1(ap->cdfhandle, ap->varid, index, &deltas[i]) < 0)
3394 DXErrorReturn(ERROR_INTERNAL,
3395 "netCDF library error getting deltas");
3396 }
3397
3398
3399 indata = (Array)DXNewRegularArray(TYPE_FLOAT, ap->data_ndims,
3400 ap->datacounts[dim],
3401 (Pointer)origins, (Pointer)deltas);
3402
3403 if(!o)
3404 o = (Object)DXNewGroup();
3405 o = (Object)DXSetMember((Group)o, NULL, (Object)indata);
3406 if(!o)
3407 return NULL;
3408
3409 start += delta;
3410 }
3411
3412 }
3413 return o;
3414 }
3415
3416
3417 /*
3418 * completely regular Ndim connections component
3419 */
build_regcon(Arrayinfo ap)3420 static Object build_regcon(Arrayinfo ap)
3421 {
3422 return (Object)DXMakeGridConnectionsV(ap->data_ndims, ap->datacounts);
3423 }
3424
3425
3426 /*
3427 * completely regular 1D connections array
3428 */
build_regcon1D(Arrayinfo ap,int dim)3429 static Object build_regcon1D(Arrayinfo ap, int dim)
3430 {
3431 return (Object)DXNewPathArray(ap->datacounts[dim]);
3432 }
3433
3434 /* given a variable, return it's name
3435 */
variablename(int hand,int varid,char * cbuf)3436 static Error variablename(int hand, int varid, char *cbuf)
3437 {
3438 nc_type datatype;
3439 int natts, ndims;
3440 int dim[MAX_VAR_DIMS];
3441
3442 /* inquire about the variable - it's size, shape and type */
3443 if(ncvarinq(hand, /* file descriptor */
3444 varid, /* variable ID */
3445 cbuf, /* variable name */
3446 &datatype, /* data item type */
3447 &ndims, /* number of dimensions */
3448 dim, /* array of dimension ID's */
3449 &natts) < 0) /* number of attributes */
3450 DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
3451
3452 if (ndims <= 0) /* no dimensions for this variable */
3453 *cbuf='\0';
3454
3455 return OK;
3456 }
3457
3458
3459
3460 /* return whether a char string matches: scalar, vector, matrix or tensor
3461 */
setrank(char * s)3462 static int setrank(char *s)
3463 {
3464 char *cp;
3465
3466 if(!s || !s[0])
3467 return 0;
3468
3469 for(cp = s; *cp; cp++)
3470 if(isupper(*cp))
3471 *cp = tolower(*cp);
3472
3473 if(!strcmp("scalar", s))
3474 return 0;
3475
3476 if(!strcmp("vector", s))
3477 return 1;
3478
3479 if(!strcmp("matrix", s))
3480 return 2;
3481
3482 if(!strcmp("tensor", s))
3483 return 3;
3484
3485 return 0;
3486 }
3487
3488
3489 /* give it a string in the form: parm, parm, parm; parm, parm, parm; ...
3490 * it will parse up to N parms or ;, whichever occurs first. it sets
3491 * the num found, pointers to each, and returns a pointer to the next set.
3492 * THIS IS DESTRUCTIVE TO THE ORIGINAL STRING (it replaces ,'s with NULLS)
3493 */
parseit(char * in,int * n,char * out[])3494 static char * parseit(char *in, int *n, char *out[])
3495 {
3496 int instring = 0;
3497 int found = 0;
3498 int i;
3499
3500
3501 while(*in != '\0') {
3502
3503 if(isspace(*in)) {
3504 in++;
3505 continue;
3506 }
3507
3508 if(*in == ';') {
3509 *in = '\0';
3510 in++;
3511 break;
3512 }
3513
3514 if(*in == ',') {
3515 *in = '\0';
3516
3517 if(!instring) {
3518 if(++found > *n) {
3519 while(*in && *in != ';')
3520 in++;
3521 if(*in)
3522 in++;
3523 found--;
3524 break;
3525 }
3526
3527 *out = in;
3528 out++;
3529 }
3530
3531 instring = 0;
3532 in++;
3533 continue;
3534 }
3535
3536 if(!instring) {
3537 if(++found > *n) {
3538 while(*in && *in != ';')
3539 in++;
3540 if(*in)
3541 in++;
3542 found--;
3543 break;
3544 }
3545
3546 *out = in;
3547 out++;
3548 instring++;
3549 }
3550
3551 in++;
3552 }
3553
3554 for(i = found; i < *n; i++)
3555 *out++ = NULL;
3556
3557 *n = found;
3558
3559 return in;
3560 }
3561
3562 #else
3563
3564 ImportStatReturn
_dxfstat_netcdf_file(char * filename)3565 _dxfstat_netcdf_file(char *filename)
3566 {
3567 return IMPORT_STAT_NOT_FOUND;
3568 }
3569
DXImportNetCDF(char * filename,char ** variable,int * starttime,int * endtime,int * deltatime)3570 Object DXImportNetCDF(char *filename, char **variable, int *starttime,
3571 int *endtime, int *deltatime)
3572 { DXSetError(ERROR_NOT_IMPLEMENTED, "netCDF libs not included"); return ERROR; }
_dxfQueryImportInfo(char * filename)3573 Object _dxfQueryImportInfo(char *filename)
3574 { DXSetError(ERROR_NOT_IMPLEMENTED, "netCDF libs not included"); return ERROR; }
3575
3576 #endif /* DXD_LACKS_NCDF_FORMAT */
3577