LCOV - code coverage report
Current view: top level - frmts/netcdf - netcdfsgwriterutil.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 393 524 75.0 %
Date: 2026-10-23 13:59:38 Functions: 20 26 76.9 %

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

Generated by: LCOV version 1.14