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 __NETCDFSGWRITERUTIL_H__
13 : #define __NETCDFSGWRITERUTIL_H__
14 : #include <cstdio>
15 : #include <queue>
16 : #include <typeinfo>
17 : #include <vector>
18 : #include "cpl_vsi.h"
19 : #include "ogr_core.h"
20 : #include "ogrsf_frmts.h"
21 : #include "netcdflayersg.h"
22 : #include "netcdfsg.h"
23 : #include "netcdfvirtual.h"
24 :
25 : namespace nccfdriver
26 : {
27 :
28 : /* OGR_SGeometry_Feature
29 : * Constructs over a OGRFeature
30 : * gives some basic information about that SGeometry Feature such as
31 : * Hold references... limited to scope of its references
32 : * - what's its geometry type
33 : * - how much total points it has
34 : * - how many parts it has
35 : * - a vector of counts of points for each part
36 : */
37 : class SGeometry_Feature
38 : {
39 : bool hasInteriorRing;
40 : const OGRGeometry *geometry_ref;
41 : geom_t type;
42 : size_t total_point_count;
43 : size_t total_part_count;
44 : std::vector<size_t> ppart_node_count;
45 : std::vector<bool> part_at_ind_interior; // for use with Multipolygons ONLY
46 : mutable OGRPoint pt_buffer;
47 :
48 : public:
49 446 : geom_t getType()
50 : {
51 446 : return this->type;
52 : }
53 :
54 409 : size_t getTotalNodeCount()
55 : {
56 409 : return this->total_point_count;
57 : }
58 :
59 1558 : size_t getTotalPartCount()
60 : {
61 1558 : return this->total_part_count;
62 : }
63 :
64 68422 : std::vector<size_t> &getPerPartNodeCount()
65 : {
66 68422 : return this->ppart_node_count;
67 : }
68 :
69 : const OGRPoint &getPoint(size_t part_no, int point_index) const;
70 : explicit SGeometry_Feature(OGRFeature &);
71 :
72 : bool getHasInteriorRing()
73 : {
74 : return this->hasInteriorRing;
75 : }
76 :
77 591 : bool IsPartAtIndInteriorRing(size_t ind)
78 : {
79 591 : return this->part_at_ind_interior[ind];
80 : } // ONLY used for Multipolygon
81 : };
82 :
83 : /* A memory buffer with a soft limit
84 : * Has basic capability of over quota checking, and memory counting
85 : */
86 : class WBuffer
87 : {
88 : unsigned long long used_mem = 0;
89 :
90 : public:
91 : /* addCount(...)
92 : * Takes in a size, and directly adds that size to memory count
93 : */
94 : void addCount(unsigned long long memuse);
95 :
96 : /* subCount(...)
97 : * Directly subtracts the specified size from used_mem
98 : */
99 : void subCount(unsigned long long memfree);
100 :
101 892 : unsigned long long &getUsage()
102 : {
103 892 : return used_mem;
104 : }
105 :
106 360 : void reset()
107 : {
108 360 : this->used_mem = 0;
109 360 : }
110 :
111 1932 : WBuffer()
112 1932 : {
113 1932 : }
114 : };
115 :
116 : /* OGR_SGFS_Transaction
117 : * Abstract class for a committable transaction
118 : *
119 : */
120 : class OGR_SGFS_Transaction
121 : {
122 : int varId = INVALID_VAR_ID;
123 :
124 : public:
125 : /* int commit(...);
126 : * Arguments: int ncid, the dataset to write to
127 : * int write_loc, the index in which to write to
128 : * Implementation: should write the transaction to netCDF file
129 : *
130 : */
131 : virtual void commit(netCDFVID &n, size_t write_loc) = 0;
132 :
133 : /* unsigned long long count(...)
134 : * Implementation: supposed to return an approximate count of memory usage
135 : * Most classes will implement with sizeof(*this), except if otherwise
136 : * uncounted for dynamic allocation is involved.
137 : */
138 : virtual unsigned long long count() = 0;
139 :
140 : /* appendToLog
141 : * Implementation - given a file pointer, a transaction will be written to
142 : * that log file in the format:
143 : * -
144 : * transactionVarId - sizeof(int) bytes
145 : * NC_TYPE - sizeof(int) bytes
146 : * (nc_char only) OP - 1 byte (0 if does not require COUNT or non-zero i.e.
147 : * 1 if does) (nc_char only): SIZE of data - sizeof(size_t) bytes DATA -
148 : * size depends on NC_TYPE
149 : */
150 : virtual void appendToLog(VSILFILE *) = 0;
151 :
152 : /* ~OGR_SGFS_Transaction()
153 : * Empty. Simply here to stop the compiler from complaining...
154 : */
155 224718 : virtual ~OGR_SGFS_Transaction()
156 224718 : {
157 224718 : }
158 :
159 : /* OGR_SGFS_Transaction()
160 : * Empty. Simply here to stop one of the CI machines from complaining...
161 : */
162 224718 : OGR_SGFS_Transaction()
163 224718 : {
164 224718 : }
165 :
166 : /* void getVarId(...);
167 : * Gets the var in which to commit the transaction to.
168 : */
169 769574 : int getVarId()
170 : {
171 769574 : return this->varId;
172 : }
173 :
174 : /* nc_type getType
175 : * Returns the type of transaction being saved
176 : */
177 : virtual nc_type getType() = 0;
178 :
179 : /* void setVarId(...);
180 : * Sets the var in which to commit the transaction to.
181 : */
182 224718 : void setVarId(int vId)
183 : {
184 224718 : this->varId = vId;
185 224718 : }
186 : };
187 :
188 : typedef std::map<int, void *> NCWMap;
189 : typedef std::pair<int, void *> NCWEntry; // NC Writer Entry
190 : typedef std::unique_ptr<OGR_SGFS_Transaction>
191 : MTPtr; // a.k.a Managed Transaction Ptr
192 :
193 : template <class T_c_type, nc_type T_nc_type>
194 87851 : void genericLogAppend(T_c_type r, int vId, VSILFILE *f)
195 : {
196 87851 : T_c_type rep = r;
197 87851 : int varId = vId;
198 87851 : int type = T_nc_type;
199 87851 : VSIFWriteL(&varId, sizeof(int), 1, f); // write varID data
200 87851 : VSIFWriteL(&type, sizeof(int), 1, f); // write NC type
201 87851 : VSIFWriteL(&rep, sizeof(T_c_type), 1, f); // write data
202 87851 : }
203 :
204 : template <class T_c_type, class T_r_type>
205 87851 : MTPtr genericLogDataRead(int varId, VSILFILE *f)
206 : {
207 : T_r_type data;
208 87851 : if (!VSIFReadL(&data, sizeof(T_r_type), 1, f))
209 : {
210 0 : return MTPtr(nullptr); // invalid read case
211 : }
212 87851 : return MTPtr(new T_c_type(varId, data));
213 : }
214 :
215 : /* OGR_SGFS_NC_Char_Transaction
216 : * Writes to an NC_CHAR variable
217 : */
218 : class OGR_SGFS_NC_Char_Transaction : public OGR_SGFS_Transaction
219 : {
220 : std::string char_rep;
221 :
222 : public:
223 0 : void commit(netCDFVID &n, size_t write_loc) override
224 : {
225 0 : n.nc_put_vvar1_text(OGR_SGFS_Transaction::getVarId(), &write_loc,
226 : char_rep.c_str());
227 0 : }
228 :
229 0 : unsigned long long count() override
230 : {
231 0 : return char_rep.size() + sizeof(*this);
232 : } // account for actual character representation, this class
233 :
234 : void appendToLog(VSILFILE *f) override;
235 :
236 0 : nc_type getType() override
237 : {
238 0 : return NC_CHAR;
239 : }
240 :
241 0 : OGR_SGFS_NC_Char_Transaction(int i_varId, const char *pszVal)
242 0 : : char_rep(pszVal)
243 : {
244 0 : OGR_SGFS_Transaction::setVarId(i_varId);
245 0 : }
246 : };
247 :
248 : /* OGR_SGFS_NC_CharA_Transaction
249 : * Writes to an NC_CHAR variable, using vara instead of var1
250 : * Used to store 2D character array values, specifically
251 : */
252 : class OGR_SGFS_NC_CharA_Transaction : public OGR_SGFS_Transaction
253 : {
254 : std::string char_rep;
255 : size_t counts[2];
256 :
257 : public:
258 884 : void commit(netCDFVID &n, size_t write_loc) override
259 : {
260 884 : size_t ind[2] = {write_loc, 0};
261 884 : n.nc_put_vvara_text(OGR_SGFS_Transaction::getVarId(), ind, counts,
262 : char_rep.c_str());
263 884 : }
264 :
265 2356 : unsigned long long count() override
266 : {
267 2356 : return char_rep.size() + sizeof(*this);
268 : } // account for actual character representation, this class
269 :
270 : void appendToLog(VSILFILE *f) override;
271 :
272 884 : nc_type getType() override
273 : {
274 884 : return NC_CHAR;
275 : }
276 :
277 1472 : OGR_SGFS_NC_CharA_Transaction(int i_varId, const char *pszVal)
278 1472 : : char_rep(pszVal), counts{1, char_rep.length()}
279 : {
280 1472 : OGR_SGFS_Transaction::setVarId(i_varId);
281 1472 : }
282 : };
283 :
284 : template <class VClass, nc_type ntype>
285 : class OGR_SGFS_NC_Transaction_Generic : public OGR_SGFS_Transaction
286 : {
287 : VClass rep;
288 :
289 : public:
290 23250 : void commit(netCDFVID &n, size_t write_loc) override
291 : {
292 23250 : n.nc_put_vvar_generic<VClass>(OGR_SGFS_Transaction::getVarId(),
293 23250 : &write_loc, &rep);
294 23250 : }
295 :
296 358613 : unsigned long long count() override
297 : {
298 358613 : return sizeof(*this);
299 : }
300 :
301 87851 : void appendToLog(VSILFILE *f) override
302 : {
303 87851 : genericLogAppend<VClass, ntype>(rep, OGR_SGFS_Transaction::getVarId(),
304 : f);
305 87851 : }
306 :
307 223232 : OGR_SGFS_NC_Transaction_Generic(int i_varId, VClass in) : rep(in)
308 : {
309 223232 : OGR_SGFS_Transaction::setVarId(i_varId);
310 223232 : }
311 :
312 112131 : VClass getData()
313 : {
314 112131 : return rep;
315 : }
316 :
317 382893 : nc_type getType() override
318 : {
319 382893 : return ntype;
320 : }
321 : };
322 :
323 : typedef OGR_SGFS_NC_Transaction_Generic<signed char, NC_BYTE>
324 : OGR_SGFS_NC_Byte_Transaction;
325 : typedef OGR_SGFS_NC_Transaction_Generic<short, NC_SHORT>
326 : OGR_SGFS_NC_Short_Transaction;
327 : typedef OGR_SGFS_NC_Transaction_Generic<int, NC_INT>
328 : OGR_SGFS_NC_Int_Transaction;
329 : typedef OGR_SGFS_NC_Transaction_Generic<float, NC_FLOAT>
330 : OGR_SGFS_NC_Float_Transaction;
331 : typedef OGR_SGFS_NC_Transaction_Generic<double, NC_DOUBLE>
332 : OGR_SGFS_NC_Double_Transaction;
333 : typedef OGR_SGFS_NC_Transaction_Generic<unsigned, NC_UINT>
334 : OGR_SGFS_NC_UInt_Transaction;
335 : typedef OGR_SGFS_NC_Transaction_Generic<unsigned long long, NC_UINT64>
336 : OGR_SGFS_NC_UInt64_Transaction;
337 : typedef OGR_SGFS_NC_Transaction_Generic<long long, NC_INT64>
338 : OGR_SGFS_NC_Int64_Transaction;
339 : typedef OGR_SGFS_NC_Transaction_Generic<unsigned char, NC_UBYTE>
340 : OGR_SGFS_NC_UByte_Transaction;
341 : typedef OGR_SGFS_NC_Transaction_Generic<unsigned short, NC_USHORT>
342 : OGR_SGFS_NC_UShort_Transaction;
343 :
344 : /* OGR_SGFS_NC_String_Transaction
345 : * Writes to an NC_STRING variable, in a similar manner as NC_Char
346 : */
347 : class OGR_SGFS_NC_String_Transaction : public OGR_SGFS_Transaction
348 : {
349 : std::string char_rep;
350 :
351 : public:
352 14 : void commit(netCDFVID &n, size_t write_loc) override
353 : {
354 14 : const char *writable = char_rep.c_str();
355 14 : n.nc_put_vvar1_string(OGR_SGFS_Transaction::getVarId(), &write_loc,
356 : &(writable));
357 14 : }
358 :
359 28 : unsigned long long count() override
360 : {
361 28 : return char_rep.size() + sizeof(*this);
362 : } // account for actual character representation, this class
363 :
364 28 : nc_type getType() override
365 : {
366 28 : return NC_STRING;
367 : }
368 :
369 : void appendToLog(VSILFILE *f) override;
370 :
371 14 : OGR_SGFS_NC_String_Transaction(int i_varId, const char *pszVal)
372 14 : : char_rep(pszVal)
373 : {
374 14 : OGR_SGFS_Transaction::setVarId(i_varId);
375 14 : }
376 : };
377 :
378 : /* WTransactionLog
379 : * -
380 : * A temporary file which contains transactions to be written to a netCDF file.
381 : * Once the transaction log is created it is set on write mode, it can only be
382 : * read to after startRead() is called
383 : */
384 : class WTransactionLog
385 : {
386 : bool readMode = false;
387 : std::string wlogName; // name of the temporary file, should be unique
388 : VSILFILE *log = nullptr;
389 :
390 : WTransactionLog(WTransactionLog &); // avoid possible undefined behavior
391 : WTransactionLog operator=(const WTransactionLog &);
392 :
393 : public:
394 360 : bool logIsNull()
395 : {
396 360 : return log == nullptr;
397 : }
398 :
399 : void startLog(); // always call this first to open the file
400 : void startRead(); // then call this before reading it
401 : void push(MTPtr);
402 :
403 : // read mode
404 : MTPtr
405 : pop(); // to test for EOF, test to see if pointer returned is null ptr
406 :
407 : // construction, destruction
408 : explicit WTransactionLog(const std::string &logName);
409 : ~WTransactionLog();
410 : };
411 :
412 : /* OGR_NCScribe
413 : * Buffers several netCDF transactions in memory or in a log.
414 : * General scribe class
415 : */
416 : class OGR_NCScribe
417 : {
418 : netCDFVID &ncvd;
419 : WBuffer buf;
420 : WTransactionLog wl;
421 : bool singleDatumMode = false;
422 :
423 : std::queue<MTPtr> transactionQueue;
424 : std::map<int, size_t> varWriteInds;
425 : std::map<int, size_t> varMaxInds;
426 :
427 : public:
428 : /* size_t getWriteCount()
429 : * Return the total write count (happened + pending) of certain variable
430 : */
431 : size_t getWriteCount(int varId)
432 : {
433 : return this->varMaxInds.at(varId);
434 : }
435 :
436 : /* void commit_transaction()
437 : * Replays all transactions to disk (according to fs stipulations)
438 : */
439 : void commit_transaction();
440 :
441 : /* MTPtr pop()
442 : * Get the next transaction, if it exists.
443 : * If not, it will just return a shared_ptr with nullptr inside
444 : */
445 : MTPtr pop();
446 :
447 : /* void log_transacion()
448 : * Saves the current queued transactions to a log.
449 : */
450 : void log_transaction();
451 :
452 : /* void enqueue_transaction()
453 : * Add a transaction to perform
454 : * Once a transaction is enqueued, it will only be dequeued on commit
455 : */
456 : void enqueue_transaction(MTPtr transactionAdd);
457 :
458 1932 : WBuffer &getMemBuffer()
459 : {
460 1932 : return buf;
461 : }
462 :
463 : /* OGR_SGeometry_Field_Scribe()
464 : * Constructs a Field Scribe over a dataset
465 : */
466 1932 : OGR_NCScribe(netCDFVID &ncd, const std::string &name) : ncvd(ncd), wl(name)
467 : {
468 1932 : }
469 :
470 : /* setSingleDatumMode(...)
471 : * Enables or disables single datum mode
472 : * DO NOT use this when a commit is taking place, otherwise
473 : * corruption may occur...
474 : */
475 2 : void setSingleDatumMode(bool sdm)
476 : {
477 2 : this->singleDatumMode = sdm;
478 2 : }
479 : };
480 :
481 : class ncLayer_SG_Metadata
482 : {
483 : int &ncID; // ncid REF. which tracks ncID changes that may be made upstream
484 :
485 : netCDFVID &vDataset;
486 : OGR_NCScribe &ncb;
487 : geom_t writableType = NONE;
488 : std::string containerVarName;
489 : int containerVar_realID = INVALID_VAR_ID;
490 : bool interiorRingDetected =
491 : false; // flips on when an interior ring polygon has been detected
492 : std::vector<int>
493 : node_coordinates_varIDs; // ids in X, Y (and then possibly Z) order
494 : int node_coordinates_dimID = INVALID_DIM_ID; // dim of all node_coordinates
495 : int node_count_dimID = INVALID_DIM_ID; // node count dim
496 : int node_count_varID = INVALID_DIM_ID;
497 : int pnc_dimID =
498 : INVALID_DIM_ID; // part node count dim AND interior ring dim
499 : int pnc_varID = INVALID_VAR_ID;
500 : int intring_varID = INVALID_VAR_ID;
501 : size_t next_write_pos_node_coord = 0;
502 : size_t next_write_pos_node_count = 0;
503 : size_t next_write_pos_pnc = 0;
504 :
505 : public:
506 216 : geom_t getWritableType()
507 : {
508 216 : return this->writableType;
509 : }
510 :
511 : void writeSGeometryFeature(SGeometry_Feature &ft);
512 :
513 525 : int get_containerRealID()
514 : {
515 525 : return this->containerVar_realID;
516 : }
517 :
518 77 : std::string get_containerName()
519 : {
520 77 : return this->containerVarName;
521 : }
522 :
523 67 : int get_node_count_dimID()
524 : {
525 67 : return this->node_count_dimID;
526 : }
527 :
528 55 : int get_node_coord_dimID()
529 : {
530 55 : return this->node_coordinates_dimID;
531 : }
532 :
533 34 : int get_pnc_dimID()
534 : {
535 34 : return this->pnc_dimID;
536 : }
537 :
538 4 : int get_pnc_varID()
539 : {
540 4 : return this->pnc_varID;
541 : }
542 :
543 17 : int get_intring_varID()
544 : {
545 17 : return this->intring_varID;
546 : }
547 :
548 38 : std::vector<int> &get_nodeCoordVarIDs()
549 : {
550 38 : return this->node_coordinates_varIDs;
551 : }
552 :
553 42 : size_t get_next_write_pos_node_coord()
554 : {
555 42 : return this->next_write_pos_node_coord;
556 : }
557 :
558 32 : size_t get_next_write_pos_node_count()
559 : {
560 32 : return this->next_write_pos_node_count;
561 : }
562 :
563 27 : size_t get_next_write_pos_pnc()
564 : {
565 27 : return this->next_write_pos_pnc;
566 : }
567 :
568 45 : bool getInteriorRingDetected()
569 : {
570 45 : return this->interiorRingDetected;
571 : }
572 :
573 : void initializeNewContainer(int containerVID);
574 : ncLayer_SG_Metadata(int &i_ncID, geom_t geo, netCDFVID &ncdf,
575 : OGR_NCScribe &scribe);
576 : };
577 :
578 : /* WBufferManager
579 : * -
580 : * Simply takes a collection of buffers in and a quota limit and sums all the
581 : * usages up to establish if buffers are over the soft limit (collectively)
582 : *
583 : * The buffers added, however, are not memory managed by WBufferManager
584 : */
585 : class WBufferManager
586 : {
587 : unsigned long long buffer_soft_limit = 0;
588 : std::vector<WBuffer *> bufs;
589 :
590 : public:
591 : bool isOverQuota();
592 :
593 4 : void adjustLimit(unsigned long long lim)
594 : {
595 4 : this->buffer_soft_limit = lim;
596 4 : }
597 :
598 1932 : void addBuffer(WBuffer *b)
599 : {
600 1932 : this->bufs.push_back(b);
601 1932 : }
602 :
603 966 : explicit WBufferManager(unsigned long long lim) : buffer_soft_limit(lim)
604 : {
605 966 : }
606 : };
607 :
608 : // Exception Classes
609 : class SGWriter_Exception : public SG_Exception
610 : {
611 : public:
612 0 : const char *get_err_msg() override
613 : {
614 0 : return "A general error occurred when writing a netCDF dataset";
615 : }
616 : };
617 :
618 : class SGWriter_Exception_NCWriteFailure : public SGWriter_Exception
619 : {
620 : std::string msg;
621 :
622 : public:
623 0 : const char *get_err_msg() override
624 : {
625 0 : return this->msg.c_str();
626 : }
627 :
628 : SGWriter_Exception_NCWriteFailure(const char *layer_name,
629 : const char *failure_name,
630 : const char *failure_type);
631 : };
632 :
633 : class SGWriter_Exception_NCInqFailure : public SGWriter_Exception
634 : {
635 : std::string msg;
636 :
637 : public:
638 0 : const char *get_err_msg() override
639 : {
640 0 : return this->msg.c_str();
641 : }
642 :
643 : SGWriter_Exception_NCInqFailure(const char *layer_name,
644 : const char *failure_name,
645 : const char *failure_type);
646 : };
647 :
648 : class SGWriter_Exception_NCDefFailure : public SGWriter_Exception
649 : {
650 : std::string msg;
651 :
652 : public:
653 0 : const char *get_err_msg() override
654 : {
655 0 : return this->msg.c_str();
656 : }
657 :
658 : SGWriter_Exception_NCDefFailure(const char *layer_name,
659 : const char *failure_name,
660 : const char *failure_type);
661 : };
662 :
663 : class SGWriter_Exception_NullGeometry : public SGWriter_Exception
664 : {
665 : std::string msg;
666 :
667 : public:
668 0 : const char *get_err_msg() override
669 : {
670 0 : return this->msg.c_str();
671 : }
672 :
673 0 : SGWriter_Exception_NullGeometry()
674 0 : : msg("A null geometry was detected when writing a netCDF file. "
675 0 : "Empty geometries are not allowed.")
676 : {
677 0 : }
678 : };
679 :
680 : class SGWriter_Exception_NCDelFailure : public SGWriter_Exception
681 : {
682 : std::string msg;
683 :
684 : public:
685 0 : const char *get_err_msg() override
686 : {
687 0 : return this->msg.c_str();
688 : }
689 :
690 0 : SGWriter_Exception_NCDelFailure(const char *layer, const char *what)
691 0 : : msg("[" + std::string(layer) +
692 0 : "] Failed to delete: " + std::string(what))
693 : {
694 0 : }
695 : };
696 :
697 : // Helper Functions that for writing
698 :
699 : /* std::vector<..> writeGeometryContainer(...)
700 : * Writes a geometry container of a given geometry type given the following
701 : * arguments: int ncID - ncid as used in netcdf.h, group or file id std::string
702 : * name - what to name this container geom_t geometry_type - the geometry type
703 : * of the container std::vector<..> node_coordinate_names - variable names
704 : * corresponding to each axis Only writes attributes that are for sure required.
705 : * i.e. does NOT required interior ring for anything or part node count for
706 : * Polygons
707 : *
708 : * Returns: geometry container variable ID
709 : */
710 : int write_Geometry_Container(
711 : int ncID, const std::string &name, geom_t geometry_type,
712 : const std::vector<std::string> &node_coordinate_names);
713 :
714 : template <class W_type>
715 112131 : inline void NCWMapAllocIfNeeded(int varid, NCWMap &mapAdd, size_t numEntries,
716 : std::vector<int> &v)
717 : {
718 112131 : if (mapAdd.count(varid) < 1)
719 : {
720 201 : mapAdd.insert(NCWEntry(varid, CPLMalloc(sizeof(W_type) * numEntries)));
721 201 : v.push_back(varid);
722 : }
723 112131 : }
724 :
725 : template <class W_type>
726 112131 : inline void NCWMapWriteAndCommit(int varid, NCWMap &mapAdd, size_t currentEntry,
727 : size_t numEntries, W_type data,
728 : netCDFVID &vcdf)
729 : {
730 112131 : W_type *ptr = static_cast<W_type *>(mapAdd.at(varid));
731 112131 : ptr[currentEntry] = data;
732 : static const size_t BEGIN = 0;
733 :
734 : // If all items are ready, write the array, and free it, delete the pointer
735 112131 : if (currentEntry == (numEntries - 1))
736 : {
737 : try
738 : {
739 : // Write the whole array at once
740 201 : vcdf.nc_put_vvara_generic<W_type>(varid, &BEGIN, &numEntries, ptr);
741 : }
742 0 : catch (SG_Exception_VWrite_Failure &e)
743 : {
744 0 : CPLError(CE_Warning, CPLE_FileIO, "%s", e.get_err_msg());
745 : }
746 :
747 201 : CPLFree(mapAdd.at(varid));
748 201 : mapAdd.erase(varid);
749 : }
750 112131 : }
751 : } // namespace nccfdriver
752 :
753 : #endif
|