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

Generated by: LCOV version 1.14