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 : : msg(sgwe_msg_builder(layer_name, failure_name, failure_type,
518 0 : "could not be written to (write failure)."))
519 : {
520 0 : }
521 :
522 0 : SGWriter_Exception_NCInqFailure::SGWriter_Exception_NCInqFailure(
523 0 : const char *layer_name, const char *failure_name, const char *failure_type)
524 : : msg(sgwe_msg_builder(
525 : layer_name, failure_name, failure_type,
526 0 : "could not be read from (property inquiry failure)."))
527 : {
528 0 : }
529 :
530 0 : SGWriter_Exception_NCDefFailure::SGWriter_Exception_NCDefFailure(
531 0 : const char *layer_name, const char *failure_name, const char *failure_type)
532 : : msg(sgwe_msg_builder(
533 : layer_name, failure_name, failure_type,
534 0 : "could not be defined in the dataset (definition failure)."))
535 : {
536 0 : }
537 :
538 : // OGR_NCScribe
539 136279 : void OGR_NCScribe::enqueue_transaction(MTPtr transactionAdd)
540 : {
541 136279 : if (transactionAdd.get() == nullptr)
542 : {
543 0 : return;
544 : }
545 :
546 : // See if the variable name is already being written to
547 136279 : if (this->varMaxInds.count(transactionAdd->getVarId()) > 0)
548 : {
549 136019 : size_t varWriteLength = this->varMaxInds[transactionAdd->getVarId()];
550 136019 : varWriteLength++;
551 136019 : this->varMaxInds[transactionAdd->getVarId()] = varWriteLength;
552 : }
553 :
554 : else
555 : {
556 : // Otherwise, just add it to the list of variable names being written to
557 260 : std::pair<int, size_t> entry(transactionAdd->getVarId(), 1);
558 260 : this->varMaxInds.insert(entry);
559 : }
560 :
561 : // Add sizes to memory count
562 136279 : this->buf.addCount(sizeof(transactionAdd)); // account for pointer
563 136279 : this->buf.addCount(transactionAdd->count()); // account for pointee
564 :
565 : // Finally push the transaction in
566 136279 : this->transactionQueue.push(MTPtr(transactionAdd.release()));
567 : }
568 :
569 80 : void OGR_NCScribe::commit_transaction()
570 : {
571 80 : wl.startRead();
572 :
573 160 : NCWMap writerMap;
574 160 : std::vector<int> varV;
575 :
576 80 : MTPtr t;
577 80 : t = this->pop();
578 :
579 136359 : while (t.get() != nullptr)
580 : {
581 136279 : int varId = t->getVarId();
582 : size_t writeInd;
583 :
584 : // First, find where to write. If doesn't exist, write to index 0
585 136279 : if (this->varWriteInds.count(varId) > 0)
586 : {
587 136019 : writeInd = this->varWriteInds[varId];
588 : }
589 :
590 : else
591 : {
592 260 : std::pair<int, size_t> insertable(varId, 0);
593 260 : this->varWriteInds.insert(insertable);
594 260 : writeInd = 0;
595 : }
596 :
597 : // Then write
598 : // Subtract sizes from memory count
599 136279 : this->buf.subCount(sizeof(t)); // account for pointer
600 136279 : this->buf.subCount(t->count()); // account for pointee
601 :
602 : try
603 : {
604 : // If variable length type, for now, continue using old committing
605 : // scheme Maybe some future work: optimize this in the similar
606 : // manner to other types However, CHAR and STRING have huge copying
607 : // overhead and are more complicated to memory manage correctly
608 271660 : if (t->getType() == NC_CHAR || t->getType() == NC_STRING ||
609 135381 : singleDatumMode)
610 : {
611 24148 : t->commit(ncvd, writeInd);
612 : }
613 :
614 : // Other types: Use a more optimized approach
615 : else
616 : {
617 112131 : int wvid = t->getVarId();
618 112131 : size_t numEntries = this->varMaxInds.at(wvid);
619 112131 : nc_type ncw = t->getType();
620 112131 : size_t curWrInd = this->varWriteInds.at(wvid);
621 :
622 : // If entry doesn't exist in map, then add it
623 112131 : switch (ncw)
624 : {
625 0 : case NC_BYTE:
626 : {
627 0 : NCWMapAllocIfNeeded<signed char>(wvid, writerMap,
628 : numEntries, varV);
629 : auto byte_trn =
630 0 : cpl::down_cast<OGR_SGFS_NC_Byte_Transaction *>(
631 : t.get());
632 0 : NCWMapWriteAndCommit<signed char>(
633 : wvid, writerMap, curWrInd, numEntries,
634 0 : byte_trn->getData(), this->ncvd);
635 0 : break;
636 : }
637 1 : case NC_SHORT:
638 : {
639 1 : NCWMapAllocIfNeeded<short>(wvid, writerMap, numEntries,
640 : varV);
641 : auto short_trn =
642 1 : cpl::down_cast<OGR_SGFS_NC_Short_Transaction *>(
643 : t.get());
644 1 : NCWMapWriteAndCommit<short>(
645 : wvid, writerMap, curWrInd, numEntries,
646 1 : short_trn->getData(), this->ncvd);
647 1 : break;
648 : }
649 1578 : case NC_INT:
650 : {
651 1578 : NCWMapAllocIfNeeded<int>(wvid, writerMap, numEntries,
652 : varV);
653 : auto int_trn =
654 1578 : cpl::down_cast<OGR_SGFS_NC_Int_Transaction *>(
655 : t.get());
656 1578 : NCWMapWriteAndCommit<int>(
657 : wvid, writerMap, curWrInd, numEntries,
658 : int_trn->getData(), this->ncvd);
659 1578 : break;
660 : }
661 1 : case NC_FLOAT:
662 : {
663 1 : NCWMapAllocIfNeeded<float>(wvid, writerMap, numEntries,
664 : varV);
665 : auto float_trn =
666 1 : cpl::down_cast<OGR_SGFS_NC_Float_Transaction *>(
667 : t.get());
668 1 : NCWMapWriteAndCommit<float>(
669 : wvid, writerMap, curWrInd, numEntries,
670 : float_trn->getData(), this->ncvd);
671 1 : break;
672 : }
673 110551 : case NC_DOUBLE:
674 : {
675 110551 : NCWMapAllocIfNeeded<double>(wvid, writerMap, numEntries,
676 : varV);
677 : auto double_trn =
678 110551 : cpl::down_cast<OGR_SGFS_NC_Double_Transaction *>(
679 : t.get());
680 110551 : NCWMapWriteAndCommit<double>(
681 : wvid, writerMap, curWrInd, numEntries,
682 : double_trn->getData(), this->ncvd);
683 110551 : break;
684 : }
685 0 : case NC_UINT:
686 : {
687 0 : NCWMapAllocIfNeeded<unsigned>(wvid, writerMap,
688 : numEntries, varV);
689 : auto uint_trn =
690 0 : cpl::down_cast<OGR_SGFS_NC_UInt_Transaction *>(
691 : t.get());
692 0 : NCWMapWriteAndCommit<unsigned>(
693 : wvid, writerMap, curWrInd, numEntries,
694 : uint_trn->getData(), this->ncvd);
695 0 : break;
696 : }
697 0 : case NC_UINT64:
698 : {
699 0 : NCWMapAllocIfNeeded<unsigned long long>(
700 : wvid, writerMap, numEntries, varV);
701 : auto uint64_trn =
702 0 : cpl::down_cast<OGR_SGFS_NC_UInt64_Transaction *>(
703 : t.get());
704 0 : NCWMapWriteAndCommit<unsigned long long>(
705 : wvid, writerMap, curWrInd, numEntries,
706 : uint64_trn->getData(), this->ncvd);
707 0 : break;
708 : }
709 0 : case NC_INT64:
710 : {
711 0 : NCWMapAllocIfNeeded<long long>(wvid, writerMap,
712 : numEntries, varV);
713 : auto int64_trn =
714 0 : cpl::down_cast<OGR_SGFS_NC_Int64_Transaction *>(
715 : t.get());
716 0 : NCWMapWriteAndCommit<long long>(
717 : wvid, writerMap, curWrInd, numEntries,
718 : int64_trn->getData(), this->ncvd);
719 0 : break;
720 : }
721 0 : case NC_UBYTE:
722 : {
723 0 : NCWMapAllocIfNeeded<unsigned char>(wvid, writerMap,
724 : numEntries, varV);
725 : auto ubyte_trn =
726 0 : cpl::down_cast<OGR_SGFS_NC_UByte_Transaction *>(
727 : t.get());
728 0 : NCWMapWriteAndCommit<unsigned char>(
729 : wvid, writerMap, curWrInd, numEntries,
730 0 : ubyte_trn->getData(), this->ncvd);
731 0 : break;
732 : }
733 0 : case NC_USHORT:
734 : {
735 0 : NCWMapAllocIfNeeded<unsigned short>(wvid, writerMap,
736 : numEntries, varV);
737 : auto ushort_trn =
738 0 : cpl::down_cast<OGR_SGFS_NC_UShort_Transaction *>(
739 : t.get());
740 0 : NCWMapWriteAndCommit<unsigned short>(
741 : wvid, writerMap, curWrInd, numEntries,
742 0 : ushort_trn->getData(), this->ncvd);
743 0 : break;
744 : }
745 0 : default:
746 : {
747 0 : break;
748 : }
749 : }
750 : }
751 : }
752 0 : catch (SG_Exception &sge)
753 : {
754 0 : CPLError(CE_Failure, CPLE_FileIO, "%s", sge.get_err_msg());
755 : }
756 :
757 : // increment index
758 136279 : this->varWriteInds[varId]++;
759 136279 : t = this->pop();
760 : }
761 :
762 : // Clean up afterwards, potential miswrites
763 281 : for (size_t vcount = 0; vcount < varV.size(); vcount++)
764 : {
765 201 : int cleanid = varV[vcount];
766 :
767 201 : if (writerMap.count(cleanid) > 0)
768 : {
769 0 : CPLFree(writerMap.at(cleanid));
770 0 : CPLError(CE_Failure, CPLE_FileIO,
771 : "Transaction corruption detected. The target variable "
772 : "will most likely be missing data.");
773 : }
774 : }
775 80 : }
776 :
777 136359 : MTPtr OGR_NCScribe::pop()
778 : {
779 : // Buffered changes are the earliest, so commit those first
780 272718 : MTPtr m = this->wl.pop();
781 136359 : if (m.get() != nullptr)
782 : {
783 : // add it to the buffer count
784 88439 : this->buf.addCount(sizeof(m));
785 88439 : this->buf.addCount(m->count());
786 88439 : return m;
787 : }
788 :
789 47920 : else if (!transactionQueue.empty())
790 : {
791 : OGR_SGFS_Transaction *value =
792 47840 : this->transactionQueue.front()
793 47840 : .release(); // due to delete copy A.K.A uniqueness of
794 : // unique_ptr
795 47840 : this->transactionQueue.pop();
796 :
797 47840 : return MTPtr(value);
798 : }
799 : else
800 : {
801 80 : return MTPtr();
802 : }
803 : }
804 :
805 360 : void OGR_NCScribe::log_transaction()
806 : {
807 360 : if (wl.logIsNull())
808 8 : wl.startLog();
809 :
810 88799 : while (!transactionQueue.empty())
811 : {
812 : // coverity[var_deref_model]
813 88439 : wl.push(MTPtr(transactionQueue.front().release()));
814 88439 : this->transactionQueue.pop();
815 : }
816 360 : this->buf.reset();
817 360 : }
818 :
819 : // WBufferManager
820 446 : bool WBufferManager::isOverQuota()
821 : {
822 446 : unsigned long long sum = 0;
823 1338 : for (size_t s = 0; s < bufs.size(); s++)
824 : {
825 892 : WBuffer &b = *(bufs[s]);
826 892 : sum += b.getUsage();
827 : }
828 :
829 446 : return sum > this->buffer_soft_limit;
830 : }
831 :
832 : // Transactions
833 0 : void OGR_SGFS_NC_Char_Transaction::appendToLog(VSILFILE *f)
834 : {
835 0 : int vid = OGR_SGFS_Transaction::getVarId();
836 0 : int type = NC_CHAR;
837 0 : int8_t OP = 0;
838 0 : size_t DATA_SIZE = char_rep.length();
839 :
840 0 : VSIFWriteL(&vid, sizeof(int), 1, f); // write varID data
841 0 : VSIFWriteL(&type, sizeof(int), 1, f); // write NC type
842 0 : VSIFWriteL(&OP, sizeof(int8_t), 1, f); // write "OP" flag
843 0 : VSIFWriteL(&DATA_SIZE, sizeof(size_t), 1, f); // write length
844 0 : VSIFWriteL(char_rep.c_str(), sizeof(char), DATA_SIZE, f); // write data
845 0 : }
846 :
847 0 : void OGR_SGFS_NC_String_Transaction::appendToLog(VSILFILE *f)
848 : {
849 0 : int vid = OGR_SGFS_Transaction::getVarId();
850 0 : int type = NC_STRING;
851 0 : size_t DATA_SIZE = char_rep.length();
852 :
853 0 : VSIFWriteL(&vid, sizeof(int), 1, f); // write varID data
854 0 : VSIFWriteL(&type, sizeof(int), 1, f); // write NC type
855 0 : VSIFWriteL(&DATA_SIZE, sizeof(size_t), 1, f); // write length
856 0 : VSIFWriteL(char_rep.c_str(), sizeof(char), DATA_SIZE, f); // write data
857 0 : }
858 :
859 588 : void OGR_SGFS_NC_CharA_Transaction::appendToLog(VSILFILE *f)
860 : {
861 588 : int vid = OGR_SGFS_Transaction::getVarId();
862 588 : int type = NC_CHAR;
863 588 : int8_t OP = 1;
864 588 : size_t DATA_SIZE = char_rep.length();
865 :
866 588 : VSIFWriteL(&vid, sizeof(int), 1, f); // write varID data
867 588 : VSIFWriteL(&type, sizeof(int), 1, f); // write NC type
868 588 : VSIFWriteL(&OP, sizeof(int8_t), 1, f); // write "OP" flag
869 588 : VSIFWriteL(&DATA_SIZE, sizeof(size_t), 1, f); // write length
870 588 : VSIFWriteL(char_rep.c_str(), sizeof(char), DATA_SIZE, f); // write data
871 588 : }
872 :
873 : // WTransactionLog
874 1932 : WTransactionLog::WTransactionLog(const std::string &logName) : wlogName(logName)
875 : {
876 1932 : }
877 :
878 8 : void WTransactionLog::startLog()
879 : {
880 8 : log = VSIFOpenL(wlogName.c_str(), "w");
881 8 : }
882 :
883 80 : void WTransactionLog::startRead()
884 : {
885 80 : if (log == nullptr)
886 72 : return;
887 :
888 8 : VSIFCloseL(this->log);
889 8 : this->log = VSIFOpenL(wlogName.c_str(), "r");
890 : }
891 :
892 88439 : void WTransactionLog::push(MTPtr t)
893 : {
894 88439 : t->appendToLog(this->log);
895 88439 : }
896 :
897 136359 : MTPtr WTransactionLog::pop()
898 : {
899 136359 : if (log == nullptr)
900 45610 : return MTPtr(nullptr);
901 :
902 : int varId;
903 : nc_type ntype;
904 : size_t itemsread;
905 90749 : itemsread = VSIFReadL(&varId, sizeof(int), 1, log);
906 90749 : itemsread &= VSIFReadL(&ntype, sizeof(nc_type), 1, log);
907 :
908 : // If one of the two reads failed, then return nullptr
909 90749 : if (!itemsread)
910 2310 : return MTPtr(nullptr);
911 :
912 : // If not, continue on and parse additional fields
913 88439 : switch (ntype)
914 : {
915 : // NC-3 Primitives
916 0 : case NC_BYTE:
917 : return genericLogDataRead<OGR_SGFS_NC_Byte_Transaction,
918 0 : signed char>(varId, log);
919 0 : case NC_SHORT:
920 : return genericLogDataRead<OGR_SGFS_NC_Short_Transaction, short>(
921 0 : varId, log);
922 1067 : case NC_INT:
923 : return genericLogDataRead<OGR_SGFS_NC_Int_Transaction, int>(varId,
924 1067 : log);
925 0 : case NC_FLOAT:
926 : return genericLogDataRead<OGR_SGFS_NC_Float_Transaction, float>(
927 0 : varId, log);
928 86784 : case NC_DOUBLE:
929 : return genericLogDataRead<OGR_SGFS_NC_Double_Transaction, double>(
930 86784 : varId, log);
931 0 : case NC_UBYTE:
932 : return genericLogDataRead<OGR_SGFS_NC_UByte_Transaction,
933 0 : unsigned char>(varId, log);
934 0 : case NC_USHORT:
935 : return genericLogDataRead<OGR_SGFS_NC_UShort_Transaction,
936 0 : unsigned short>(varId, log);
937 0 : case NC_UINT:
938 : return genericLogDataRead<OGR_SGFS_NC_UInt_Transaction,
939 0 : unsigned int>(varId, log);
940 0 : case NC_INT64:
941 : return genericLogDataRead<OGR_SGFS_NC_Int64_Transaction, long long>(
942 0 : varId, log);
943 0 : case NC_UINT64:
944 : return genericLogDataRead<OGR_SGFS_NC_UInt64_Transaction,
945 0 : unsigned long long>(varId, log);
946 588 : case NC_CHAR:
947 : {
948 : size_t readcheck; // 0 means at least one read 0 bytes
949 :
950 : // Check what type of OP is requested
951 588 : int8_t op = 0;
952 588 : readcheck = VSIFReadL(&op, sizeof(int8_t), 1, log);
953 588 : if (!readcheck)
954 0 : return MTPtr(); // read failure
955 :
956 : size_t strsize;
957 :
958 : // get how much data to read
959 588 : readcheck = VSIFReadL(&strsize, sizeof(size_t), 1, log);
960 588 : if (!readcheck)
961 0 : return MTPtr(); // read failure
962 :
963 1176 : std::string data;
964 588 : data.resize(strsize);
965 :
966 : // read that data and return it
967 588 : readcheck = VSIFReadL(&data[0], sizeof(char), strsize, log);
968 588 : if (!readcheck)
969 0 : return MTPtr(); // read failure
970 :
971 : // case: its a standard CHAR op
972 588 : if (!op)
973 : {
974 : return MTPtr(new OGR_SGFS_NC_Char_Transaction(
975 0 : varId, &data[0])); // data is copied so okay!
976 : }
977 :
978 : // case: its a CHARA op, additional processing
979 : else
980 : {
981 : return MTPtr(
982 588 : new OGR_SGFS_NC_CharA_Transaction(varId, &data[0]));
983 : }
984 : }
985 :
986 0 : case NC_STRING:
987 : {
988 : size_t readcheck; // 0 means at least one read 0 bytes
989 :
990 : size_t strsize;
991 :
992 : // get how much data to read
993 0 : readcheck = VSIFReadL(&strsize, sizeof(size_t), 1, log);
994 :
995 0 : if (!readcheck)
996 0 : return MTPtr(); // read failure
997 :
998 0 : std::string data;
999 0 : data.resize(strsize);
1000 :
1001 : // read that data and return it
1002 0 : readcheck = VSIFReadL(&data[0], sizeof(char), strsize, log);
1003 :
1004 0 : if (!readcheck)
1005 0 : return MTPtr(); // read failure
1006 :
1007 : return MTPtr(new OGR_SGFS_NC_String_Transaction(
1008 0 : varId, &data[0])); // data is copied so okay!
1009 : }
1010 :
1011 0 : default:
1012 : // Unsupported type
1013 0 : return MTPtr();
1014 : }
1015 : }
1016 :
1017 1932 : WTransactionLog::~WTransactionLog()
1018 : {
1019 1932 : if (log != nullptr)
1020 : {
1021 8 : VSIFCloseL(log);
1022 8 : VSIUnlink(this->wlogName.c_str());
1023 : }
1024 1932 : }
1025 :
1026 : // Helper function definitions
1027 42 : int write_Geometry_Container(
1028 : int ncID, const std::string &name, geom_t geometry_type,
1029 : const std::vector<std::string> &node_coordinate_names)
1030 : {
1031 :
1032 : int write_var_id;
1033 : int err_code;
1034 :
1035 : // Define geometry container variable
1036 : err_code =
1037 42 : nc_def_var(ncID, name.c_str(), NC_FLOAT, 0, nullptr, &write_var_id);
1038 : // todo: exception handling of err_code
1039 42 : NCDF_ERR(err_code);
1040 42 : if (err_code != NC_NOERR)
1041 : {
1042 : throw SGWriter_Exception_NCDefFailure(name.c_str(),
1043 0 : "geometry_container", "variable");
1044 : }
1045 :
1046 : /* Geometry Type Attribute
1047 : * -
1048 : */
1049 :
1050 : // Next, go on to add attributes needed for each geometry type
1051 : std::string geometry_str =
1052 32 : (geometry_type == POINT || geometry_type == MULTIPOINT)
1053 : ? CF_SG_TYPE_POINT
1054 27 : : (geometry_type == LINE || geometry_type == MULTILINE)
1055 56 : ? CF_SG_TYPE_LINE
1056 18 : : (geometry_type == POLYGON || geometry_type == MULTIPOLYGON)
1057 42 : ? CF_SG_TYPE_POLY
1058 116 : : ""; // obviously an error condition...
1059 :
1060 42 : if (geometry_str == "")
1061 : {
1062 0 : throw SG_Exception_BadFeature();
1063 : }
1064 :
1065 : // Add the geometry type attribute
1066 42 : err_code = nc_put_att_text(ncID, write_var_id, CF_SG_GEOMETRY_TYPE,
1067 : geometry_str.size(), geometry_str.c_str());
1068 42 : NCDF_ERR(err_code);
1069 42 : if (err_code != NC_NOERR)
1070 : {
1071 : throw SGWriter_Exception_NCWriteFailure(
1072 : name.c_str(), CF_SG_GEOMETRY_TYPE,
1073 0 : "attribute in geometry_container");
1074 : }
1075 :
1076 : /* Node Coordinates Attribute
1077 : * -
1078 : */
1079 42 : std::string ncoords_atr_str = "";
1080 :
1081 142 : for (size_t itr = 0; itr < node_coordinate_names.size(); itr++)
1082 : {
1083 100 : ncoords_atr_str += node_coordinate_names[itr];
1084 100 : if (itr < node_coordinate_names.size() - 1)
1085 : {
1086 58 : ncoords_atr_str += " ";
1087 : }
1088 : }
1089 :
1090 42 : err_code = nc_put_att_text(ncID, write_var_id, CF_SG_NODE_COORDINATES,
1091 : ncoords_atr_str.size(), ncoords_atr_str.c_str());
1092 :
1093 42 : NCDF_ERR(err_code);
1094 42 : if (err_code != NC_NOERR)
1095 : {
1096 : throw SGWriter_Exception_NCWriteFailure(
1097 : name.c_str(), CF_SG_NODE_COORDINATES,
1098 0 : "attribute in geometry_container");
1099 : }
1100 : // The previous two attributes are all that are required from POINT
1101 :
1102 : /* Node_Count Attribute
1103 : * (not needed for POINT)
1104 : */
1105 42 : if (geometry_type != POINT)
1106 : {
1107 64 : std::string nodecount_atr_str = name + "_node_count";
1108 :
1109 32 : err_code = nc_put_att_text(ncID, write_var_id, CF_SG_NODE_COUNT,
1110 : nodecount_atr_str.size(),
1111 : nodecount_atr_str.c_str());
1112 32 : NCDF_ERR(err_code);
1113 32 : if (err_code != NC_NOERR)
1114 : {
1115 : throw SGWriter_Exception_NCWriteFailure(
1116 : name.c_str(), CF_SG_NODE_COUNT,
1117 0 : "attribute in geometry_container");
1118 : }
1119 : }
1120 :
1121 : /* Part_Node_Count Attribute
1122 : * (only needed for MULTILINE, MULTIPOLYGON, and (potentially) POLYGON)
1123 : */
1124 42 : if (geometry_type == MULTILINE || geometry_type == MULTIPOLYGON ||
1125 : geometry_type == POLYGON)
1126 : {
1127 54 : std::string pnc_atr_str = name + "_part_node_count";
1128 :
1129 27 : err_code = nc_put_att_text(ncID, write_var_id, CF_SG_PART_NODE_COUNT,
1130 : pnc_atr_str.size(), pnc_atr_str.c_str());
1131 :
1132 27 : NCDF_ERR(err_code);
1133 27 : if (err_code != NC_NOERR)
1134 : {
1135 : throw SGWriter_Exception_NCWriteFailure(
1136 : name.c_str(), CF_SG_PART_NODE_COUNT,
1137 0 : "attribute in geometry_container");
1138 : }
1139 : }
1140 :
1141 : /* Interior Ring Attribute
1142 : * (only needed potentially for MULTIPOLYGON and POLYGON)
1143 : */
1144 :
1145 42 : if (geometry_type == MULTIPOLYGON || geometry_type == POLYGON)
1146 : {
1147 48 : std::string ir_atr_str = name + "_interior_ring";
1148 :
1149 24 : err_code = nc_put_att_text(ncID, write_var_id, CF_SG_INTERIOR_RING,
1150 : ir_atr_str.size(), ir_atr_str.c_str());
1151 24 : NCDF_ERR(err_code);
1152 24 : if (err_code != NC_NOERR)
1153 : {
1154 : throw nccfdriver::SGWriter_Exception_NCWriteFailure(
1155 : name.c_str(), CF_SG_INTERIOR_RING,
1156 0 : "attribute in geometry_container");
1157 : }
1158 : }
1159 :
1160 84 : return write_var_id;
1161 : }
1162 : } // namespace nccfdriver
|