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