Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: netCDF read/write Driver
4 : * Purpose: GDAL bindings over netCDF library.
5 : * Author: Even Rouault <even.rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2016, Even Rouault <even.rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "netcdfdataset.h"
14 : #include "netcdfsgwriterutil.h"
15 : #include "netcdfsg.h"
16 : #include "netcdflayersg.h"
17 : #include "cpl_time.h"
18 :
19 : /************************************************************************/
20 : /* netCDFLayer() */
21 : /************************************************************************/
22 :
23 174 : netCDFLayer::netCDFLayer(netCDFDataset *poDS, int nLayerCDFId,
24 : const char *pszName, OGRwkbGeometryType eGeomType,
25 174 : OGRSpatialReference *poSRS)
26 : : m_poDS(poDS), m_nLayerCDFId(nLayerCDFId),
27 174 : m_poFeatureDefn(new OGRFeatureDefn(pszName)), m_osRecordDimName("record"),
28 : m_nRecordDimID(-1), m_nDefaultWidth(10), m_bAutoGrowStrings(true),
29 : m_nDefaultMaxWidthDimId(-1), m_nXVarID(-1), m_nYVarID(-1), m_nZVarID(-1),
30 : m_nXVarNCDFType(NC_NAT), m_nYVarNCDFType(NC_NAT), m_nZVarNCDFType(NC_NAT),
31 : m_osWKTVarName("ogc_wkt"), m_nWKTMaxWidth(10000), m_nWKTMaxWidthDimId(-1),
32 : m_nWKTVarID(-1), m_nWKTNCDFType(NC_NAT), m_bLegacyCreateMode(true),
33 : m_nCurFeatureId(1), m_bWriteGDALTags(true), m_bUseStringInNC4(true),
34 : m_bNCDumpCompat(true), m_nProfileDimID(-1), m_nProfileVarID(-1),
35 : m_bProfileVarUnlimited(false), m_nParentIndexVarID(-1),
36 174 : layerVID_alloc(poDS->cdfid == m_nLayerCDFId
37 : ? nullptr
38 70 : : new nccfdriver::netCDFVID(poDS, m_nLayerCDFId)),
39 174 : layerVID(layerVID_alloc.get() == nullptr ? poDS->vcdf : *layerVID_alloc),
40 : m_SGeometryFeatInd(0), m_poLayerConfig(nullptr),
41 348 : m_layerSGDefn(poDS->cdfid, nccfdriver::OGRtoRaw(eGeomType), poDS->vcdf,
42 592 : poDS->GeometryScribe)
43 : {
44 174 : m_uXVarNoData.nVal64 = 0;
45 174 : m_uYVarNoData.nVal64 = 0;
46 174 : m_uZVarNoData.nVal64 = 0;
47 174 : m_poFeatureDefn->SetGeomType(eGeomType);
48 174 : if (eGeomType != wkbNone)
49 167 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
50 174 : m_poFeatureDefn->Reference();
51 174 : SetDescription(pszName);
52 174 : }
53 :
54 : /************************************************************************/
55 : /* ~netCDFLayer() */
56 : /************************************************************************/
57 :
58 348 : netCDFLayer::~netCDFLayer()
59 : {
60 174 : m_poFeatureDefn->Release();
61 348 : }
62 :
63 : /************************************************************************/
64 : /* netCDFWriteAttributesFromConf() */
65 : /************************************************************************/
66 :
67 8 : void netCDFLayer::netCDFWriteAttributesFromConf(
68 : int cdfid, int varid,
69 : const std::vector<netCDFWriterConfigAttribute> &aoAttributes)
70 : {
71 15 : for (size_t i = 0; i < aoAttributes.size(); i++)
72 : {
73 : try
74 : {
75 7 : const netCDFWriterConfigAttribute &oAtt = aoAttributes[i];
76 7 : int status = NC_NOERR;
77 7 : if (oAtt.m_osValue.empty())
78 : {
79 1 : int attid = -1;
80 1 : status = nc_inq_attid(cdfid, varid, oAtt.m_osName, &attid);
81 1 : if (status == NC_NOERR)
82 1 : status = nc_del_att(cdfid, varid, oAtt.m_osName);
83 : else
84 0 : status = NC_NOERR;
85 : }
86 6 : else if (EQUAL(oAtt.m_osType, "string"))
87 : {
88 4 : layerVID.nc_put_vatt_text(varid, oAtt.m_osName, oAtt.m_osValue);
89 : }
90 :
91 2 : else if (EQUAL(oAtt.m_osType, "integer"))
92 : {
93 1 : int nVal = atoi(oAtt.m_osValue);
94 1 : layerVID.nc_put_vatt_int(varid, oAtt.m_osName, &nVal);
95 : }
96 :
97 1 : else if (EQUAL(oAtt.m_osType, "double"))
98 : {
99 1 : double dfVal = CPLAtof(oAtt.m_osValue);
100 1 : layerVID.nc_put_vatt_double(varid, oAtt.m_osName, &dfVal);
101 : }
102 :
103 7 : NCDF_ERR(status);
104 : }
105 :
106 0 : catch (nccfdriver::SG_Exception &e)
107 : {
108 0 : CPLError(CE_Failure, CPLE_FileIO, "%s", e.get_err_msg());
109 : }
110 : }
111 8 : }
112 :
113 : /************************************************************************/
114 : /* Create() */
115 : /************************************************************************/
116 :
117 59 : bool netCDFLayer::Create(char **papszOptions,
118 : const netCDFWriterConfigLayer *poLayerConfig)
119 : {
120 59 : m_poDS->SetDefineMode(true);
121 :
122 59 : if (m_poDS->bSGSupport)
123 : {
124 42 : m_bLegacyCreateMode = false;
125 : }
126 :
127 : else
128 : {
129 17 : m_bLegacyCreateMode = true;
130 : }
131 :
132 59 : long long newbufsize = 0;
133 : const char *memorySizeLimitation =
134 59 : CSLFetchNameValueDef(papszOptions, "BUFFER_SIZE", "");
135 118 : std::string memorySizeLimitation_s = std::string(memorySizeLimitation);
136 59 : if (memorySizeLimitation_s != "")
137 : {
138 4 : newbufsize = strtoll(memorySizeLimitation, nullptr, 10);
139 : }
140 :
141 : const char *singleDatumMode =
142 59 : CSLFetchNameValueDef(papszOptions, "GROUPLESS_WRITE_BACK", "NO");
143 :
144 59 : if (CPLTestBool(singleDatumMode))
145 : {
146 1 : m_poDS->GeometryScribe.setSingleDatumMode(true);
147 1 : m_poDS->FieldScribe.setSingleDatumMode(true);
148 : }
149 :
150 59 : if (m_bLegacyCreateMode)
151 : {
152 : m_osRecordDimName = CSLFetchNameValueDef(
153 17 : papszOptions, "RECORD_DIM_NAME", m_osRecordDimName.c_str());
154 : }
155 :
156 59 : m_bAutoGrowStrings =
157 59 : CPL_TO_BOOL(CSLFetchBoolean(papszOptions, "AUTOGROW_STRINGS", TRUE));
158 59 : m_nDefaultWidth = atoi(
159 : CSLFetchNameValueDef(papszOptions, "STRING_DEFAULT_WIDTH",
160 59 : CPLSPrintf("%d", m_bAutoGrowStrings ? 10 : 80)));
161 59 : m_bWriteGDALTags = CPL_TO_BOOL(
162 59 : CSLFetchBoolean(m_poDS->papszCreationOptions, "WRITE_GDAL_TAGS", TRUE));
163 59 : m_bUseStringInNC4 =
164 59 : CPL_TO_BOOL(CSLFetchBoolean(papszOptions, "USE_STRING_IN_NC4", TRUE));
165 59 : m_bNCDumpCompat =
166 59 : CPL_TO_BOOL(CSLFetchBoolean(papszOptions, "NCDUMP_COMPAT", TRUE));
167 :
168 118 : std::vector<std::pair<CPLString, int>> aoAutoVariables;
169 :
170 : const char *pszFeatureType =
171 59 : CSLFetchNameValue(papszOptions, "FEATURE_TYPE");
172 59 : if (pszFeatureType != nullptr)
173 : {
174 4 : if (EQUAL(pszFeatureType, "POINT"))
175 : {
176 0 : if (wkbFlatten(m_poFeatureDefn->GetGeomType()) != wkbPoint)
177 : {
178 0 : CPLError(CE_Warning, CPLE_NotSupported,
179 : "FEATURE_TYPE=POINT only supported for Point layer "
180 : "geometry type.");
181 : }
182 : }
183 :
184 4 : else if (EQUAL(pszFeatureType, "PROFILE"))
185 : {
186 4 : if (wkbFlatten(m_poFeatureDefn->GetGeomType()) != wkbPoint)
187 : {
188 0 : CPLError(CE_Warning, CPLE_NotSupported,
189 : "FEATURE_TYPE=PROFILE only supported for Point layer "
190 : "geometry type.");
191 : }
192 : else
193 : {
194 : // Cf
195 : // http://cfconventions.org/cf-conventions/v1.6.0/cf-conventions.html#_indexed_ragged_array_representation_of_profiles
196 :
197 : m_osProfileDimName = CSLFetchNameValueDef(
198 4 : papszOptions, "PROFILE_DIM_NAME", "profile");
199 : m_osProfileVariables =
200 4 : CSLFetchNameValueDef(papszOptions, "PROFILE_VARIABLES", "");
201 :
202 4 : const char *pszProfileInitSize = CSLFetchNameValueDef(
203 : papszOptions, "PROFILE_DIM_INIT_SIZE",
204 4 : (m_poDS->eFormat == NCDF_FORMAT_NC4) ? "UNLIMITED" : "100");
205 4 : m_bProfileVarUnlimited = EQUAL(pszProfileInitSize, "UNLIMITED");
206 4 : size_t nProfileInitSize =
207 4 : m_bProfileVarUnlimited
208 4 : ? NC_UNLIMITED
209 3 : : static_cast<size_t>(atoi(pszProfileInitSize));
210 4 : int status = nc_def_dim(m_nLayerCDFId, m_osProfileDimName,
211 : nProfileInitSize, &m_nProfileDimID);
212 4 : NCDF_ERR(status);
213 4 : if (status != NC_NOERR)
214 0 : return false;
215 :
216 4 : status = nc_def_var(m_nLayerCDFId, m_osProfileDimName, NC_INT,
217 4 : 1, &m_nProfileDimID, &m_nProfileVarID);
218 4 : NCDF_ERR(status);
219 4 : if (status != NC_NOERR)
220 0 : return false;
221 :
222 4 : aoAutoVariables.push_back(
223 8 : std::pair(m_osProfileDimName, m_nProfileVarID));
224 :
225 : status =
226 4 : nc_put_att_text(m_nLayerCDFId, m_nProfileVarID, "cf_role",
227 : strlen("profile_id"), "profile_id");
228 4 : NCDF_ERR(status);
229 : }
230 : }
231 0 : else if (!EQUAL(pszFeatureType, "AUTO"))
232 : {
233 0 : CPLError(CE_Warning, CPLE_NotSupported,
234 : "FEATURE_TYPE=%s not supported.", pszFeatureType);
235 : }
236 : }
237 :
238 59 : const OGRSpatialReference *poSRS = nullptr;
239 59 : if (m_poFeatureDefn->GetGeomFieldCount())
240 57 : poSRS = m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef();
241 :
242 : int status;
243 59 : if (m_bLegacyCreateMode)
244 : {
245 :
246 17 : if (m_bWriteGDALTags)
247 : {
248 15 : status = nc_put_att_text(m_nLayerCDFId, NC_GLOBAL, "ogr_layer_name",
249 15 : strlen(m_poFeatureDefn->GetName()),
250 15 : m_poFeatureDefn->GetName());
251 15 : NCDF_ERR(status);
252 : }
253 :
254 17 : status = nc_def_dim(m_nLayerCDFId, m_osRecordDimName, NC_UNLIMITED,
255 : &m_nRecordDimID);
256 17 : NCDF_ERR(status);
257 17 : if (status != NC_NOERR)
258 0 : return false;
259 :
260 17 : if (!m_osProfileDimName.empty())
261 : {
262 8 : status = nc_def_var(m_nLayerCDFId, "parentIndex", NC_INT, 1,
263 4 : &m_nRecordDimID, &m_nParentIndexVarID);
264 4 : NCDF_ERR(status);
265 4 : if (status != NC_NOERR)
266 0 : return false;
267 :
268 4 : aoAutoVariables.push_back(
269 4 : std::pair("parentIndex", m_nParentIndexVarID));
270 :
271 : status =
272 4 : nc_put_att_text(m_nLayerCDFId, m_nParentIndexVarID, CF_LNG_NAME,
273 : strlen("index of profile"), "index of profile");
274 4 : NCDF_ERR(status);
275 :
276 4 : status = nc_put_att_text(
277 : m_nLayerCDFId, m_nParentIndexVarID, "instance_dimension",
278 : m_osProfileDimName.size(), m_osProfileDimName.c_str());
279 4 : NCDF_ERR(status);
280 : }
281 :
282 17 : if (wkbFlatten(m_poFeatureDefn->GetGeomType()) == wkbPoint)
283 : {
284 : const int nPointDim =
285 8 : !m_osProfileDimName.empty() ? m_nProfileDimID : m_nRecordDimID;
286 : const bool bIsGeographic =
287 8 : (poSRS == nullptr || poSRS->IsGeographic());
288 :
289 8 : const char *pszXVarName =
290 8 : bIsGeographic ? CF_LONGITUDE_VAR_NAME : CF_PROJ_X_VAR_NAME;
291 :
292 8 : status = nc_def_var(m_nLayerCDFId, pszXVarName, NC_DOUBLE, 1,
293 : &nPointDim, &m_nXVarID);
294 :
295 8 : NCDF_ERR(status);
296 8 : if (status != NC_NOERR)
297 : {
298 0 : return false;
299 : }
300 :
301 8 : const char *pszYVarName =
302 8 : bIsGeographic ? CF_LATITUDE_VAR_NAME : CF_PROJ_Y_VAR_NAME;
303 :
304 8 : status = nc_def_var(m_nLayerCDFId, pszYVarName, NC_DOUBLE, 1,
305 : &nPointDim, &m_nYVarID);
306 8 : NCDF_ERR(status);
307 8 : if (status != NC_NOERR)
308 : {
309 0 : return false;
310 : }
311 :
312 8 : aoAutoVariables.push_back(std::pair(pszXVarName, m_nXVarID));
313 8 : aoAutoVariables.push_back(std::pair(pszYVarName, m_nYVarID));
314 :
315 8 : m_nXVarNCDFType = NC_DOUBLE;
316 8 : m_nYVarNCDFType = NC_DOUBLE;
317 8 : m_uXVarNoData.dfVal = NC_FILL_DOUBLE;
318 8 : m_uYVarNoData.dfVal = NC_FILL_DOUBLE;
319 :
320 8 : m_osCoordinatesValue = pszXVarName;
321 8 : m_osCoordinatesValue += " ";
322 8 : m_osCoordinatesValue += pszYVarName;
323 :
324 8 : if (poSRS == nullptr || poSRS->IsGeographic())
325 : {
326 : // Deal with potentional issues of multiple groups
327 5 : NCDFWriteLonLatVarsAttributes(layerVID, m_nXVarID, m_nYVarID);
328 : }
329 :
330 3 : else if (poSRS->IsProjected())
331 : {
332 : // Deal with potentional issues of multiple groups
333 3 : NCDFWriteXYVarsAttributes(layerVID, m_nXVarID, m_nYVarID,
334 : poSRS);
335 : }
336 :
337 8 : if (m_poFeatureDefn->GetGeomType() == wkbPoint25D)
338 : {
339 8 : const char *pszZVarName = "z";
340 :
341 16 : status = nc_def_var(m_nLayerCDFId, pszZVarName, NC_DOUBLE, 1,
342 8 : &m_nRecordDimID, &m_nZVarID);
343 8 : NCDF_ERR(status);
344 8 : if (status != NC_NOERR)
345 : {
346 0 : return false;
347 : }
348 :
349 8 : aoAutoVariables.push_back(std::pair(pszZVarName, m_nZVarID));
350 :
351 8 : m_nZVarNCDFType = NC_DOUBLE;
352 8 : m_uZVarNoData.dfVal = NC_FILL_DOUBLE;
353 :
354 : status =
355 8 : nc_put_att_text(m_nLayerCDFId, m_nZVarID, CF_LNG_NAME,
356 : strlen("z coordinate"), "z coordinate");
357 8 : NCDF_ERR(status);
358 :
359 8 : status = nc_put_att_text(m_nLayerCDFId, m_nZVarID, CF_STD_NAME,
360 : strlen("height"), "height");
361 8 : NCDF_ERR(status);
362 :
363 8 : status = nc_put_att_text(m_nLayerCDFId, m_nZVarID, CF_AXIS,
364 : strlen("Z"), "Z");
365 8 : NCDF_ERR(status);
366 :
367 8 : status = nc_put_att_text(m_nLayerCDFId, m_nZVarID, CF_UNITS,
368 : strlen("m"), "m");
369 8 : NCDF_ERR(status);
370 :
371 8 : m_osCoordinatesValue += " ";
372 8 : m_osCoordinatesValue += pszZVarName;
373 : }
374 :
375 : const char *pszFeatureTypeVal =
376 8 : !m_osProfileDimName.empty() ? "profile" : "point";
377 :
378 : status =
379 8 : nc_put_att_text(m_nLayerCDFId, NC_GLOBAL, "featureType",
380 : strlen(pszFeatureTypeVal), pszFeatureTypeVal);
381 :
382 8 : NCDF_ERR(status);
383 : }
384 9 : else if (m_poFeatureDefn->GetGeomType() != wkbNone)
385 : {
386 7 : if (m_poDS->eFormat == NCDF_FORMAT_NC4 && m_bUseStringInNC4)
387 : {
388 2 : m_nWKTNCDFType = NC_STRING;
389 2 : status =
390 2 : nc_def_var(m_nLayerCDFId, m_osWKTVarName.c_str(), NC_STRING,
391 2 : 1, &m_nRecordDimID, &m_nWKTVarID);
392 : }
393 : else
394 : {
395 5 : m_nWKTNCDFType = NC_CHAR;
396 5 : m_nWKTMaxWidth = atoi(CSLFetchNameValueDef(
397 : papszOptions, "WKT_DEFAULT_WIDTH",
398 5 : CPLSPrintf("%d", m_bAutoGrowStrings ? 1000 : 10000)));
399 5 : status = nc_def_dim(
400 : m_nLayerCDFId,
401 : CPLSPrintf("%s_max_width", m_osWKTVarName.c_str()),
402 5 : m_nWKTMaxWidth, &m_nWKTMaxWidthDimId);
403 5 : NCDF_ERR(status);
404 5 : if (status != NC_NOERR)
405 : {
406 0 : return false;
407 : }
408 :
409 5 : int anDims[2] = {m_nRecordDimID, m_nWKTMaxWidthDimId};
410 5 : status = nc_def_var(m_nLayerCDFId, m_osWKTVarName.c_str(),
411 : NC_CHAR, 2, anDims, &m_nWKTVarID);
412 : }
413 7 : NCDF_ERR(status);
414 7 : if (status != NC_NOERR)
415 : {
416 0 : return false;
417 : }
418 :
419 7 : aoAutoVariables.push_back(std::pair(m_osWKTVarName, m_nWKTVarID));
420 :
421 7 : status = nc_put_att_text(m_nLayerCDFId, m_nWKTVarID, CF_LNG_NAME,
422 : strlen("Geometry as ISO WKT"),
423 : "Geometry as ISO WKT");
424 7 : NCDF_ERR(status);
425 :
426 : // nc_put_att_text(m_nLayerCDFId, m_nWKTVarID, CF_UNITS,
427 : // strlen("none"), "none");
428 :
429 7 : if (m_bWriteGDALTags)
430 : {
431 7 : status = nc_put_att_text(
432 : m_nLayerCDFId, NC_GLOBAL, "ogr_geometry_field",
433 : m_osWKTVarName.size(), m_osWKTVarName.c_str());
434 7 : NCDF_ERR(status);
435 :
436 : CPLString osGeometryType =
437 14 : OGRToOGCGeomType(m_poFeatureDefn->GetGeomType());
438 7 : if (wkbHasZ(m_poFeatureDefn->GetGeomType()))
439 0 : osGeometryType += " Z";
440 7 : status = nc_put_att_text(
441 : m_nLayerCDFId, NC_GLOBAL, "ogr_layer_type",
442 : osGeometryType.size(), osGeometryType.c_str());
443 7 : NCDF_ERR(status);
444 : }
445 : }
446 : }
447 :
448 59 : if (poSRS != nullptr)
449 : {
450 43 : char *pszCFProjection = nullptr;
451 43 : m_sgCRSname = m_bLegacyCreateMode
452 129 : ? ""
453 124 : : std::string(this->GetName()) + std::string("_crs");
454 :
455 : int nSRSVarId =
456 86 : NCDFWriteSRSVariable(m_nLayerCDFId, poSRS, &pszCFProjection,
457 43 : m_bWriteGDALTags, m_sgCRSname);
458 43 : if (nSRSVarId < 0)
459 0 : return false;
460 43 : if (pszCFProjection != nullptr)
461 : {
462 43 : aoAutoVariables.push_back(std::pair(pszCFProjection, nSRSVarId));
463 :
464 43 : m_osGridMapping = pszCFProjection;
465 43 : CPLFree(pszCFProjection);
466 : }
467 :
468 43 : if (m_nWKTVarID >= 0 && !m_osGridMapping.empty())
469 : {
470 2 : status = nc_put_att_text(m_nLayerCDFId, m_nWKTVarID, CF_GRD_MAPPING,
471 : m_osGridMapping.size(),
472 : m_osGridMapping.c_str());
473 2 : NCDF_ERR(status);
474 : }
475 : }
476 :
477 59 : if (m_poDS->oWriterConfig.m_bIsValid)
478 : {
479 2 : m_poLayerConfig = poLayerConfig;
480 :
481 2 : netCDFWriteAttributesFromConf(m_nLayerCDFId, NC_GLOBAL,
482 2 : m_poDS->oWriterConfig.m_aoAttributes);
483 2 : if (poLayerConfig != nullptr)
484 : {
485 1 : netCDFWriteAttributesFromConf(m_nLayerCDFId, NC_GLOBAL,
486 1 : poLayerConfig->m_aoAttributes);
487 : }
488 :
489 10 : for (size_t i = 0; i < aoAutoVariables.size(); i++)
490 : {
491 8 : const netCDFWriterConfigField *poConfig = nullptr;
492 16 : CPLString osLookup = "__" + aoAutoVariables[i].first;
493 8 : std::map<CPLString, netCDFWriterConfigField>::const_iterator oIter;
494 13 : if (m_poLayerConfig != nullptr &&
495 5 : (oIter = m_poLayerConfig->m_oFields.find(osLookup)) !=
496 13 : m_poLayerConfig->m_oFields.end())
497 : {
498 1 : poConfig = &(oIter->second);
499 : }
500 7 : else if ((oIter = m_poDS->oWriterConfig.m_oFields.find(osLookup)) !=
501 14 : m_poDS->oWriterConfig.m_oFields.end())
502 : {
503 1 : poConfig = &(oIter->second);
504 : }
505 :
506 8 : if (poConfig != nullptr)
507 : {
508 2 : netCDFWriteAttributesFromConf(m_nLayerCDFId,
509 2 : aoAutoVariables[i].second,
510 2 : poConfig->m_aoAttributes);
511 : }
512 : }
513 : }
514 :
515 : try
516 : {
517 59 : if (!m_bLegacyCreateMode)
518 : {
519 : // Write a geometry container
520 : OGRwkbGeometryType geometryContainerType =
521 42 : m_poFeatureDefn->GetGeomType();
522 84 : std::vector<std::string> coordNames;
523 : std::string strXVarName =
524 126 : std::string(this->GetName()) + std::string("_coordX");
525 : std::string strYVarName =
526 126 : std::string(this->GetName()) + std::string("_coordY");
527 42 : coordNames.push_back(strXVarName);
528 42 : coordNames.push_back(strYVarName);
529 :
530 42 : if (nccfdriver::OGRHasZandSupported(geometryContainerType))
531 : {
532 : std::string strZVarName =
533 48 : std::string(this->GetName()) + std::string("_coordZ");
534 16 : coordNames.push_back(strZVarName);
535 : }
536 :
537 42 : if (m_layerSGDefn.getWritableType() == nccfdriver::NONE)
538 : {
539 0 : throw nccfdriver::SG_Exception_BadFeature();
540 : }
541 :
542 126 : int writableSGContVarID = nccfdriver::write_Geometry_Container(
543 84 : m_poDS->cdfid, this->GetName(), m_layerSGDefn.getWritableType(),
544 : coordNames);
545 42 : m_layerSGDefn.initializeNewContainer(writableSGContVarID);
546 :
547 42 : if (newbufsize >= 4096)
548 : {
549 4 : m_poDS->bufManager.adjustLimit(newbufsize);
550 : }
551 :
552 : // Set record dim ID; POINT it's the node coordinate dim ID and
553 : // everything else it's node count:
554 42 : if (m_layerSGDefn.getWritableType() == nccfdriver::POINT)
555 : {
556 10 : m_nRecordDimID = m_layerSGDefn.get_node_coord_dimID();
557 20 : m_osRecordDimName = std::string(this->GetName()) +
558 40 : std::string("_") +
559 30 : std::string(CF_SG_NODE_COORDINATES);
560 : }
561 : else
562 : {
563 32 : m_nRecordDimID = m_layerSGDefn.get_node_count_dimID();
564 64 : m_osRecordDimName = std::string(this->GetName()) +
565 128 : std::string("_") +
566 96 : std::string(CF_SG_NODE_COUNT);
567 : }
568 :
569 : // Write the grid mapping, if it exists:
570 42 : if (poSRS != nullptr)
571 : {
572 38 : status = nc_put_att_text(
573 : m_nLayerCDFId, m_layerSGDefn.get_containerRealID(),
574 : CF_GRD_MAPPING, strlen(m_sgCRSname.c_str()),
575 : m_sgCRSname.c_str());
576 :
577 38 : if (status != NC_NOERR)
578 : {
579 : throw nccfdriver::SGWriter_Exception_NCWriteFailure(
580 0 : m_layerSGDefn.get_containerName().c_str(),
581 0 : CF_GRD_MAPPING, "attribute");
582 : }
583 :
584 : const std::vector<int> &ncv =
585 38 : m_layerSGDefn.get_nodeCoordVarIDs();
586 38 : int xVar = ncv[0];
587 38 : int yVar = ncv[1];
588 :
589 38 : if (poSRS->IsGeographic())
590 : {
591 37 : NCDFWriteLonLatVarsAttributes(layerVID, xVar, yVar);
592 : }
593 :
594 1 : else if (poSRS->IsProjected())
595 : {
596 1 : NCDFWriteXYVarsAttributes(layerVID, xVar, yVar, poSRS);
597 : }
598 : }
599 : }
600 : }
601 0 : catch (nccfdriver::SG_Exception &sge)
602 : {
603 0 : CPLError(
604 : CE_Failure, CPLE_AppDefined,
605 : "An error occurred while writing metadata to the netCDF file.\n%s",
606 0 : sge.get_err_msg());
607 0 : return false;
608 : }
609 :
610 59 : m_poDS->SetDefineMode(false);
611 59 : return true;
612 : }
613 :
614 : /************************************************************************/
615 : /* SetRecordDimID() */
616 : /************************************************************************/
617 :
618 49 : void netCDFLayer::SetRecordDimID(int nRecordDimID)
619 : {
620 49 : m_nRecordDimID = nRecordDimID;
621 : char szTemp[NC_MAX_NAME + 1];
622 49 : szTemp[0] = 0;
623 49 : int status = nc_inq_dimname(m_nLayerCDFId, m_nRecordDimID, szTemp);
624 49 : NCDF_ERR(status);
625 49 : m_osRecordDimName = szTemp;
626 49 : }
627 :
628 : /************************************************************************/
629 : /* GetFillValue() */
630 : /************************************************************************/
631 :
632 273 : CPLErr netCDFLayer::GetFillValue(int nVarId, char **ppszValue)
633 : {
634 273 : if (NCDFGetAttr(m_nLayerCDFId, nVarId, NCDF_FillValue, ppszValue) ==
635 : CE_None)
636 65 : return CE_None;
637 208 : return NCDFGetAttr(m_nLayerCDFId, nVarId, "missing_value", ppszValue);
638 : }
639 :
640 293 : CPLErr netCDFLayer::GetFillValue(int nVarId, double *pdfValue)
641 : {
642 293 : if (NCDFGetAttr(m_nLayerCDFId, nVarId, NCDF_FillValue, pdfValue) == CE_None)
643 40 : return CE_None;
644 253 : return NCDFGetAttr(m_nLayerCDFId, nVarId, "missing_value", pdfValue);
645 : }
646 :
647 : /************************************************************************/
648 : /* GetNoDataValueForFloat() */
649 : /************************************************************************/
650 :
651 6 : void netCDFLayer::GetNoDataValueForFloat(int nVarId, NCDFNoDataUnion *puNoData)
652 : {
653 : double dfValue;
654 6 : if (GetFillValue(nVarId, &dfValue) == CE_None)
655 2 : puNoData->fVal = static_cast<float>(dfValue);
656 : else
657 4 : puNoData->fVal = NC_FILL_FLOAT;
658 6 : }
659 :
660 : /************************************************************************/
661 : /* GetNoDataValueForDouble() */
662 : /************************************************************************/
663 :
664 98 : void netCDFLayer::GetNoDataValueForDouble(int nVarId, NCDFNoDataUnion *puNoData)
665 : {
666 : double dfValue;
667 98 : if (GetFillValue(nVarId, &dfValue) == CE_None)
668 0 : puNoData->dfVal = dfValue;
669 : else
670 98 : puNoData->dfVal = NC_FILL_DOUBLE;
671 98 : }
672 :
673 : /************************************************************************/
674 : /* GetNoDataValue() */
675 : /************************************************************************/
676 :
677 104 : void netCDFLayer::GetNoDataValue(int nVarId, nc_type nVarType,
678 : NCDFNoDataUnion *puNoData)
679 : {
680 104 : if (nVarType == NC_DOUBLE)
681 98 : GetNoDataValueForDouble(nVarId, puNoData);
682 6 : else if (nVarType == NC_FLOAT)
683 6 : GetNoDataValueForFloat(nVarId, puNoData);
684 104 : }
685 :
686 : /************************************************************************/
687 : /* SetXYZVars() */
688 : /************************************************************************/
689 :
690 35 : void netCDFLayer::SetXYZVars(int nXVarId, int nYVarId, int nZVarId)
691 : {
692 35 : m_nXVarID = nXVarId;
693 35 : m_nYVarID = nYVarId;
694 35 : m_nZVarID = nZVarId;
695 :
696 35 : nc_inq_vartype(m_nLayerCDFId, m_nXVarID, &m_nXVarNCDFType);
697 35 : nc_inq_vartype(m_nLayerCDFId, m_nYVarID, &m_nYVarNCDFType);
698 35 : if ((m_nXVarNCDFType != NC_FLOAT && m_nXVarNCDFType != NC_DOUBLE) ||
699 35 : (m_nYVarNCDFType != NC_FLOAT && m_nYVarNCDFType != NC_DOUBLE))
700 : {
701 0 : CPLError(CE_Warning, CPLE_NotSupported,
702 : "X or Y variable of type X=%d,Y=%d not handled",
703 : m_nXVarNCDFType, m_nYVarNCDFType);
704 0 : m_nXVarID = -1;
705 0 : m_nYVarID = -1;
706 : }
707 35 : if (m_nZVarID >= 0)
708 : {
709 34 : nc_inq_vartype(m_nLayerCDFId, m_nZVarID, &m_nZVarNCDFType);
710 34 : if (m_nZVarNCDFType != NC_FLOAT && m_nZVarNCDFType != NC_DOUBLE)
711 : {
712 0 : CPLError(CE_Warning, CPLE_NotSupported,
713 : "Z variable of type %d not handled", m_nZVarNCDFType);
714 0 : m_nZVarID = -1;
715 : }
716 : }
717 :
718 35 : if (m_nXVarID >= 0 && m_nYVarID >= 0)
719 : {
720 : char szVarName[NC_MAX_NAME + 1];
721 35 : szVarName[0] = '\0';
722 35 : CPL_IGNORE_RET_VAL(nc_inq_varname(m_nLayerCDFId, m_nXVarID, szVarName));
723 35 : m_osCoordinatesValue = szVarName;
724 :
725 35 : szVarName[0] = '\0';
726 35 : CPL_IGNORE_RET_VAL(nc_inq_varname(m_nLayerCDFId, m_nYVarID, szVarName));
727 35 : m_osCoordinatesValue += " ";
728 35 : m_osCoordinatesValue += szVarName;
729 :
730 35 : if (m_nZVarID >= 0)
731 : {
732 34 : szVarName[0] = '\0';
733 34 : CPL_IGNORE_RET_VAL(
734 34 : nc_inq_varname(m_nLayerCDFId, m_nZVarID, szVarName));
735 34 : m_osCoordinatesValue += " ";
736 34 : m_osCoordinatesValue += szVarName;
737 : }
738 : }
739 :
740 35 : if (m_nXVarID >= 0)
741 35 : GetNoDataValue(m_nXVarID, m_nXVarNCDFType, &m_uXVarNoData);
742 35 : if (m_nYVarID >= 0)
743 35 : GetNoDataValue(m_nYVarID, m_nYVarNCDFType, &m_uYVarNoData);
744 35 : if (m_nZVarID >= 0)
745 34 : GetNoDataValue(m_nZVarID, m_nZVarNCDFType, &m_uZVarNoData);
746 35 : }
747 :
748 : /************************************************************************/
749 : /* SetWKTGeometryField() */
750 : /************************************************************************/
751 :
752 9 : void netCDFLayer::SetWKTGeometryField(const char *pszWKTVarName)
753 : {
754 9 : m_nWKTVarID = -1;
755 9 : nc_inq_varid(m_nLayerCDFId, pszWKTVarName, &m_nWKTVarID);
756 9 : if (m_nWKTVarID < 0)
757 0 : return;
758 : int nd;
759 9 : nc_inq_varndims(m_nLayerCDFId, m_nWKTVarID, &nd);
760 9 : nc_inq_vartype(m_nLayerCDFId, m_nWKTVarID, &m_nWKTNCDFType);
761 9 : if (nd == 1 && m_nWKTNCDFType == NC_STRING)
762 : {
763 : int nDimID;
764 4 : if (nc_inq_vardimid(m_nLayerCDFId, m_nWKTVarID, &nDimID) != NC_NOERR ||
765 2 : nDimID != m_nRecordDimID)
766 : {
767 0 : m_nWKTVarID = -1;
768 0 : return;
769 2 : }
770 : }
771 7 : else if (nd == 2 && m_nWKTNCDFType == NC_CHAR)
772 : {
773 7 : int anDimIds[2] = {-1, -1};
774 7 : size_t nLen = 0;
775 7 : if (nc_inq_vardimid(m_nLayerCDFId, m_nWKTVarID, anDimIds) != NC_NOERR ||
776 14 : anDimIds[0] != m_nRecordDimID ||
777 7 : nc_inq_dimlen(m_nLayerCDFId, anDimIds[1], &nLen) != NC_NOERR)
778 : {
779 0 : m_nWKTVarID = -1;
780 0 : return;
781 : }
782 7 : m_nWKTMaxWidth = static_cast<int>(nLen);
783 7 : m_nWKTMaxWidthDimId = anDimIds[1];
784 : }
785 : else
786 : {
787 0 : m_nWKTVarID = -1;
788 0 : return;
789 : }
790 :
791 9 : m_osWKTVarName = pszWKTVarName;
792 : }
793 :
794 : /************************************************************************/
795 : /* SetGridMapping() */
796 : /************************************************************************/
797 :
798 21 : void netCDFLayer::SetGridMapping(const char *pszGridMapping)
799 : {
800 21 : m_osGridMapping = pszGridMapping;
801 21 : }
802 :
803 : /************************************************************************/
804 : /* SetProfile() */
805 : /************************************************************************/
806 :
807 49 : void netCDFLayer::SetProfile(int nProfileDimID, int nParentIndexVarID)
808 : {
809 49 : m_nProfileDimID = nProfileDimID;
810 49 : m_nParentIndexVarID = nParentIndexVarID;
811 49 : if (m_nProfileDimID >= 0)
812 : {
813 : char szTemp[NC_MAX_NAME + 1];
814 13 : szTemp[0] = 0;
815 13 : int status = nc_inq_dimname(m_nLayerCDFId, m_nProfileDimID, szTemp);
816 13 : NCDF_ERR(status);
817 13 : m_osProfileDimName = szTemp;
818 :
819 13 : nc_inq_varid(m_nLayerCDFId, m_osProfileDimName, &m_nProfileVarID);
820 13 : m_bProfileVarUnlimited = NCDFIsUnlimitedDim(
821 13 : m_poDS->eFormat == NCDF_FORMAT_NC4, m_nLayerCDFId, m_nProfileVarID);
822 : }
823 49 : }
824 :
825 : /************************************************************************/
826 : /* ResetReading() */
827 : /************************************************************************/
828 :
829 133 : void netCDFLayer::ResetReading()
830 : {
831 133 : if (!m_bLegacyCreateMode)
832 : {
833 6 : m_SGeometryFeatInd = 0;
834 : }
835 : else
836 : {
837 127 : m_nCurFeatureId = 1;
838 : }
839 133 : }
840 :
841 : /************************************************************************/
842 : /* Get1DVarAsDouble() */
843 : /************************************************************************/
844 :
845 633 : double netCDFLayer::Get1DVarAsDouble(int nVarId, nc_type nVarType,
846 : size_t nIndex,
847 : const NCDFNoDataUnion &noDataVal,
848 : bool *pbIsNoData)
849 : {
850 633 : double dfVal = 0;
851 633 : if (nVarType == NC_DOUBLE)
852 : {
853 622 : nc_get_var1_double(m_nLayerCDFId, nVarId, &nIndex, &dfVal);
854 622 : if (pbIsNoData)
855 622 : *pbIsNoData = dfVal == noDataVal.dfVal;
856 : }
857 11 : else if (nVarType == NC_FLOAT)
858 : {
859 11 : float fVal = 0.f;
860 11 : nc_get_var1_float(m_nLayerCDFId, nVarId, &nIndex, &fVal);
861 11 : if (pbIsNoData)
862 11 : *pbIsNoData = fVal == noDataVal.fVal;
863 11 : dfVal = fVal;
864 : }
865 0 : else if (pbIsNoData)
866 0 : *pbIsNoData = true;
867 633 : return dfVal;
868 : }
869 :
870 : /************************************************************************/
871 : /* GetNextRawFeature() */
872 : /************************************************************************/
873 :
874 776 : OGRFeature *netCDFLayer::GetNextRawFeature()
875 : {
876 :
877 776 : if (m_simpleGeometryReader.get() != nullptr)
878 : {
879 497 : if (m_SGeometryFeatInd >= m_simpleGeometryReader->get_geometry_count())
880 : {
881 5 : return nullptr;
882 : }
883 :
884 492 : OGRFeature *ft = nullptr;
885 :
886 : try
887 : {
888 492 : ft = buildSGeometryFeature(m_SGeometryFeatInd);
889 492 : m_SGeometryFeatInd++;
890 : }
891 0 : catch (nccfdriver::SG_Exception &sge)
892 : {
893 0 : CPLError(CE_Warning, CPLE_AppDefined,
894 : "An error occurred while retrieving a feature.\n%s",
895 0 : sge.get_err_msg());
896 : }
897 :
898 492 : return ft;
899 : }
900 :
901 279 : m_poDS->SetDefineMode(false);
902 :
903 : // In update mode, nc_get_varXXX() doesn't return error if we are
904 : // beyond the end of dimension
905 279 : size_t nDimLen = 0;
906 279 : nc_inq_dimlen(m_nLayerCDFId, m_nRecordDimID, &nDimLen);
907 279 : if (m_nCurFeatureId > static_cast<GIntBig>(nDimLen))
908 55 : return nullptr;
909 :
910 224 : OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
911 :
912 224 : if (m_nParentIndexVarID >= 0)
913 : {
914 60 : int nProfileIdx = 0;
915 60 : size_t nIdx = static_cast<size_t>(m_nCurFeatureId - 1);
916 60 : int status = nc_get_var1_int(m_nLayerCDFId, m_nParentIndexVarID, &nIdx,
917 : &nProfileIdx);
918 60 : if (status == NC_NOERR && nProfileIdx >= 0)
919 : {
920 60 : nIdx = static_cast<size_t>(nProfileIdx);
921 60 : FillFeatureFromVar(poFeature, m_nProfileDimID, nIdx);
922 : }
923 : }
924 :
925 224 : if (!FillFeatureFromVar(poFeature, m_nRecordDimID,
926 224 : static_cast<size_t>(m_nCurFeatureId - 1)))
927 : {
928 0 : m_nCurFeatureId++;
929 0 : delete poFeature;
930 0 : return nullptr;
931 : }
932 :
933 224 : poFeature->SetFID(m_nCurFeatureId);
934 224 : m_nCurFeatureId++;
935 :
936 224 : return poFeature;
937 : }
938 :
939 : /************************************************************************/
940 : /* FillFeatureFromVar() */
941 : /************************************************************************/
942 :
943 798 : bool netCDFLayer::FillFeatureFromVar(OGRFeature *poFeature, int nMainDimId,
944 : size_t nIndex)
945 : {
946 : size_t anIndex[2];
947 798 : anIndex[0] = nIndex;
948 798 : anIndex[1] = 0;
949 :
950 6223 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
951 : {
952 5425 : if (m_aoFieldDesc[i].nMainDimId != nMainDimId && m_bLegacyCreateMode)
953 296 : continue;
954 :
955 5129 : switch (m_aoFieldDesc[i].nType)
956 : {
957 1455 : case NC_CHAR:
958 : {
959 1455 : if (m_aoFieldDesc[i].nDimCount == 1)
960 : {
961 122 : char szVal[2] = {0, 0};
962 122 : int status = nc_get_var1_text(
963 122 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, szVal);
964 122 : if (status != NC_NOERR)
965 : {
966 0 : NCDF_ERR(status);
967 0 : continue;
968 : }
969 122 : poFeature->SetField(i, szVal);
970 : }
971 : else
972 : {
973 : size_t anCount[2];
974 1333 : anCount[0] = 1;
975 1333 : anCount[1] = 0;
976 1333 : nc_inq_dimlen(m_nLayerCDFId, m_aoFieldDesc[i].nSecDimId,
977 : &(anCount[1]));
978 1333 : char *pszVal = (char *)CPLCalloc(1, anCount[1] + 1);
979 : int status =
980 1333 : nc_get_vara_text(m_nLayerCDFId, m_aoFieldDesc[i].nVarId,
981 : anIndex, anCount, pszVal);
982 1333 : if (status != NC_NOERR)
983 : {
984 0 : NCDF_ERR(status);
985 0 : CPLFree(pszVal);
986 0 : continue;
987 : }
988 1333 : poFeature->SetField(i, pszVal);
989 1333 : CPLFree(pszVal);
990 : }
991 1455 : break;
992 : }
993 :
994 49 : case NC_STRING:
995 : {
996 49 : char *pszVal = nullptr;
997 49 : int status = nc_get_var1_string(
998 49 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &pszVal);
999 49 : if (status != NC_NOERR)
1000 : {
1001 0 : NCDF_ERR(status);
1002 0 : continue;
1003 : }
1004 49 : if (pszVal != nullptr)
1005 : {
1006 49 : poFeature->SetField(i, pszVal);
1007 49 : nc_free_string(1, &pszVal);
1008 : }
1009 49 : break;
1010 : }
1011 :
1012 419 : case NC_BYTE:
1013 : {
1014 419 : signed char chVal = 0;
1015 419 : int status = nc_get_var1_schar(
1016 419 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &chVal);
1017 419 : if (status != NC_NOERR)
1018 : {
1019 0 : NCDF_ERR(status);
1020 242 : continue;
1021 : }
1022 419 : if (chVal == m_aoFieldDesc[i].uNoData.chVal)
1023 242 : continue;
1024 177 : poFeature->SetField(i, static_cast<int>(chVal));
1025 177 : break;
1026 : }
1027 :
1028 32 : case NC_UBYTE:
1029 : {
1030 32 : unsigned char uchVal = 0;
1031 32 : int status = nc_get_var1_uchar(
1032 32 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &uchVal);
1033 32 : if (status != NC_NOERR)
1034 : {
1035 0 : NCDF_ERR(status);
1036 16 : continue;
1037 : }
1038 32 : if (uchVal == m_aoFieldDesc[i].uNoData.uchVal)
1039 16 : continue;
1040 16 : poFeature->SetField(i, static_cast<int>(uchVal));
1041 16 : break;
1042 : }
1043 :
1044 294 : case NC_SHORT:
1045 : {
1046 294 : short sVal = 0;
1047 294 : int status = nc_get_var1_short(
1048 294 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &sVal);
1049 :
1050 294 : if (status != NC_NOERR)
1051 : {
1052 0 : NCDF_ERR(status);
1053 168 : continue;
1054 : }
1055 294 : if (sVal == m_aoFieldDesc[i].uNoData.sVal)
1056 168 : continue;
1057 126 : poFeature->SetField(i, static_cast<int>(sVal));
1058 126 : break;
1059 : }
1060 :
1061 32 : case NC_USHORT:
1062 : {
1063 32 : unsigned short usVal = 0;
1064 32 : int status = nc_get_var1_ushort(
1065 32 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &usVal);
1066 32 : if (status != NC_NOERR)
1067 : {
1068 0 : NCDF_ERR(status);
1069 16 : continue;
1070 : }
1071 32 : if (usVal == m_aoFieldDesc[i].uNoData.usVal)
1072 16 : continue;
1073 16 : poFeature->SetField(i, static_cast<int>(usVal));
1074 16 : break;
1075 : }
1076 :
1077 722 : case NC_INT:
1078 : {
1079 722 : int nVal = 0;
1080 722 : int status = nc_get_var1_int(
1081 722 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &nVal);
1082 722 : if (status != NC_NOERR)
1083 : {
1084 0 : NCDF_ERR(status);
1085 214 : continue;
1086 : }
1087 722 : if (nVal == m_aoFieldDesc[i].uNoData.nVal)
1088 214 : continue;
1089 1007 : if (m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDate ||
1090 499 : m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
1091 : {
1092 : struct tm brokendowntime;
1093 9 : GIntBig nVal64 = static_cast<GIntBig>(nVal);
1094 9 : if (m_aoFieldDesc[i].bIsDays)
1095 9 : nVal64 *= 86400;
1096 9 : CPLUnixTimeToYMDHMS(nVal64, &brokendowntime);
1097 9 : poFeature->SetField(
1098 9 : i, brokendowntime.tm_year + 1900,
1099 9 : brokendowntime.tm_mon + 1, brokendowntime.tm_mday,
1100 : brokendowntime.tm_hour, brokendowntime.tm_min,
1101 9 : static_cast<float>(brokendowntime.tm_sec), 0);
1102 : }
1103 : else
1104 : {
1105 499 : poFeature->SetField(i, nVal);
1106 : }
1107 508 : break;
1108 : }
1109 :
1110 32 : case NC_UINT:
1111 : {
1112 32 : unsigned int unVal = 0;
1113 : // nc_get_var1_uint() doesn't work on old netCDF version when
1114 : // the returned value is > INT_MAX
1115 : // https://bugtracking.unidata.ucar.edu/browse/NCF-226
1116 : // nc_get_vara_uint() has not this bug
1117 32 : size_t nCount = 1;
1118 : int status =
1119 32 : nc_get_vara_uint(m_nLayerCDFId, m_aoFieldDesc[i].nVarId,
1120 : anIndex, &nCount, &unVal);
1121 32 : if (status != NC_NOERR)
1122 : {
1123 0 : NCDF_ERR(status);
1124 16 : continue;
1125 : }
1126 32 : if (unVal == m_aoFieldDesc[i].uNoData.unVal)
1127 16 : continue;
1128 16 : poFeature->SetField(i, static_cast<GIntBig>(unVal));
1129 16 : break;
1130 : }
1131 66 : case NC_INT64:
1132 : {
1133 66 : GIntBig nVal = 0;
1134 66 : int status = nc_get_var1_longlong(
1135 66 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &nVal);
1136 66 : if (status != NC_NOERR)
1137 : {
1138 0 : NCDF_ERR(status);
1139 44 : continue;
1140 : }
1141 66 : if (nVal == m_aoFieldDesc[i].uNoData.nVal64)
1142 44 : continue;
1143 22 : poFeature->SetField(i, nVal);
1144 22 : break;
1145 : }
1146 :
1147 32 : case NC_UINT64:
1148 : {
1149 32 : GUIntBig nVal = 0;
1150 32 : int status = nc_get_var1_ulonglong(
1151 32 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &nVal);
1152 32 : if (status != NC_NOERR)
1153 : {
1154 0 : NCDF_ERR(status);
1155 24 : continue;
1156 : }
1157 32 : if (nVal == m_aoFieldDesc[i].uNoData.unVal64)
1158 24 : continue;
1159 8 : poFeature->SetField(i, static_cast<double>(nVal));
1160 8 : break;
1161 : }
1162 :
1163 294 : case NC_FLOAT:
1164 : {
1165 294 : float fVal = 0.f;
1166 294 : int status = nc_get_var1_float(
1167 294 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &fVal);
1168 294 : if (status != NC_NOERR)
1169 : {
1170 0 : NCDF_ERR(status);
1171 168 : continue;
1172 : }
1173 294 : if (fVal == m_aoFieldDesc[i].uNoData.fVal)
1174 168 : continue;
1175 126 : poFeature->SetField(i, static_cast<double>(fVal));
1176 126 : break;
1177 : }
1178 :
1179 1702 : case NC_DOUBLE:
1180 : {
1181 1702 : double dfVal = 0.0;
1182 1702 : int status = nc_get_var1_double(
1183 1702 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &dfVal);
1184 1702 : if (status != NC_NOERR)
1185 : {
1186 0 : NCDF_ERR(status);
1187 650 : continue;
1188 : }
1189 1702 : if (dfVal == m_aoFieldDesc[i].uNoData.dfVal)
1190 650 : continue;
1191 2051 : if (m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDate ||
1192 999 : m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
1193 : {
1194 177 : if (m_aoFieldDesc[i].bIsDays)
1195 0 : dfVal *= 86400.0;
1196 : struct tm brokendowntime;
1197 177 : GIntBig nVal = static_cast<GIntBig>(floor(dfVal));
1198 177 : CPLUnixTimeToYMDHMS(nVal, &brokendowntime);
1199 177 : poFeature->SetField(
1200 177 : i, brokendowntime.tm_year + 1900,
1201 177 : brokendowntime.tm_mon + 1, brokendowntime.tm_mday,
1202 : brokendowntime.tm_hour, brokendowntime.tm_min,
1203 177 : static_cast<float>(brokendowntime.tm_sec +
1204 177 : (dfVal - nVal)),
1205 : 0);
1206 : }
1207 : else
1208 : {
1209 875 : poFeature->SetField(i, dfVal);
1210 : }
1211 1052 : break;
1212 : }
1213 :
1214 0 : default:
1215 0 : break;
1216 : }
1217 : }
1218 :
1219 : // For CF-1.8 simple geometry specifically
1220 : // Only need fields to be set here
1221 798 : if (!m_bLegacyCreateMode)
1222 492 : return true; // todo: remove this, refactor to allow for CF-1.6 CF-1.8
1223 : // mixed datasets (multi group)
1224 :
1225 741 : if (m_nXVarID >= 0 && m_nYVarID >= 0 &&
1226 435 : (m_osProfileDimName.empty() || nMainDimId == m_nProfileDimID))
1227 : {
1228 233 : bool bXIsNoData = false;
1229 466 : const double dfX = Get1DVarAsDouble(
1230 233 : m_nXVarID, m_nXVarNCDFType, anIndex[0], m_uXVarNoData, &bXIsNoData);
1231 233 : bool bYIsNoData = false;
1232 466 : const double dfY = Get1DVarAsDouble(
1233 233 : m_nYVarID, m_nYVarNCDFType, anIndex[0], m_uYVarNoData, &bYIsNoData);
1234 :
1235 233 : if (!bXIsNoData && !bYIsNoData)
1236 : {
1237 190 : OGRPoint *poPoint = nullptr;
1238 190 : if (m_nZVarID >= 0 && m_osProfileDimName.empty())
1239 : {
1240 107 : bool bZIsNoData = false;
1241 : const double dfZ =
1242 214 : Get1DVarAsDouble(m_nZVarID, m_nZVarNCDFType, anIndex[0],
1243 107 : m_uZVarNoData, &bZIsNoData);
1244 107 : if (bZIsNoData)
1245 38 : poPoint = new OGRPoint(dfX, dfY);
1246 : else
1247 69 : poPoint = new OGRPoint(dfX, dfY, dfZ);
1248 : }
1249 : else
1250 83 : poPoint = new OGRPoint(dfX, dfY);
1251 190 : poPoint->assignSpatialReference(GetSpatialRef());
1252 190 : poFeature->SetGeometryDirectly(poPoint);
1253 : }
1254 : }
1255 60 : else if (m_nXVarID >= 0 && m_nYVarID >= 0 && m_nZVarID >= 0 &&
1256 133 : !m_osProfileDimName.empty() && nMainDimId == m_nRecordDimID)
1257 : {
1258 60 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1259 120 : if (poGeom != nullptr &&
1260 60 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1261 : {
1262 60 : bool bZIsNoData = false;
1263 : const double dfZ =
1264 120 : Get1DVarAsDouble(m_nZVarID, m_nZVarNCDFType, anIndex[0],
1265 60 : m_uZVarNoData, &bZIsNoData);
1266 60 : if (!bZIsNoData)
1267 60 : poGeom->toPoint()->setZ(dfZ);
1268 : }
1269 : }
1270 13 : else if (m_nWKTVarID >= 0)
1271 : {
1272 10 : char *pszWKT = nullptr;
1273 10 : if (m_nWKTNCDFType == NC_CHAR)
1274 : {
1275 : size_t anCount[2];
1276 7 : anCount[0] = 1;
1277 7 : anCount[1] = m_nWKTMaxWidth;
1278 7 : pszWKT = (char *)CPLCalloc(1, anCount[1] + 1);
1279 7 : int status = nc_get_vara_text(m_nLayerCDFId, m_nWKTVarID, anIndex,
1280 : anCount, pszWKT);
1281 7 : if (status == NC_EINVALCOORDS || status == NC_EEDGE)
1282 : {
1283 0 : CPLFree(pszWKT);
1284 0 : return false;
1285 : }
1286 7 : if (status != NC_NOERR)
1287 : {
1288 0 : NCDF_ERR(status);
1289 0 : CPLFree(pszWKT);
1290 0 : pszWKT = nullptr;
1291 : }
1292 : }
1293 3 : else if (m_nWKTNCDFType == NC_STRING)
1294 : {
1295 3 : char *pszVal = nullptr;
1296 3 : int status = nc_get_var1_string(m_nLayerCDFId, m_nWKTVarID, anIndex,
1297 : &pszVal);
1298 3 : if (status == NC_EINVALCOORDS || status == NC_EEDGE)
1299 : {
1300 0 : return false;
1301 : }
1302 3 : if (status != NC_NOERR)
1303 : {
1304 0 : NCDF_ERR(status);
1305 : }
1306 3 : else if (pszVal != nullptr)
1307 : {
1308 3 : pszWKT = CPLStrdup(pszVal);
1309 3 : nc_free_string(1, &pszVal);
1310 : }
1311 : }
1312 10 : if (pszWKT != nullptr)
1313 : {
1314 10 : OGRGeometry *poGeom = nullptr;
1315 10 : CPL_IGNORE_RET_VAL(
1316 10 : OGRGeometryFactory::createFromWkt(pszWKT, nullptr, &poGeom));
1317 10 : if (poGeom != nullptr)
1318 : {
1319 6 : poGeom->assignSpatialReference(GetSpatialRef());
1320 6 : poFeature->SetGeometryDirectly(poGeom);
1321 : }
1322 10 : CPLFree(pszWKT);
1323 : }
1324 : }
1325 :
1326 306 : return true;
1327 : }
1328 :
1329 : /************************************************************************/
1330 : /* GetNextFeature() */
1331 : /************************************************************************/
1332 :
1333 776 : OGRFeature *netCDFLayer::GetNextFeature()
1334 : {
1335 : while (true)
1336 : {
1337 776 : OGRFeature *poFeature = GetNextRawFeature();
1338 776 : if (poFeature == nullptr)
1339 60 : return nullptr;
1340 :
1341 1465 : if ((m_poFilterGeom == nullptr ||
1342 1413 : FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
1343 697 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
1344 673 : return poFeature;
1345 :
1346 43 : delete poFeature;
1347 43 : }
1348 : }
1349 :
1350 : /************************************************************************/
1351 : /* GetLayerDefn() */
1352 : /************************************************************************/
1353 :
1354 4097 : OGRFeatureDefn *netCDFLayer::GetLayerDefn()
1355 : {
1356 4097 : return m_poFeatureDefn;
1357 : }
1358 :
1359 : /************************************************************************/
1360 : /* ICreateFeature() */
1361 : /************************************************************************/
1362 :
1363 509 : OGRErr netCDFLayer::ICreateFeature(OGRFeature *poFeature)
1364 : {
1365 509 : m_poDS->SetDefineMode(false);
1366 :
1367 509 : size_t nFeatureIdx = 0;
1368 509 : nc_inq_dimlen(m_nLayerCDFId, m_nRecordDimID, &nFeatureIdx);
1369 :
1370 509 : if (!m_bLegacyCreateMode)
1371 : {
1372 : // Detects: append mode
1373 446 : if (m_layerSGDefn.get_containerRealID() == nccfdriver::INVALID_VAR_ID)
1374 : {
1375 0 : CPLError(CE_Failure, CPLE_NotSupported,
1376 : "Append mode is not supported for CF-1.8 datasets.");
1377 0 : return OGRERR_UNSUPPORTED_OPERATION;
1378 : }
1379 : }
1380 :
1381 509 : if (m_nProfileDimID >= 0)
1382 : {
1383 20 : size_t nProfileCount = 0;
1384 20 : nc_inq_dimlen(m_nLayerCDFId, m_nProfileDimID, &nProfileCount);
1385 :
1386 20 : OGRFeature *poProfileToLookup = poFeature->Clone();
1387 20 : poProfileToLookup->SetFID(-1);
1388 96 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
1389 : {
1390 152 : if (!(poProfileToLookup->IsFieldSetAndNotNull(i)) ||
1391 76 : m_aoFieldDesc[i].nMainDimId != m_nProfileDimID)
1392 : {
1393 52 : poProfileToLookup->UnsetField(i);
1394 52 : continue;
1395 : }
1396 : }
1397 20 : OGRGeometry *poGeom = poProfileToLookup->GetGeometryRef();
1398 40 : if (poGeom != nullptr &&
1399 20 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1400 : {
1401 20 : poGeom->toPoint()->setZ(0);
1402 : }
1403 :
1404 20 : size_t nProfileIdx = 0;
1405 20 : bool bFoundProfile = false;
1406 30 : for (; nProfileIdx < nProfileCount; nProfileIdx++)
1407 : {
1408 26 : int nId = NC_FILL_INT;
1409 26 : int status = nc_get_var1_int(m_nLayerCDFId, m_nProfileVarID,
1410 : &nProfileIdx, &nId);
1411 26 : NCDF_ERR(status);
1412 26 : if (nId == NC_FILL_INT)
1413 16 : break;
1414 :
1415 22 : OGRFeature *poIterFeature = new OGRFeature(m_poFeatureDefn);
1416 22 : if (FillFeatureFromVar(poIterFeature, m_nProfileDimID, nProfileIdx))
1417 : {
1418 22 : poGeom = poIterFeature->GetGeometryRef();
1419 44 : if (poGeom != nullptr &&
1420 22 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1421 : {
1422 22 : poGeom->toPoint()->setZ(0);
1423 : }
1424 22 : if (poIterFeature->Equal(poProfileToLookup))
1425 : {
1426 12 : bFoundProfile = true;
1427 12 : delete poIterFeature;
1428 12 : break;
1429 : }
1430 : }
1431 10 : delete poIterFeature;
1432 : }
1433 :
1434 20 : if (!bFoundProfile)
1435 : {
1436 8 : if (!m_bProfileVarUnlimited && nProfileIdx == nProfileCount)
1437 : {
1438 2 : size_t nNewSize = 1 + nProfileCount + nProfileCount / 3;
1439 2 : m_poDS->GrowDim(m_nLayerCDFId, m_nProfileDimID, nNewSize);
1440 : }
1441 :
1442 8 : if (!FillVarFromFeature(poProfileToLookup, m_nProfileDimID,
1443 : nProfileIdx))
1444 : {
1445 0 : delete poProfileToLookup;
1446 0 : return OGRERR_FAILURE;
1447 : }
1448 : }
1449 :
1450 20 : int nProfileIdIdx = m_poFeatureDefn->GetFieldIndex(m_osProfileDimName);
1451 36 : if (nProfileIdIdx < 0 ||
1452 16 : m_poFeatureDefn->GetFieldDefn(nProfileIdIdx)->GetType() !=
1453 : OFTInteger)
1454 : {
1455 4 : int nVal = static_cast<int>(nProfileIdx);
1456 4 : int status = nc_put_var1_int(m_nLayerCDFId, m_nProfileVarID,
1457 : &nProfileIdx, &nVal);
1458 4 : NCDF_ERR(status);
1459 : }
1460 :
1461 20 : int nVal = static_cast<int>(nProfileIdx);
1462 20 : int status = nc_put_var1_int(m_nLayerCDFId, m_nParentIndexVarID,
1463 : &nFeatureIdx, &nVal);
1464 20 : NCDF_ERR(status);
1465 :
1466 20 : delete poProfileToLookup;
1467 : }
1468 :
1469 509 : if (!FillVarFromFeature(poFeature, m_nRecordDimID, nFeatureIdx))
1470 0 : return OGRERR_FAILURE;
1471 :
1472 509 : poFeature->SetFID(nFeatureIdx + 1);
1473 :
1474 509 : return OGRERR_NONE;
1475 : }
1476 :
1477 : /************************************************************************/
1478 : /* FillVarFromFeature() */
1479 : /************************************************************************/
1480 :
1481 517 : bool netCDFLayer::FillVarFromFeature(OGRFeature *poFeature, int nMainDimId,
1482 : size_t nIndex)
1483 : {
1484 : size_t anIndex[2];
1485 517 : anIndex[0] = nIndex;
1486 517 : anIndex[1] = 0;
1487 :
1488 2715 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
1489 : {
1490 2198 : if (m_aoFieldDesc[i].nMainDimId != nMainDimId)
1491 46 : continue;
1492 :
1493 2152 : if (!(poFeature->IsFieldSetAndNotNull(i)) && m_bLegacyCreateMode)
1494 : {
1495 127 : if (m_bNCDumpCompat && m_aoFieldDesc[i].nType == NC_STRING)
1496 : {
1497 1 : const char *pszVal = "";
1498 1 : int status = nc_put_var1_string(
1499 1 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &pszVal);
1500 1 : NCDF_ERR(status);
1501 : }
1502 127 : continue;
1503 : }
1504 :
1505 2025 : int status = NC_NOERR;
1506 2025 : switch (m_aoFieldDesc[i].nType)
1507 : {
1508 963 : case NC_CHAR:
1509 : {
1510 963 : const char *pszVal = poFeature->GetFieldAsString(i);
1511 963 : if (m_aoFieldDesc[i].nDimCount == 1)
1512 : {
1513 7 : if (strlen(pszVal) > 1 &&
1514 0 : !m_aoFieldDesc[i].bHasWarnedAboutTruncation)
1515 : {
1516 0 : CPLError(
1517 : CE_Warning, CPLE_AppDefined,
1518 : "Content of field %s exceeded the 1 character "
1519 : "limit and will be truncated",
1520 0 : m_poFeatureDefn->GetFieldDefn(i)->GetNameRef());
1521 0 : m_aoFieldDesc[i].bHasWarnedAboutTruncation = true;
1522 : }
1523 7 : if (m_poDS->HasInfiniteRecordDim())
1524 : {
1525 7 : status = nc_put_var1_text(m_nLayerCDFId,
1526 7 : m_aoFieldDesc[i].nVarId,
1527 : anIndex, pszVal);
1528 : }
1529 : else
1530 : {
1531 0 : m_poDS->FieldScribe.enqueue_transaction(
1532 0 : nccfdriver::MTPtr(
1533 : new nccfdriver::OGR_SGFS_NC_Char_Transaction(
1534 0 : m_aoFieldDesc[i].nVarId, pszVal)));
1535 : }
1536 : }
1537 : else
1538 : {
1539 : size_t anCount[2];
1540 956 : anCount[0] = 1;
1541 956 : anCount[1] = strlen(pszVal);
1542 956 : size_t nWidth = 0;
1543 :
1544 956 : if (m_bLegacyCreateMode)
1545 : {
1546 72 : nc_inq_dimlen(m_nLayerCDFId, m_aoFieldDesc[i].nSecDimId,
1547 : &nWidth);
1548 : }
1549 : else
1550 : {
1551 884 : nWidth =
1552 884 : layerVID.virtualDIDToDim(m_aoFieldDesc[i].nSecDimId)
1553 884 : .getLen();
1554 : }
1555 :
1556 956 : if (anCount[1] > nWidth)
1557 : {
1558 : // Always grow the dim if not writing to WKT- it's
1559 : // rather inexpensive in CF-1.8
1560 :
1561 43 : if ((m_bAutoGrowStrings &&
1562 43 : m_poFeatureDefn->GetFieldDefn(i)->GetWidth() ==
1563 88 : 0) ||
1564 1 : !m_bLegacyCreateMode)
1565 : {
1566 43 : size_t nNewSize = anCount[1] + anCount[1] / 3;
1567 :
1568 43 : CPLDebug(
1569 : "GDAL_netCDF", "Growing %s from %u to %u",
1570 43 : m_poFeatureDefn->GetFieldDefn(i)->GetNameRef(),
1571 : static_cast<unsigned>(nWidth),
1572 : static_cast<unsigned>(nNewSize));
1573 :
1574 43 : if (m_bLegacyCreateMode)
1575 : {
1576 8 : m_poDS->GrowDim(m_nLayerCDFId,
1577 8 : m_aoFieldDesc[i].nSecDimId,
1578 : nNewSize);
1579 : }
1580 : else
1581 : {
1582 35 : layerVID.nc_resize_vdim(
1583 35 : m_aoFieldDesc[i].nSecDimId, nNewSize);
1584 : }
1585 :
1586 43 : pszVal = poFeature->GetFieldAsString(i);
1587 : }
1588 : else
1589 : {
1590 1 : anCount[1] = nWidth;
1591 1 : if (!m_aoFieldDesc[i].bHasWarnedAboutTruncation)
1592 : {
1593 1 : CPLError(CE_Warning, CPLE_AppDefined,
1594 : "Content of field %s exceeded the %u "
1595 : "character "
1596 : "limit and will be truncated",
1597 1 : m_poFeatureDefn->GetFieldDefn(i)
1598 : ->GetNameRef(),
1599 : static_cast<unsigned int>(nWidth));
1600 1 : m_aoFieldDesc[i].bHasWarnedAboutTruncation =
1601 : true;
1602 : }
1603 : }
1604 : }
1605 :
1606 956 : if (m_poDS->HasInfiniteRecordDim())
1607 : {
1608 72 : status = nc_put_vara_text(m_nLayerCDFId,
1609 72 : m_aoFieldDesc[i].nVarId,
1610 : anIndex, anCount, pszVal);
1611 : }
1612 : else
1613 : {
1614 884 : m_poDS->FieldScribe.enqueue_transaction(
1615 1768 : nccfdriver::MTPtr(
1616 : new nccfdriver::OGR_SGFS_NC_CharA_Transaction(
1617 884 : m_aoFieldDesc[i].nVarId, pszVal)));
1618 : }
1619 : }
1620 963 : break;
1621 : }
1622 :
1623 34 : case NC_STRING:
1624 : {
1625 34 : const char *pszVal = poFeature->GetFieldAsString(i);
1626 :
1627 34 : if (m_poDS->HasInfiniteRecordDim())
1628 : {
1629 20 : status = nc_put_var1_string(m_nLayerCDFId,
1630 20 : m_aoFieldDesc[i].nVarId,
1631 : anIndex, &pszVal);
1632 : }
1633 :
1634 : else
1635 : {
1636 14 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1637 : new nccfdriver::OGR_SGFS_NC_String_Transaction(
1638 14 : m_aoFieldDesc[i].nVarId, pszVal)));
1639 : }
1640 :
1641 34 : break;
1642 : }
1643 :
1644 16 : case NC_BYTE:
1645 : {
1646 16 : int nVal = poFeature->GetFieldAsInteger(i);
1647 16 : signed char chVal = static_cast<signed char>(nVal);
1648 :
1649 16 : if (m_poDS->HasInfiniteRecordDim())
1650 : {
1651 16 : status = nc_put_var1_schar(m_nLayerCDFId,
1652 16 : m_aoFieldDesc[i].nVarId, anIndex,
1653 : &chVal);
1654 : }
1655 :
1656 : else
1657 : {
1658 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1659 : new nccfdriver::OGR_SGFS_NC_Byte_Transaction(
1660 0 : m_aoFieldDesc[i].nVarId, chVal)));
1661 : }
1662 16 : break;
1663 : }
1664 :
1665 4 : case NC_UBYTE:
1666 : {
1667 4 : int nVal = poFeature->GetFieldAsInteger(i);
1668 4 : unsigned char uchVal = static_cast<unsigned char>(nVal);
1669 :
1670 4 : if (m_poDS->HasInfiniteRecordDim())
1671 : {
1672 4 : status = nc_put_var1_uchar(m_nLayerCDFId,
1673 4 : m_aoFieldDesc[i].nVarId, anIndex,
1674 : &uchVal);
1675 : }
1676 :
1677 : else
1678 : {
1679 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1680 : new nccfdriver::OGR_SGFS_NC_UByte_Transaction(
1681 0 : m_aoFieldDesc[i].nVarId, uchVal)));
1682 : }
1683 4 : break;
1684 : }
1685 :
1686 15 : case NC_SHORT:
1687 : {
1688 15 : int nVal = poFeature->GetFieldAsInteger(i);
1689 15 : short sVal = static_cast<short>(nVal);
1690 :
1691 15 : if (m_poDS->HasInfiniteRecordDim())
1692 : {
1693 14 : status = nc_put_var1_short(
1694 14 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &sVal);
1695 : }
1696 : else
1697 : {
1698 1 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1699 : new nccfdriver::OGR_SGFS_NC_Short_Transaction(
1700 1 : m_aoFieldDesc[i].nVarId, sVal)));
1701 : }
1702 15 : break;
1703 : }
1704 :
1705 4 : case NC_USHORT:
1706 : {
1707 4 : int nVal = poFeature->GetFieldAsInteger(i);
1708 4 : unsigned short usVal = static_cast<unsigned short>(nVal);
1709 :
1710 4 : if (m_poDS->HasInfiniteRecordDim())
1711 : {
1712 4 : status = nc_put_var1_ushort(m_nLayerCDFId,
1713 4 : m_aoFieldDesc[i].nVarId,
1714 : anIndex, &usVal);
1715 : }
1716 : else
1717 : {
1718 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1719 : new nccfdriver::OGR_SGFS_NC_UShort_Transaction(
1720 0 : m_aoFieldDesc[i].nVarId, usVal)));
1721 : }
1722 :
1723 4 : break;
1724 : }
1725 :
1726 286 : case NC_INT:
1727 : {
1728 : int nVal;
1729 286 : if (m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDate)
1730 : {
1731 : int nYear;
1732 : int nMonth;
1733 : int nDay;
1734 : int nHour;
1735 : int nMinute;
1736 : float fSecond;
1737 : int nTZ;
1738 5 : poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
1739 : &nHour, &nMinute, &fSecond,
1740 : &nTZ);
1741 : struct tm brokendowntime;
1742 5 : brokendowntime.tm_year = nYear - 1900;
1743 5 : brokendowntime.tm_mon = nMonth - 1;
1744 5 : brokendowntime.tm_mday = nDay;
1745 5 : brokendowntime.tm_hour = 0;
1746 5 : brokendowntime.tm_min = 0;
1747 5 : brokendowntime.tm_sec = 0;
1748 5 : GIntBig nVal64 = CPLYMDHMSToUnixTime(&brokendowntime);
1749 5 : if (m_aoFieldDesc[i].bIsDays)
1750 5 : nVal64 /= 86400;
1751 5 : nVal = static_cast<int>(nVal64);
1752 : }
1753 : else
1754 : {
1755 281 : nVal = poFeature->GetFieldAsInteger(i);
1756 : }
1757 :
1758 286 : if (m_poDS->HasInfiniteRecordDim())
1759 : {
1760 70 : status = nc_put_var1_int(
1761 70 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &nVal);
1762 : }
1763 : else
1764 : {
1765 216 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1766 : new nccfdriver::OGR_SGFS_NC_Int_Transaction(
1767 216 : m_aoFieldDesc[i].nVarId, nVal)));
1768 : }
1769 :
1770 286 : break;
1771 : }
1772 :
1773 4 : case NC_UINT:
1774 : {
1775 4 : GIntBig nVal = poFeature->GetFieldAsInteger64(i);
1776 4 : unsigned int unVal = static_cast<unsigned int>(nVal);
1777 :
1778 4 : if (m_poDS->HasInfiniteRecordDim())
1779 : {
1780 : status =
1781 4 : nc_put_var1_uint(m_nLayerCDFId, m_aoFieldDesc[i].nVarId,
1782 : anIndex, &unVal);
1783 : }
1784 : else
1785 : {
1786 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1787 : new nccfdriver::OGR_SGFS_NC_UInt_Transaction(
1788 0 : m_aoFieldDesc[i].nVarId, unVal)));
1789 : }
1790 :
1791 4 : break;
1792 : }
1793 :
1794 18 : case NC_INT64:
1795 : {
1796 18 : GIntBig nVal = poFeature->GetFieldAsInteger64(i);
1797 18 : if (m_poDS->HasInfiniteRecordDim())
1798 : {
1799 18 : status = nc_put_var1_longlong(
1800 18 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &nVal);
1801 : }
1802 : else
1803 : {
1804 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1805 : new nccfdriver::OGR_SGFS_NC_Int64_Transaction(
1806 0 : m_aoFieldDesc[i].nVarId, nVal)));
1807 : }
1808 18 : break;
1809 : }
1810 :
1811 2 : case NC_UINT64:
1812 : {
1813 2 : double dfVal = poFeature->GetFieldAsDouble(i);
1814 2 : GUIntBig nVal = static_cast<GUIntBig>(dfVal);
1815 :
1816 2 : if (m_poDS->HasInfiniteRecordDim())
1817 : {
1818 2 : status = nc_put_var1_ulonglong(
1819 2 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &nVal);
1820 : }
1821 : else
1822 : {
1823 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1824 : new nccfdriver::OGR_SGFS_NC_UInt64_Transaction(
1825 0 : m_aoFieldDesc[i].nVarId, nVal)));
1826 : }
1827 :
1828 2 : break;
1829 : }
1830 :
1831 15 : case NC_FLOAT:
1832 : {
1833 15 : double dfVal = poFeature->GetFieldAsDouble(i);
1834 15 : float fVal = static_cast<float>(dfVal);
1835 :
1836 15 : if (m_poDS->HasInfiniteRecordDim())
1837 : {
1838 14 : status = nc_put_var1_float(
1839 14 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &fVal);
1840 : }
1841 : else
1842 : {
1843 1 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1844 : new nccfdriver::OGR_SGFS_NC_Float_Transaction(
1845 1 : m_aoFieldDesc[i].nVarId, fVal)));
1846 : }
1847 :
1848 15 : break;
1849 : }
1850 :
1851 664 : case NC_DOUBLE:
1852 : {
1853 : double dfVal;
1854 1326 : if (m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDate ||
1855 662 : m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
1856 : {
1857 : int nYear;
1858 : int nMonth;
1859 : int nDay;
1860 : int nHour;
1861 : int nMinute;
1862 : float fSecond;
1863 : int nTZ;
1864 16 : poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
1865 : &nHour, &nMinute, &fSecond,
1866 : &nTZ);
1867 : struct tm brokendowntime;
1868 16 : brokendowntime.tm_year = nYear - 1900;
1869 16 : brokendowntime.tm_mon = nMonth - 1;
1870 16 : brokendowntime.tm_mday = nDay;
1871 16 : brokendowntime.tm_hour = nHour;
1872 16 : brokendowntime.tm_min = nMinute;
1873 16 : brokendowntime.tm_sec = static_cast<int>(fSecond);
1874 16 : GIntBig nVal = CPLYMDHMSToUnixTime(&brokendowntime);
1875 16 : dfVal = static_cast<double>(nVal) + fmod(fSecond, 1.0f);
1876 16 : if (m_aoFieldDesc[i].bIsDays)
1877 0 : dfVal /= 86400.0;
1878 : }
1879 : else
1880 : {
1881 648 : dfVal = poFeature->GetFieldAsDouble(i);
1882 : }
1883 :
1884 664 : if (m_poDS->HasInfiniteRecordDim())
1885 : {
1886 75 : status = nc_put_var1_double(m_nLayerCDFId,
1887 75 : m_aoFieldDesc[i].nVarId,
1888 : anIndex, &dfVal);
1889 : }
1890 : else
1891 : {
1892 589 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1893 : new nccfdriver::OGR_SGFS_NC_Double_Transaction(
1894 589 : m_aoFieldDesc[i].nVarId, dfVal)));
1895 : }
1896 :
1897 664 : break;
1898 : }
1899 :
1900 0 : default:
1901 0 : break;
1902 : }
1903 :
1904 2025 : NCDF_ERR(status);
1905 2025 : if (status != NC_NOERR)
1906 : {
1907 0 : return false;
1908 : }
1909 : }
1910 :
1911 517 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1912 599 : if (wkbFlatten(m_poFeatureDefn->GetGeomType()) == wkbPoint &&
1913 79 : poGeom != nullptr &&
1914 678 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint &&
1915 79 : m_bLegacyCreateMode)
1916 : {
1917 42 : if (m_osProfileDimName.empty() || nMainDimId == m_nProfileDimID)
1918 : {
1919 22 : auto poPoint = poGeom->toPoint();
1920 22 : double dfX = poPoint->getX();
1921 22 : double dfY = poPoint->getY();
1922 :
1923 : int status;
1924 :
1925 22 : if (m_nXVarNCDFType == NC_DOUBLE)
1926 : status =
1927 22 : nc_put_var1_double(m_nLayerCDFId, m_nXVarID, anIndex, &dfX);
1928 : else
1929 : {
1930 0 : float fX = static_cast<float>(dfX);
1931 : status =
1932 0 : nc_put_var1_float(m_nLayerCDFId, m_nXVarID, anIndex, &fX);
1933 : }
1934 22 : NCDF_ERR(status);
1935 22 : if (status != NC_NOERR)
1936 : {
1937 0 : return false;
1938 : }
1939 :
1940 22 : if (m_nYVarNCDFType == NC_DOUBLE)
1941 : status =
1942 22 : nc_put_var1_double(m_nLayerCDFId, m_nYVarID, anIndex, &dfY);
1943 : else
1944 : {
1945 0 : float fY = static_cast<float>(dfY);
1946 : status =
1947 0 : nc_put_var1_float(m_nLayerCDFId, m_nYVarID, anIndex, &fY);
1948 : }
1949 22 : NCDF_ERR(status);
1950 22 : if (status != NC_NOERR)
1951 : {
1952 0 : return false;
1953 : }
1954 : }
1955 :
1956 112 : if (m_poFeatureDefn->GetGeomType() == wkbPoint25D &&
1957 70 : (m_osProfileDimName.empty() || nMainDimId == m_nRecordDimID))
1958 : {
1959 : int status;
1960 34 : double dfZ = poGeom->toPoint()->getZ();
1961 34 : if (m_nZVarNCDFType == NC_DOUBLE)
1962 : status =
1963 34 : nc_put_var1_double(m_nLayerCDFId, m_nZVarID, anIndex, &dfZ);
1964 : else
1965 : {
1966 0 : float fZ = static_cast<float>(dfZ);
1967 : status =
1968 0 : nc_put_var1_float(m_nLayerCDFId, m_nZVarID, anIndex, &fZ);
1969 : }
1970 34 : NCDF_ERR(status);
1971 34 : if (status != NC_NOERR)
1972 : {
1973 0 : return false;
1974 : }
1975 : }
1976 : }
1977 948 : else if (m_poFeatureDefn->GetGeomType() != wkbNone && m_nWKTVarID >= 0 &&
1978 948 : poGeom != nullptr && m_bLegacyCreateMode)
1979 : {
1980 21 : char *pszWKT = nullptr;
1981 21 : poGeom->exportToWkt(&pszWKT, wkbVariantIso);
1982 : int status;
1983 21 : if (m_nWKTNCDFType == NC_STRING)
1984 : {
1985 10 : const char *pszWKTConst = pszWKT;
1986 10 : status = nc_put_var1_string(m_nLayerCDFId, m_nWKTVarID, anIndex,
1987 : &pszWKTConst);
1988 : }
1989 : else
1990 : {
1991 : size_t anCount[2];
1992 11 : anCount[0] = 1;
1993 11 : anCount[1] = strlen(pszWKT);
1994 11 : if (anCount[1] > static_cast<unsigned int>(m_nWKTMaxWidth))
1995 : {
1996 4 : if (m_bAutoGrowStrings)
1997 : {
1998 3 : size_t nNewSize = anCount[1] + anCount[1] / 3;
1999 :
2000 3 : CPLDebug("GDAL_netCDF", "Growing %s from %u to %u",
2001 : m_osWKTVarName.c_str(),
2002 3 : static_cast<unsigned>(m_nWKTMaxWidth),
2003 : static_cast<unsigned>(nNewSize));
2004 3 : m_poDS->GrowDim(m_nLayerCDFId, m_nWKTMaxWidthDimId,
2005 : nNewSize);
2006 :
2007 3 : m_nWKTMaxWidth = static_cast<int>(nNewSize);
2008 :
2009 3 : status = nc_put_vara_text(m_nLayerCDFId, m_nWKTVarID,
2010 : anIndex, anCount, pszWKT);
2011 : }
2012 : else
2013 : {
2014 1 : CPLError(CE_Failure, CPLE_AppDefined,
2015 : "Cannot write geometry as WKT. Would require %d "
2016 : "characters but field width is %d",
2017 1 : static_cast<int>(anCount[1]), m_nWKTMaxWidth);
2018 1 : status = NC_NOERR;
2019 : }
2020 : }
2021 : else
2022 : {
2023 7 : status = nc_put_vara_text(m_nLayerCDFId, m_nWKTVarID, anIndex,
2024 : anCount, pszWKT);
2025 : }
2026 : }
2027 21 : CPLFree(pszWKT);
2028 21 : NCDF_ERR(status);
2029 21 : if (status != NC_NOERR)
2030 : {
2031 0 : return false;
2032 : }
2033 : }
2034 906 : else if (m_poFeatureDefn->GetGeomType() != wkbNone && m_nWKTVarID >= 0 &&
2035 3 : poGeom == nullptr && m_nWKTNCDFType == NC_STRING &&
2036 906 : m_bNCDumpCompat && m_bLegacyCreateMode)
2037 : {
2038 1 : const char *pszWKTConst = "";
2039 1 : int status = nc_put_var1_string(m_nLayerCDFId, m_nWKTVarID, anIndex,
2040 : &pszWKTConst);
2041 1 : NCDF_ERR(status);
2042 : }
2043 :
2044 : try
2045 : {
2046 : // CF 1.8 simple geometry, only
2047 517 : if (!m_bLegacyCreateMode && poGeom != nullptr)
2048 : {
2049 892 : nccfdriver::SGeometry_Feature featWithMetaData(*poFeature);
2050 :
2051 : // Check if ready to dump buffer to LOG
2052 446 : if (m_poDS->bufManager.isOverQuota())
2053 : {
2054 180 : m_poDS->SGLogPendingTransaction();
2055 : }
2056 :
2057 : // Finally, "write" the feature
2058 446 : m_layerSGDefn.writeSGeometryFeature(featWithMetaData);
2059 : }
2060 : }
2061 :
2062 0 : catch (nccfdriver::SG_Exception &sge)
2063 : {
2064 0 : CPLError(CE_Failure, CPLE_AppDefined,
2065 : "An error occurred while attempting to write a feature to the "
2066 : "target netCDF file.\n%s",
2067 0 : sge.get_err_msg());
2068 0 : return false;
2069 : }
2070 :
2071 517 : return true;
2072 : }
2073 :
2074 : /************************************************************************/
2075 : /* AddField() */
2076 : /************************************************************************/
2077 :
2078 633 : bool netCDFLayer::AddField(int nVarID)
2079 : {
2080 633 : if (nVarID == m_nWKTVarID)
2081 9 : return false;
2082 :
2083 : char szName[NC_MAX_NAME + 1];
2084 624 : szName[0] = '\0';
2085 624 : CPL_IGNORE_RET_VAL(nc_inq_varname(m_nLayerCDFId, nVarID, szName));
2086 :
2087 624 : nc_type vartype = NC_NAT;
2088 624 : nc_inq_vartype(m_nLayerCDFId, nVarID, &vartype);
2089 :
2090 624 : OGRFieldType eType = OFTString;
2091 624 : OGRFieldSubType eSubType = OFSTNone;
2092 624 : int nWidth = 0;
2093 :
2094 : NCDFNoDataUnion nodata;
2095 624 : memset(&nodata, 0, sizeof(nodata));
2096 624 : int nDimCount = 1;
2097 624 : nc_inq_varndims(m_nLayerCDFId, nVarID, &nDimCount);
2098 624 : int anDimIds[2] = {-1, -1};
2099 624 : if ((vartype == NC_CHAR && nDimCount <= 2) ||
2100 482 : (vartype != NC_CHAR && nDimCount == 1))
2101 : {
2102 624 : nc_inq_vardimid(m_nLayerCDFId, nVarID, anDimIds);
2103 : }
2104 : else
2105 : {
2106 0 : return false;
2107 : }
2108 :
2109 624 : switch (vartype)
2110 : {
2111 47 : case NC_BYTE:
2112 : {
2113 47 : eType = OFTInteger;
2114 47 : char *pszValue = nullptr;
2115 47 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2116 11 : nodata.chVal = static_cast<signed char>(atoi(pszValue));
2117 : else
2118 36 : nodata.chVal = NC_FILL_BYTE;
2119 47 : CPLFree(pszValue);
2120 47 : break;
2121 : }
2122 :
2123 12 : case NC_UBYTE:
2124 : {
2125 12 : eType = OFTInteger;
2126 12 : char *pszValue = nullptr;
2127 12 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2128 6 : nodata.uchVal = static_cast<unsigned char>(atoi(pszValue));
2129 : else
2130 6 : nodata.uchVal = NC_FILL_UBYTE;
2131 12 : CPLFree(pszValue);
2132 12 : break;
2133 : }
2134 :
2135 142 : case NC_CHAR:
2136 : {
2137 142 : eType = OFTString;
2138 142 : if (nDimCount == 1)
2139 : {
2140 9 : nWidth = 1;
2141 : }
2142 133 : else if (nDimCount == 2)
2143 : {
2144 133 : size_t nDimLen = 0;
2145 133 : nc_inq_dimlen(m_nLayerCDFId, anDimIds[1], &nDimLen);
2146 133 : nWidth = static_cast<int>(nDimLen);
2147 : }
2148 142 : break;
2149 : }
2150 :
2151 20 : case NC_STRING:
2152 : {
2153 20 : eType = OFTString;
2154 20 : break;
2155 : }
2156 :
2157 38 : case NC_SHORT:
2158 : {
2159 38 : eType = OFTInteger;
2160 38 : eSubType = OFSTInt16;
2161 38 : char *pszValue = nullptr;
2162 38 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2163 11 : nodata.sVal = static_cast<short>(atoi(pszValue));
2164 : else
2165 27 : nodata.sVal = NC_FILL_SHORT;
2166 38 : CPLFree(pszValue);
2167 38 : break;
2168 : }
2169 :
2170 12 : case NC_USHORT:
2171 : {
2172 12 : eType = OFTInteger;
2173 12 : char *pszValue = nullptr;
2174 12 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2175 6 : nodata.usVal = static_cast<unsigned short>(atoi(pszValue));
2176 : else
2177 6 : nodata.usVal = NC_FILL_USHORT;
2178 12 : CPLFree(pszValue);
2179 12 : break;
2180 : }
2181 :
2182 115 : case NC_INT:
2183 : {
2184 115 : eType = OFTInteger;
2185 115 : char *pszValue = nullptr;
2186 115 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2187 13 : nodata.nVal = atoi(pszValue);
2188 : else
2189 102 : nodata.nVal = NC_FILL_INT;
2190 115 : CPLFree(pszValue);
2191 115 : break;
2192 : }
2193 :
2194 12 : case NC_UINT:
2195 : {
2196 12 : eType = OFTInteger64;
2197 12 : char *pszValue = nullptr;
2198 12 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2199 6 : nodata.unVal =
2200 6 : static_cast<unsigned int>(CPLAtoGIntBig(pszValue));
2201 : else
2202 6 : nodata.unVal = NC_FILL_UINT;
2203 12 : CPLFree(pszValue);
2204 12 : break;
2205 : }
2206 :
2207 25 : case NC_INT64:
2208 : {
2209 25 : eType = OFTInteger64;
2210 25 : char *pszValue = nullptr;
2211 25 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2212 6 : nodata.nVal64 = CPLAtoGIntBig(pszValue);
2213 : else
2214 19 : nodata.nVal64 = NC_FILL_INT64;
2215 25 : CPLFree(pszValue);
2216 25 : break;
2217 : }
2218 :
2219 12 : case NC_UINT64:
2220 : {
2221 12 : eType = OFTReal;
2222 12 : char *pszValue = nullptr;
2223 12 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2224 : {
2225 6 : nodata.unVal64 = 0;
2226 126 : for (int i = 0; pszValue[i] != '\0'; i++)
2227 : {
2228 120 : nodata.unVal64 = nodata.unVal64 * 10 + (pszValue[i] - '0');
2229 : }
2230 : }
2231 : else
2232 6 : nodata.unVal64 = NC_FILL_UINT64;
2233 12 : CPLFree(pszValue);
2234 12 : break;
2235 : }
2236 :
2237 38 : case NC_FLOAT:
2238 : {
2239 38 : eType = OFTReal;
2240 38 : eSubType = OFSTFloat32;
2241 : double dfValue;
2242 38 : if (GetFillValue(nVarID, &dfValue) == CE_None)
2243 11 : nodata.fVal = static_cast<float>(dfValue);
2244 : else
2245 27 : nodata.fVal = NC_FILL_FLOAT;
2246 38 : break;
2247 : }
2248 :
2249 151 : case NC_DOUBLE:
2250 : {
2251 151 : eType = OFTReal;
2252 : double dfValue;
2253 151 : if (GetFillValue(nVarID, &dfValue) == CE_None)
2254 31 : nodata.dfVal = dfValue;
2255 : else
2256 120 : nodata.dfVal = NC_FILL_DOUBLE;
2257 151 : break;
2258 : }
2259 :
2260 0 : default:
2261 : {
2262 0 : CPLDebug("GDAL_netCDF",
2263 : "Variable %s has type %d, which is unhandled", szName,
2264 : vartype);
2265 0 : return false;
2266 : }
2267 : }
2268 :
2269 624 : bool bIsDays = false;
2270 :
2271 624 : char *pszValue = nullptr;
2272 624 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_type", &pszValue) ==
2273 : CE_None)
2274 : {
2275 434 : if ((eType == OFTInteger || eType == OFTReal) &&
2276 281 : EQUAL(pszValue, "Date"))
2277 : {
2278 17 : eType = OFTDate;
2279 : // cppcheck-suppress knownConditionTrueFalse
2280 17 : bIsDays = (eType == OFTInteger);
2281 : }
2282 417 : else if ((eType == OFTInteger || eType == OFTReal) &&
2283 264 : EQUAL(pszValue, "DateTime"))
2284 23 : eType = OFTDateTime;
2285 394 : else if (eType == OFTReal && EQUAL(pszValue, "Integer64"))
2286 20 : eType = OFTInteger64;
2287 374 : else if (eType == OFTInteger && EQUAL(pszValue, "Integer(Boolean)"))
2288 34 : eSubType = OFSTBoolean;
2289 : }
2290 624 : CPLFree(pszValue);
2291 :
2292 624 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "units", &pszValue) == CE_None)
2293 : {
2294 86 : if ((eType == OFTInteger || eType == OFTReal || eType == OFTDate) &&
2295 40 : (EQUAL(pszValue, "seconds since 1970-1-1 0:0:0") ||
2296 16 : EQUAL(pszValue, "seconds since 1970-01-01 00:00:00")))
2297 : {
2298 24 : if (eType != OFTDate)
2299 13 : eType = OFTDateTime;
2300 24 : bIsDays = false;
2301 : }
2302 62 : else if ((eType == OFTInteger || eType == OFTReal ||
2303 16 : eType == OFTDate) &&
2304 16 : (EQUAL(pszValue, "days since 1970-1-1") ||
2305 9 : EQUAL(pszValue, "days since 1970-01-01")))
2306 : {
2307 7 : eType = OFTDate;
2308 7 : bIsDays = true;
2309 : }
2310 : }
2311 624 : CPLFree(pszValue);
2312 :
2313 624 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_name", &pszValue) ==
2314 : CE_None)
2315 : {
2316 429 : snprintf(szName, sizeof(szName), "%s", pszValue);
2317 : }
2318 624 : CPLFree(pszValue);
2319 :
2320 624 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_width", &pszValue) ==
2321 : CE_None)
2322 : {
2323 151 : nWidth = atoi(pszValue);
2324 : }
2325 624 : CPLFree(pszValue);
2326 :
2327 624 : int nPrecision = 0;
2328 624 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_precision", &pszValue) ==
2329 : CE_None)
2330 : {
2331 5 : nPrecision = atoi(pszValue);
2332 : }
2333 624 : CPLFree(pszValue);
2334 :
2335 624 : OGRFieldDefn oFieldDefn(szName, eType);
2336 624 : oFieldDefn.SetSubType(eSubType);
2337 624 : oFieldDefn.SetWidth(nWidth);
2338 624 : oFieldDefn.SetPrecision(nPrecision);
2339 :
2340 1078 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, CF_LNG_NAME, &pszValue) == CE_None &&
2341 1078 : pszValue != std::string("Field ") + szName)
2342 : {
2343 7 : oFieldDefn.SetComment(pszValue);
2344 : }
2345 624 : CPLFree(pszValue);
2346 :
2347 629 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, CF_STD_NAME, &pszValue) == CE_None &&
2348 5 : strcmp(pszValue, szName) != 0)
2349 : {
2350 3 : oFieldDefn.SetAlternativeName(pszValue);
2351 : }
2352 624 : CPLFree(pszValue);
2353 :
2354 : FieldDesc fieldDesc;
2355 624 : fieldDesc.uNoData = nodata;
2356 624 : fieldDesc.nType = vartype;
2357 624 : fieldDesc.nVarId = nVarID;
2358 624 : fieldDesc.nDimCount = nDimCount;
2359 624 : fieldDesc.nMainDimId = anDimIds[0];
2360 624 : fieldDesc.nSecDimId = anDimIds[1];
2361 624 : fieldDesc.bHasWarnedAboutTruncation = false;
2362 624 : fieldDesc.bIsDays = bIsDays;
2363 624 : m_aoFieldDesc.push_back(fieldDesc);
2364 :
2365 624 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
2366 :
2367 624 : return true;
2368 : }
2369 :
2370 : /************************************************************************/
2371 : /* CreateField() */
2372 : /************************************************************************/
2373 :
2374 182 : OGRErr netCDFLayer::CreateField(const OGRFieldDefn *poFieldDefn,
2375 : int /* bApproxOK */)
2376 : {
2377 182 : int nSecDimId = -1;
2378 182 : int nVarID = -1;
2379 : int status;
2380 :
2381 182 : const netCDFWriterConfigField *poConfig = nullptr;
2382 182 : if (m_poDS->oWriterConfig.m_bIsValid)
2383 : {
2384 8 : std::map<CPLString, netCDFWriterConfigField>::const_iterator oIter;
2385 12 : if (m_poLayerConfig != nullptr &&
2386 4 : (oIter =
2387 12 : m_poLayerConfig->m_oFields.find(poFieldDefn->GetNameRef())) !=
2388 12 : m_poLayerConfig->m_oFields.end())
2389 : {
2390 1 : poConfig = &(oIter->second);
2391 : }
2392 7 : else if ((oIter = m_poDS->oWriterConfig.m_oFields.find(
2393 14 : poFieldDefn->GetNameRef())) !=
2394 14 : m_poDS->oWriterConfig.m_oFields.end())
2395 : {
2396 2 : poConfig = &(oIter->second);
2397 : }
2398 : }
2399 :
2400 182 : if (!m_osProfileDimName.empty() &&
2401 185 : EQUAL(poFieldDefn->GetNameRef(), m_osProfileDimName) &&
2402 3 : poFieldDefn->GetType() == OFTInteger)
2403 : {
2404 : FieldDesc fieldDesc;
2405 3 : fieldDesc.uNoData.nVal = NC_FILL_INT;
2406 3 : fieldDesc.nType = NC_INT;
2407 3 : fieldDesc.nVarId = m_nProfileVarID;
2408 3 : fieldDesc.nDimCount = 1;
2409 3 : fieldDesc.nMainDimId = m_nProfileDimID;
2410 3 : fieldDesc.nSecDimId = -1;
2411 3 : fieldDesc.bHasWarnedAboutTruncation = false;
2412 3 : fieldDesc.bIsDays = false;
2413 3 : m_aoFieldDesc.push_back(fieldDesc);
2414 3 : m_poFeatureDefn->AddFieldDefn(poFieldDefn);
2415 3 : return OGRERR_NONE;
2416 : }
2417 :
2418 179 : m_poDS->SetDefineMode(true);
2419 :
2420 : // Try to use the field name as variable name, but detects conflict first
2421 : CPLString osVarName(poConfig != nullptr
2422 3 : ? poConfig->m_osNetCDFName
2423 358 : : CPLString(poFieldDefn->GetNameRef()));
2424 :
2425 179 : if (!m_bLegacyCreateMode && m_bWriteGDALTags)
2426 : {
2427 : // To help avoid naming conflicts, append the layer name as a prefix
2428 77 : const char *prefix = this->GetName();
2429 77 : const char *fprefix = "_field_";
2430 :
2431 77 : osVarName = CPLString(prefix) + CPLString(fprefix) + osVarName;
2432 : }
2433 :
2434 179 : bool vCDFHas = false;
2435 179 : if (!m_bLegacyCreateMode)
2436 : {
2437 77 : vCDFHas = layerVID.virtualVarNameDefined(osVarName);
2438 : }
2439 :
2440 : // Also check the real
2441 179 : status = nc_inq_varid(m_nLayerCDFId, osVarName, &nVarID);
2442 179 : if (status == NC_NOERR || vCDFHas)
2443 : {
2444 3 : for (int i = 1; i <= 100; i++)
2445 : {
2446 3 : osVarName = CPLSPrintf("%s%d", poFieldDefn->GetNameRef(), i);
2447 3 : status = nc_inq_varid(m_nLayerCDFId, osVarName, &nVarID);
2448 3 : if (!m_bLegacyCreateMode)
2449 0 : vCDFHas = layerVID.virtualVarNameDefined(osVarName);
2450 3 : if (status != NC_NOERR && !vCDFHas)
2451 3 : break;
2452 : }
2453 :
2454 3 : CPLDebug("netCDF", "Field %s is written in variable %s",
2455 : poFieldDefn->GetNameRef(), osVarName.c_str());
2456 : }
2457 :
2458 179 : const char *pszVarName = osVarName.c_str();
2459 :
2460 : NCDFNoDataUnion nodata;
2461 179 : memset(&nodata, 0, sizeof(nodata));
2462 :
2463 179 : const OGRFieldType eType = poFieldDefn->GetType();
2464 179 : const OGRFieldSubType eSubType = poFieldDefn->GetSubType();
2465 179 : nc_type nType = NC_NAT;
2466 179 : int nDimCount = 1;
2467 :
2468 : // Find which is the dimension that this variable should be indexed against
2469 179 : int nMainDimId = m_nRecordDimID;
2470 179 : if (!m_osProfileVariables.empty())
2471 : {
2472 : char **papszTokens =
2473 3 : CSLTokenizeString2(m_osProfileVariables, ",", CSLT_HONOURSTRINGS);
2474 3 : if (CSLFindString(papszTokens, poFieldDefn->GetNameRef()) >= 0)
2475 1 : nMainDimId = m_nProfileDimID;
2476 3 : CSLDestroy(papszTokens);
2477 : }
2478 181 : if (poConfig != nullptr && !poConfig->m_osMainDim.empty() &&
2479 2 : m_bLegacyCreateMode)
2480 : {
2481 2 : int ndims = 0;
2482 2 : status = nc_inq_ndims(m_nLayerCDFId, &ndims);
2483 2 : NCDF_ERR(status);
2484 2 : bool bFound = false;
2485 4 : for (int idim = 0; idim < ndims; idim++)
2486 : {
2487 : char szDimName[NC_MAX_NAME + 1];
2488 3 : szDimName[0] = 0;
2489 3 : status = nc_inq_dimname(m_poDS->cdfid, idim, szDimName);
2490 3 : NCDF_ERR(status);
2491 3 : if (strcmp(poConfig->m_osMainDim, szDimName) == 0)
2492 : {
2493 1 : nMainDimId = idim;
2494 1 : bFound = true;
2495 1 : break;
2496 : }
2497 : }
2498 2 : if (!bFound)
2499 : {
2500 1 : CPLError(CE_Failure, CPLE_AppDefined,
2501 : "Dimension '%s' does not exist",
2502 : poConfig->m_osMainDim.c_str());
2503 : }
2504 : }
2505 :
2506 : try
2507 : {
2508 179 : switch (eType)
2509 : {
2510 78 : case OFTString:
2511 : case OFTStringList:
2512 : case OFTIntegerList:
2513 : case OFTRealList:
2514 : {
2515 78 : if (poFieldDefn->GetWidth() == 1)
2516 : {
2517 2 : nType = NC_CHAR;
2518 2 : nVarID =
2519 2 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2520 : }
2521 76 : else if (m_poDS->eFormat == NCDF_FORMAT_NC4 &&
2522 13 : m_bUseStringInNC4)
2523 : {
2524 9 : nType = NC_STRING;
2525 9 : nVarID =
2526 9 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2527 : }
2528 : else
2529 : {
2530 67 : if (poFieldDefn->GetWidth() == 0 && !m_bAutoGrowStrings)
2531 : {
2532 1 : if (m_nDefaultMaxWidthDimId < 0)
2533 : {
2534 1 : m_nDefaultMaxWidthDimId = layerVID.nc_def_vdim(
2535 1 : "string_default_max_width", m_nDefaultWidth);
2536 : }
2537 :
2538 1 : nSecDimId = m_nDefaultMaxWidthDimId;
2539 : }
2540 : else
2541 : {
2542 66 : size_t nDim = poFieldDefn->GetWidth() == 0
2543 72 : ? m_nDefaultWidth
2544 6 : : poFieldDefn->GetWidth();
2545 :
2546 : std::string ndimname =
2547 132 : std::string(pszVarName) + std::string("_max_width");
2548 : nSecDimId =
2549 66 : layerVID.nc_def_vdim(ndimname.c_str(), nDim);
2550 : }
2551 :
2552 67 : nDimCount = 2;
2553 67 : int anDims[2] = {nMainDimId, nSecDimId};
2554 67 : nType = NC_CHAR;
2555 67 : nVarID = layerVID.nc_def_vvar(pszVarName, nType, 2, anDims);
2556 : }
2557 :
2558 78 : break;
2559 : }
2560 :
2561 51 : case OFTInteger:
2562 : {
2563 96 : nType = eSubType == OFSTBoolean ? NC_BYTE
2564 45 : : (eSubType == OFSTInt16) ? NC_SHORT
2565 : : NC_INT;
2566 :
2567 51 : if (nType == NC_BYTE)
2568 6 : nodata.chVal = NC_FILL_BYTE;
2569 45 : else if (nType == NC_SHORT)
2570 7 : nodata.sVal = NC_FILL_SHORT;
2571 38 : else if (nType == NC_INT)
2572 38 : nodata.nVal = NC_FILL_INT;
2573 :
2574 51 : nVarID =
2575 51 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2576 :
2577 51 : if (eSubType == OFSTBoolean)
2578 : {
2579 6 : if (m_bLegacyCreateMode)
2580 : {
2581 6 : signed char anRange[2] = {0, 1};
2582 6 : nc_put_att_schar(m_nLayerCDFId, nVarID, "valid_range",
2583 : NC_BYTE, 2, anRange);
2584 : }
2585 : }
2586 :
2587 51 : break;
2588 : }
2589 :
2590 11 : case OFTInteger64:
2591 : {
2592 11 : nType = NC_DOUBLE;
2593 11 : nodata.dfVal = NC_FILL_DOUBLE;
2594 11 : if (m_poDS->eFormat == NCDF_FORMAT_NC4)
2595 : {
2596 5 : nType = NC_INT64;
2597 5 : nodata.nVal64 = NC_FILL_INT64;
2598 : }
2599 :
2600 11 : nVarID =
2601 11 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2602 11 : break;
2603 : }
2604 :
2605 30 : case OFTReal:
2606 : {
2607 30 : nType = (eSubType == OFSTFloat32) ? NC_FLOAT : NC_DOUBLE;
2608 30 : if (eSubType == OFSTFloat32)
2609 7 : nodata.fVal = NC_FILL_FLOAT;
2610 : else
2611 23 : nodata.dfVal = NC_FILL_DOUBLE;
2612 :
2613 30 : nVarID =
2614 30 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2615 30 : break;
2616 : }
2617 :
2618 3 : case OFTDate:
2619 : {
2620 3 : nType = NC_INT;
2621 :
2622 3 : nVarID =
2623 3 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2624 3 : nodata.nVal = NC_FILL_INT;
2625 :
2626 3 : layerVID.nc_put_vatt_text(nVarID, CF_UNITS,
2627 : "days since 1970-1-1");
2628 3 : break;
2629 : }
2630 :
2631 6 : case OFTDateTime:
2632 : {
2633 6 : nType = NC_DOUBLE;
2634 6 : nVarID =
2635 6 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2636 :
2637 6 : nodata.dfVal = NC_FILL_DOUBLE;
2638 :
2639 6 : layerVID.nc_put_vatt_text(nVarID, CF_UNITS,
2640 : "seconds since 1970-1-1 0:0:0");
2641 6 : break;
2642 : }
2643 :
2644 0 : default:
2645 0 : return OGRERR_FAILURE;
2646 : }
2647 :
2648 : FieldDesc fieldDesc;
2649 179 : fieldDesc.uNoData = nodata;
2650 179 : fieldDesc.nType = nType;
2651 179 : fieldDesc.nVarId = nVarID;
2652 179 : fieldDesc.nDimCount = nDimCount;
2653 179 : fieldDesc.nMainDimId = nMainDimId;
2654 179 : fieldDesc.nSecDimId = nSecDimId;
2655 179 : fieldDesc.bHasWarnedAboutTruncation = false;
2656 179 : fieldDesc.bIsDays = (eType == OFTDate);
2657 179 : m_aoFieldDesc.push_back(fieldDesc);
2658 :
2659 : // If we have an alternative name that is compatible of the
2660 : // standard_name attribute, then put it in it.
2661 : // http://cfconventions.org/Data/cf-standard-names/docs/guidelines.html:
2662 : // "Standard names consist of lower-letters, digits and underscores,
2663 : // and begin with a letter. Upper case is not used."
2664 : // Otherwise use it as the long_name, unless we have a comment
2665 179 : bool bAlternativeNameCompatibleOfStandardName = false;
2666 179 : const char *pszAlternativeName = poFieldDefn->GetAlternativeNameRef();
2667 179 : if (pszAlternativeName[0] >= 'a' && pszAlternativeName[0] <= 'z')
2668 : {
2669 2 : bAlternativeNameCompatibleOfStandardName = true;
2670 15 : for (const char *chPtr = pszAlternativeName; *chPtr; ++chPtr)
2671 : {
2672 14 : if (!((*chPtr >= 'a' && *chPtr <= 'z') || *chPtr == '_' ||
2673 1 : (*chPtr >= '0' && *chPtr <= '9')))
2674 : {
2675 1 : bAlternativeNameCompatibleOfStandardName = false;
2676 1 : break;
2677 : }
2678 : }
2679 : }
2680 :
2681 179 : if (!poFieldDefn->GetComment().empty())
2682 : {
2683 2 : layerVID.nc_put_vatt_text(nVarID, CF_LNG_NAME,
2684 2 : poFieldDefn->GetComment().c_str());
2685 : }
2686 177 : else if (pszAlternativeName[0] &&
2687 1 : !bAlternativeNameCompatibleOfStandardName)
2688 : {
2689 1 : layerVID.nc_put_vatt_text(nVarID, CF_LNG_NAME, pszAlternativeName);
2690 : }
2691 : else
2692 : {
2693 : const char *pszLongName =
2694 176 : CPLSPrintf("Field %s", poFieldDefn->GetNameRef());
2695 :
2696 176 : layerVID.nc_put_vatt_text(nVarID, CF_LNG_NAME, pszLongName);
2697 : }
2698 :
2699 179 : if (bAlternativeNameCompatibleOfStandardName)
2700 : {
2701 1 : layerVID.nc_put_vatt_text(nVarID, CF_STD_NAME, pszAlternativeName);
2702 : }
2703 :
2704 179 : if (!m_bLegacyCreateMode)
2705 : {
2706 154 : std::string ct_name(m_layerSGDefn.get_containerName());
2707 77 : layerVID.nc_put_vatt_text(nVarID, CF_SG_GEOMETRY, ct_name.c_str());
2708 : }
2709 :
2710 179 : if (m_bWriteGDALTags)
2711 : {
2712 156 : layerVID.nc_put_vatt_text(nVarID, "ogr_field_name",
2713 : poFieldDefn->GetNameRef());
2714 :
2715 156 : const char *pszType = OGRFieldDefn::GetFieldTypeName(eType);
2716 156 : if (eSubType != OFSTNone)
2717 : {
2718 : pszType =
2719 14 : CPLSPrintf("%s(%s)", pszType,
2720 : OGRFieldDefn::GetFieldSubTypeName(eSubType));
2721 : }
2722 :
2723 156 : layerVID.nc_put_vatt_text(nVarID, "ogr_field_type", pszType);
2724 :
2725 156 : const int nWidth = poFieldDefn->GetWidth();
2726 156 : if (nWidth || nType == NC_CHAR)
2727 : {
2728 74 : layerVID.nc_put_vatt_int(nVarID, "ogr_field_width", &nWidth);
2729 :
2730 74 : const int nPrecision = poFieldDefn->GetPrecision();
2731 74 : if (nPrecision)
2732 : {
2733 3 : layerVID.nc_put_vatt_int(nVarID, "ogr_field_precision",
2734 : &nPrecision);
2735 : }
2736 : }
2737 : }
2738 :
2739 : // nc_put_att_text(m_nLayerCDFId, nVarID, CF_UNITS,
2740 : // strlen("none"), "none");
2741 :
2742 179 : if (!m_osGridMapping.empty() && nMainDimId == m_nRecordDimID)
2743 : {
2744 138 : layerVID.nc_put_vatt_text(nVarID, CF_GRD_MAPPING,
2745 : m_osGridMapping.c_str());
2746 : }
2747 :
2748 179 : if (!m_osCoordinatesValue.empty() && nMainDimId == m_nRecordDimID)
2749 : {
2750 85 : layerVID.nc_put_vatt_text(nVarID, CF_COORDINATES,
2751 : m_osCoordinatesValue.c_str());
2752 : }
2753 :
2754 179 : if (poConfig != nullptr)
2755 : {
2756 3 : netCDFWriteAttributesFromConf(m_nLayerCDFId, nVarID,
2757 3 : poConfig->m_aoAttributes);
2758 : }
2759 : }
2760 :
2761 0 : catch (nccfdriver::SG_Exception &e)
2762 : {
2763 0 : CPLError(CE_Failure, CPLE_FileIO, "%s", e.get_err_msg());
2764 0 : return OGRERR_FAILURE;
2765 : }
2766 :
2767 179 : m_poFeatureDefn->AddFieldDefn(poFieldDefn);
2768 179 : return OGRERR_NONE;
2769 : }
2770 :
2771 : /************************************************************************/
2772 : /* GetFeatureCount() */
2773 : /************************************************************************/
2774 :
2775 52 : GIntBig netCDFLayer::GetFeatureCount(int bForce)
2776 : {
2777 52 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
2778 : {
2779 46 : if (!m_bLegacyCreateMode)
2780 : {
2781 32 : return m_simpleGeometryReader->get_geometry_count();
2782 : }
2783 :
2784 : size_t nDimLen;
2785 14 : nc_inq_dimlen(m_nLayerCDFId, m_nRecordDimID, &nDimLen);
2786 14 : return static_cast<GIntBig>(nDimLen);
2787 : }
2788 6 : return OGRLayer::GetFeatureCount(bForce);
2789 : }
2790 :
2791 : /************************************************************************/
2792 : /* TestCapability() */
2793 : /************************************************************************/
2794 :
2795 245 : int netCDFLayer::TestCapability(const char *pszCap)
2796 : {
2797 245 : if (EQUAL(pszCap, OLCSequentialWrite))
2798 1 : return m_poDS->GetAccess() == GA_Update;
2799 244 : if (EQUAL(pszCap, OLCCreateField))
2800 2 : return m_poDS->GetAccess() == GA_Update;
2801 242 : if (EQUAL(pszCap, OLCFastFeatureCount))
2802 0 : return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
2803 242 : if (EQUAL(pszCap, OLCZGeometries))
2804 3 : return true;
2805 239 : return false;
2806 : }
2807 :
2808 : /************************************************************************/
2809 : /* GetDataset() */
2810 : /************************************************************************/
2811 :
2812 2 : GDALDataset *netCDFLayer::GetDataset()
2813 : {
2814 2 : return m_poDS;
2815 : }
|