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

Generated by: LCOV version 1.14