LCOV - code coverage report
Current view: top level - frmts/netcdf - netcdfsg.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 427 489 87.3 %
Date: 2024-05-04 12:52:34 Functions: 19 23 82.6 %

          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 <cstdio>
      29             : #include <cstring>
      30             : #include <set>
      31             : #include <vector>
      32             : #include "netcdf.h"
      33             : #include "netcdfdataset.h"
      34             : #include "netcdfsg.h"
      35             : 
      36             : namespace nccfdriver
      37             : {
      38             : /* Attribute Fetch
      39             :  * -
      40             :  * A function which makes it a bit easier to fetch single text attribute values
      41             :  * ncid: as used in netcdf.h
      42             :  * varID: variable id in which to look for the attribute
      43             :  * attrName: name of attribute to fine
      44             :  * alloc: a reference to a string that will be filled with the attribute (i.e.
      45             :  * truncated and filled with the return value) Returns: a reference to the
      46             :  * string to fill (a.k.a. string pointed to by alloc reference)
      47             :  */
      48        1111 : std::string &attrf(int ncid, int varId, const char *attrName,
      49             :                    std::string &alloc)
      50             : {
      51        1111 :     size_t len = 0;
      52        1111 :     nc_inq_attlen(ncid, varId, attrName, &len);
      53             : 
      54        1111 :     if (len < 1)
      55             :     {
      56         196 :         alloc.clear();
      57         196 :         return alloc;
      58             :     }
      59             : 
      60         915 :     alloc.resize(len);
      61         915 :     memset(&alloc[0], 0, len);
      62             : 
      63             :     // Now look through this variable for the attribute
      64         915 :     nc_get_att_text(ncid, varId, attrName, &alloc[0]);
      65             : 
      66         915 :     return alloc;
      67             : }
      68             : 
      69             : /* SGeometry_Reader
      70             :  * (implementations)
      71             :  *
      72             :  */
      73          78 : SGeometry_Reader::SGeometry_Reader(int ncId, int geoVarId)
      74         144 :     : gc_varId(geoVarId), touple_order(0)
      75             : {
      76             : 
      77             :     char container_name[NC_MAX_NAME + 1];
      78          78 :     memset(container_name, 0, NC_MAX_NAME + 1);
      79             : 
      80             :     // Get geometry container name
      81          78 :     if (nc_inq_varname(ncId, geoVarId, container_name) != NC_NOERR)
      82             :     {
      83             :         throw SG_Exception_Existential("new geometry container",
      84           0 :                                        "the variable of the given ID");
      85             :     }
      86             : 
      87             :     // Establish string version of container_name
      88          78 :     container_name_s = std::string(container_name);
      89             : 
      90             :     // Find geometry type
      91          78 :     this->type = nccfdriver::getGeometryType(ncId, geoVarId);
      92             : 
      93          78 :     if (this->type == NONE)
      94             :     {
      95             :         throw SG_Exception_Existential(
      96           1 :             static_cast<const char *>(container_name), CF_SG_GEOMETRY_TYPE);
      97             :     }
      98             : 
      99             :     // Get grid mapping variable, if it exists
     100          77 :     this->gm_varId = INVALID_VAR_ID;
     101          77 :     if (attrf(ncId, geoVarId, CF_GRD_MAPPING, gm_name_s) != "")
     102             :     {
     103          51 :         const char *gm_name = gm_name_s.c_str();
     104             :         int gmVID;
     105          51 :         if (nc_inq_varid(ncId, gm_name, &gmVID) == NC_NOERR)
     106             :         {
     107          51 :             this->gm_varId = gmVID;
     108             :         }
     109             :     }
     110             : 
     111             :     // Find a list of node counts and part node count
     112         154 :     std::string nc_name_s;
     113         154 :     std::string pnc_name_s;
     114         154 :     std::string ir_name_s;
     115          77 :     int pnc_vid = INVALID_VAR_ID;
     116          77 :     int nc_vid = INVALID_VAR_ID;
     117          77 :     int ir_vid = INVALID_VAR_ID;
     118             :     int buf;
     119          77 :     size_t bound = 0;
     120          77 :     size_t total_node_count = 0;  // used in error checks later
     121          77 :     size_t total_part_node_count = 0;
     122          77 :     if (attrf(ncId, geoVarId, CF_SG_NODE_COUNT, nc_name_s) != "")
     123             :     {
     124          61 :         const char *nc_name = nc_name_s.c_str();
     125          61 :         nc_inq_varid(ncId, nc_name, &nc_vid);
     126         707 :         while (nc_get_var1_int(ncId, nc_vid, &bound, &buf) == NC_NOERR)
     127             :         {
     128         647 :             if (buf < 0)
     129             :             {
     130             :                 throw SG_Exception_Value_Violation(
     131             :                     static_cast<const char *>(container_name), CF_SG_NODE_COUNT,
     132           1 :                     "negative");
     133             :             }
     134             : 
     135         646 :             this->node_counts.push_back(buf);
     136         646 :             total_node_count += buf;
     137         646 :             bound++;
     138             :         }
     139             :     }
     140             : 
     141          76 :     if (attrf(ncId, geoVarId, CF_SG_PART_NODE_COUNT, pnc_name_s) != "")
     142             :     {
     143          41 :         const char *pnc_name = pnc_name_s.c_str();
     144          41 :         bound = 0;
     145          41 :         nc_inq_varid(ncId, pnc_name, &pnc_vid);
     146         974 :         while (nc_get_var1_int(ncId, pnc_vid, &bound, &buf) == NC_NOERR)
     147             :         {
     148         934 :             if (buf < 0)
     149             :             {
     150             :                 throw SG_Exception_Value_Violation(
     151             :                     static_cast<const char *>(container_name),
     152           1 :                     CF_SG_PART_NODE_COUNT, "negative");
     153             :             }
     154             : 
     155         933 :             this->pnode_counts.push_back(buf);
     156         933 :             total_part_node_count += buf;
     157         933 :             bound++;
     158             :         }
     159             :     }
     160             : 
     161          75 :     if (attrf(ncId, geoVarId, CF_SG_INTERIOR_RING, ir_name_s) != "")
     162             :     {
     163          12 :         const char *ir_name = ir_name_s.c_str();
     164          12 :         bound = 0;
     165          12 :         nc_inq_varid(ncId, ir_name, &ir_vid);
     166         295 :         while (nc_get_var1_int(ncId, ir_vid, &bound, &buf) == NC_NOERR)
     167             :         {
     168         284 :             bool store = buf == 0 ? false : true;
     169             : 
     170         284 :             if (buf != 0 && buf != 1)
     171             :             {
     172             :                 throw SG_Exception_Value_Required(
     173             :                     static_cast<const char *>(container_name),
     174           1 :                     CF_SG_INTERIOR_RING, "0 or 1");
     175             :             }
     176             : 
     177         283 :             this->int_rings.push_back(store);
     178         283 :             bound++;
     179             :         }
     180             :     }
     181             : 
     182             :     /* Enforcement of well formed CF files
     183             :      * If these are not met then the dataset is malformed and will halt further
     184             :      * processing of simple geometries.
     185             :      */
     186             : 
     187             :     // part node count exists only when node count exists
     188          74 :     if (pnode_counts.size() > 0 && node_counts.size() == 0)
     189             :     {
     190             :         throw SG_Exception_Dep(static_cast<const char *>(container_name),
     191           0 :                                CF_SG_PART_NODE_COUNT, CF_SG_NODE_COUNT);
     192             :     }
     193             : 
     194             :     // part node counts and node counts don't match up in count
     195          74 :     if (pnc_vid != INVALID_VAR_ID && total_node_count != total_part_node_count)
     196             :     {
     197             :         throw SG_Exception_BadSum(static_cast<const char *>(container_name),
     198           0 :                                   CF_SG_PART_NODE_COUNT, CF_SG_PART_NODE_COUNT);
     199             :     }
     200             : 
     201             :     // interior rings only exist when part node counts exist
     202          74 :     if (int_rings.size() > 0 && pnode_counts.size() == 0)
     203             :     {
     204             :         throw SG_Exception_Dep(static_cast<const char *>(container_name),
     205           0 :                                CF_SG_INTERIOR_RING, CF_SG_PART_NODE_COUNT);
     206             :     }
     207             : 
     208             :     // cardinality of part_node_counts == cardinality of interior_ring (if
     209             :     // interior ring > 0)
     210          74 :     if (int_rings.size() > 0)
     211             :     {
     212          11 :         if (int_rings.size() != pnode_counts.size())
     213             :         {
     214             :             throw SG_Exception_Dim_MM(static_cast<const char *>(container_name),
     215             :                                       CF_SG_INTERIOR_RING,
     216           0 :                                       CF_SG_PART_NODE_COUNT);
     217             :         }
     218             :     }
     219             : 
     220             :     // lines and polygons require node_counts, multipolygons are checked with
     221             :     // part_node_count
     222          74 :     if (this->type == POLYGON || this->type == LINE)
     223             :     {
     224          14 :         if (node_counts.size() < 1)
     225             :         {
     226             :             throw SG_Exception_Existential(
     227           1 :                 static_cast<const char *>(container_name), CF_SG_NODE_COUNT);
     228             :         }
     229             :     }
     230             : 
     231             :     /* Basic Safety checks End
     232             :      */
     233             : 
     234             :     // Create bound list
     235          73 :     size_t rc = 0;
     236          73 :     bound_list.push_back(0);  // start with 0
     237             : 
     238          73 :     if (node_counts.size() > 0)
     239             :     {
     240         640 :         for (size_t i = 0; i < node_counts.size() - 1; i++)
     241             :         {
     242         582 :             rc = rc + node_counts[i];
     243         582 :             bound_list.push_back(rc);
     244             :         }
     245             :     }
     246             : 
     247          74 :     std::string cart_s;
     248             :     // Node Coordinates
     249          73 :     if (attrf(ncId, geoVarId, CF_SG_NODE_COORDINATES, cart_s) == "")
     250             :     {
     251           0 :         throw SG_Exception_Existential(container_name, CF_SG_NODE_COORDINATES);
     252             :     }
     253             : 
     254             :     // Create parts count list and an offset list for parts indexing
     255          73 :     if (this->node_counts.size() > 0)
     256             :     {
     257          58 :         int ind = 0;
     258          58 :         int parts = 0;
     259          58 :         int prog = 0;
     260          58 :         int c = 0;
     261             : 
     262         987 :         for (size_t pcnt = 0; pcnt < pnode_counts.size(); pcnt++)
     263             :         {
     264         929 :             if (prog == 0)
     265         574 :                 pnc_bl.push_back(pcnt);
     266             : 
     267         929 :             if (int_rings.size() > 0 && !int_rings[pcnt])
     268         236 :                 c++;
     269             : 
     270         929 :             prog = prog + pnode_counts[pcnt];
     271         929 :             parts++;
     272             : 
     273         929 :             if (prog == node_counts[ind])
     274             :             {
     275         574 :                 ind++;
     276         574 :                 this->parts_count.push_back(parts);
     277         574 :                 if (int_rings.size() > 0)
     278         230 :                     this->poly_count.push_back(c);
     279         574 :                 c = 0;
     280         574 :                 prog = 0;
     281         574 :                 parts = 0;
     282             :             }
     283         355 :             else if (prog > node_counts[ind])
     284             :             {
     285             :                 throw SG_Exception_BadSum(container_name, CF_SG_PART_NODE_COUNT,
     286           0 :                                           CF_SG_NODE_COUNT);
     287             :             }
     288             :         }
     289             :     }
     290             : 
     291             :     // (1) the tuple order for a single point
     292             :     // (2) the variable ids with the relevant coordinate values
     293          73 :     int X = INVALID_VAR_ID;
     294          73 :     int Y = INVALID_VAR_ID;
     295          73 :     int Z = INVALID_VAR_ID;
     296             : 
     297             :     char cart[NC_MAX_NAME + 1];
     298          73 :     memset(cart, 0, NC_MAX_NAME + 1);
     299          73 :     strncpy(cart, cart_s.c_str(), NC_MAX_NAME);
     300             : 
     301          73 :     char *dim = strtok(cart, " ");
     302          73 :     int axis_id = 0;
     303             : 
     304         245 :     while (dim != nullptr)
     305             :     {
     306         172 :         if (nc_inq_varid(ncId, dim, &axis_id) == NC_NOERR)
     307             :         {
     308             : 
     309             :             // Check axis signature
     310         172 :             std::string a_sig;
     311         172 :             attrf(ncId, axis_id, CF_AXIS, a_sig);
     312             : 
     313             :             // If valid signify axis correctly
     314         172 :             if (a_sig == "X")
     315             :             {
     316          73 :                 X = axis_id;
     317             :             }
     318          99 :             else if (a_sig == "Y")
     319             :             {
     320          73 :                 Y = axis_id;
     321             :             }
     322          26 :             else if (a_sig == "Z")
     323             :             {
     324          26 :                 Z = axis_id;
     325             :             }
     326             :             else
     327             :             {
     328             :                 throw SG_Exception_Dep(container_name,
     329           0 :                                        "A node_coordinates variable", CF_AXIS);
     330             :             }
     331             : 
     332         172 :             this->touple_order++;
     333             :         }
     334             :         else
     335             :         {
     336           0 :             throw SG_Exception_Existential(container_name, dim);
     337             :         }
     338             : 
     339         172 :         dim = strtok(nullptr, " ");
     340             :     }
     341             : 
     342             :     // Write axis in X, Y, Z order
     343             : 
     344          73 :     if (X != INVALID_VAR_ID)
     345          73 :         this->nodec_varIds.push_back(X);
     346             :     else
     347             :     {
     348             :         throw SG_Exception_Existential(container_name,
     349           0 :                                        "node_coordinates: X-axis");
     350             :     }
     351          73 :     if (Y != INVALID_VAR_ID)
     352          73 :         this->nodec_varIds.push_back(Y);
     353             :     else
     354             :     {
     355             :         throw SG_Exception_Existential(container_name,
     356           0 :                                        "node_coordinates: Y-axis");
     357             :     }
     358          73 :     if (Z != INVALID_VAR_ID)
     359          26 :         this->nodec_varIds.push_back(Z);
     360             : 
     361             :     /* Final Checks for node coordinates
     362             :      * (1) Each axis has one and only one dimension, dim length of each node
     363             :      * coordinates are all equal (2) total node count == dim length of each node
     364             :      * coordinates (if node_counts not empty) (3) there are at least two node
     365             :      * coordinate variable ids
     366             :      */
     367             : 
     368          73 :     int all_dim = INVALID_VAR_ID;
     369          73 :     bool dim_set = false;
     370             :     //(1) one dimension check, each node_coordinates have same dimension
     371         244 :     for (size_t nvitr = 0; nvitr < nodec_varIds.size(); nvitr++)
     372             :     {
     373         172 :         int dimC = 0;
     374         172 :         nc_inq_varndims(ncId, nodec_varIds[nvitr], &dimC);
     375             : 
     376         172 :         if (dimC != 1)
     377             :         {
     378           0 :             throw SG_Exception_Not1D();
     379             :         }
     380             : 
     381             :         // check that all have same dimension
     382             :         int inter_dim[1];
     383         172 :         if (nc_inq_vardimid(ncId, nodec_varIds[nvitr], inter_dim) != NC_NOERR)
     384             :         {
     385             :             throw SG_Exception_Existential(
     386           0 :                 container_name, "one or more node_coordinate dimensions");
     387             :         }
     388             : 
     389         172 :         if (!dim_set)
     390             :         {
     391          73 :             all_dim = inter_dim[0];
     392          73 :             dim_set = true;
     393             :         }
     394             : 
     395             :         else
     396             :         {
     397          99 :             if (inter_dim[0] != all_dim)
     398             :                 throw SG_Exception_Dim_MM(
     399             :                     container_name, "X, Y",
     400           1 :                     "in general all node coordinate axes");
     401             :         }
     402             :     }
     403             : 
     404             :     // (2) check equality one
     405          72 :     if (node_counts.size() > 0)
     406             :     {
     407          57 :         size_t diml = 0;
     408          57 :         nc_inq_dimlen(ncId, all_dim, &diml);
     409             : 
     410          57 :         if (diml != total_node_count)
     411             :             throw SG_Exception_BadSum(container_name, "node_count",
     412           0 :                                       "node coordinate dimension length");
     413             :     }
     414             : 
     415             :     // (3) check tuple order
     416          72 :     if (this->touple_order < 2)
     417             :     {
     418             :         throw SG_Exception_Existential(
     419             :             container_name,
     420           0 :             "insufficient node coordinates must have at least two axis");
     421             :     }
     422             : 
     423             :     /* Investigate for instance dimension
     424             :      * The procedure is as follows
     425             :      *
     426             :      * (1) if there's node_count, use the dimension used to index node count
     427             :      * (2) otherwise it's point (singleton) data, in this case use the node
     428             :      * coordinate dimension
     429             :      */
     430          72 :     size_t instance_dim_len = 0;
     431             : 
     432          72 :     if (node_counts.size() >= 1)
     433             :     {
     434          57 :         int nc_dims = 0;
     435          57 :         nc_inq_varndims(ncId, nc_vid, &nc_dims);
     436             : 
     437          57 :         if (nc_dims != 1)
     438           0 :             throw SG_Exception_Not1D();
     439             : 
     440             :         int nc_dim_id[1];
     441             : 
     442          57 :         if (nc_inq_vardimid(ncId, nc_vid, nc_dim_id) != NC_NOERR)
     443             :         {
     444             :             throw SG_Exception_Existential(container_name,
     445           0 :                                            "node_count dimension");
     446             :         }
     447             : 
     448          57 :         this->inst_dimId = nc_dim_id[0];
     449             :     }
     450             : 
     451             :     else
     452             :     {
     453          15 :         this->inst_dimId = all_dim;
     454             :     }
     455             : 
     456          72 :     nc_inq_dimlen(ncId, this->inst_dimId, &instance_dim_len);
     457             : 
     458          72 :     if (instance_dim_len == 0)
     459           0 :         throw SG_Exception_EmptyDim();
     460             : 
     461             :     // Set values accordingly
     462          72 :     this->inst_dimLen = instance_dim_len;
     463          72 :     this->pt_buffer = std::make_unique<Point>(this->touple_order);
     464          72 :     this->gc_varId = geoVarId;
     465          72 :     this->ncid = ncId;
     466          72 : }
     467             : 
     468       67341 : Point &SGeometry_Reader::operator[](size_t index)
     469             : {
     470      202262 :     for (int order = 0; order < touple_order; order++)
     471             :     {
     472      134921 :         Point &pt = *(this->pt_buffer);
     473             :         double data;
     474      134921 :         size_t real_ind = index;
     475             : 
     476             :         // Read a single coord
     477             :         int err =
     478      134921 :             nc_get_var1_double(ncid, nodec_varIds[order], &real_ind, &data);
     479             : 
     480      134921 :         if (err != NC_NOERR)
     481             :         {
     482           0 :             throw SG_Exception_BadPoint();
     483             :         }
     484             : 
     485      134921 :         pt[order] = data;
     486             :     }
     487             : 
     488       67341 :     return *(this->pt_buffer);
     489             : }
     490             : 
     491         529 : size_t SGeometry_Reader::get_geometry_count()
     492             : {
     493         529 :     if (type == POINT)
     494             :     {
     495          47 :         if (this->nodec_varIds.size() < 1)
     496           0 :             return 0;
     497             : 
     498             :         // If more than one dim, then error. Otherwise inquire its length and
     499             :         // return that
     500             :         int dims;
     501          47 :         if (nc_inq_varndims(this->ncid, nodec_varIds[0], &dims) != NC_NOERR)
     502           0 :             return 0;
     503          47 :         if (dims != 1)
     504           0 :             return 0;
     505             : 
     506             :         // Find which dimension is used for x
     507             :         int index;
     508          47 :         if (nc_inq_vardimid(this->ncid, nodec_varIds[0], &index) != NC_NOERR)
     509             :         {
     510           0 :             return 0;
     511             :         }
     512             : 
     513             :         // Finally find the length
     514             :         size_t len;
     515          47 :         if (nc_inq_dimlen(this->ncid, index, &len) != NC_NOERR)
     516             :         {
     517           0 :             return 0;
     518             :         }
     519          47 :         return len;
     520             :     }
     521             : 
     522             :     else
     523         482 :         return this->node_counts.size();
     524             : }
     525             : 
     526        1200 : static void add_to_buffer(std::vector<unsigned char> &buffer, uint8_t v)
     527             : {
     528        1200 :     buffer.push_back(v);
     529        1200 : }
     530             : 
     531        2899 : static void add_to_buffer(std::vector<unsigned char> &buffer, uint32_t v)
     532             : {
     533        2899 :     const size_t old_size = buffer.size();
     534        2899 :     buffer.resize(old_size + sizeof(v));
     535        2899 :     memcpy(&buffer[old_size], &v, sizeof(v));
     536        2899 : }
     537             : 
     538             : static void add_to_buffer(std::vector<unsigned char> &, int) = delete;
     539             : 
     540      134921 : static void add_to_buffer(std::vector<unsigned char> &buffer, double v)
     541             : {
     542      134921 :     const size_t old_size = buffer.size();
     543      134921 :     buffer.resize(old_size + sizeof(v));
     544      134921 :     memcpy(&buffer[old_size], &v, sizeof(v));
     545      134921 : }
     546             : 
     547             : /* serializeToWKB
     548             :  * Takes the geometry in SGeometry at a given index and converts it into WKB
     549             :  * format. Converting SGeometry into WKB automatically allocates the required
     550             :  * buffer space and returns a buffer that MUST be free'd
     551             :  */
     552         492 : std::vector<unsigned char> SGeometry_Reader::serializeToWKB(size_t featureInd)
     553             : {
     554         492 :     std::vector<unsigned char> ret;
     555         492 :     int nc = 0;
     556         492 :     size_t sb = 0;
     557             : 
     558             :     // Points don't have node_count entry... only inspect and set node_counts if
     559             :     // not a point
     560         492 :     if (this->getGeometryType() != POINT)
     561             :     {
     562         453 :         nc = node_counts[featureInd];
     563         453 :         sb = bound_list[featureInd];
     564             :     }
     565             : 
     566             :     // Serialization occurs differently depending on geometry
     567             :     // The memory requirements also differ between geometries
     568         492 :     switch (this->getGeometryType())
     569             :     {
     570          39 :         case POINT:
     571          39 :             inPlaceSerialize_Point(this, featureInd, ret);
     572          39 :             break;
     573             : 
     574          16 :         case LINE:
     575          16 :             inPlaceSerialize_LineString(this, nc, sb, ret);
     576          16 :             break;
     577             : 
     578          13 :         case POLYGON:
     579             :             // A polygon has:
     580             :             // 1 byte header
     581             :             // 4 byte Type
     582             :             // 4 byte ring count (1 (exterior)) [assume only exterior rings,
     583             :             // otherwise multipolygon] For each ring: 4 byte point count, 8 byte
     584             :             // double x point count x # dimension (This is equivalent to
     585             :             // requesting enough for all points and a point count header for
     586             :             // each point) (Or 8 byte double x node count x # dimension + 4 byte
     587             :             // point count x part_count)
     588             : 
     589             :             // if interior ring, then assume that it will be a multipolygon
     590             :             // (maybe future work?)
     591          13 :             inPlaceSerialize_PolygonExtOnly(this, nc, sb, ret);
     592          13 :             break;
     593             : 
     594          25 :         case MULTIPOINT:
     595             :         {
     596          25 :             uint8_t header = PLATFORM_HEADER;
     597          32 :             uint32_t t = this->touple_order == 2   ? wkbMultiPoint
     598           7 :                          : this->touple_order == 3 ? wkbMultiPoint25D
     599             :                                                    : wkbNone;
     600             : 
     601          25 :             if (t == wkbNone)
     602           0 :                 throw SG_Exception_BadFeature();
     603             : 
     604             :             // Add metadata
     605          25 :             add_to_buffer(ret, header);
     606          25 :             add_to_buffer(ret, t);
     607          25 :             add_to_buffer(ret, static_cast<uint32_t>(nc));
     608             : 
     609             :             // Add points
     610         119 :             for (int pts = 0; pts < nc; pts++)
     611             :             {
     612          94 :                 inPlaceSerialize_Point(this, static_cast<size_t>(sb + pts),
     613             :                                        ret);
     614             :             }
     615             :         }
     616             : 
     617          25 :         break;
     618             : 
     619          18 :         case MULTILINE:
     620             :         {
     621          18 :             uint8_t header = PLATFORM_HEADER;
     622          25 :             uint32_t t = this->touple_order == 2   ? wkbMultiLineString
     623           7 :                          : this->touple_order == 3 ? wkbMultiLineString25D
     624             :                                                    : wkbNone;
     625             : 
     626          18 :             if (t == wkbNone)
     627           0 :                 throw SG_Exception_BadFeature();
     628          18 :             int32_t lc = parts_count[featureInd];
     629          18 :             size_t seek_begin = sb;
     630             :             size_t pc_begin =
     631          18 :                 pnc_bl[featureInd];  // initialize with first part count, list
     632             :                                      // of part counts is contiguous
     633          36 :             std::vector<int> pnc;
     634             : 
     635             :             // Build sub vector for part_node_counts
     636             :             // + Calculate wkbSize
     637          47 :             for (int itr = 0; itr < lc; itr++)
     638             :             {
     639          29 :                 pnc.push_back(pnode_counts[pc_begin + itr]);
     640             :             }
     641             : 
     642          18 :             size_t cur_point = seek_begin;
     643          18 :             size_t pcount = pnc.size();
     644             : 
     645             :             // Begin Writing
     646          18 :             add_to_buffer(ret, header);
     647          18 :             add_to_buffer(ret, t);
     648          18 :             add_to_buffer(ret, static_cast<uint32_t>(pcount));
     649             : 
     650          47 :             for (size_t itr = 0; itr < pcount; itr++)
     651             :             {
     652          29 :                 inPlaceSerialize_LineString(this, pnc[itr], cur_point, ret);
     653          29 :                 cur_point = pnc[itr] + cur_point;
     654             :             }
     655             :         }
     656             : 
     657          18 :         break;
     658             : 
     659         381 :         case MULTIPOLYGON:
     660             :         {
     661         381 :             uint8_t header = PLATFORM_HEADER;
     662         395 :             uint32_t t = this->touple_order == 2   ? wkbMultiPolygon
     663          14 :                          : this->touple_order == 3 ? wkbMultiPolygon25D
     664             :                                                    : wkbNone;
     665             : 
     666         381 :             if (t == wkbNone)
     667           0 :                 throw SG_Exception_BadFeature();
     668         381 :             bool noInteriors = this->int_rings.size() == 0 ? true : false;
     669         381 :             int32_t rc = parts_count[featureInd];
     670         381 :             size_t seek_begin = sb;
     671             :             size_t pc_begin =
     672         381 :                 pnc_bl[featureInd];  // initialize with first part count, list
     673             :                                      // of part counts is contiguous
     674         762 :             std::vector<int> pnc;
     675             : 
     676             :             // Build sub vector for part_node_counts
     677        1000 :             for (int itr = 0; itr < rc; itr++)
     678             :             {
     679         619 :                 pnc.push_back(pnode_counts[pc_begin + itr]);
     680             :             }
     681             : 
     682             :             // Create Multipolygon headers
     683         381 :             add_to_buffer(ret, header);
     684         381 :             add_to_buffer(ret, t);
     685             : 
     686         381 :             if (noInteriors)
     687             :             {
     688         221 :                 size_t cur_point = seek_begin;
     689         221 :                 size_t pcount = pnc.size();
     690         221 :                 add_to_buffer(ret, static_cast<uint32_t>(pcount));
     691             : 
     692         640 :                 for (size_t itr = 0; itr < pcount; itr++)
     693             :                 {
     694         419 :                     inPlaceSerialize_PolygonExtOnly(this, pnc[itr], cur_point,
     695             :                                                     ret);
     696         419 :                     cur_point = pnc[itr] + cur_point;
     697             :                 }
     698             :             }
     699             : 
     700             :             else
     701             :             {
     702         160 :                 int32_t polys = poly_count[featureInd];
     703         160 :                 add_to_buffer(ret, static_cast<uint32_t>(polys));
     704             : 
     705         160 :                 size_t base = pnc_bl[featureInd];  // beginning of parts_count
     706             :                                                    // for this multigeometry
     707         160 :                 size_t seek = seek_begin;  // beginning of node range for this
     708             :                                            // multigeometry
     709         160 :                 size_t ir_base = base + 1;
     710             : 
     711             :                 // has interior rings,
     712         326 :                 for (int32_t itr = 0; itr < polys; itr++)
     713             :                 {
     714         166 :                     int rc_m = 1;
     715             : 
     716             :                     // count how many parts belong to each Polygon
     717         200 :                     while (ir_base < int_rings.size() && int_rings[ir_base])
     718             :                     {
     719          34 :                         rc_m++;
     720          34 :                         ir_base++;
     721             :                     }
     722             : 
     723         166 :                     if (rc_m == 1)
     724         142 :                         ir_base++;  // single ring case
     725             : 
     726         166 :                     std::vector<int> poly_parts;
     727             : 
     728             :                     // Make part node count sub vector
     729         366 :                     for (int itr_2 = 0; itr_2 < rc_m; itr_2++)
     730             :                     {
     731         200 :                         poly_parts.push_back(pnode_counts[base + itr_2]);
     732             :                     }
     733             : 
     734         166 :                     inPlaceSerialize_Polygon(this, poly_parts, rc_m, seek, ret);
     735             : 
     736             :                     // Update seek position
     737         366 :                     for (size_t itr_3 = 0; itr_3 < poly_parts.size(); itr_3++)
     738             :                     {
     739         200 :                         seek += poly_parts[itr_3];
     740             :                     }
     741             : 
     742         166 :                     base += poly_parts.size();
     743             :                     // cppcheck-suppress redundantAssignment
     744         166 :                     ir_base = base + 1;
     745             :                 }
     746             :             }
     747             :         }
     748         381 :         break;
     749             : 
     750           0 :         default:
     751             : 
     752           0 :             throw SG_Exception_BadFeature();
     753             :             break;
     754             :     }
     755             : 
     756         492 :     return ret;
     757             : }
     758             : 
     759          72 : void SGeometry_PropertyScanner::open(int container_id)
     760             : {
     761             :     // First check for container_id, if variable doesn't exist error out
     762          72 :     if (nc_inq_var(this->nc, container_id, nullptr, nullptr, nullptr, nullptr,
     763          72 :                    nullptr) != NC_NOERR)
     764             :     {
     765           0 :         return;  // change to exception
     766             :     }
     767             : 
     768             :     // Now exists, see what variables refer to this one
     769             :     // First get name of this container
     770             :     char contname[NC_MAX_NAME + 1];
     771          72 :     memset(contname, 0, NC_MAX_NAME + 1);
     772          72 :     if (nc_inq_varname(this->nc, container_id, contname) != NC_NOERR)
     773             :     {
     774           0 :         return;
     775             :     }
     776             : 
     777             :     // Then scan throughout the netcdfDataset if those variables
     778             :     // geometry_container attribute matches the container
     779          72 :     int varCount = 0;
     780          72 :     if (nc_inq_nvars(this->nc, &varCount) != NC_NOERR)
     781             :     {
     782           0 :         return;
     783             :     }
     784             : 
     785         683 :     for (int curr = 0; curr < varCount; curr++)
     786             :     {
     787         611 :         size_t contname2_len = 0;
     788             : 
     789             :         // First find container length, and make buf that size in chars
     790         611 :         if (nc_inq_attlen(this->nc, curr, CF_SG_GEOMETRY, &contname2_len) !=
     791             :             NC_NOERR)
     792             :         {
     793             :             // not a geometry variable, continue
     794         477 :             continue;
     795             :         }
     796             : 
     797             :         // Also if present but empty, go on
     798         134 :         if (contname2_len == 0)
     799           0 :             continue;
     800             : 
     801             :         // Otherwise, geometry: see what container it has
     802             :         char buf[NC_MAX_CHAR + 1];
     803         134 :         memset(buf, 0, NC_MAX_CHAR + 1);
     804             : 
     805         134 :         if (nc_get_att_text(this->nc, curr, CF_SG_GEOMETRY, buf) != NC_NOERR)
     806             :         {
     807           0 :             continue;
     808             :         }
     809             : 
     810             :         // If matches, then establish a reference by placing this variable's
     811             :         // data in both vectors
     812         134 :         if (!strcmp(contname, buf))
     813             :         {
     814         118 :             char property_name[NC_MAX_NAME + 1] = {0};
     815             : 
     816             :             // look for special OGR original name field
     817         118 :             if (nc_get_att_text(this->nc, curr, OGR_SG_ORIGINAL_LAYERNAME,
     818         118 :                                 property_name) != NC_NOERR)
     819             :             {
     820             :                 // if doesn't exist, then just use the variable name
     821         118 :                 if (nc_inq_varname(this->nc, curr, property_name) != NC_NOERR)
     822             :                 {
     823           0 :                     throw SG_Exception_General_Malformed(contname);
     824             :                 }
     825             :             }
     826             : 
     827         236 :             std::string n(property_name);
     828         118 :             v_ids.push_back(curr);
     829         118 :             v_headers.push_back(n);
     830             :         }
     831             :     }
     832             : }
     833             : 
     834             : // Exception Class Implementations
     835           1 : SG_Exception_Dim_MM::SG_Exception_Dim_MM(const char *container_name,
     836             :                                          const char *field_1,
     837           1 :                                          const char *field_2)
     838             : {
     839           2 :     std::string cn_s(container_name);
     840           2 :     std::string field1_s(field_1);
     841           1 :     std::string field2_s(field_2);
     842             : 
     843           2 :     this->err_msg = "[" + cn_s + "] One or more dimensions of " + field1_s +
     844           1 :                     " and " + field2_s + " do not match but must match.";
     845           1 : }
     846             : 
     847           2 : SG_Exception_Existential::SG_Exception_Existential(const char *container_name,
     848           2 :                                                    const char *missing_name)
     849             : {
     850           4 :     std::string cn_s(container_name);
     851           2 :     std::string mn_s(missing_name);
     852             : 
     853           4 :     this->err_msg = "[" + cn_s +
     854           4 :                     "] The property or the variable associated with " + mn_s +
     855           2 :                     " is missing.";
     856           2 : }
     857             : 
     858           0 : SG_Exception_Dep::SG_Exception_Dep(const char *container_name, const char *arg1,
     859           0 :                                    const char *arg2)
     860             : {
     861           0 :     std::string cn_s(container_name);
     862           0 :     std::string arg1_s(arg1);
     863           0 :     std::string arg2_s(arg2);
     864             : 
     865           0 :     this->err_msg = "[" + cn_s + "] The attribute " + arg1_s +
     866           0 :                     " may not exist without the attribute " + arg2_s +
     867           0 :                     " existing.";
     868           0 : }
     869             : 
     870           0 : SG_Exception_BadSum::SG_Exception_BadSum(const char *container_name,
     871           0 :                                          const char *arg1, const char *arg2)
     872             : {
     873           0 :     std::string cn_s(container_name);
     874           0 :     std::string arg1_s(arg1);
     875           0 :     std::string arg2_s(arg2);
     876             : 
     877           0 :     this->err_msg = "[" + cn_s + "]" + " The sum of all values in " + arg1_s +
     878           0 :                     " and " + arg2_s + " do not match.";
     879           0 : }
     880             : 
     881           0 : SG_Exception_General_Malformed ::SG_Exception_General_Malformed(const char *arg)
     882             : {
     883           0 :     std::string arg1_s(arg);
     884             : 
     885             :     this->err_msg =
     886           0 :         "Corruption or malformed formatting has been detected in: " + arg1_s;
     887           0 : }
     888             : 
     889             : // to get past linker
     890           7 : SG_Exception::~SG_Exception()
     891             : {
     892           7 : }
     893             : 
     894             : // Helpers
     895             : // following is a short hand for a clean up and exit, since goto isn't allowed
     896         483 : double getCFVersion(int ncid)
     897             : {
     898         483 :     double ver = -1.0;
     899         966 :     std::string attrVal;
     900             : 
     901             :     // Fetch the CF attribute
     902         483 :     if (attrf(ncid, NC_GLOBAL, NCDF_CONVENTIONS, attrVal) == "")
     903             :     {
     904          55 :         return ver;
     905             :     }
     906             : 
     907         428 :     if (sscanf(attrVal.c_str(), "CF-%lf", &ver) != 1)
     908             :     {
     909          32 :         return -1.0;
     910             :     }
     911             : 
     912         396 :     return ver;
     913             : }
     914             : 
     915          78 : geom_t getGeometryType(int ncid, int varid)
     916             : {
     917          78 :     geom_t ret = UNSUPPORTED;
     918         156 :     std::string gt_name_s;
     919             :     const char *gt_name =
     920          78 :         attrf(ncid, varid, CF_SG_GEOMETRY_TYPE, gt_name_s).c_str();
     921             : 
     922          78 :     if (gt_name[0] == '\0')
     923             :     {
     924           1 :         return NONE;
     925             :     }
     926             : 
     927             :     // Points
     928          77 :     if (!strcmp(gt_name, CF_SG_TYPE_POINT))
     929             :     {
     930             :         // Node Count not present? Assume that it is a multipoint.
     931          20 :         if (nc_inq_att(ncid, varid, CF_SG_NODE_COUNT, nullptr, nullptr) ==
     932             :             NC_ENOTATT)
     933             :         {
     934          14 :             ret = POINT;
     935             :         }
     936             :         else
     937           6 :             ret = MULTIPOINT;
     938             :     }
     939             : 
     940             :     // Lines
     941          57 :     else if (!strcmp(gt_name, CF_SG_TYPE_LINE))
     942             :     {
     943             :         // Part Node Count present? Assume multiline
     944          10 :         if (nc_inq_att(ncid, varid, CF_SG_PART_NODE_COUNT, nullptr, nullptr) ==
     945             :             NC_ENOTATT)
     946             :         {
     947           4 :             ret = LINE;
     948             :         }
     949             :         else
     950           6 :             ret = MULTILINE;
     951             :     }
     952             : 
     953             :     // Polygons
     954          47 :     else if (!strcmp(gt_name, CF_SG_TYPE_POLY))
     955             :     {
     956             :         /* Polygons versus MultiPolygons, slightly ambiguous
     957             :          * Part Node Count & no Interior Ring - MultiPolygon
     958             :          * no Part Node Count & no Interior Ring - Polygon
     959             :          * Part Node Count & Interior Ring - assume that it is a MultiPolygon
     960             :          */
     961             :         int pnc_present =
     962          46 :             nc_inq_att(ncid, varid, CF_SG_PART_NODE_COUNT, nullptr, nullptr);
     963             :         int ir_present =
     964          46 :             nc_inq_att(ncid, varid, CF_SG_INTERIOR_RING, nullptr, nullptr);
     965             : 
     966          46 :         if (pnc_present == NC_ENOTATT && ir_present == NC_ENOTATT)
     967             :         {
     968          10 :             ret = POLYGON;
     969             :         }
     970             :         else
     971          36 :             ret = MULTIPOLYGON;
     972             :     }
     973             : 
     974          77 :     return ret;
     975             : }
     976             : 
     977         133 : void inPlaceSerialize_Point(SGeometry_Reader *ge, size_t seek_pos,
     978             :                             std::vector<unsigned char> &buffer)
     979             : {
     980         133 :     uint8_t order = PLATFORM_HEADER;
     981         187 :     uint32_t t = ge->get_axisCount() == 2   ? wkbPoint
     982          54 :                  : ge->get_axisCount() == 3 ? wkbPoint25D
     983         133 :                                             : wkbNone;
     984             : 
     985         133 :     if (t == wkbNone)
     986           0 :         throw SG_Exception_BadFeature();
     987             : 
     988         133 :     add_to_buffer(buffer, order);
     989         133 :     add_to_buffer(buffer, t);
     990             : 
     991             :     // Now get point data;
     992         133 :     Point &p = (*ge)[seek_pos];
     993         133 :     add_to_buffer(buffer, p[0]);
     994         133 :     add_to_buffer(buffer, p[1]);
     995             : 
     996         133 :     if (ge->get_axisCount() >= 3)
     997             :     {
     998          54 :         add_to_buffer(buffer, p[2]);
     999             :     }
    1000         133 : }
    1001             : 
    1002          45 : void inPlaceSerialize_LineString(SGeometry_Reader *ge, int node_count,
    1003             :                                  size_t seek_begin,
    1004             :                                  std::vector<unsigned char> &buffer)
    1005             : {
    1006          45 :     uint8_t order = PLATFORM_HEADER;
    1007          65 :     uint32_t t = ge->get_axisCount() == 2   ? wkbLineString
    1008          20 :                  : ge->get_axisCount() == 3 ? wkbLineString25D
    1009          45 :                                             : wkbNone;
    1010             : 
    1011          45 :     if (t == wkbNone)
    1012           0 :         throw SG_Exception_BadFeature();
    1013          45 :     uint32_t nc = (uint32_t)node_count;
    1014             : 
    1015          45 :     add_to_buffer(buffer, order);
    1016          45 :     add_to_buffer(buffer, t);
    1017          45 :     add_to_buffer(buffer, nc);
    1018             : 
    1019             :     // Now serialize points
    1020         170 :     for (int ind = 0; ind < node_count; ind++)
    1021             :     {
    1022         125 :         Point &p = (*ge)[seek_begin + ind];
    1023         125 :         add_to_buffer(buffer, p[0]);
    1024         125 :         add_to_buffer(buffer, p[1]);
    1025             : 
    1026         125 :         if (ge->get_axisCount() >= 3)
    1027             :         {
    1028          56 :             add_to_buffer(buffer, p[2]);
    1029             :         }
    1030             :     }
    1031          45 : }
    1032             : 
    1033         432 : void inPlaceSerialize_PolygonExtOnly(SGeometry_Reader *ge, int node_count,
    1034             :                                      size_t seek_begin,
    1035             :                                      std::vector<unsigned char> &buffer)
    1036             : {
    1037         432 :     uint8_t order = PLATFORM_HEADER;
    1038         444 :     uint32_t t = ge->get_axisCount() == 2   ? wkbPolygon
    1039          12 :                  : ge->get_axisCount() == 3 ? wkbPolygon25D
    1040         432 :                                             : wkbNone;
    1041             : 
    1042         432 :     if (t == wkbNone)
    1043           0 :         throw SG_Exception_BadFeature();
    1044         432 :     uint32_t rc = 1;
    1045             : 
    1046         432 :     add_to_buffer(buffer, order);
    1047         432 :     add_to_buffer(buffer, t);
    1048         432 :     add_to_buffer(buffer, rc);
    1049         432 :     add_to_buffer(buffer, static_cast<uint32_t>(node_count));
    1050       47383 :     for (int pind = 0; pind < node_count; pind++)
    1051             :     {
    1052       46951 :         Point &pt = (*ge)[seek_begin + pind];
    1053       46951 :         add_to_buffer(buffer, pt[0]);
    1054       46951 :         add_to_buffer(buffer, pt[1]);
    1055             : 
    1056       46951 :         if (ge->get_axisCount() >= 3)
    1057             :         {
    1058          51 :             add_to_buffer(buffer, pt[2]);
    1059             :         }
    1060             :     }
    1061         432 : }
    1062             : 
    1063         166 : void inPlaceSerialize_Polygon(SGeometry_Reader *ge, std::vector<int> &pnc,
    1064             :                               int ring_count, size_t seek_begin,
    1065             :                               std::vector<unsigned char> &buffer)
    1066             : {
    1067         166 :     uint8_t order = PLATFORM_HEADER;
    1068         180 :     uint32_t t = ge->get_axisCount() == 2   ? wkbPolygon
    1069          14 :                  : ge->get_axisCount() == 3 ? wkbPolygon25D
    1070         166 :                                             : wkbNone;
    1071             : 
    1072         166 :     if (t == wkbNone)
    1073           0 :         throw SG_Exception_BadFeature();
    1074         166 :     uint32_t rc = static_cast<uint32_t>(ring_count);
    1075             : 
    1076         166 :     add_to_buffer(buffer, order);
    1077         166 :     add_to_buffer(buffer, t);
    1078         166 :     add_to_buffer(buffer, rc);
    1079         166 :     int cmoffset = 0;
    1080         366 :     for (int ring_c = 0; ring_c < ring_count; ring_c++)
    1081             :     {
    1082         200 :         uint32_t node_count = pnc[ring_c];
    1083         200 :         add_to_buffer(buffer, node_count);
    1084             : 
    1085         200 :         int pind = 0;
    1086       20332 :         for (pind = 0; pind < pnc[ring_c]; pind++)
    1087             :         {
    1088       20132 :             Point &pt = (*ge)[seek_begin + cmoffset + pind];
    1089       20132 :             add_to_buffer(buffer, pt[0]);
    1090       20132 :             add_to_buffer(buffer, pt[1]);
    1091             : 
    1092       20132 :             if (ge->get_axisCount() >= 3)
    1093             :             {
    1094          78 :                 add_to_buffer(buffer, pt[2]);
    1095             :             }
    1096             :         }
    1097             : 
    1098         200 :         cmoffset += pind;
    1099             :     }
    1100         166 : }
    1101             : 
    1102          72 : int scanForGeometryContainers(int ncid, std::set<int> &r_ids)
    1103             : {
    1104             :     int nvars;
    1105          72 :     if (nc_inq_nvars(ncid, &nvars) != NC_NOERR)
    1106             :     {
    1107           0 :         return -1;
    1108             :     }
    1109             : 
    1110          72 :     r_ids.clear();
    1111             : 
    1112             :     // For each variable check for geometry attribute
    1113             :     // If has geometry attribute, then check the associated variable ID
    1114             : 
    1115         636 :     for (int itr = 0; itr < nvars; itr++)
    1116             :     {
    1117             :         char c[NC_MAX_CHAR];
    1118         564 :         memset(c, 0, NC_MAX_CHAR);
    1119         564 :         if (nc_get_att_text(ncid, itr, CF_SG_GEOMETRY, c) != NC_NOERR)
    1120             :         {
    1121         440 :             continue;
    1122             :         }
    1123             : 
    1124             :         int varID;
    1125         124 :         if (nc_inq_varid(ncid, c, &varID) != NC_NOERR)
    1126             :         {
    1127           0 :             continue;
    1128             :         }
    1129             : 
    1130             :         // Now have variable ID. See if vector contains it, and if not
    1131             :         // insert
    1132         124 :         r_ids.insert(varID);  // It's a set. No big deal sets don't allow
    1133             :                               // duplicates anyways
    1134             :     }
    1135             : 
    1136          72 :     return 0;
    1137             : }
    1138             : }  // namespace nccfdriver

Generated by: LCOV version 1.14