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 : #include "netcdfsgwriterutil.h"
13 : #include "netcdfdataset.h"
14 :
15 : namespace nccfdriver
16 : {
17 442 : SGeometry_Feature::SGeometry_Feature(OGRFeature &ft)
18 : {
19 442 : this->hasInteriorRing = false;
20 442 : const OGRGeometry *geom = ft.GetGeometryRef();
21 :
22 442 : if (geom == nullptr)
23 : {
24 0 : throw SGWriter_Exception_NullGeometry();
25 : }
26 :
27 442 : OGRwkbGeometryType ogwkt = geom->getGeometryType();
28 442 : this->type = OGRtoRaw(ogwkt);
29 :
30 442 : if (this->type == POINT)
31 : {
32 : // Set total node count (1)
33 33 : this->total_point_count = 1;
34 :
35 : // Also single part geometry (1)
36 33 : this->total_part_count = 1;
37 :
38 : // One part per part
39 33 : ppart_node_count.push_back(1);
40 : }
41 :
42 409 : else if (this->type == MULTIPOINT)
43 : {
44 10 : const auto mp = geom->toMultiPoint();
45 :
46 : // Set total node count
47 10 : this->total_point_count = mp->getNumGeometries();
48 :
49 : // The amount of nodes is also the amount of parts
50 44 : for (size_t pc = 0; pc < total_point_count; pc++)
51 : {
52 34 : ppart_node_count.push_back(1);
53 : }
54 :
55 : // total part count == total node count
56 10 : this->total_part_count = this->total_point_count;
57 : }
58 :
59 399 : else if (this->type == LINE)
60 : {
61 6 : const auto line = geom->toLineString();
62 : // to do: check for std::bad_cast somewhere?
63 :
64 : // Get node count
65 6 : this->total_point_count = line->getNumPoints();
66 :
67 : // Single line: 1 part count == node count
68 6 : this->ppart_node_count.push_back(line->getNumPoints());
69 :
70 : // One part
71 6 : this->total_part_count = 1;
72 : }
73 :
74 393 : else if (this->type == MULTILINE)
75 : {
76 7 : const auto mls = geom->toMultiLineString();
77 7 : this->total_point_count = 0;
78 7 : this->total_part_count = mls->getNumGeometries();
79 :
80 : // Take each geometry, just add up the corresponding parts
81 :
82 18 : for (const auto ls : *mls)
83 : {
84 11 : int pt_count = ls->getNumPoints();
85 :
86 11 : this->ppart_node_count.push_back(pt_count);
87 11 : this->total_point_count += pt_count;
88 : }
89 : }
90 :
91 386 : else if (this->type == POLYGON)
92 : {
93 12 : const auto poly = geom->toPolygon();
94 :
95 12 : this->total_point_count = 0;
96 12 : this->total_part_count = 0;
97 :
98 : // Get node count
99 : // First count exterior ring
100 12 : const auto exterior_ring = poly->getExteriorRing();
101 : const size_t outer_ring_ct =
102 12 : exterior_ring ? exterior_ring->getNumPoints() : 0;
103 :
104 12 : this->total_point_count += outer_ring_ct;
105 12 : this->ppart_node_count.push_back(outer_ring_ct);
106 12 : this->total_part_count++;
107 :
108 : // Then count all from the interior rings
109 : // While doing the following
110 : // Get per part node count (per part RING count)
111 : // Get part count (in this case it's the amount of RINGS)
112 :
113 14 : for (int iRingCt = 0; iRingCt < poly->getNumInteriorRings(); iRingCt++)
114 : {
115 2 : this->hasInteriorRing = true;
116 2 : const auto iring = poly->getInteriorRing(iRingCt);
117 2 : if (iring)
118 : {
119 2 : this->total_point_count += iring->getNumPoints();
120 2 : this->ppart_node_count.push_back(iring->getNumPoints());
121 2 : this->total_part_count++;
122 : }
123 : }
124 : }
125 :
126 374 : else if (this->type == MULTIPOLYGON)
127 : {
128 374 : const auto poMP = geom->toMultiPolygon();
129 :
130 374 : this->total_point_count = 0;
131 374 : this->total_part_count = 0;
132 :
133 946 : for (const auto poly : *poMP)
134 : {
135 572 : const auto exterior_ring = poly->getExteriorRing();
136 : const size_t outer_ring_ct =
137 572 : exterior_ring ? exterior_ring->getNumPoints() : 0;
138 :
139 572 : this->total_point_count += outer_ring_ct;
140 572 : this->ppart_node_count.push_back(outer_ring_ct);
141 572 : this->total_part_count++;
142 572 : this->part_at_ind_interior.push_back(false);
143 :
144 : // Then count all from the interior rings
145 : // While doing the following
146 : // Get per part node count (per part RING count)
147 : // Get part count (in this case it's the amount of RINGS)
148 :
149 601 : for (int iRingCt = 0; iRingCt < poly->getNumInteriorRings();
150 : iRingCt++)
151 : {
152 29 : const auto iring = poly->getInteriorRing(iRingCt);
153 29 : if (iring)
154 : {
155 29 : this->hasInteriorRing = true;
156 29 : this->total_point_count += iring->getNumPoints();
157 29 : this->ppart_node_count.push_back(iring->getNumPoints());
158 29 : this->total_part_count++;
159 29 : this->part_at_ind_interior.push_back(true);
160 : }
161 : }
162 : }
163 : }
164 :
165 : else
166 : {
167 0 : throw SG_Exception_BadFeature();
168 : }
169 :
170 442 : this->geometry_ref = geom;
171 442 : }
172 :
173 449380 : inline void WBuffer::addCount(unsigned long long memuse)
174 : {
175 449380 : this->used_mem += memuse;
176 449380 : }
177 :
178 272502 : inline void WBuffer::subCount(unsigned long long memfree)
179 : {
180 : /* detect underflow cases-
181 : * better not subtract more than we already counted...
182 : */
183 272502 : CPLAssert(this->used_mem >= memfree);
184 :
185 272502 : this->used_mem -= memfree;
186 272502 : }
187 :
188 41 : void ncLayer_SG_Metadata::initializeNewContainer(int containerVID)
189 : {
190 41 : this->containerVar_realID = containerVID;
191 :
192 41 : netCDFVID &ncdf = this->vDataset;
193 41 : geom_t geo = this->writableType;
194 :
195 : // Define some virtual dimensions, and some virtual variables
196 41 : char container_name[NC_MAX_CHAR + 1] = {0};
197 41 : char node_coord_names[NC_MAX_CHAR + 1] = {0};
198 :
199 : // Set default values
200 41 : pnc_varID = INVALID_VAR_ID;
201 41 : pnc_dimID = INVALID_DIM_ID;
202 41 : intring_varID = INVALID_VAR_ID;
203 :
204 41 : int err_code = nc_inq_varname(ncID, containerVar_realID, container_name);
205 41 : NCDF_ERR(err_code);
206 41 : if (err_code != NC_NOERR)
207 : {
208 : throw SGWriter_Exception_NCInqFailure("new layer", "geometry container",
209 0 : "var name of");
210 : }
211 :
212 41 : this->containerVarName = std::string(container_name);
213 :
214 : // Node Coordinates - Dim
215 : std::string nodecoord_name =
216 123 : containerVarName + "_" + std::string(CF_SG_NODE_COORDINATES);
217 :
218 41 : node_coordinates_dimID = ncdf.nc_def_vdim(nodecoord_name.c_str(), 1);
219 :
220 : // Node Coordinates - Variable Names
221 41 : err_code = nc_get_att_text(ncID, containerVar_realID,
222 : CF_SG_NODE_COORDINATES, node_coord_names);
223 41 : NCDF_ERR(err_code);
224 41 : if (err_code != NC_NOERR)
225 : {
226 : throw SGWriter_Exception_NCInqFailure(
227 0 : containerVarName.c_str(), CF_SG_NODE_COORDINATES, "varName");
228 : }
229 :
230 : // Node Count
231 41 : if (geo != POINT)
232 : {
233 : std::string nodecount_name =
234 64 : containerVarName + "_" + std::string(CF_SG_NODE_COUNT);
235 32 : node_count_dimID = ncdf.nc_def_vdim(nodecount_name.c_str(), 1);
236 32 : node_count_varID = ncdf.nc_def_vvar(nodecount_name.c_str(), NC_INT, 1,
237 32 : &node_count_dimID);
238 : }
239 :
240 : // Do the same for part node count, if it exists
241 41 : char pnc_name[NC_MAX_CHAR + 1] = {0};
242 41 : err_code = nc_get_att_text(ncID, containerVar_realID, CF_SG_PART_NODE_COUNT,
243 : pnc_name);
244 :
245 41 : if (err_code == NC_NOERR)
246 : {
247 27 : pnc_dimID = ncdf.nc_def_vdim(pnc_name, 1);
248 27 : pnc_varID = ncdf.nc_def_vvar(pnc_name, NC_INT, 1, &pnc_dimID);
249 :
250 27 : char ir_name[NC_MAX_CHAR + 1] = {0};
251 27 : nc_get_att_text(ncID, containerVar_realID, CF_SG_INTERIOR_RING,
252 : ir_name);
253 :
254 : // For interior ring too (for POLYGON and MULTIPOLYGON); there's always
255 : // an assumption that interior rings really do exist until the very end
256 : // in which case it's known whether or not that assumption was true or
257 : // false (if false, this (and PNC attribute for Polygons) will just be
258 : // deleted)
259 27 : if (this->writableType == POLYGON || this->writableType == MULTIPOLYGON)
260 : {
261 24 : intring_varID = ncdf.nc_def_vvar(ir_name, NC_INT, 1, &pnc_dimID);
262 : }
263 : }
264 :
265 : // Node coordinates Var Definitions
266 82 : CPLStringList aosNcoord(CSLTokenizeString2(node_coord_names, " ", 0));
267 :
268 41 : if (aosNcoord.size() < 2)
269 0 : throw SGWriter_Exception();
270 :
271 : // first it's X
272 : int new_varID =
273 41 : ncdf.nc_def_vvar(aosNcoord[0], NC_DOUBLE, 1, &node_coordinates_dimID);
274 41 : ncdf.nc_put_vatt_text(new_varID, CF_AXIS, CF_SG_X_AXIS);
275 :
276 41 : this->node_coordinates_varIDs.push_back(new_varID);
277 :
278 : // second it's Y
279 41 : new_varID =
280 41 : ncdf.nc_def_vvar(aosNcoord[1], NC_DOUBLE, 1, &node_coordinates_dimID);
281 41 : ncdf.nc_put_vatt_text(new_varID, CF_AXIS, CF_SG_Y_AXIS);
282 :
283 41 : this->node_coordinates_varIDs.push_back(new_varID);
284 :
285 : // (and perhaps) third it's Z
286 41 : if (aosNcoord.size() > 2)
287 : {
288 15 : new_varID = ncdf.nc_def_vvar(aosNcoord[2], NC_DOUBLE, 1,
289 15 : &node_coordinates_dimID);
290 15 : ncdf.nc_put_vatt_text(new_varID, CF_AXIS, CF_SG_Z_AXIS);
291 :
292 15 : this->node_coordinates_varIDs.push_back(new_varID);
293 : }
294 41 : }
295 :
296 223 : ncLayer_SG_Metadata::ncLayer_SG_Metadata(int &i_ncID, geom_t geo,
297 223 : netCDFVID &ncdf, OGR_NCScribe &ncs)
298 223 : : ncID(i_ncID), vDataset(ncdf), ncb(ncs), writableType(geo)
299 : {
300 223 : }
301 :
302 66386 : const OGRPoint &SGeometry_Feature::getPoint(size_t part_no,
303 : int point_index) const
304 : {
305 66386 : if (this->type == POINT)
306 : {
307 : // Point case: always return the single point regardless of any thing
308 :
309 33 : const OGRPoint *as_p_ref = geometry_ref->toPoint();
310 33 : return *as_p_ref;
311 : }
312 :
313 66353 : if (this->type == MULTIPOINT)
314 : {
315 34 : const OGRMultiPoint *as_mp_ref = geometry_ref->toMultiPoint();
316 34 : int part_ind = static_cast<int>(part_no);
317 34 : const OGRPoint *pt = as_mp_ref->getGeometryRef(part_ind);
318 34 : return *pt;
319 : }
320 :
321 66319 : if (this->type == LINE)
322 : {
323 13 : const OGRLineString *as_line_ref = geometry_ref->toLineString();
324 13 : as_line_ref->getPoint(point_index, &pt_buffer);
325 : }
326 :
327 66319 : if (this->type == MULTILINE)
328 : {
329 : const OGRMultiLineString *as_mline_ref =
330 24 : geometry_ref->toMultiLineString();
331 24 : CPLAssert(as_mline_ref);
332 24 : int part_ind = static_cast<int>(part_no);
333 24 : const OGRLineString *lstring = as_mline_ref->getGeometryRef(part_ind);
334 24 : CPLAssert(lstring);
335 24 : lstring->getPoint(point_index, &pt_buffer);
336 : }
337 :
338 66319 : if (this->type == POLYGON)
339 : {
340 58 : const OGRPolygon *as_polygon_ref = geometry_ref->toPolygon();
341 58 : int ring_ind = static_cast<int>(part_no);
342 :
343 58 : if (part_no == 0)
344 : {
345 48 : as_polygon_ref->getExteriorRing()->getPoint(point_index,
346 : &pt_buffer);
347 : }
348 :
349 : else
350 : {
351 10 : as_polygon_ref->getInteriorRing(ring_ind - 1)
352 10 : ->getPoint(point_index, &pt_buffer);
353 : }
354 : }
355 :
356 66319 : if (this->type == MULTIPOLYGON)
357 : {
358 66224 : const OGRMultiPolygon *as_mpolygon_ref = geometry_ref->toMultiPolygon();
359 66224 : int polygon_num = 0;
360 66224 : int ring_number = 0;
361 66224 : int pno_itr = static_cast<int>(part_no);
362 :
363 : // Find the right polygon, and the right ring number
364 74040 : for (int pind = 0; pind < as_mpolygon_ref->getNumGeometries(); pind++)
365 : {
366 74040 : const OGRPolygon *itr_poly = as_mpolygon_ref->getGeometryRef(pind);
367 74040 : if (pno_itr < (itr_poly->getNumInteriorRings() +
368 : 1)) // + 1 is counting the EXTERIOR ring
369 : {
370 66224 : ring_number = static_cast<int>(pno_itr);
371 66224 : break;
372 : }
373 :
374 : else
375 : {
376 7816 : pno_itr -= (itr_poly->getNumInteriorRings() + 1);
377 7816 : polygon_num++;
378 : }
379 : }
380 :
381 : const OGRPolygon *key_polygon =
382 66224 : as_mpolygon_ref->getGeometryRef(polygon_num);
383 :
384 66224 : if (ring_number == 0)
385 : {
386 65921 : key_polygon->getExteriorRing()->getPoint(point_index, &pt_buffer);
387 : }
388 :
389 : else
390 : {
391 303 : key_polygon->getInteriorRing(ring_number - 1)
392 303 : ->getPoint(point_index, &pt_buffer);
393 : }
394 : }
395 :
396 66319 : return pt_buffer;
397 : }
398 :
399 442 : void ncLayer_SG_Metadata::writeSGeometryFeature(SGeometry_Feature &ft)
400 : {
401 442 : if (ft.getType() == NONE)
402 : {
403 0 : throw SG_Exception_BadFeature();
404 : }
405 :
406 : // Write each point from each part in node coordinates
407 1141 : for (size_t part_no = 0; part_no < ft.getTotalPartCount(); part_no++)
408 : {
409 699 : if (this->writableType == POLYGON || this->writableType == MULTIPOLYGON)
410 : {
411 615 : int interior_ring_fl = 1;
412 :
413 615 : if (this->writableType == POLYGON)
414 : {
415 14 : interior_ring_fl = part_no == 0 ? 0 : 1;
416 : }
417 :
418 601 : else if (this->writableType == MULTIPOLYGON)
419 : {
420 601 : if (ft.IsPartAtIndInteriorRing(part_no))
421 : {
422 29 : interior_ring_fl = 1;
423 : }
424 :
425 : else
426 : {
427 572 : interior_ring_fl = 0;
428 : }
429 : }
430 :
431 615 : if (interior_ring_fl)
432 : {
433 31 : this->interiorRingDetected = true;
434 : }
435 :
436 615 : ncb.enqueue_transaction(
437 615 : std::make_unique<OGR_SGFS_NC_Int_Transaction>(
438 615 : intring_varID, interior_ring_fl));
439 : }
440 :
441 699 : if (this->writableType == POLYGON || this->writableType == MULTILINE ||
442 674 : this->writableType == MULTIPOLYGON)
443 : {
444 : int pnc_writable =
445 626 : static_cast<int>(ft.getPerPartNodeCount()[part_no]);
446 626 : ncb.enqueue_transaction(
447 1252 : std::make_unique<OGR_SGFS_NC_Int_Transaction>(pnc_varID,
448 : pnc_writable));
449 626 : this->next_write_pos_pnc++;
450 : }
451 :
452 67085 : for (size_t pt_ind = 0; pt_ind < ft.getPerPartNodeCount()[part_no];
453 : pt_ind++)
454 : {
455 66386 : int pt_ind_int = static_cast<int>(pt_ind);
456 66386 : const OGRPoint &write_pt = ft.getPoint(part_no, pt_ind_int);
457 :
458 : // Write each node coordinate
459 66386 : double x = write_pt.getX();
460 66386 : ncb.enqueue_transaction(
461 132772 : std::make_unique<OGR_SGFS_NC_Double_Transaction>(
462 66386 : node_coordinates_varIDs[0], x));
463 :
464 66386 : double y = write_pt.getY();
465 66386 : ncb.enqueue_transaction(
466 132772 : std::make_unique<OGR_SGFS_NC_Double_Transaction>(
467 66386 : node_coordinates_varIDs[1], y));
468 :
469 66386 : if (this->node_coordinates_varIDs.size() > 2)
470 : {
471 140 : double z = write_pt.getZ();
472 140 : ncb.enqueue_transaction(
473 280 : std::make_unique<OGR_SGFS_NC_Double_Transaction>(
474 140 : node_coordinates_varIDs[2], z));
475 : }
476 : }
477 :
478 699 : this->next_write_pos_node_coord += ft.getPerPartNodeCount()[part_no];
479 : }
480 :
481 : // Append node counts from the end, if not a POINT
482 442 : if (this->writableType != POINT)
483 : {
484 409 : int ncount_add = static_cast<int>(ft.getTotalNodeCount());
485 409 : ncb.enqueue_transaction(std::make_unique<OGR_SGFS_NC_Int_Transaction>(
486 409 : node_count_varID, ncount_add));
487 409 : this->next_write_pos_node_count++;
488 :
489 : // Special case: The "empty" MultiPolygon type
490 : // MultiPolygon part_node_counts are counted in terms of "rings" not
491 : // parts contrary to the name so an empty multipolygon with no rings
492 : // will slip past the regular part_node_count placement In essence this
493 : // is probably taken as "if there are no rings" then "there are also no
494 : // points"
495 409 : if (ft.getTotalPartCount() == 0 && this->writableType == MULTIPOLYGON &&
496 0 : (ft.getType() == POLYGON || ft.getType() == MULTIPOLYGON))
497 : {
498 0 : ncb.enqueue_transaction(
499 0 : std::make_unique<OGR_SGFS_NC_Int_Transaction>(pnc_varID, 0));
500 0 : this->next_write_pos_pnc++;
501 : }
502 : }
503 442 : }
504 :
505 0 : static std::string sgwe_msg_builder(const char *layer_name,
506 : const char *failure_name,
507 : const char *failure_type,
508 : const char *special_msg)
509 : {
510 0 : return std::string("[") + std::string(layer_name) + std::string("] ") +
511 0 : std::string(failure_type) + std::string(" ") +
512 0 : std::string(failure_name) + std::string(" ") +
513 0 : std::string(special_msg);
514 : }
515 :
516 : // Exception related definitions
517 0 : SGWriter_Exception_NCWriteFailure::SGWriter_Exception_NCWriteFailure(
518 0 : const char *layer_name, const char *failure_name, const char *failure_type)
519 : : SGWriter_Exception(
520 0 : sgwe_msg_builder(layer_name, failure_name, failure_type,
521 0 : "could not be written to (write failure)."))
522 : {
523 0 : }
524 :
525 0 : SGWriter_Exception_NCInqFailure::SGWriter_Exception_NCInqFailure(
526 0 : const char *layer_name, const char *failure_name, const char *failure_type)
527 0 : : SGWriter_Exception(sgwe_msg_builder(
528 : layer_name, failure_name, failure_type,
529 0 : "could not be read from (property inquiry failure)."))
530 : {
531 0 : }
532 :
533 0 : SGWriter_Exception_NCDefFailure::SGWriter_Exception_NCDefFailure(
534 0 : const char *layer_name, const char *failure_name, const char *failure_type)
535 0 : : SGWriter_Exception(sgwe_msg_builder(
536 : layer_name, failure_name, failure_type,
537 0 : "could not be defined in the dataset (definition failure)."))
538 : {
539 0 : }
540 :
541 : // OGR_NCScribe
542 136251 : void OGR_NCScribe::enqueue_transaction(MTPtr transactionAdd)
543 : {
544 136251 : if (transactionAdd.get() == nullptr)
545 : {
546 0 : return;
547 : }
548 :
549 : // See if the variable name is already being written to
550 136251 : if (this->varMaxInds.count(transactionAdd->getVarId()) > 0)
551 : {
552 135998 : size_t varWriteLength = this->varMaxInds[transactionAdd->getVarId()];
553 135998 : varWriteLength++;
554 135998 : this->varMaxInds[transactionAdd->getVarId()] = varWriteLength;
555 : }
556 :
557 : else
558 : {
559 : // Otherwise, just add it to the list of variable names being written to
560 253 : std::pair<int, size_t> entry(transactionAdd->getVarId(), 1);
561 253 : this->varMaxInds.insert(entry);
562 : }
563 :
564 : // Add sizes to memory count
565 136251 : this->buf.addCount(sizeof(transactionAdd)); // account for pointer
566 136251 : this->buf.addCount(transactionAdd->count()); // account for pointee
567 :
568 : // Finally push the transaction in
569 136251 : this->transactionQueue.push(MTPtr(transactionAdd.release()));
570 : }
571 :
572 78 : void OGR_NCScribe::commit_transaction()
573 : {
574 78 : wl.startRead();
575 :
576 156 : NCWMap writerMap;
577 156 : std::vector<int> varV;
578 :
579 78 : MTPtr t;
580 78 : t = this->pop();
581 :
582 136329 : while (t.get() != nullptr)
583 : {
584 136251 : int varId = t->getVarId();
585 : size_t writeInd;
586 :
587 : // First, find where to write. If doesn't exist, write to index 0
588 136251 : if (this->varWriteInds.count(varId) > 0)
589 : {
590 135998 : writeInd = this->varWriteInds[varId];
591 : }
592 :
593 : else
594 : {
595 253 : std::pair<int, size_t> insertable(varId, 0);
596 253 : this->varWriteInds.insert(insertable);
597 253 : writeInd = 0;
598 : }
599 :
600 : // Then write
601 : // Subtract sizes from memory count
602 136251 : this->buf.subCount(sizeof(t)); // account for pointer
603 136251 : this->buf.subCount(t->count()); // account for pointee
604 :
605 : try
606 : {
607 : // If variable length type, for now, continue using old committing
608 : // scheme Maybe some future work: optimize this in the similar
609 : // manner to other types However, CHAR and STRING have huge copying
610 : // overhead and are more complicated to memory manage correctly
611 271612 : if (t->getType() == NC_CHAR || t->getType() == NC_STRING ||
612 135361 : singleDatumMode)
613 : {
614 24140 : t->commit(ncvd, writeInd);
615 : }
616 :
617 : // Other types: Use a more optimized approach
618 : else
619 : {
620 112111 : int wvid = t->getVarId();
621 112111 : size_t numEntries = this->varMaxInds.at(wvid);
622 112111 : nc_type ncw = t->getType();
623 112111 : size_t curWrInd = this->varWriteInds.at(wvid);
624 :
625 : // If entry doesn't exist in map, then add it
626 112111 : switch (ncw)
627 : {
628 0 : case NC_BYTE:
629 : {
630 0 : NCWMapAllocIfNeeded<signed char>(wvid, writerMap,
631 : numEntries, varV);
632 : auto byte_trn =
633 0 : cpl::down_cast<OGR_SGFS_NC_Byte_Transaction *>(
634 : t.get());
635 0 : NCWMapWriteAndCommit<signed char>(
636 : wvid, writerMap, curWrInd, numEntries,
637 0 : byte_trn->getData(), this->ncvd);
638 0 : break;
639 : }
640 1 : case NC_SHORT:
641 : {
642 1 : NCWMapAllocIfNeeded<short>(wvid, writerMap, numEntries,
643 : varV);
644 : auto short_trn =
645 1 : cpl::down_cast<OGR_SGFS_NC_Short_Transaction *>(
646 : t.get());
647 1 : NCWMapWriteAndCommit<short>(
648 : wvid, writerMap, curWrInd, numEntries,
649 1 : short_trn->getData(), this->ncvd);
650 1 : break;
651 : }
652 1570 : case NC_INT:
653 : {
654 1570 : NCWMapAllocIfNeeded<int>(wvid, writerMap, numEntries,
655 : varV);
656 : auto int_trn =
657 1570 : cpl::down_cast<OGR_SGFS_NC_Int_Transaction *>(
658 : t.get());
659 1570 : NCWMapWriteAndCommit<int>(
660 : wvid, writerMap, curWrInd, numEntries,
661 : int_trn->getData(), this->ncvd);
662 1570 : break;
663 : }
664 1 : case NC_FLOAT:
665 : {
666 1 : NCWMapAllocIfNeeded<float>(wvid, writerMap, numEntries,
667 : varV);
668 : auto float_trn =
669 1 : cpl::down_cast<OGR_SGFS_NC_Float_Transaction *>(
670 : t.get());
671 1 : NCWMapWriteAndCommit<float>(
672 : wvid, writerMap, curWrInd, numEntries,
673 : float_trn->getData(), this->ncvd);
674 1 : break;
675 : }
676 110539 : case NC_DOUBLE:
677 : {
678 110539 : NCWMapAllocIfNeeded<double>(wvid, writerMap, numEntries,
679 : varV);
680 : auto double_trn =
681 110539 : cpl::down_cast<OGR_SGFS_NC_Double_Transaction *>(
682 : t.get());
683 110539 : NCWMapWriteAndCommit<double>(
684 : wvid, writerMap, curWrInd, numEntries,
685 : double_trn->getData(), this->ncvd);
686 110539 : break;
687 : }
688 0 : case NC_UINT:
689 : {
690 0 : NCWMapAllocIfNeeded<unsigned>(wvid, writerMap,
691 : numEntries, varV);
692 : auto uint_trn =
693 0 : cpl::down_cast<OGR_SGFS_NC_UInt_Transaction *>(
694 : t.get());
695 0 : NCWMapWriteAndCommit<unsigned>(
696 : wvid, writerMap, curWrInd, numEntries,
697 : uint_trn->getData(), this->ncvd);
698 0 : break;
699 : }
700 0 : case NC_UINT64:
701 : {
702 0 : NCWMapAllocIfNeeded<unsigned long long>(
703 : wvid, writerMap, numEntries, varV);
704 : auto uint64_trn =
705 0 : cpl::down_cast<OGR_SGFS_NC_UInt64_Transaction *>(
706 : t.get());
707 0 : NCWMapWriteAndCommit<unsigned long long>(
708 : wvid, writerMap, curWrInd, numEntries,
709 : uint64_trn->getData(), this->ncvd);
710 0 : break;
711 : }
712 0 : case NC_INT64:
713 : {
714 0 : NCWMapAllocIfNeeded<long long>(wvid, writerMap,
715 : numEntries, varV);
716 : auto int64_trn =
717 0 : cpl::down_cast<OGR_SGFS_NC_Int64_Transaction *>(
718 : t.get());
719 0 : NCWMapWriteAndCommit<long long>(
720 : wvid, writerMap, curWrInd, numEntries,
721 : int64_trn->getData(), this->ncvd);
722 0 : break;
723 : }
724 0 : case NC_UBYTE:
725 : {
726 0 : NCWMapAllocIfNeeded<unsigned char>(wvid, writerMap,
727 : numEntries, varV);
728 : auto ubyte_trn =
729 0 : cpl::down_cast<OGR_SGFS_NC_UByte_Transaction *>(
730 : t.get());
731 0 : NCWMapWriteAndCommit<unsigned char>(
732 : wvid, writerMap, curWrInd, numEntries,
733 0 : ubyte_trn->getData(), this->ncvd);
734 0 : break;
735 : }
736 0 : case NC_USHORT:
737 : {
738 0 : NCWMapAllocIfNeeded<unsigned short>(wvid, writerMap,
739 : numEntries, varV);
740 : auto ushort_trn =
741 0 : cpl::down_cast<OGR_SGFS_NC_UShort_Transaction *>(
742 : t.get());
743 0 : NCWMapWriteAndCommit<unsigned short>(
744 : wvid, writerMap, curWrInd, numEntries,
745 0 : ushort_trn->getData(), this->ncvd);
746 0 : break;
747 : }
748 0 : default:
749 : {
750 0 : break;
751 : }
752 : }
753 : }
754 : }
755 0 : catch (SG_Exception &sge)
756 : {
757 0 : CPLError(CE_Failure, CPLE_FileIO, "%s", sge.get_err_msg());
758 : }
759 :
760 : // increment index
761 136251 : this->varWriteInds[varId]++;
762 136251 : t = this->pop();
763 : }
764 :
765 : // Clean up afterwards, potential miswrites
766 274 : for (size_t vcount = 0; vcount < varV.size(); vcount++)
767 : {
768 196 : int cleanid = varV[vcount];
769 :
770 196 : if (writerMap.count(cleanid) > 0)
771 : {
772 0 : CPLFree(writerMap.at(cleanid));
773 0 : CPLError(CE_Failure, CPLE_FileIO,
774 : "Transaction corruption detected. The target variable "
775 : "will most likely be missing data.");
776 : }
777 : }
778 78 : }
779 :
780 136329 : MTPtr OGR_NCScribe::pop()
781 : {
782 : // Buffered changes are the earliest, so commit those first
783 272658 : MTPtr m = this->wl.pop();
784 136329 : if (m.get() != nullptr)
785 : {
786 : // add it to the buffer count
787 88439 : this->buf.addCount(sizeof(m));
788 88439 : this->buf.addCount(m->count());
789 88439 : return m;
790 : }
791 :
792 47890 : else if (!transactionQueue.empty())
793 : {
794 : OGR_SGFS_Transaction *value =
795 47812 : this->transactionQueue.front()
796 47812 : .release(); // due to delete copy A.K.A uniqueness of
797 : // unique_ptr
798 47812 : this->transactionQueue.pop();
799 :
800 47812 : return MTPtr(value);
801 : }
802 : else
803 : {
804 78 : return MTPtr();
805 : }
806 : }
807 :
808 360 : void OGR_NCScribe::log_transaction()
809 : {
810 360 : if (wl.logIsNull())
811 8 : wl.startLog();
812 :
813 88799 : while (!transactionQueue.empty())
814 : {
815 : // coverity[var_deref_model]
816 88439 : wl.push(MTPtr(transactionQueue.front().release()));
817 88439 : this->transactionQueue.pop();
818 : }
819 360 : this->buf.reset();
820 360 : }
821 :
822 : // WBufferManager
823 442 : bool WBufferManager::isOverQuota() const
824 : {
825 442 : unsigned long long sum = 0;
826 1326 : for (size_t s = 0; s < bufs.size(); s++)
827 : {
828 884 : WBuffer &b = *(bufs[s]);
829 884 : sum += b.getUsage();
830 : }
831 :
832 442 : return sum > this->buffer_soft_limit;
833 : }
834 :
835 : // Transactions
836 0 : void OGR_SGFS_NC_Char_Transaction::appendToLog(VSILFILE *f)
837 : {
838 0 : int vid = OGR_SGFS_Transaction::getVarId();
839 0 : int type = NC_CHAR;
840 0 : int8_t OP = 0;
841 0 : size_t DATA_SIZE = char_rep.length();
842 :
843 0 : VSIFWriteL(&vid, sizeof(int), 1, f); // write varID data
844 0 : VSIFWriteL(&type, sizeof(int), 1, f); // write NC type
845 0 : VSIFWriteL(&OP, sizeof(int8_t), 1, f); // write "OP" flag
846 0 : VSIFWriteL(&DATA_SIZE, sizeof(size_t), 1, f); // write length
847 0 : VSIFWriteL(char_rep.c_str(), sizeof(char), DATA_SIZE, f); // write data
848 0 : }
849 :
850 0 : void OGR_SGFS_NC_String_Transaction::appendToLog(VSILFILE *f)
851 : {
852 0 : int vid = OGR_SGFS_Transaction::getVarId();
853 0 : int type = NC_STRING;
854 0 : size_t DATA_SIZE = char_rep.length();
855 :
856 0 : VSIFWriteL(&vid, sizeof(int), 1, f); // write varID data
857 0 : VSIFWriteL(&type, sizeof(int), 1, f); // write NC type
858 0 : VSIFWriteL(&DATA_SIZE, sizeof(size_t), 1, f); // write length
859 0 : VSIFWriteL(char_rep.c_str(), sizeof(char), DATA_SIZE, f); // write data
860 0 : }
861 :
862 588 : void OGR_SGFS_NC_CharA_Transaction::appendToLog(VSILFILE *f)
863 : {
864 588 : int vid = OGR_SGFS_Transaction::getVarId();
865 588 : int type = NC_CHAR;
866 588 : int8_t OP = 1;
867 588 : size_t DATA_SIZE = char_rep.length();
868 :
869 588 : VSIFWriteL(&vid, sizeof(int), 1, f); // write varID data
870 588 : VSIFWriteL(&type, sizeof(int), 1, f); // write NC type
871 588 : VSIFWriteL(&OP, sizeof(int8_t), 1, f); // write "OP" flag
872 588 : VSIFWriteL(&DATA_SIZE, sizeof(size_t), 1, f); // write length
873 588 : VSIFWriteL(char_rep.c_str(), sizeof(char), DATA_SIZE, f); // write data
874 588 : }
875 :
876 : // WTransactionLog
877 2612 : WTransactionLog::WTransactionLog(const std::string &logName) : wlogName(logName)
878 : {
879 2612 : }
880 :
881 8 : void WTransactionLog::startLog()
882 : {
883 8 : log = VSIFOpenL(wlogName.c_str(), "w");
884 8 : }
885 :
886 78 : void WTransactionLog::startRead()
887 : {
888 78 : if (log == nullptr)
889 70 : return;
890 :
891 8 : VSIFCloseL(this->log);
892 8 : this->log = VSIFOpenL(wlogName.c_str(), "r");
893 : }
894 :
895 88439 : void WTransactionLog::push(const MTPtr &t) const
896 : {
897 88439 : t->appendToLog(this->log);
898 88439 : }
899 :
900 136329 : MTPtr WTransactionLog::pop() const
901 : {
902 136329 : if (log == nullptr)
903 45580 : return MTPtr(nullptr);
904 :
905 : int varId;
906 : nc_type ntype;
907 90749 : size_t itemsread = VSIFReadL(&varId, sizeof(int), 1, log);
908 90749 : itemsread &= VSIFReadL(&ntype, sizeof(nc_type), 1, log);
909 :
910 : // If one of the two reads failed, then return nullptr
911 90749 : if (!itemsread)
912 2310 : return MTPtr(nullptr);
913 :
914 : // If not, continue on and parse additional fields
915 88439 : switch (ntype)
916 : {
917 : // NC-3 Primitives
918 0 : case NC_BYTE:
919 : return genericLogDataRead<OGR_SGFS_NC_Byte_Transaction,
920 0 : signed char>(varId, log);
921 0 : case NC_SHORT:
922 : return genericLogDataRead<OGR_SGFS_NC_Short_Transaction, short>(
923 0 : varId, log);
924 1067 : case NC_INT:
925 : return genericLogDataRead<OGR_SGFS_NC_Int_Transaction, int>(varId,
926 1067 : log);
927 0 : case NC_FLOAT:
928 : return genericLogDataRead<OGR_SGFS_NC_Float_Transaction, float>(
929 0 : varId, log);
930 86784 : case NC_DOUBLE:
931 : return genericLogDataRead<OGR_SGFS_NC_Double_Transaction, double>(
932 86784 : varId, log);
933 0 : case NC_UBYTE:
934 : return genericLogDataRead<OGR_SGFS_NC_UByte_Transaction,
935 0 : unsigned char>(varId, log);
936 0 : case NC_USHORT:
937 : return genericLogDataRead<OGR_SGFS_NC_UShort_Transaction,
938 0 : unsigned short>(varId, log);
939 0 : case NC_UINT:
940 : return genericLogDataRead<OGR_SGFS_NC_UInt_Transaction,
941 0 : unsigned int>(varId, log);
942 0 : case NC_INT64:
943 : return genericLogDataRead<OGR_SGFS_NC_Int64_Transaction, long long>(
944 0 : varId, log);
945 0 : case NC_UINT64:
946 : return genericLogDataRead<OGR_SGFS_NC_UInt64_Transaction,
947 0 : unsigned long long>(varId, log);
948 588 : case NC_CHAR:
949 : {
950 : size_t readcheck; // 0 means at least one read 0 bytes
951 :
952 : // Check what type of OP is requested
953 588 : int8_t op = 0;
954 588 : readcheck = VSIFReadL(&op, sizeof(int8_t), 1, log);
955 588 : if (!readcheck)
956 0 : return MTPtr(); // read failure
957 :
958 : size_t strsize;
959 :
960 : // get how much data to read
961 588 : readcheck = VSIFReadL(&strsize, sizeof(size_t), 1, log);
962 588 : if (!readcheck)
963 0 : return MTPtr(); // read failure
964 :
965 1176 : std::string data;
966 588 : data.resize(strsize);
967 :
968 : // read that data and return it
969 588 : readcheck = VSIFReadL(&data[0], sizeof(char), strsize, log);
970 588 : if (!readcheck)
971 0 : return MTPtr(); // read failure
972 :
973 : // case: its a standard CHAR op
974 588 : if (!op)
975 : {
976 0 : return std::make_unique<OGR_SGFS_NC_Char_Transaction>(
977 0 : varId, &data[0]); // data is copied so okay!
978 : }
979 :
980 : // case: its a CHARA op, additional processing
981 : else
982 : {
983 588 : return std::make_unique<OGR_SGFS_NC_CharA_Transaction>(
984 1176 : varId, &data[0]);
985 : }
986 : }
987 :
988 0 : case NC_STRING:
989 : {
990 : size_t readcheck; // 0 means at least one read 0 bytes
991 :
992 : size_t strsize;
993 :
994 : // get how much data to read
995 0 : readcheck = VSIFReadL(&strsize, sizeof(size_t), 1, log);
996 :
997 0 : if (!readcheck)
998 0 : return MTPtr(); // read failure
999 :
1000 0 : std::string data;
1001 0 : data.resize(strsize);
1002 :
1003 : // read that data and return it
1004 0 : readcheck = VSIFReadL(&data[0], sizeof(char), strsize, log);
1005 :
1006 0 : if (!readcheck)
1007 0 : return MTPtr(); // read failure
1008 :
1009 0 : return std::make_unique<OGR_SGFS_NC_String_Transaction>(
1010 0 : varId, &data[0]); // data is copied so okay!
1011 : }
1012 :
1013 0 : default:
1014 : // Unsupported type
1015 0 : return MTPtr();
1016 : }
1017 : }
1018 :
1019 2612 : WTransactionLog::~WTransactionLog()
1020 : {
1021 2612 : if (log != nullptr)
1022 : {
1023 8 : VSIFCloseL(log);
1024 8 : VSIUnlink(this->wlogName.c_str());
1025 : }
1026 2612 : }
1027 :
1028 : // Helper function definitions
1029 41 : int write_Geometry_Container(
1030 : int ncID, const std::string &name, geom_t geometry_type,
1031 : const std::vector<std::string> &node_coordinate_names)
1032 : {
1033 :
1034 : int write_var_id;
1035 :
1036 : // Define geometry container variable
1037 : int err_code =
1038 41 : nc_def_var(ncID, name.c_str(), NC_FLOAT, 0, nullptr, &write_var_id);
1039 : // todo: exception handling of err_code
1040 41 : NCDF_ERR(err_code);
1041 41 : if (err_code != NC_NOERR)
1042 : {
1043 : throw SGWriter_Exception_NCDefFailure(name.c_str(),
1044 0 : "geometry_container", "variable");
1045 : }
1046 :
1047 : /* Geometry Type Attribute
1048 : * -
1049 : */
1050 :
1051 : // Next, go on to add attributes needed for each geometry type
1052 : std::string geometry_str =
1053 32 : (geometry_type == POINT || geometry_type == MULTIPOINT)
1054 : ? CF_SG_TYPE_POINT
1055 27 : : (geometry_type == LINE || geometry_type == MULTILINE)
1056 56 : ? CF_SG_TYPE_LINE
1057 19 : : (geometry_type == POLYGON || geometry_type == MULTIPOLYGON)
1058 43 : ? CF_SG_TYPE_POLY
1059 114 : : ""; // obviously an error condition...
1060 :
1061 41 : if (geometry_str == "")
1062 : {
1063 0 : throw SG_Exception_BadFeature();
1064 : }
1065 :
1066 : // Add the geometry type attribute
1067 41 : err_code = nc_put_att_text(ncID, write_var_id, CF_SG_GEOMETRY_TYPE,
1068 : geometry_str.size(), geometry_str.c_str());
1069 41 : NCDF_ERR(err_code);
1070 41 : if (err_code != NC_NOERR)
1071 : {
1072 : throw SGWriter_Exception_NCWriteFailure(
1073 : name.c_str(), CF_SG_GEOMETRY_TYPE,
1074 0 : "attribute in geometry_container");
1075 : }
1076 :
1077 : /* Node Coordinates Attribute
1078 : * -
1079 : */
1080 41 : std::string ncoords_atr_str = "";
1081 :
1082 138 : for (size_t itr = 0; itr < node_coordinate_names.size(); itr++)
1083 : {
1084 97 : ncoords_atr_str += node_coordinate_names[itr];
1085 97 : if (itr < node_coordinate_names.size() - 1)
1086 : {
1087 56 : ncoords_atr_str += " ";
1088 : }
1089 : }
1090 :
1091 41 : err_code = nc_put_att_text(ncID, write_var_id, CF_SG_NODE_COORDINATES,
1092 : ncoords_atr_str.size(), ncoords_atr_str.c_str());
1093 :
1094 41 : NCDF_ERR(err_code);
1095 41 : if (err_code != NC_NOERR)
1096 : {
1097 : throw SGWriter_Exception_NCWriteFailure(
1098 : name.c_str(), CF_SG_NODE_COORDINATES,
1099 0 : "attribute in geometry_container");
1100 : }
1101 : // The previous two attributes are all that are required from POINT
1102 :
1103 : /* Node_Count Attribute
1104 : * (not needed for POINT)
1105 : */
1106 41 : if (geometry_type != POINT)
1107 : {
1108 64 : std::string nodecount_atr_str = name + "_node_count";
1109 :
1110 32 : err_code = nc_put_att_text(ncID, write_var_id, CF_SG_NODE_COUNT,
1111 : nodecount_atr_str.size(),
1112 : nodecount_atr_str.c_str());
1113 32 : NCDF_ERR(err_code);
1114 32 : if (err_code != NC_NOERR)
1115 : {
1116 : throw SGWriter_Exception_NCWriteFailure(
1117 : name.c_str(), CF_SG_NODE_COUNT,
1118 0 : "attribute in geometry_container");
1119 : }
1120 : }
1121 :
1122 : /* Part_Node_Count Attribute
1123 : * (only needed for MULTILINE, MULTIPOLYGON, and (potentially) POLYGON)
1124 : */
1125 41 : if (geometry_type == MULTILINE || geometry_type == MULTIPOLYGON ||
1126 : geometry_type == POLYGON)
1127 : {
1128 54 : std::string pnc_atr_str = name + "_part_node_count";
1129 :
1130 27 : err_code = nc_put_att_text(ncID, write_var_id, CF_SG_PART_NODE_COUNT,
1131 : pnc_atr_str.size(), pnc_atr_str.c_str());
1132 :
1133 27 : NCDF_ERR(err_code);
1134 27 : if (err_code != NC_NOERR)
1135 : {
1136 : throw SGWriter_Exception_NCWriteFailure(
1137 : name.c_str(), CF_SG_PART_NODE_COUNT,
1138 0 : "attribute in geometry_container");
1139 : }
1140 : }
1141 :
1142 : /* Interior Ring Attribute
1143 : * (only needed potentially for MULTIPOLYGON and POLYGON)
1144 : */
1145 :
1146 41 : if (geometry_type == MULTIPOLYGON || geometry_type == POLYGON)
1147 : {
1148 48 : std::string ir_atr_str = name + "_interior_ring";
1149 :
1150 24 : err_code = nc_put_att_text(ncID, write_var_id, CF_SG_INTERIOR_RING,
1151 : ir_atr_str.size(), ir_atr_str.c_str());
1152 24 : NCDF_ERR(err_code);
1153 24 : if (err_code != NC_NOERR)
1154 : {
1155 : throw nccfdriver::SGWriter_Exception_NCWriteFailure(
1156 : name.c_str(), CF_SG_INTERIOR_RING,
1157 0 : "attribute in geometry_container");
1158 : }
1159 : }
1160 :
1161 82 : return write_var_id;
1162 : }
1163 :
1164 : OGR_SGFS_Transaction::~OGR_SGFS_Transaction() = default;
1165 :
1166 : } // namespace nccfdriver
|