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 #ifndef __NETCDFSG_H__
29 #define __NETCDFSG_H__
30 #include <cstring>
31 #include <memory>
32 #include <set>
33 #include <string>
34 #include <vector>
35 #include "netcdf.h"
36 
37 // Interface used for netCDF functions
38 // implementing awareness for the CF-1.8 convention
39 //
40 // Author: wchen329
41 namespace nccfdriver
42 {
43     // Constants
44     const int INVALID_VAR_ID = -2;
45     const int INVALID_DIM_ID = INVALID_VAR_ID;
46 
47     // Enum used for easily identifying Geometry types
48     enum geom_t
49     {
50         NONE,        // no geometry found
51         POLYGON,    // OGRPolygon
52         MULTIPOLYGON,    // OGRMultipolygon
53         LINE,        // OGRLineString
54         MULTILINE,    // OGRMultiLineString
55         POINT,        // OGRPoint
56         MULTIPOINT,    // OGRMultiPoint
57         UNSUPPORTED    // Unsupported feature type
58     };
59 
60     // Concrete "Point" class, holds n dimensional double precision floating point value, defaults to all zero values
61     class Point
62     {
63         int size;
64         std::unique_ptr<double, std::default_delete<double[]>> values;
65         Point(Point&);
66         Point operator=(const Point &);
67 
68         public:
Point(int dim)69         explicit Point(int dim) : size(dim), values(std::unique_ptr<double, std::default_delete<double[]>>(new double[dim])) {}
70         double& operator[](size_t i) { return this->values.get()[i]; }
getOrder()71         int getOrder() { return this->size; }
72     };
73 
74 
75 
76     // Simple geometry - doesn't actually hold the points, rather serves
77     // as a pseudo reference to a NC variable
78     class SGeometry_Reader
79     {
80         std::string container_name_s;        // name of the underlying geometry container
81         geom_t type;         // internal geometry type structure
82         int ncid;        // ncid - as used in netcdf.h
83         int gc_varId;        // the id of the underlying geometry_container variable
84         std::string gm_name_s; // grid mapping variable name
85         int gm_varId;        // id used for grid mapping
86         int inst_dimId;        // dimension id for geometry instance dimension
87         size_t inst_dimLen;    // value of instance dimension
88         int touple_order;    // amount of "coordinates" in a point
89         std::vector<int> nodec_varIds;    // varIds for each node_coordinate entry
90         std::vector<int> node_counts;    // node counts of each geometry in a container
91         std::vector<int> pnode_counts;    // part node counts of each geometry in a container
92         std::vector<bool> int_rings;    // list of parts that are interior rings
93         std::vector<size_t> bound_list;    // a quick list used to store the real beginning indices of shapes
94         std::vector<size_t> pnc_bl;    // a quick list of indices for part counts corresponding to a geometry
95         std::vector<int> parts_count;    // a count of total parts in a single geometry instance
96         std::vector<int> poly_count;    // count of polygons, for use only when interior rings are present
97         std::unique_ptr<Point> pt_buffer;    // holds the current point
98         SGeometry_Reader(SGeometry_Reader&);
99         SGeometry_Reader operator=(const SGeometry_Reader&);
100 
101         public:
102 
103         /* int SGeometry_Reader::get_ncID()
104          * return the group/file ID that the SGeometry object is operating over
105          */
get_ncID()106         int get_ncID() { return ncid; }
107 
108         /* int SGeometry_Reader::get_axisCount()
109          * Returns the count of axis (i.e. X, Y, Z)
110          */
get_axisCount()111         int get_axisCount() { return this->touple_order; }
112 
113         /* int SGeometry_Reader::getInstDim()
114          * Returns the geometry instance dimension ID of this geometry
115          */
getInstDim()116         int getInstDim() { return this->inst_dimId; }
117 
118         /* size_t SGeometry_Reader::getInstDimLen()
119          * Returns the length of the instance dimension
120          */
getInstDimLen()121         size_t getInstDimLen() { return this->inst_dimLen; }
122 
123         /* std::string& getGridMappingName()
124          * returns the variable name which holds grid mapping data
125          */
getGridMappingName()126         std::string& getGridMappingName() { return this->gm_name_s; }
127 
128         /* int SGeometry_Reader::getGridMappingVarID();
129          * returns the varID of the associated grid mapping variable ID
130          */
getGridMappingVarID()131         int getGridMappingVarID() { return this->gm_varId; }
132 
133         /* geom_t getGeometryType()
134          * Retrieves the associated geometry type with this geometry
135          */
getGeometryType()136         geom_t getGeometryType() { return this->type; }
137 
138         /* void SGeometry_Reader::get_geometry_count()
139          * returns a size, indicating the amount of geometries
140          * contained in the variable
141          */
142         size_t get_geometry_count();
143 
144         /* const char* SGeometry_Reader::getContainerName()
145          * Returns the container name as a string
146          */
getContainerName()147         std::string& getContainerName() { return container_name_s; }
148 
149         /* int SGeometry_Reader::getContainerId()
150          * Get the ncID of the geometry_container variable
151          */
getContainerId()152         int getContainerId() { return gc_varId; }
153 
154         /* std::vector<unsigned char> serializeToWKB
155          * Returns a pre-allocated array which serves as the WKB reference to this geometry
156          */
157         std::vector<unsigned char> serializeToWKB(size_t featureInd);
158 
159         /* Return a point at a specific index specifically
160          * this point should NOT be explicitly freed.
161          *
162          */
163         Point& operator[](size_t ind);
164 
165         /* std::vector<int>& getNodeCoordVars
166          * Returns a vector with the node coord vars in X, Y, Z (if present) order
167          */
getNodeCoordVars()168         std::vector<int>& getNodeCoordVars() { return this->nodec_varIds; }
169 
170         /* ncID - as used in netcdf.h
171          * baseVarId - the id of a variable with a geometry container attribute
172          */
173         SGeometry_Reader(int ncId, int baseVarId);
174 
175     };
176 
177     /* SGeometry_PropertyScanner
178      * Holds names of properties for geometry containers
179      * Pass in the geometry_container ID, automatically scans the netcdf Dataset for properties associated
180      *
181      * to construct: pass in the ncid which the reader should work over
182      */
183     class SGeometry_PropertyScanner
184     {
185         std::vector<int> v_ids;
186         std::vector<std::string> v_headers;
187         int nc;
188 
189         void open(int container_id);    // opens and initializes a geometry_container into the scanner
190 
191         public:
headers()192             std::vector<std::string>& headers() { return this->v_headers; }
ids()193             std::vector<int>& ids() { return this->v_ids; }
SGeometry_PropertyScanner(int ncid,int cid)194             SGeometry_PropertyScanner(int ncid, int cid) : nc(ncid) { this->open(cid); }
195     };
196 
197     // General exception interface for Simple Geometries
198     // Whatever pointer returned should NOT be freed- it will be deconstructed automatically, if needed
199     class SG_Exception
200     {
201         public:
202             virtual const char * get_err_msg() = 0;
203             virtual ~SG_Exception();
204     };
205 
206     // Mismatched dimension exception
207     class SG_Exception_Dim_MM : public SG_Exception
208     {
209         std::string err_msg;
210 
211         public:
get_err_msg()212             const char* get_err_msg() override { return err_msg.c_str(); }
213 
214         SG_Exception_Dim_MM(const char* geometry_container, const char* field_1, const char *field_2);
215     };
216 
217     // Missing (existential) property error
218     class SG_Exception_Existential: public SG_Exception
219     {
220         std::string err_msg;
221 
222         public:
get_err_msg()223             const char* get_err_msg() override { return err_msg.c_str(); }
224 
225         SG_Exception_Existential(const char* geometry_container, const char* missing_name);
226     };
227 
228     // Missing dependent property (arg_1 is dependent on arg_2)
229     class SG_Exception_Dep: public SG_Exception
230     {
231         std::string err_msg;
232 
233         public:
get_err_msg()234             const char* get_err_msg() override { return err_msg.c_str(); }
235 
236         SG_Exception_Dep(const char* geometry_container, const char* arg_1, const char* arg_2);
237     };
238 
239     // The sum of all values in a variable does not match the sum of another variable
240     class SG_Exception_BadSum : public SG_Exception
241     {
242         std::string err_msg;
243 
244         public:
get_err_msg()245             const char* get_err_msg() override { return err_msg.c_str(); }
246 
247         SG_Exception_BadSum(const char* geometry_container, const char* arg_1, const char* arg_2);
248     };
249 
250     // Unsupported Feature Type
251     class SG_Exception_BadFeature : public SG_Exception
252     {
253         std::string err_msg;
254 
255         public:
get_err_msg()256             const char* get_err_msg() override { return err_msg.c_str(); }
257 
SG_Exception_BadFeature()258         SG_Exception_BadFeature() : err_msg("Unsupported or unrecognized feature type.") {}
259     };
260 
261     // Failed Read
262     class SG_Exception_BadPoint : public SG_Exception
263     {
264         std::string err_msg;
265 
266         public:
get_err_msg()267             const char* get_err_msg() override { return err_msg.c_str(); }
268 
SG_Exception_BadPoint()269         SG_Exception_BadPoint() : err_msg("An attempt was made to read an invalid point (likely index out of bounds).") {}
270     };
271 
272     // Too many dimensions on node coordinates variable
273     class SG_Exception_Not1D : public SG_Exception
274     {
275         std::string err_msg;
276 
277         public:
get_err_msg()278             const char* get_err_msg() override { return err_msg.c_str(); }
279 
SG_Exception_Not1D()280         SG_Exception_Not1D() : err_msg("A node coordinates axis variable or node_counts is not one dimensional.") {}
281     };
282 
283     // Too many empty dimension
284     class SG_Exception_EmptyDim : public SG_Exception
285     {
286         std::string err_msg;
287 
288         public:
get_err_msg()289             const char* get_err_msg() override { return err_msg.c_str(); }
290 
SG_Exception_EmptyDim()291         SG_Exception_EmptyDim() : err_msg("A dimension has length <= 0, but it must have length > 0") {}
292     };
293 
294     //general corruption or malformed error
295     class SG_Exception_General_Malformed : public SG_Exception
296     {
297         std::string err_msg;
298 
299         public:
get_err_msg()300             const char* get_err_msg() override { return err_msg.c_str(); }
301 
302         explicit SG_Exception_General_Malformed(const char*);
303     };
304 
305     // Invalid value detected
306     class SG_Exception_Value_Violation : public SG_Exception
307     {
308         std::string err_msg;
309 
310         public:
get_err_msg()311             const char* get_err_msg() override { return err_msg.c_str(); }
SG_Exception_Value_Violation(const char * containername,const char * type,const char * badvalue)312             SG_Exception_Value_Violation(const char* containername, const char* type, const char* badvalue) :
313                 err_msg( std::string("[") + std::string(containername) + std::string("] ") + std::string(type) +
314                          std::string(" values may not be ") + std::string(badvalue)
315                        ) {}
316     };
317 
318     // Required value(s)
319     class SG_Exception_Value_Required : public SG_Exception
320     {
321         std::string err_msg;
322 
323         public:
get_err_msg()324             const char* get_err_msg() override { return err_msg.c_str(); }
SG_Exception_Value_Required(const char * containername,const char * type,const char * expvalue)325             SG_Exception_Value_Required(const char* containername, const char* type, const char* expvalue) :
326                 err_msg( std::string("[") + std::string(containername) + std::string("] ") + std::string(type) +
327                          std::string(" values must be ") + std::string(expvalue)
328                        ) {}
329     };
330 
331     // Some helpers which simply call some netcdf library functions, unless otherwise mentioned, ncid, refers to its use in netcdf.h
332 
333     /* Retrieves the version from the value Conventions global attr
334      * Returns: a double precision decimal corresponding to the conventions value
335      *    if not CF-x.y then return negative value, -1
336      */
337     double getCFVersion(int ncid);
338 
339     /* Given a geometry_container varID, searches that variable for a geometry_type attribute
340      * Returns: the equivalent geometry type
341      */
342     geom_t getGeometryType(int ncid, int varid);
343 
344     void inPlaceSerialize_Point(SGeometry_Reader * ge, size_t seek_pos, std::vector<unsigned char>& buffer);
345     void inPlaceSerialize_LineString(SGeometry_Reader * ge, int node_count, size_t seek_begin, std::vector<unsigned char>& buffer);
346     void inPlaceSerialize_PolygonExtOnly(SGeometry_Reader * ge, int node_count, size_t seek_begin, std::vector<unsigned char>& buffer);
347     void inPlaceSerialize_Polygon(SGeometry_Reader * ge, std::vector<int>& pnc, int ring_count, size_t seek_begin, std::vector<unsigned char>& buffer);
348 
349     /* scanForGeometryContainers
350      * A simple function that scans a netCDF File for Geometry Containers
351      * -
352      * Scans the given ncid for geometry containers
353      * The vector passed in will be overwritten with a vector of scan results
354      */
355     int scanForGeometryContainers(int ncid, std::set<int> & r_ids);
356 
357     /* Attribute Fetch
358      * -
359      * A function which makes it a bit easier to fetch single text attribute values
360      * ncid: as used in netcdf.h
361      * varID: variable id in which to look for the attribute
362      * attrName: name of attribute to fine
363      * alloc: a reference to a string that will be filled with the attribute (i.e. truncated and filled with the return value)
364      * Returns: a reference to the string to fill (a.k.a. string pointed to by alloc reference)
365      */
366     std::string& attrf(int ncid, int varId, const char * attrName, std::string & alloc);
367 }
368 
369 #endif
370