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