Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: netCDF read/write Driver
4 : * Purpose: GDAL bindings over netCDF library.
5 : * Author: Winor Chen <wchen329 at wisc.edu>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2019, Winor Chen <wchen329 at wisc.edu>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 : #ifndef __NETCDFVIRTUAL_H__
13 : #define __NETCDFVIRTUAL_H__
14 : #include <map>
15 : #include <memory>
16 : #include <string>
17 : #include <vector>
18 : #include "netcdfsg.h"
19 : #include "netcdf.h"
20 :
21 : class netCDFDataset;
22 :
23 : // netCDF Virtual
24 : // Provides a layer of "virtual ncID"
25 : // that can be mapped to a real netCDF ID
26 : namespace nccfdriver
27 : {
28 : // Exceptions
29 : class SG_Exception_NVOOB : public SG_Exception
30 : {
31 : std::string err_msg;
32 :
33 : public:
34 0 : explicit SG_Exception_NVOOB(const char *dsname)
35 0 : : err_msg(std::string("An attempt to read an undefined ID from ") +
36 0 : std::string(dsname) + std::string(" was made"))
37 : {
38 0 : }
39 :
40 0 : const char *get_err_msg() override
41 : {
42 0 : return this->err_msg.c_str();
43 : }
44 : };
45 :
46 : class SG_Exception_DupName : public SG_Exception
47 : {
48 : std::string err_msg;
49 :
50 : public:
51 0 : SG_Exception_DupName(const char *keyn, const char *dsname)
52 0 : : err_msg(std::string("The key ") + std::string(keyn) +
53 0 : std::string(" already exists in") + std::string(dsname))
54 : {
55 0 : }
56 :
57 0 : const char *get_err_msg() override
58 : {
59 0 : return this->err_msg.c_str();
60 : }
61 : };
62 :
63 : class SG_Exception_BadMapping : public SG_Exception
64 : {
65 : std::string err_msg;
66 :
67 : public:
68 0 : SG_Exception_BadMapping(const char *key, const char *where)
69 0 : : err_msg(std::string(key) + std::string(" not found in ") +
70 0 : std::string(where))
71 : {
72 0 : }
73 :
74 0 : const char *get_err_msg() override
75 : {
76 0 : return this->err_msg.c_str();
77 : }
78 : };
79 :
80 : class SG_Exception_VWrite_Failure : public SG_Exception
81 : {
82 : std::string err_msg;
83 :
84 : public:
85 0 : SG_Exception_VWrite_Failure(const char *where, const char *type)
86 0 : : err_msg(std::string("Failed to write ") + std::string(type) +
87 0 : std::string(" to ") + std::string(where))
88 : {
89 0 : }
90 :
91 0 : const char *get_err_msg() override
92 : {
93 0 : return this->err_msg.c_str();
94 : }
95 : };
96 :
97 : /* netCDFVAttribute
98 : * -
99 : * Contains attribute name and data.
100 : * Central to derived types are reimplementations of vsync
101 : */
102 : class netCDFVAttribute
103 : {
104 : public:
105 : /* vsync(...)
106 : * Implementation: Given the REAL ncID and REAL variable ID
107 : * Write the attribute to the variable
108 : */
109 : virtual void vsync(int realncid, int realvarid) = 0;
110 :
111 : /* ~netCDFVAttribute()
112 : * Virtual destructor
113 : */
114 749 : virtual ~netCDFVAttribute()
115 749 : {
116 749 : }
117 : };
118 :
119 : template <class VClass, nc_type ntype>
120 : class netCDFVGeneralAttribute : public netCDFVAttribute
121 : {
122 : std::string name;
123 : VClass value;
124 :
125 : public:
126 51 : netCDFVGeneralAttribute(const char *a_name, const VClass *a_value)
127 51 : : name(a_name), value(*a_value)
128 : {
129 51 : }
130 :
131 51 : void vsync(int realncid, int realvarid) override
132 : {
133 51 : if (nc_put_att(realncid, realvarid, name.c_str(), ntype, 1, &value) !=
134 : NC_NOERR)
135 : {
136 0 : throw SG_Exception_VWrite_Failure("variable", "attribute");
137 : }
138 51 : }
139 : };
140 :
141 : /* netCDFVTextAttribute
142 : * -
143 : * Attribute that has a text string value
144 : */
145 : class netCDFVTextAttribute : public netCDFVAttribute
146 : {
147 : std::string name;
148 : std::string value;
149 :
150 : public:
151 698 : netCDFVTextAttribute(const char *a_name, const char *a_value)
152 698 : : name(a_name), value(a_value)
153 : {
154 698 : }
155 :
156 : void vsync(int realncid, int realvarid) override;
157 : };
158 :
159 : typedef netCDFVGeneralAttribute<signed char, NC_BYTE> netCDFVByteAttribute;
160 : typedef netCDFVGeneralAttribute<int, NC_INT> netCDFVIntAttribute;
161 : typedef netCDFVGeneralAttribute<double, NC_DOUBLE> netCDFVDoubleAttribute;
162 : typedef netCDFVGeneralAttribute<float, NC_FLOAT> netCDFVFloatAttribute;
163 :
164 : /* netCDFVDimension
165 : * -
166 : * Contains the real dim id, real dimension name, and dimension length
167 : */
168 : class netCDFVDimension
169 : {
170 : friend class netCDFVID;
171 :
172 : std::string real_dim_name;
173 : int r_did = INVALID_DIM_ID;
174 : int v_did;
175 : size_t dim_len;
176 : bool valid = true;
177 :
178 : protected:
179 145 : void setRealID(int realID)
180 : {
181 145 : this->r_did = realID;
182 145 : }
183 :
184 : void invalidate();
185 :
186 136 : void setLen(size_t len)
187 : {
188 136 : this->dim_len = len;
189 136 : }
190 :
191 : public:
192 149 : netCDFVDimension(const char *name, size_t len, int dimid)
193 149 : : real_dim_name(name), v_did(dimid), dim_len(len)
194 : {
195 149 : }
196 :
197 149 : std::string &getName()
198 : {
199 149 : return this->real_dim_name;
200 : }
201 :
202 1029 : size_t getLen()
203 : {
204 1029 : return this->dim_len;
205 : }
206 :
207 423 : int getRealID()
208 : {
209 423 : return this->r_did;
210 : }
211 :
212 : int getVirtualID()
213 : {
214 : return this->v_did;
215 : }
216 :
217 149 : bool isValid()
218 : {
219 149 : return this->valid;
220 : }
221 : };
222 :
223 : /* netCDFVVariable
224 : * -
225 : * Contains the variable name, variable type, etc.
226 : */
227 : class netCDFVVariable
228 : {
229 : friend class netCDFVID;
230 :
231 : std::string real_var_name;
232 : nc_type ntype;
233 : int r_vid = INVALID_VAR_ID;
234 : int ndimc;
235 : std::unique_ptr<int, std::default_delete<int[]>> dimid;
236 : std::vector<std::shared_ptr<netCDFVAttribute>> attribs;
237 : bool valid = true;
238 :
239 : protected:
240 2725 : std::vector<std::shared_ptr<netCDFVAttribute>> &getAttributes()
241 : {
242 2725 : return attribs;
243 : }
244 :
245 : void invalidate();
246 :
247 239 : void setRealID(int realID)
248 : {
249 239 : this->r_vid = realID;
250 239 : }
251 :
252 : public:
253 : netCDFVVariable(const char *name, nc_type xtype, int ndims,
254 : const int *dimidsp);
255 :
256 260 : std::string &getName()
257 : {
258 260 : return real_var_name;
259 : }
260 :
261 24349 : int getRealID()
262 : {
263 24349 : return r_vid;
264 : }
265 :
266 239 : nc_type getType()
267 : {
268 239 : return ntype;
269 : }
270 :
271 1004 : int getDimCount()
272 : {
273 1004 : return this->ndimc;
274 : }
275 :
276 287 : const int *getDimIds()
277 : {
278 287 : return this->dimid.get();
279 : }
280 :
281 260 : bool isValid()
282 : {
283 260 : return this->valid;
284 : }
285 : };
286 :
287 : /* netCDFVID
288 : * -
289 : * A netCDF ID that sits on top of an actual netCDF ID
290 : * And manages actual interaction with the real netCDF file
291 : *
292 : * A bit difference is that netCDFVID
293 : * doesn't have fixed dim sizes, until defines are committed
294 : *
295 : * Also, virtual attributes only exist until the variable is committed. Use
296 : * "real" attributes and "real" IDs for a variable after its been committed.
297 : *
298 : * ** Do not mix netCDF virtual dim and variable IDs with regular netCDF dim
299 : * (a.k.a. "real") ids and variable ids. They are NOT necessarily compatible,
300 : * and must be translated first, to be used in this manner **
301 : *
302 : * The netCDFVID can also be used in what is called "direct mode" and the
303 : * netCDFVID will just act as a wrapper to the netCDF Library. In such a case
304 : * netCDFVID should take real IDs, not real ones. However, the big advantages of
305 : * using netCDFVID (such as quick dim resizing) are no longer are available.
306 : */
307 : class netCDFVID
308 : {
309 : netCDFDataset *m_poDS = nullptr;
310 : int &ncid; // ncid REF. which tracks ncID changes that may be made upstream
311 : int dimTicket = 0;
312 : int varTicket = 0;
313 : bool directMode = true;
314 :
315 : std::vector<netCDFVVariable> varList;
316 : std::vector<netCDFVDimension> dimList;
317 :
318 : std::map<std::string, int> nameDimTable;
319 : std::map<std::string, int> nameVarTable;
320 :
321 : public:
322 : // Each of these returns an ID, NOT an error code
323 :
324 : /* nc_def_vdim(...)
325 : * Defines a virtual dim given the parameters NAME and LENGTH.
326 : * Returns: virtual dimID
327 : */
328 : int nc_def_vdim(
329 : const char *name,
330 : size_t dimlen); // for dims that don't already exist in netCDF file
331 :
332 : /* nc_def_vvar(...)
333 : * Defines a virtual var given the parameters NAME, NC TYPE, NUMBER OF DIMS,
334 : * and DIM IDS The dim IDs in dimidsp given are to be virtual dim IDs, using
335 : * real dim IDs is undefined
336 : */
337 : int nc_def_vvar(const char *name, nc_type xtype, int ndims,
338 : const int *dimidsp);
339 :
340 : /* nc_del_vdim(...)
341 : * Delete a virtual dimension
342 : * NOTES:
343 : * This doesn't work on committed IDs.
344 : * Also the dimension (for now) will be only invalidated, doesn't
345 : * completely *delete* it in memory.
346 : */
347 : void nc_del_vdim(int dimid);
348 :
349 : /* nc_del_vvar(...)
350 : * Delete a virtual variable
351 : * NOTES:
352 : * This doesn't work on committed IDs.
353 : * Also the variable (for now) will be only invalidated, doesn't
354 : * completely *delete* it in memory.
355 : */
356 : void nc_del_vvar(int varid);
357 :
358 : /* nc_resize_vdim(...)
359 : * Change the size of a virtual dim to the given size.
360 : * NOTE: if the dim has committed using nc_vmap then this has no effect.
361 : */
362 : void nc_resize_vdim(
363 : int dimid,
364 : size_t dimlen); // for dims that haven't been mapped to physical yet
365 :
366 : /* nc_set_define_mode()
367 : * Convenience function for setting the ncid to define mode
368 : */
369 : void nc_set_define_mode();
370 :
371 : /* nc_set_data_mode()
372 : * Convenience function for setting the ncid to data mode
373 : */
374 : void nc_set_data_mode();
375 :
376 : /* nc_vmap()
377 : * Maps virtual IDs to real physical ID if that mapping doesn't already
378 : * exist This is required before writing data to virtual IDs that do not
379 : * exist yet in the netCDF file
380 : */
381 : void nc_vmap();
382 :
383 : /* void enableFullVirtualMode
384 : * Enables full virtual mode (i.e. allows netCDFVID to use its full
385 : * capabilities).
386 : */
387 104 : void enableFullVirtualMode()
388 : {
389 104 : this->directMode = false;
390 104 : }
391 :
392 : // Attribute function(s)
393 : template <class attrC, class attrT>
394 749 : void nc_put_vatt_generic(int varid, const char *name, const attrT *value)
395 : {
396 :
397 749 : if (varid >= static_cast<int>(varList.size()) || varid < 0)
398 : {
399 0 : throw SG_Exception_NVOOB("virtual variable collection");
400 : }
401 :
402 749 : netCDFVVariable &v = virtualVIDToVar(varid);
403 1498 : v.getAttributes().push_back(
404 749 : std::shared_ptr<netCDFVAttribute>(new attrC(name, value)));
405 749 : }
406 :
407 : void nc_put_vatt_text(int varid, const char *name, const char *value);
408 : void nc_put_vatt_int(int varid, const char *name, const int *value);
409 : void nc_put_vatt_double(int varid, const char *name, const double *value);
410 : void nc_put_vatt_float(int varid, const char *name, const float *value);
411 : void nc_put_vatt_byte(int varid, const char *name,
412 : const signed char *value);
413 :
414 : // Writing Functions
415 : template <class out_T>
416 23250 : void nc_put_vvar_generic(int varid, const size_t *index, const out_T *value)
417 : {
418 23250 : int rvarid = !directMode ? virtualVIDToVar(varid).getRealID() : varid;
419 :
420 23250 : if (rvarid == INVALID_VAR_ID)
421 95 : return; // invalidated variable, specific condition that Scribe
422 : // relies on
423 :
424 23155 : if (nc_put_var1(ncid, rvarid, index, value) != NC_NOERR)
425 : {
426 0 : throw SG_Exception_VWrite_Failure("variable", "datum");
427 : }
428 : }
429 :
430 : template <class outArr_T>
431 201 : void nc_put_vvara_generic(int varid, const size_t *index,
432 : const size_t *count, const outArr_T *value)
433 : {
434 201 : int rvarid = !directMode ? virtualVIDToVar(varid).getRealID() : varid;
435 :
436 201 : if (rvarid == INVALID_VAR_ID)
437 20 : return; // invalidated variable, specific condition that Scribe
438 : // relies on
439 :
440 181 : if (nc_put_vara(ncid, rvarid, index, count, value) != NC_NOERR)
441 : {
442 0 : throw SG_Exception_VWrite_Failure("variable", "data array");
443 : }
444 : }
445 :
446 : void nc_put_vvar1_text(int varid, const size_t *index, const char *value);
447 : void nc_put_vvara_text(int varid, const size_t *start, const size_t *index,
448 : const char *value);
449 : void nc_put_vvar1_string(int varid, const size_t *index,
450 : const char **value);
451 :
452 : // Equivalent "enquiry" functions
453 : netCDFVVariable &
454 : virtualVIDToVar(int virtualID); // converts a virtual var ID to a real ID
455 : netCDFVDimension &
456 : virtualDIDToDim(int virtualID); // converts a virtual dim ID to a real ID
457 : int nameToVirtualVID(const std::string &name);
458 : int nameToVirtualDID(const std::string &name);
459 :
460 77 : bool virtualVarNameDefined(const std::string &nm)
461 : {
462 77 : return nameVarTable.count(nm) > 0;
463 : }
464 :
465 : // Constructor
466 1036 : explicit netCDFVID(netCDFDataset *poDS, int &ncid_in)
467 1036 : : m_poDS(poDS), ncid(ncid_in)
468 : {
469 1036 : }
470 : };
471 :
472 : } // namespace nccfdriver
473 : #endif
|