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