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 222 : netCDFLayer::netCDFLayer(netCDFDataset *poDS, int nLayerCDFId,
24 : const char *pszName, OGRwkbGeometryType eGeomType,
25 222 : OGRSpatialReference *poSRS)
26 : : m_poDS(poDS), m_nLayerCDFId(nLayerCDFId),
27 222 : 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 222 : layerVID_alloc(poDS->cdfid == m_nLayerCDFId
37 : ? nullptr
38 68 : : new nccfdriver::netCDFVID(poDS, m_nLayerCDFId)),
39 222 : layerVID(layerVID_alloc.get() == nullptr ? poDS->vcdf : *layerVID_alloc),
40 : m_SGeometryFeatInd(0), m_poLayerConfig(nullptr),
41 444 : m_layerSGDefn(poDS->cdfid, nccfdriver::OGRtoRaw(eGeomType), poDS->vcdf,
42 734 : poDS->GeometryScribe)
43 : {
44 222 : m_uXVarNoData.nVal64 = 0;
45 222 : m_uYVarNoData.nVal64 = 0;
46 222 : m_uZVarNoData.nVal64 = 0;
47 222 : m_poFeatureDefn->SetGeomType(eGeomType);
48 222 : if (eGeomType != wkbNone)
49 186 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
50 222 : m_poFeatureDefn->Reference();
51 222 : SetDescription(pszName);
52 222 : }
53 :
54 : /************************************************************************/
55 : /* ~netCDFLayer() */
56 : /************************************************************************/
57 :
58 444 : netCDFLayer::~netCDFLayer()
59 : {
60 222 : m_poFeatureDefn->Release();
61 444 : }
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 58 : bool netCDFLayer::Create(CSLConstList papszOptions,
118 : const netCDFWriterConfigLayer *poLayerConfig)
119 : {
120 58 : m_poDS->SetDefineMode(true);
121 :
122 58 : if (m_poDS->bSGSupport)
123 : {
124 41 : m_bLegacyCreateMode = false;
125 : }
126 :
127 : else
128 : {
129 17 : m_bLegacyCreateMode = true;
130 : }
131 :
132 58 : long long newbufsize = 0;
133 : const char *memorySizeLimitation =
134 58 : CSLFetchNameValueDef(papszOptions, "BUFFER_SIZE", "");
135 116 : std::string memorySizeLimitation_s = std::string(memorySizeLimitation);
136 58 : if (memorySizeLimitation_s != "")
137 : {
138 4 : newbufsize = strtoll(memorySizeLimitation, nullptr, 10);
139 : }
140 :
141 : const char *singleDatumMode =
142 58 : CSLFetchNameValueDef(papszOptions, "GROUPLESS_WRITE_BACK", "NO");
143 :
144 58 : if (CPLTestBool(singleDatumMode))
145 : {
146 1 : m_poDS->GeometryScribe.setSingleDatumMode(true);
147 1 : m_poDS->FieldScribe.setSingleDatumMode(true);
148 : }
149 :
150 58 : if (m_bLegacyCreateMode)
151 : {
152 : m_osRecordDimName = CSLFetchNameValueDef(
153 17 : papszOptions, "RECORD_DIM_NAME", m_osRecordDimName.c_str());
154 : }
155 :
156 58 : m_bAutoGrowStrings =
157 58 : CPL_TO_BOOL(CSLFetchBoolean(papszOptions, "AUTOGROW_STRINGS", TRUE));
158 58 : m_nDefaultWidth = atoi(
159 : CSLFetchNameValueDef(papszOptions, "STRING_DEFAULT_WIDTH",
160 58 : CPLSPrintf("%d", m_bAutoGrowStrings ? 10 : 80)));
161 58 : m_bWriteGDALTags =
162 58 : m_poDS->aosCreationOptions.FetchBool("WRITE_GDAL_TAGS", true);
163 58 : m_bUseStringInNC4 =
164 58 : CPL_TO_BOOL(CSLFetchBoolean(papszOptions, "USE_STRING_IN_NC4", TRUE));
165 58 : m_bNCDumpCompat =
166 58 : CPL_TO_BOOL(CSLFetchBoolean(papszOptions, "NCDUMP_COMPAT", TRUE));
167 :
168 116 : std::vector<std::pair<CPLString, int>> aoAutoVariables;
169 :
170 : const char *pszFeatureType =
171 58 : CSLFetchNameValue(papszOptions, "FEATURE_TYPE");
172 58 : 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 58 : const OGRSpatialReference *poSRS = nullptr;
239 58 : if (m_poFeatureDefn->GetGeomFieldCount())
240 56 : poSRS = m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef();
241 :
242 : int status;
243 58 : 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 58 : 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 58 : if (m_poDS->oWriterConfig.m_bIsValid)
478 : {
479 3 : m_poLayerConfig = poLayerConfig;
480 :
481 3 : netCDFWriteAttributesFromConf(m_nLayerCDFId, NC_GLOBAL,
482 3 : m_poDS->oWriterConfig.m_aoAttributes);
483 3 : if (poLayerConfig != nullptr)
484 : {
485 1 : netCDFWriteAttributesFromConf(m_nLayerCDFId, NC_GLOBAL,
486 1 : poLayerConfig->m_aoAttributes);
487 : }
488 :
489 11 : 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 58 : if (!m_bLegacyCreateMode)
518 : {
519 : // Write a geometry container
520 : OGRwkbGeometryType geometryContainerType =
521 41 : m_poFeatureDefn->GetGeomType();
522 82 : std::vector<std::string> coordNames;
523 : std::string strXVarName =
524 123 : std::string(this->GetName()).append("_coordX");
525 : std::string strYVarName =
526 123 : std::string(this->GetName()).append("_coordY");
527 41 : coordNames.push_back(std::move(strXVarName));
528 41 : coordNames.push_back(std::move(strYVarName));
529 :
530 41 : if (nccfdriver::OGRHasZandSupported(geometryContainerType))
531 : {
532 : std::string strZVarName =
533 45 : std::string(this->GetName()).append("_coordZ");
534 15 : coordNames.push_back(std::move(strZVarName));
535 : }
536 :
537 41 : if (m_layerSGDefn.getWritableType() == nccfdriver::NONE)
538 : {
539 0 : throw nccfdriver::SG_Exception_BadFeature();
540 : }
541 :
542 123 : int writableSGContVarID = nccfdriver::write_Geometry_Container(
543 82 : m_poDS->cdfid, this->GetName(), m_layerSGDefn.getWritableType(),
544 : coordNames);
545 41 : m_layerSGDefn.initializeNewContainer(writableSGContVarID);
546 :
547 41 : 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 41 : if (m_layerSGDefn.getWritableType() == nccfdriver::POINT)
555 : {
556 9 : m_nRecordDimID = m_layerSGDefn.get_node_coord_dimID();
557 18 : m_osRecordDimName = std::string(this->GetName()) +
558 36 : std::string("_") +
559 27 : 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 41 : 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 : sge.get_err_msg());
607 0 : return false;
608 : }
609 :
610 58 : m_poDS->SetDefineMode(false);
611 58 : return true;
612 : }
613 :
614 : /************************************************************************/
615 : /* SetRecordDimID() */
616 : /************************************************************************/
617 :
618 100 : void netCDFLayer::SetRecordDimID(int nRecordDimID)
619 : {
620 100 : m_nRecordDimID = nRecordDimID;
621 : char szTemp[NC_MAX_NAME + 1];
622 100 : szTemp[0] = 0;
623 100 : int status = nc_inq_dimname(m_nLayerCDFId, m_nRecordDimID, szTemp);
624 100 : NCDF_ERR(status);
625 100 : m_osRecordDimName = szTemp;
626 100 : }
627 :
628 : /************************************************************************/
629 : /* GetFillValue() */
630 : /************************************************************************/
631 :
632 320 : CPLErr netCDFLayer::GetFillValue(int nVarId, char **ppszValue)
633 : {
634 320 : if (NCDFGetAttr(m_nLayerCDFId, nVarId, NCDF_FillValue, ppszValue) ==
635 : CE_None)
636 67 : return CE_None;
637 253 : return NCDFGetAttr(m_nLayerCDFId, nVarId, "missing_value", ppszValue);
638 : }
639 :
640 378 : CPLErr netCDFLayer::GetFillValue(int nVarId, double *pdfValue)
641 : {
642 378 : if (NCDFGetAttr(m_nLayerCDFId, nVarId, NCDF_FillValue, pdfValue) == CE_None)
643 40 : return CE_None;
644 338 : 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 161 : void netCDFLayer::GetNoDataValueForDouble(int nVarId, NCDFNoDataUnion *puNoData)
665 : {
666 : double dfValue;
667 161 : if (GetFillValue(nVarId, &dfValue) == CE_None)
668 0 : puNoData->dfVal = dfValue;
669 : else
670 161 : puNoData->dfVal = NC_FILL_DOUBLE;
671 161 : }
672 :
673 : /************************************************************************/
674 : /* GetNoDataValue() */
675 : /************************************************************************/
676 :
677 167 : void netCDFLayer::GetNoDataValue(int nVarId, nc_type nVarType,
678 : NCDFNoDataUnion *puNoData)
679 : {
680 167 : if (nVarType == NC_DOUBLE)
681 161 : GetNoDataValueForDouble(nVarId, puNoData);
682 6 : else if (nVarType == NC_FLOAT)
683 6 : GetNoDataValueForFloat(nVarId, puNoData);
684 167 : }
685 :
686 : /************************************************************************/
687 : /* SetXYZVars() */
688 : /************************************************************************/
689 :
690 56 : void netCDFLayer::SetXYZVars(int nXVarId, int nYVarId, int nZVarId)
691 : {
692 56 : m_nXVarID = nXVarId;
693 56 : m_nYVarID = nYVarId;
694 56 : m_nZVarID = nZVarId;
695 :
696 56 : nc_inq_vartype(m_nLayerCDFId, m_nXVarID, &m_nXVarNCDFType);
697 56 : nc_inq_vartype(m_nLayerCDFId, m_nYVarID, &m_nYVarNCDFType);
698 56 : if ((m_nXVarNCDFType != NC_FLOAT && m_nXVarNCDFType != NC_DOUBLE) ||
699 56 : (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 56 : if (m_nZVarID >= 0)
708 : {
709 55 : nc_inq_vartype(m_nLayerCDFId, m_nZVarID, &m_nZVarNCDFType);
710 55 : 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 56 : if (m_nXVarID >= 0 && m_nYVarID >= 0)
719 : {
720 : char szVarName[NC_MAX_NAME + 1];
721 56 : szVarName[0] = '\0';
722 56 : CPL_IGNORE_RET_VAL(nc_inq_varname(m_nLayerCDFId, m_nXVarID, szVarName));
723 56 : m_osCoordinatesValue = szVarName;
724 :
725 56 : szVarName[0] = '\0';
726 56 : CPL_IGNORE_RET_VAL(nc_inq_varname(m_nLayerCDFId, m_nYVarID, szVarName));
727 56 : m_osCoordinatesValue += " ";
728 56 : m_osCoordinatesValue += szVarName;
729 :
730 56 : if (m_nZVarID >= 0)
731 : {
732 55 : szVarName[0] = '\0';
733 55 : CPL_IGNORE_RET_VAL(
734 55 : nc_inq_varname(m_nLayerCDFId, m_nZVarID, szVarName));
735 55 : m_osCoordinatesValue += " ";
736 55 : m_osCoordinatesValue += szVarName;
737 : }
738 : }
739 :
740 56 : if (m_nXVarID >= 0)
741 56 : GetNoDataValue(m_nXVarID, m_nXVarNCDFType, &m_uXVarNoData);
742 56 : if (m_nYVarID >= 0)
743 56 : GetNoDataValue(m_nYVarID, m_nYVarNCDFType, &m_uYVarNoData);
744 56 : if (m_nZVarID >= 0)
745 55 : GetNoDataValue(m_nZVarID, m_nZVarNCDFType, &m_uZVarNoData);
746 56 : }
747 :
748 : /************************************************************************/
749 : /* SetWKTGeometryField() */
750 : /************************************************************************/
751 :
752 10 : void netCDFLayer::SetWKTGeometryField(const char *pszWKTVarName)
753 : {
754 10 : m_nWKTVarID = -1;
755 10 : nc_inq_varid(m_nLayerCDFId, pszWKTVarName, &m_nWKTVarID);
756 10 : if (m_nWKTVarID < 0)
757 0 : return;
758 : int nd;
759 10 : nc_inq_varndims(m_nLayerCDFId, m_nWKTVarID, &nd);
760 10 : nc_inq_vartype(m_nLayerCDFId, m_nWKTVarID, &m_nWKTNCDFType);
761 10 : 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 8 : else if (nd == 2 && m_nWKTNCDFType == NC_CHAR)
772 : {
773 8 : int anDimIds[2] = {-1, -1};
774 8 : size_t nLen = 0;
775 8 : if (nc_inq_vardimid(m_nLayerCDFId, m_nWKTVarID, anDimIds) != NC_NOERR ||
776 16 : anDimIds[0] != m_nRecordDimID ||
777 8 : nc_inq_dimlen(m_nLayerCDFId, anDimIds[1], &nLen) != NC_NOERR)
778 : {
779 0 : m_nWKTVarID = -1;
780 0 : return;
781 : }
782 8 : m_nWKTMaxWidth = static_cast<int>(nLen);
783 8 : m_nWKTMaxWidthDimId = anDimIds[1];
784 : }
785 : else
786 : {
787 0 : m_nWKTVarID = -1;
788 0 : return;
789 : }
790 :
791 10 : 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 100 : void netCDFLayer::SetProfile(int nProfileDimID, int nParentIndexVarID)
808 : {
809 100 : m_nProfileDimID = nProfileDimID;
810 100 : m_nParentIndexVarID = nParentIndexVarID;
811 100 : if (m_nProfileDimID >= 0)
812 : {
813 : char szTemp[NC_MAX_NAME + 1];
814 35 : szTemp[0] = 0;
815 35 : int status = nc_inq_dimname(m_nLayerCDFId, m_nProfileDimID, szTemp);
816 35 : NCDF_ERR(status);
817 35 : m_osProfileDimName = szTemp;
818 :
819 35 : nc_inq_varid(m_nLayerCDFId, m_osProfileDimName, &m_nProfileVarID);
820 35 : m_bProfileVarUnlimited = NCDFIsUnlimitedDim(
821 35 : m_poDS->eFormat == NCDF_FORMAT_NC4, m_nLayerCDFId, m_nProfileVarID);
822 : }
823 100 : }
824 :
825 : /************************************************************************/
826 : /* ResetReading() */
827 : /************************************************************************/
828 :
829 134 : void netCDFLayer::ResetReading()
830 : {
831 134 : if (!m_bLegacyCreateMode)
832 : {
833 6 : m_SGeometryFeatInd = 0;
834 : }
835 : else
836 : {
837 128 : m_nCurFeatureId = 1;
838 : }
839 134 : }
840 :
841 : /************************************************************************/
842 : /* Get1DVarAsDouble() */
843 : /************************************************************************/
844 :
845 620 : double netCDFLayer::Get1DVarAsDouble(int nVarId, nc_type nVarType,
846 : size_t nIndex,
847 : const NCDFNoDataUnion &noDataVal,
848 : bool *pbIsNoData)
849 : {
850 620 : double dfVal = 0;
851 620 : if (nVarType == NC_DOUBLE)
852 : {
853 609 : nc_get_var1_double(m_nLayerCDFId, nVarId, &nIndex, &dfVal);
854 609 : if (pbIsNoData)
855 609 : *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 620 : return dfVal;
868 : }
869 :
870 : /************************************************************************/
871 : /* GetNextRawFeature() */
872 : /************************************************************************/
873 :
874 773 : OGRFeature *netCDFLayer::GetNextRawFeature()
875 : {
876 :
877 773 : 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 : sge.get_err_msg());
896 : }
897 :
898 492 : return ft;
899 : }
900 :
901 276 : 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 276 : size_t nDimLen = 0;
906 276 : nc_inq_dimlen(m_nLayerCDFId, m_nRecordDimID, &nDimLen);
907 276 : if (m_nCurFeatureId > static_cast<GIntBig>(nDimLen))
908 56 : return nullptr;
909 :
910 220 : OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
911 :
912 220 : if (m_nParentIndexVarID >= 0)
913 : {
914 52 : int nProfileIdx = 0;
915 52 : size_t nIdx = static_cast<size_t>(m_nCurFeatureId - 1);
916 52 : int status = nc_get_var1_int(m_nLayerCDFId, m_nParentIndexVarID, &nIdx,
917 : &nProfileIdx);
918 52 : if (status == NC_NOERR && nProfileIdx >= 0)
919 : {
920 52 : nIdx = static_cast<size_t>(nProfileIdx);
921 52 : FillFeatureFromVar(poFeature, m_nProfileDimID, nIdx);
922 : }
923 : }
924 :
925 220 : if (!FillFeatureFromVar(poFeature, m_nRecordDimID,
926 220 : static_cast<size_t>(m_nCurFeatureId - 1)))
927 : {
928 0 : m_nCurFeatureId++;
929 0 : delete poFeature;
930 0 : return nullptr;
931 : }
932 :
933 220 : poFeature->SetFID(m_nCurFeatureId);
934 220 : m_nCurFeatureId++;
935 :
936 220 : return poFeature;
937 : }
938 :
939 : /************************************************************************/
940 : /* FillFeatureFromVar() */
941 : /************************************************************************/
942 :
943 786 : bool netCDFLayer::FillFeatureFromVar(OGRFeature *poFeature, int nMainDimId,
944 : size_t nIndex)
945 : {
946 : size_t anIndex[2];
947 786 : anIndex[0] = nIndex;
948 786 : anIndex[1] = 0;
949 :
950 6227 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
951 : {
952 5441 : if (m_aoFieldDesc[i].nMainDimId != nMainDimId && m_bLegacyCreateMode)
953 264 : continue;
954 :
955 5177 : switch (m_aoFieldDesc[i].nType)
956 : {
957 1451 : case NC_CHAR:
958 : {
959 1451 : if (m_aoFieldDesc[i].nDimCount == 1)
960 : {
961 126 : char szVal[2] = {0, 0};
962 126 : int status = nc_get_var1_text(
963 126 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, szVal);
964 126 : if (status != NC_NOERR)
965 : {
966 0 : NCDF_ERR(status);
967 0 : continue;
968 : }
969 126 : poFeature->SetField(i, szVal);
970 : }
971 : else
972 : {
973 : size_t anCount[2];
974 1325 : anCount[0] = 1;
975 1325 : anCount[1] = 0;
976 1325 : nc_inq_dimlen(m_nLayerCDFId, m_aoFieldDesc[i].nSecDimId,
977 : &(anCount[1]));
978 1325 : char *pszVal = (char *)CPLCalloc(1, anCount[1] + 1);
979 : int status =
980 1325 : nc_get_vara_text(m_nLayerCDFId, m_aoFieldDesc[i].nVarId,
981 : anIndex, anCount, pszVal);
982 1325 : if (status != NC_NOERR)
983 : {
984 0 : NCDF_ERR(status);
985 0 : CPLFree(pszVal);
986 0 : continue;
987 : }
988 1325 : poFeature->SetField(i, pszVal);
989 1325 : CPLFree(pszVal);
990 : }
991 1451 : 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 431 : case NC_BYTE:
1013 : {
1014 431 : signed char chVal = 0;
1015 431 : int status = nc_get_var1_schar(
1016 431 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &chVal);
1017 431 : if (status != NC_NOERR)
1018 : {
1019 0 : NCDF_ERR(status);
1020 248 : continue;
1021 : }
1022 431 : if (chVal == m_aoFieldDesc[i].uNoData.chVal)
1023 248 : continue;
1024 183 : poFeature->SetField(i, static_cast<int>(chVal));
1025 183 : 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 302 : case NC_SHORT:
1045 : {
1046 302 : short sVal = 0;
1047 302 : int status = nc_get_var1_short(
1048 302 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &sVal);
1049 :
1050 302 : if (status != NC_NOERR)
1051 : {
1052 0 : NCDF_ERR(status);
1053 172 : continue;
1054 : }
1055 302 : if (sVal == m_aoFieldDesc[i].uNoData.sVal)
1056 172 : continue;
1057 130 : poFeature->SetField(i, static_cast<int>(sVal));
1058 130 : 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 714 : case NC_INT:
1078 : {
1079 714 : int nVal = 0;
1080 714 : int status = nc_get_var1_int(
1081 714 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &nVal);
1082 714 : if (status != NC_NOERR)
1083 : {
1084 0 : NCDF_ERR(status);
1085 218 : continue;
1086 : }
1087 714 : if (nVal == m_aoFieldDesc[i].uNoData.nVal)
1088 218 : continue;
1089 983 : if (m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDate ||
1090 487 : 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 487 : poFeature->SetField(i, nVal);
1106 : }
1107 496 : 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 302 : case NC_FLOAT:
1164 : {
1165 302 : float fVal = 0.f;
1166 302 : int status = nc_get_var1_float(
1167 302 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &fVal);
1168 302 : if (status != NC_NOERR)
1169 : {
1170 0 : NCDF_ERR(status);
1171 172 : continue;
1172 : }
1173 302 : if (fVal == m_aoFieldDesc[i].uNoData.fVal)
1174 172 : continue;
1175 130 : poFeature->SetField(i, static_cast<double>(fVal));
1176 130 : break;
1177 : }
1178 :
1179 1734 : case NC_DOUBLE:
1180 : {
1181 1734 : double dfVal = 0.0;
1182 1734 : int status = nc_get_var1_double(
1183 1734 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &dfVal);
1184 1734 : if (status != NC_NOERR)
1185 : {
1186 0 : NCDF_ERR(status);
1187 666 : continue;
1188 : }
1189 1734 : if (dfVal == m_aoFieldDesc[i].uNoData.dfVal)
1190 666 : continue;
1191 2081 : if (m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDate ||
1192 1013 : m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
1193 : {
1194 183 : if (m_aoFieldDesc[i].bIsDays)
1195 0 : dfVal *= 86400.0;
1196 : struct tm brokendowntime;
1197 183 : GIntBig nVal = static_cast<GIntBig>(floor(dfVal));
1198 183 : CPLUnixTimeToYMDHMS(nVal, &brokendowntime);
1199 183 : poFeature->SetField(
1200 183 : i, brokendowntime.tm_year + 1900,
1201 183 : brokendowntime.tm_mon + 1, brokendowntime.tm_mday,
1202 : brokendowntime.tm_hour, brokendowntime.tm_min,
1203 183 : static_cast<float>(brokendowntime.tm_sec +
1204 183 : (dfVal - nVal)),
1205 : 0);
1206 : }
1207 : else
1208 : {
1209 885 : poFeature->SetField(i, dfVal);
1210 : }
1211 1068 : 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 786 : 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 701 : if (m_nXVarID >= 0 && m_nYVarID >= 0 &&
1226 407 : (m_osProfileDimName.empty() || nMainDimId == m_nProfileDimID))
1227 : {
1228 229 : bool bXIsNoData = false;
1229 458 : const double dfX = Get1DVarAsDouble(
1230 229 : m_nXVarID, m_nXVarNCDFType, anIndex[0], m_uXVarNoData, &bXIsNoData);
1231 229 : bool bYIsNoData = false;
1232 458 : const double dfY = Get1DVarAsDouble(
1233 229 : m_nYVarID, m_nYVarNCDFType, anIndex[0], m_uYVarNoData, &bYIsNoData);
1234 :
1235 229 : if (!bXIsNoData && !bYIsNoData)
1236 : {
1237 185 : OGRPoint *poPoint = nullptr;
1238 185 : if (m_nZVarID >= 0 && m_osProfileDimName.empty())
1239 : {
1240 110 : bool bZIsNoData = false;
1241 : const double dfZ =
1242 220 : Get1DVarAsDouble(m_nZVarID, m_nZVarNCDFType, anIndex[0],
1243 110 : m_uZVarNoData, &bZIsNoData);
1244 110 : if (bZIsNoData)
1245 39 : poPoint = new OGRPoint(dfX, dfY);
1246 : else
1247 71 : poPoint = new OGRPoint(dfX, dfY, dfZ);
1248 : }
1249 : else
1250 75 : poPoint = new OGRPoint(dfX, dfY);
1251 185 : poPoint->assignSpatialReference(GetSpatialRef());
1252 185 : poFeature->SetGeometryDirectly(poPoint);
1253 : }
1254 : }
1255 52 : else if (m_nXVarID >= 0 && m_nYVarID >= 0 && m_nZVarID >= 0 &&
1256 117 : !m_osProfileDimName.empty() && nMainDimId == m_nRecordDimID)
1257 : {
1258 52 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1259 104 : if (poGeom != nullptr &&
1260 52 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1261 : {
1262 52 : bool bZIsNoData = false;
1263 : const double dfZ =
1264 104 : Get1DVarAsDouble(m_nZVarID, m_nZVarNCDFType, anIndex[0],
1265 52 : m_uZVarNoData, &bZIsNoData);
1266 52 : if (!bZIsNoData)
1267 52 : 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 20 : auto [poGeom, _] = OGRGeometryFactory::createFromWkt(pszWKT);
1315 10 : if (poGeom != nullptr)
1316 : {
1317 6 : poGeom->assignSpatialReference(GetSpatialRef());
1318 6 : poFeature->SetGeometry(std::move(poGeom));
1319 : }
1320 10 : CPLFree(pszWKT);
1321 : }
1322 : }
1323 :
1324 294 : return true;
1325 : }
1326 :
1327 : /************************************************************************/
1328 : /* GetNextFeature() */
1329 : /************************************************************************/
1330 :
1331 773 : OGRFeature *netCDFLayer::GetNextFeature()
1332 : {
1333 : while (true)
1334 : {
1335 773 : OGRFeature *poFeature = GetNextRawFeature();
1336 773 : if (poFeature == nullptr)
1337 61 : return nullptr;
1338 :
1339 1457 : if ((m_poFilterGeom == nullptr ||
1340 1405 : FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
1341 693 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
1342 669 : return poFeature;
1343 :
1344 43 : delete poFeature;
1345 43 : }
1346 : }
1347 :
1348 : /************************************************************************/
1349 : /* GetLayerDefn() */
1350 : /************************************************************************/
1351 :
1352 3349 : const OGRFeatureDefn *netCDFLayer::GetLayerDefn() const
1353 : {
1354 3349 : return m_poFeatureDefn;
1355 : }
1356 :
1357 : /************************************************************************/
1358 : /* ICreateFeature() */
1359 : /************************************************************************/
1360 :
1361 501 : OGRErr netCDFLayer::ICreateFeature(OGRFeature *poFeature)
1362 : {
1363 501 : m_poDS->SetDefineMode(false);
1364 :
1365 501 : size_t nFeatureIdx = 0;
1366 501 : nc_inq_dimlen(m_nLayerCDFId, m_nRecordDimID, &nFeatureIdx);
1367 :
1368 501 : if (!m_bLegacyCreateMode)
1369 : {
1370 : // Detects: append mode
1371 442 : if (m_layerSGDefn.get_containerRealID() == nccfdriver::INVALID_VAR_ID)
1372 : {
1373 0 : CPLError(CE_Failure, CPLE_NotSupported,
1374 : "Append mode is not supported for CF-1.8 datasets.");
1375 0 : return OGRERR_UNSUPPORTED_OPERATION;
1376 : }
1377 : }
1378 :
1379 501 : if (m_nProfileDimID >= 0)
1380 : {
1381 20 : size_t nProfileCount = 0;
1382 20 : nc_inq_dimlen(m_nLayerCDFId, m_nProfileDimID, &nProfileCount);
1383 :
1384 20 : OGRFeature *poProfileToLookup = poFeature->Clone();
1385 20 : poProfileToLookup->SetFID(-1);
1386 96 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
1387 : {
1388 152 : if (!(poProfileToLookup->IsFieldSetAndNotNull(i)) ||
1389 76 : m_aoFieldDesc[i].nMainDimId != m_nProfileDimID)
1390 : {
1391 52 : poProfileToLookup->UnsetField(i);
1392 52 : continue;
1393 : }
1394 : }
1395 20 : OGRGeometry *poGeom = poProfileToLookup->GetGeometryRef();
1396 40 : if (poGeom != nullptr &&
1397 20 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1398 : {
1399 20 : poGeom->toPoint()->setZ(0);
1400 : }
1401 :
1402 20 : size_t nProfileIdx = 0;
1403 20 : bool bFoundProfile = false;
1404 30 : for (; nProfileIdx < nProfileCount; nProfileIdx++)
1405 : {
1406 26 : int nId = NC_FILL_INT;
1407 26 : int status = nc_get_var1_int(m_nLayerCDFId, m_nProfileVarID,
1408 : &nProfileIdx, &nId);
1409 26 : NCDF_ERR(status);
1410 26 : if (nId == NC_FILL_INT)
1411 16 : break;
1412 :
1413 22 : OGRFeature *poIterFeature = new OGRFeature(m_poFeatureDefn);
1414 22 : if (FillFeatureFromVar(poIterFeature, m_nProfileDimID, nProfileIdx))
1415 : {
1416 22 : poGeom = poIterFeature->GetGeometryRef();
1417 44 : if (poGeom != nullptr &&
1418 22 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1419 : {
1420 22 : poGeom->toPoint()->setZ(0);
1421 : }
1422 22 : if (poIterFeature->Equal(poProfileToLookup))
1423 : {
1424 12 : bFoundProfile = true;
1425 12 : delete poIterFeature;
1426 12 : break;
1427 : }
1428 : }
1429 10 : delete poIterFeature;
1430 : }
1431 :
1432 20 : if (!bFoundProfile)
1433 : {
1434 8 : if (!m_bProfileVarUnlimited && nProfileIdx == nProfileCount)
1435 : {
1436 2 : size_t nNewSize = 1 + nProfileCount + nProfileCount / 3;
1437 2 : m_poDS->GrowDim(m_nLayerCDFId, m_nProfileDimID, nNewSize);
1438 : }
1439 :
1440 8 : if (!FillVarFromFeature(poProfileToLookup, m_nProfileDimID,
1441 : nProfileIdx))
1442 : {
1443 0 : delete poProfileToLookup;
1444 0 : return OGRERR_FAILURE;
1445 : }
1446 : }
1447 :
1448 20 : int nProfileIdIdx = m_poFeatureDefn->GetFieldIndex(m_osProfileDimName);
1449 36 : if (nProfileIdIdx < 0 ||
1450 16 : m_poFeatureDefn->GetFieldDefn(nProfileIdIdx)->GetType() !=
1451 : OFTInteger)
1452 : {
1453 4 : int nVal = static_cast<int>(nProfileIdx);
1454 4 : int status = nc_put_var1_int(m_nLayerCDFId, m_nProfileVarID,
1455 : &nProfileIdx, &nVal);
1456 4 : NCDF_ERR(status);
1457 : }
1458 :
1459 20 : int nVal = static_cast<int>(nProfileIdx);
1460 20 : int status = nc_put_var1_int(m_nLayerCDFId, m_nParentIndexVarID,
1461 : &nFeatureIdx, &nVal);
1462 20 : NCDF_ERR(status);
1463 :
1464 20 : delete poProfileToLookup;
1465 : }
1466 :
1467 501 : if (!FillVarFromFeature(poFeature, m_nRecordDimID, nFeatureIdx))
1468 1 : return OGRERR_FAILURE;
1469 :
1470 500 : poFeature->SetFID(nFeatureIdx + 1);
1471 :
1472 500 : return OGRERR_NONE;
1473 : }
1474 :
1475 : /************************************************************************/
1476 : /* FillVarFromFeature() */
1477 : /************************************************************************/
1478 :
1479 509 : bool netCDFLayer::FillVarFromFeature(OGRFeature *poFeature, int nMainDimId,
1480 : size_t nIndex)
1481 : {
1482 : size_t anIndex[2];
1483 509 : anIndex[0] = nIndex;
1484 509 : anIndex[1] = 0;
1485 :
1486 2675 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
1487 : {
1488 2166 : if (m_aoFieldDesc[i].nMainDimId != nMainDimId)
1489 46 : continue;
1490 :
1491 2120 : if (!(poFeature->IsFieldSetAndNotNull(i)) && m_bLegacyCreateMode)
1492 : {
1493 127 : if (m_bNCDumpCompat && m_aoFieldDesc[i].nType == NC_STRING)
1494 : {
1495 1 : const char *pszVal = "";
1496 1 : int status = nc_put_var1_string(
1497 1 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &pszVal);
1498 1 : NCDF_ERR(status);
1499 : }
1500 127 : continue;
1501 : }
1502 :
1503 1993 : int status = NC_NOERR;
1504 1993 : switch (m_aoFieldDesc[i].nType)
1505 : {
1506 947 : case NC_CHAR:
1507 : {
1508 947 : const char *pszVal = poFeature->GetFieldAsString(i);
1509 947 : if (m_aoFieldDesc[i].nDimCount == 1)
1510 : {
1511 7 : if (strlen(pszVal) > 1 &&
1512 0 : !m_aoFieldDesc[i].bHasWarnedAboutTruncation)
1513 : {
1514 0 : CPLError(
1515 : CE_Warning, CPLE_AppDefined,
1516 : "Content of field %s exceeded the 1 character "
1517 : "limit and will be truncated",
1518 0 : m_poFeatureDefn->GetFieldDefn(i)->GetNameRef());
1519 0 : m_aoFieldDesc[i].bHasWarnedAboutTruncation = true;
1520 : }
1521 7 : if (m_poDS->HasInfiniteRecordDim())
1522 : {
1523 7 : status = nc_put_var1_text(m_nLayerCDFId,
1524 7 : m_aoFieldDesc[i].nVarId,
1525 : anIndex, pszVal);
1526 : }
1527 : else
1528 : {
1529 0 : m_poDS->FieldScribe.enqueue_transaction(
1530 0 : nccfdriver::MTPtr(
1531 : new nccfdriver::OGR_SGFS_NC_Char_Transaction(
1532 0 : m_aoFieldDesc[i].nVarId, pszVal)));
1533 : }
1534 : }
1535 : else
1536 : {
1537 : size_t anCount[2];
1538 940 : anCount[0] = 1;
1539 940 : anCount[1] = strlen(pszVal);
1540 940 : size_t nWidth = 0;
1541 :
1542 940 : if (m_bLegacyCreateMode)
1543 : {
1544 64 : nc_inq_dimlen(m_nLayerCDFId, m_aoFieldDesc[i].nSecDimId,
1545 : &nWidth);
1546 : }
1547 : else
1548 : {
1549 876 : nWidth =
1550 876 : layerVID.virtualDIDToDim(m_aoFieldDesc[i].nSecDimId)
1551 876 : .getLen();
1552 : }
1553 :
1554 940 : if (anCount[1] > nWidth)
1555 : {
1556 : // Always grow the dim if not writing to WKT- it's
1557 : // rather inexpensive in CF-1.8
1558 :
1559 43 : if ((m_bAutoGrowStrings &&
1560 43 : m_poFeatureDefn->GetFieldDefn(i)->GetWidth() ==
1561 88 : 0) ||
1562 1 : !m_bLegacyCreateMode)
1563 : {
1564 43 : size_t nNewSize = anCount[1] + anCount[1] / 3;
1565 :
1566 43 : CPLDebug(
1567 : "GDAL_netCDF", "Growing %s from %u to %u",
1568 43 : m_poFeatureDefn->GetFieldDefn(i)->GetNameRef(),
1569 : static_cast<unsigned>(nWidth),
1570 : static_cast<unsigned>(nNewSize));
1571 :
1572 43 : if (m_bLegacyCreateMode)
1573 : {
1574 8 : m_poDS->GrowDim(m_nLayerCDFId,
1575 8 : m_aoFieldDesc[i].nSecDimId,
1576 : nNewSize);
1577 : }
1578 : else
1579 : {
1580 35 : layerVID.nc_resize_vdim(
1581 35 : m_aoFieldDesc[i].nSecDimId, nNewSize);
1582 : }
1583 :
1584 43 : pszVal = poFeature->GetFieldAsString(i);
1585 : }
1586 : else
1587 : {
1588 1 : anCount[1] = nWidth;
1589 1 : if (!m_aoFieldDesc[i].bHasWarnedAboutTruncation)
1590 : {
1591 1 : CPLError(CE_Warning, CPLE_AppDefined,
1592 : "Content of field %s exceeded the %u "
1593 : "character "
1594 : "limit and will be truncated",
1595 1 : m_poFeatureDefn->GetFieldDefn(i)
1596 : ->GetNameRef(),
1597 : static_cast<unsigned int>(nWidth));
1598 1 : m_aoFieldDesc[i].bHasWarnedAboutTruncation =
1599 : true;
1600 : }
1601 : }
1602 : }
1603 :
1604 940 : if (m_poDS->HasInfiniteRecordDim())
1605 : {
1606 64 : status = nc_put_vara_text(m_nLayerCDFId,
1607 64 : m_aoFieldDesc[i].nVarId,
1608 : anIndex, anCount, pszVal);
1609 : }
1610 : else
1611 : {
1612 876 : m_poDS->FieldScribe.enqueue_transaction(
1613 1752 : nccfdriver::MTPtr(
1614 : new nccfdriver::OGR_SGFS_NC_CharA_Transaction(
1615 876 : m_aoFieldDesc[i].nVarId, pszVal)));
1616 : }
1617 : }
1618 947 : break;
1619 : }
1620 :
1621 34 : case NC_STRING:
1622 : {
1623 34 : const char *pszVal = poFeature->GetFieldAsString(i);
1624 :
1625 34 : if (m_poDS->HasInfiniteRecordDim())
1626 : {
1627 20 : status = nc_put_var1_string(m_nLayerCDFId,
1628 20 : m_aoFieldDesc[i].nVarId,
1629 : anIndex, &pszVal);
1630 : }
1631 :
1632 : else
1633 : {
1634 14 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1635 : new nccfdriver::OGR_SGFS_NC_String_Transaction(
1636 14 : m_aoFieldDesc[i].nVarId, pszVal)));
1637 : }
1638 :
1639 34 : break;
1640 : }
1641 :
1642 16 : case NC_BYTE:
1643 : {
1644 16 : int nVal = poFeature->GetFieldAsInteger(i);
1645 16 : signed char chVal = static_cast<signed char>(nVal);
1646 :
1647 16 : if (m_poDS->HasInfiniteRecordDim())
1648 : {
1649 16 : status = nc_put_var1_schar(m_nLayerCDFId,
1650 16 : m_aoFieldDesc[i].nVarId, anIndex,
1651 : &chVal);
1652 : }
1653 :
1654 : else
1655 : {
1656 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1657 : new nccfdriver::OGR_SGFS_NC_Byte_Transaction(
1658 0 : m_aoFieldDesc[i].nVarId, chVal)));
1659 : }
1660 16 : break;
1661 : }
1662 :
1663 4 : case NC_UBYTE:
1664 : {
1665 4 : int nVal = poFeature->GetFieldAsInteger(i);
1666 4 : unsigned char uchVal = static_cast<unsigned char>(nVal);
1667 :
1668 4 : if (m_poDS->HasInfiniteRecordDim())
1669 : {
1670 4 : status = nc_put_var1_uchar(m_nLayerCDFId,
1671 4 : m_aoFieldDesc[i].nVarId, anIndex,
1672 : &uchVal);
1673 : }
1674 :
1675 : else
1676 : {
1677 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1678 : new nccfdriver::OGR_SGFS_NC_UByte_Transaction(
1679 0 : m_aoFieldDesc[i].nVarId, uchVal)));
1680 : }
1681 4 : break;
1682 : }
1683 :
1684 15 : case NC_SHORT:
1685 : {
1686 15 : int nVal = poFeature->GetFieldAsInteger(i);
1687 15 : short sVal = static_cast<short>(nVal);
1688 :
1689 15 : if (m_poDS->HasInfiniteRecordDim())
1690 : {
1691 14 : status = nc_put_var1_short(
1692 14 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &sVal);
1693 : }
1694 : else
1695 : {
1696 1 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1697 : new nccfdriver::OGR_SGFS_NC_Short_Transaction(
1698 1 : m_aoFieldDesc[i].nVarId, sVal)));
1699 : }
1700 15 : break;
1701 : }
1702 :
1703 4 : case NC_USHORT:
1704 : {
1705 4 : int nVal = poFeature->GetFieldAsInteger(i);
1706 4 : unsigned short usVal = static_cast<unsigned short>(nVal);
1707 :
1708 4 : if (m_poDS->HasInfiniteRecordDim())
1709 : {
1710 4 : status = nc_put_var1_ushort(m_nLayerCDFId,
1711 4 : m_aoFieldDesc[i].nVarId,
1712 : anIndex, &usVal);
1713 : }
1714 : else
1715 : {
1716 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1717 : new nccfdriver::OGR_SGFS_NC_UShort_Transaction(
1718 0 : m_aoFieldDesc[i].nVarId, usVal)));
1719 : }
1720 :
1721 4 : break;
1722 : }
1723 :
1724 270 : case NC_INT:
1725 : {
1726 : int nVal;
1727 270 : if (m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDate)
1728 : {
1729 : int nYear;
1730 : int nMonth;
1731 : int nDay;
1732 : int nHour;
1733 : int nMinute;
1734 : float fSecond;
1735 : int nTZ;
1736 5 : poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
1737 : &nHour, &nMinute, &fSecond,
1738 : &nTZ);
1739 : struct tm brokendowntime;
1740 5 : brokendowntime.tm_year = nYear - 1900;
1741 5 : brokendowntime.tm_mon = nMonth - 1;
1742 5 : brokendowntime.tm_mday = nDay;
1743 5 : brokendowntime.tm_hour = 0;
1744 5 : brokendowntime.tm_min = 0;
1745 5 : brokendowntime.tm_sec = 0;
1746 5 : GIntBig nVal64 = CPLYMDHMSToUnixTime(&brokendowntime);
1747 5 : if (m_aoFieldDesc[i].bIsDays)
1748 5 : nVal64 /= 86400;
1749 5 : nVal = static_cast<int>(nVal64);
1750 : }
1751 : else
1752 : {
1753 265 : nVal = poFeature->GetFieldAsInteger(i);
1754 : }
1755 :
1756 270 : if (m_poDS->HasInfiniteRecordDim())
1757 : {
1758 62 : status = nc_put_var1_int(
1759 62 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &nVal);
1760 : }
1761 : else
1762 : {
1763 208 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1764 : new nccfdriver::OGR_SGFS_NC_Int_Transaction(
1765 208 : m_aoFieldDesc[i].nVarId, nVal)));
1766 : }
1767 :
1768 270 : break;
1769 : }
1770 :
1771 4 : case NC_UINT:
1772 : {
1773 4 : GIntBig nVal = poFeature->GetFieldAsInteger64(i);
1774 4 : unsigned int unVal = static_cast<unsigned int>(nVal);
1775 :
1776 4 : if (m_poDS->HasInfiniteRecordDim())
1777 : {
1778 : status =
1779 4 : nc_put_var1_uint(m_nLayerCDFId, m_aoFieldDesc[i].nVarId,
1780 : anIndex, &unVal);
1781 : }
1782 : else
1783 : {
1784 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1785 : new nccfdriver::OGR_SGFS_NC_UInt_Transaction(
1786 0 : m_aoFieldDesc[i].nVarId, unVal)));
1787 : }
1788 :
1789 4 : break;
1790 : }
1791 :
1792 18 : case NC_INT64:
1793 : {
1794 18 : GIntBig nVal = poFeature->GetFieldAsInteger64(i);
1795 18 : if (m_poDS->HasInfiniteRecordDim())
1796 : {
1797 18 : status = nc_put_var1_longlong(
1798 18 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &nVal);
1799 : }
1800 : else
1801 : {
1802 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1803 : new nccfdriver::OGR_SGFS_NC_Int64_Transaction(
1804 0 : m_aoFieldDesc[i].nVarId, nVal)));
1805 : }
1806 18 : break;
1807 : }
1808 :
1809 2 : case NC_UINT64:
1810 : {
1811 2 : double dfVal = poFeature->GetFieldAsDouble(i);
1812 2 : GUIntBig nVal = static_cast<GUIntBig>(dfVal);
1813 :
1814 2 : if (m_poDS->HasInfiniteRecordDim())
1815 : {
1816 2 : status = nc_put_var1_ulonglong(
1817 2 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &nVal);
1818 : }
1819 : else
1820 : {
1821 0 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1822 : new nccfdriver::OGR_SGFS_NC_UInt64_Transaction(
1823 0 : m_aoFieldDesc[i].nVarId, nVal)));
1824 : }
1825 :
1826 2 : break;
1827 : }
1828 :
1829 15 : case NC_FLOAT:
1830 : {
1831 15 : double dfVal = poFeature->GetFieldAsDouble(i);
1832 15 : float fVal = static_cast<float>(dfVal);
1833 :
1834 15 : if (m_poDS->HasInfiniteRecordDim())
1835 : {
1836 14 : status = nc_put_var1_float(
1837 14 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &fVal);
1838 : }
1839 : else
1840 : {
1841 1 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1842 : new nccfdriver::OGR_SGFS_NC_Float_Transaction(
1843 1 : m_aoFieldDesc[i].nVarId, fVal)));
1844 : }
1845 :
1846 15 : break;
1847 : }
1848 :
1849 664 : case NC_DOUBLE:
1850 : {
1851 : double dfVal;
1852 1326 : if (m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDate ||
1853 662 : m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
1854 : {
1855 : int nYear;
1856 : int nMonth;
1857 : int nDay;
1858 : int nHour;
1859 : int nMinute;
1860 : float fSecond;
1861 : int nTZ;
1862 16 : poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay,
1863 : &nHour, &nMinute, &fSecond,
1864 : &nTZ);
1865 : struct tm brokendowntime;
1866 16 : brokendowntime.tm_year = nYear - 1900;
1867 16 : brokendowntime.tm_mon = nMonth - 1;
1868 16 : brokendowntime.tm_mday = nDay;
1869 16 : brokendowntime.tm_hour = nHour;
1870 16 : brokendowntime.tm_min = nMinute;
1871 16 : brokendowntime.tm_sec = static_cast<int>(fSecond);
1872 16 : GIntBig nVal = CPLYMDHMSToUnixTime(&brokendowntime);
1873 16 : dfVal = static_cast<double>(nVal) + fmod(fSecond, 1.0f);
1874 16 : if (m_aoFieldDesc[i].bIsDays)
1875 0 : dfVal /= 86400.0;
1876 : }
1877 : else
1878 : {
1879 648 : dfVal = poFeature->GetFieldAsDouble(i);
1880 : }
1881 :
1882 664 : if (m_poDS->HasInfiniteRecordDim())
1883 : {
1884 75 : status = nc_put_var1_double(m_nLayerCDFId,
1885 75 : m_aoFieldDesc[i].nVarId,
1886 : anIndex, &dfVal);
1887 : }
1888 : else
1889 : {
1890 589 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1891 : new nccfdriver::OGR_SGFS_NC_Double_Transaction(
1892 589 : m_aoFieldDesc[i].nVarId, dfVal)));
1893 : }
1894 :
1895 664 : break;
1896 : }
1897 :
1898 0 : default:
1899 0 : break;
1900 : }
1901 :
1902 1993 : NCDF_ERR(status);
1903 1993 : if (status != NC_NOERR)
1904 : {
1905 0 : return false;
1906 : }
1907 : }
1908 :
1909 509 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1910 583 : if (wkbFlatten(m_poFeatureDefn->GetGeomType()) == wkbPoint &&
1911 71 : poGeom != nullptr &&
1912 654 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint &&
1913 71 : m_bLegacyCreateMode)
1914 : {
1915 38 : if (m_osProfileDimName.empty() || nMainDimId == m_nProfileDimID)
1916 : {
1917 18 : auto poPoint = poGeom->toPoint();
1918 18 : double dfX = poPoint->getX();
1919 18 : double dfY = poPoint->getY();
1920 :
1921 : int status;
1922 :
1923 18 : if (m_nXVarNCDFType == NC_DOUBLE)
1924 : status =
1925 18 : nc_put_var1_double(m_nLayerCDFId, m_nXVarID, anIndex, &dfX);
1926 : else
1927 : {
1928 0 : float fX = static_cast<float>(dfX);
1929 : status =
1930 0 : nc_put_var1_float(m_nLayerCDFId, m_nXVarID, anIndex, &fX);
1931 : }
1932 18 : NCDF_ERR(status);
1933 18 : if (status != NC_NOERR)
1934 : {
1935 0 : return false;
1936 : }
1937 :
1938 18 : if (m_nYVarNCDFType == NC_DOUBLE)
1939 : status =
1940 18 : nc_put_var1_double(m_nLayerCDFId, m_nYVarID, anIndex, &dfY);
1941 : else
1942 : {
1943 0 : float fY = static_cast<float>(dfY);
1944 : status =
1945 0 : nc_put_var1_float(m_nLayerCDFId, m_nYVarID, anIndex, &fY);
1946 : }
1947 18 : NCDF_ERR(status);
1948 18 : if (status != NC_NOERR)
1949 : {
1950 0 : return false;
1951 : }
1952 : }
1953 :
1954 104 : if (m_poFeatureDefn->GetGeomType() == wkbPoint25D &&
1955 66 : (m_osProfileDimName.empty() || nMainDimId == m_nRecordDimID))
1956 : {
1957 : int status;
1958 30 : double dfZ = poGeom->toPoint()->getZ();
1959 30 : if (m_nZVarNCDFType == NC_DOUBLE)
1960 : status =
1961 30 : nc_put_var1_double(m_nLayerCDFId, m_nZVarID, anIndex, &dfZ);
1962 : else
1963 : {
1964 0 : float fZ = static_cast<float>(dfZ);
1965 : status =
1966 0 : nc_put_var1_float(m_nLayerCDFId, m_nZVarID, anIndex, &fZ);
1967 : }
1968 30 : NCDF_ERR(status);
1969 30 : if (status != NC_NOERR)
1970 : {
1971 0 : return false;
1972 : }
1973 : }
1974 : }
1975 940 : else if (m_poFeatureDefn->GetGeomType() != wkbNone && m_nWKTVarID >= 0 &&
1976 940 : poGeom != nullptr && m_bLegacyCreateMode)
1977 : {
1978 21 : OGRWktOptions opts;
1979 21 : opts.variant = wkbVariantIso;
1980 :
1981 21 : std::string osWKT = poGeom->exportToWkt(opts);
1982 21 : int status = NC_NOERR;
1983 21 : if (m_nWKTNCDFType == NC_STRING)
1984 : {
1985 10 : const char *pszWKTConst = osWKT.c_str();
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] = osWKT.size();
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, osWKT.c_str());
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 =
2019 : NC_EEDGE; // error that would be returned had we called nc_put_var_text
2020 : }
2021 : }
2022 : else
2023 : {
2024 7 : status = nc_put_vara_text(m_nLayerCDFId, m_nWKTVarID, anIndex,
2025 : anCount, osWKT.c_str());
2026 : }
2027 : }
2028 21 : NCDF_ERR(status);
2029 21 : if (status != NC_NOERR)
2030 : {
2031 1 : return false;
2032 : }
2033 : }
2034 898 : else if (m_poFeatureDefn->GetGeomType() != wkbNone && m_nWKTVarID >= 0 &&
2035 3 : poGeom == nullptr && m_nWKTNCDFType == NC_STRING &&
2036 898 : 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 508 : if (!m_bLegacyCreateMode && poGeom != nullptr)
2048 : {
2049 884 : nccfdriver::SGeometry_Feature featWithMetaData(*poFeature);
2050 :
2051 : // Check if ready to dump buffer to LOG
2052 442 : if (m_poDS->bufManager.isOverQuota())
2053 : {
2054 180 : m_poDS->SGLogPendingTransaction();
2055 : }
2056 :
2057 : // Finally, "write" the feature
2058 442 : 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 : sge.get_err_msg());
2068 0 : return false;
2069 : }
2070 :
2071 508 : return true;
2072 : }
2073 :
2074 : /************************************************************************/
2075 : /* AddField() */
2076 : /************************************************************************/
2077 :
2078 742 : bool netCDFLayer::AddField(int nVarID)
2079 : {
2080 742 : if (nVarID == m_nWKTVarID)
2081 10 : return false;
2082 :
2083 : char szName[NC_MAX_NAME + 1];
2084 732 : szName[0] = '\0';
2085 732 : CPL_IGNORE_RET_VAL(nc_inq_varname(m_nLayerCDFId, nVarID, szName));
2086 :
2087 732 : nc_type vartype = NC_NAT;
2088 732 : nc_inq_vartype(m_nLayerCDFId, nVarID, &vartype);
2089 :
2090 732 : OGRFieldType eType = OFTString;
2091 732 : OGRFieldSubType eSubType = OFSTNone;
2092 732 : int nWidth = 0;
2093 :
2094 : NCDFNoDataUnion nodata;
2095 732 : memset(&nodata, 0, sizeof(nodata));
2096 732 : int nDimCount = 1;
2097 732 : nc_inq_varndims(m_nLayerCDFId, nVarID, &nDimCount);
2098 732 : int anDimIds[2] = {-1, -1};
2099 732 : if ((vartype == NC_CHAR && nDimCount <= 2) ||
2100 551 : (vartype != NC_CHAR && nDimCount == 1))
2101 : {
2102 732 : nc_inq_vardimid(m_nLayerCDFId, nVarID, anDimIds);
2103 : }
2104 : else
2105 : {
2106 0 : return false;
2107 : }
2108 :
2109 732 : 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 181 : case NC_CHAR:
2136 : {
2137 181 : eType = OFTString;
2138 181 : if (nDimCount == 1)
2139 : {
2140 9 : nWidth = 1;
2141 : }
2142 172 : else if (nDimCount == 2)
2143 : {
2144 172 : size_t nDimLen = 0;
2145 172 : nc_inq_dimlen(m_nLayerCDFId, anDimIds[1], &nDimLen);
2146 172 : nWidth = static_cast<int>(nDimLen);
2147 : }
2148 181 : 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 161 : case NC_INT:
2183 : {
2184 161 : eType = OFTInteger;
2185 161 : char *pszValue = nullptr;
2186 161 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2187 15 : nodata.nVal = atoi(pszValue);
2188 : else
2189 146 : nodata.nVal = NC_FILL_INT;
2190 161 : CPLFree(pszValue);
2191 161 : 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 26 : case NC_INT64:
2208 : {
2209 26 : eType = OFTInteger64;
2210 26 : char *pszValue = nullptr;
2211 26 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2212 6 : nodata.nVal64 = CPLAtoGIntBig(pszValue);
2213 : else
2214 20 : nodata.nVal64 = NC_FILL_INT64;
2215 26 : CPLFree(pszValue);
2216 26 : 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 173 : case NC_DOUBLE:
2250 : {
2251 173 : eType = OFTReal;
2252 : double dfValue;
2253 173 : if (GetFillValue(nVarID, &dfValue) == CE_None)
2254 31 : nodata.dfVal = dfValue;
2255 : else
2256 142 : nodata.dfVal = NC_FILL_DOUBLE;
2257 173 : 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 732 : bool bIsDays = false;
2270 :
2271 732 : char *pszValue = nullptr;
2272 732 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_type", &pszValue) ==
2273 : CE_None)
2274 : {
2275 491 : if ((eType == OFTInteger || eType == OFTReal) &&
2276 299 : EQUAL(pszValue, "Date"))
2277 : {
2278 17 : eType = OFTDate;
2279 : // cppcheck-suppress knownConditionTrueFalse
2280 17 : bIsDays = (eType == OFTInteger);
2281 : }
2282 474 : else if ((eType == OFTInteger || eType == OFTReal) &&
2283 282 : EQUAL(pszValue, "DateTime"))
2284 23 : eType = OFTDateTime;
2285 451 : else if (eType == OFTReal && EQUAL(pszValue, "Integer64"))
2286 21 : eType = OFTInteger64;
2287 430 : else if (eType == OFTInteger && EQUAL(pszValue, "Integer(Boolean)"))
2288 34 : eSubType = OFSTBoolean;
2289 : }
2290 732 : CPLFree(pszValue);
2291 :
2292 732 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "units", &pszValue) == CE_None)
2293 : {
2294 110 : if ((eType == OFTInteger || eType == OFTReal || eType == OFTDate) &&
2295 63 : (EQUAL(pszValue, "seconds since 1970-1-1 0:0:0") ||
2296 39 : 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 86 : else if ((eType == OFTInteger || eType == OFTReal ||
2303 39 : eType == OFTDate) &&
2304 39 : (EQUAL(pszValue, "days since 1970-1-1") ||
2305 32 : EQUAL(pszValue, "days since 1970-01-01")))
2306 : {
2307 7 : eType = OFTDate;
2308 7 : bIsDays = true;
2309 : }
2310 : }
2311 732 : CPLFree(pszValue);
2312 :
2313 732 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_name", &pszValue) ==
2314 : CE_None)
2315 : {
2316 486 : snprintf(szName, sizeof(szName), "%s", pszValue);
2317 : }
2318 732 : CPLFree(pszValue);
2319 :
2320 732 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_width", &pszValue) ==
2321 : CE_None)
2322 : {
2323 192 : nWidth = atoi(pszValue);
2324 : }
2325 732 : CPLFree(pszValue);
2326 :
2327 732 : int nPrecision = 0;
2328 732 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_precision", &pszValue) ==
2329 : CE_None)
2330 : {
2331 6 : nPrecision = atoi(pszValue);
2332 : }
2333 732 : CPLFree(pszValue);
2334 :
2335 732 : OGRFieldDefn oFieldDefn(szName, eType);
2336 732 : oFieldDefn.SetSubType(eSubType);
2337 732 : oFieldDefn.SetWidth(nWidth);
2338 732 : oFieldDefn.SetPrecision(nPrecision);
2339 :
2340 1255 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, CF_LNG_NAME, &pszValue) == CE_None &&
2341 1255 : pszValue != std::string("Field ") + szName)
2342 : {
2343 19 : oFieldDefn.SetComment(pszValue);
2344 : }
2345 732 : CPLFree(pszValue);
2346 :
2347 760 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, CF_STD_NAME, &pszValue) == CE_None &&
2348 28 : strcmp(pszValue, szName) != 0)
2349 : {
2350 4 : oFieldDefn.SetAlternativeName(pszValue);
2351 : }
2352 732 : CPLFree(pszValue);
2353 :
2354 : FieldDesc fieldDesc;
2355 732 : fieldDesc.uNoData = nodata;
2356 732 : fieldDesc.nType = vartype;
2357 732 : fieldDesc.nVarId = nVarID;
2358 732 : fieldDesc.nDimCount = nDimCount;
2359 732 : fieldDesc.nMainDimId = anDimIds[0];
2360 732 : fieldDesc.nSecDimId = anDimIds[1];
2361 732 : fieldDesc.bHasWarnedAboutTruncation = false;
2362 732 : fieldDesc.bIsDays = bIsDays;
2363 732 : m_aoFieldDesc.push_back(fieldDesc);
2364 :
2365 732 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
2366 :
2367 732 : return true;
2368 : }
2369 :
2370 : /************************************************************************/
2371 : /* CreateField() */
2372 : /************************************************************************/
2373 :
2374 177 : OGRErr netCDFLayer::CreateField(const OGRFieldDefn *poFieldDefn,
2375 : int /* bApproxOK */)
2376 : {
2377 177 : int nSecDimId = -1;
2378 177 : int nVarID = -1;
2379 : int status;
2380 :
2381 177 : const netCDFWriterConfigField *poConfig = nullptr;
2382 177 : if (m_poDS->oWriterConfig.m_bIsValid)
2383 : {
2384 11 : std::map<CPLString, netCDFWriterConfigField>::const_iterator oIter;
2385 15 : if (m_poLayerConfig != nullptr &&
2386 4 : (oIter =
2387 15 : m_poLayerConfig->m_oFields.find(poFieldDefn->GetNameRef())) !=
2388 15 : m_poLayerConfig->m_oFields.end())
2389 : {
2390 1 : poConfig = &(oIter->second);
2391 : }
2392 10 : else if ((oIter = m_poDS->oWriterConfig.m_oFields.find(
2393 20 : poFieldDefn->GetNameRef())) !=
2394 20 : m_poDS->oWriterConfig.m_oFields.end())
2395 : {
2396 2 : poConfig = &(oIter->second);
2397 : }
2398 : }
2399 :
2400 177 : if (!m_osProfileDimName.empty() &&
2401 180 : 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 174 : 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 348 : : CPLString(poFieldDefn->GetNameRef()));
2424 :
2425 174 : if (!m_bLegacyCreateMode && m_bWriteGDALTags)
2426 : {
2427 : // To help avoid naming conflicts, append the layer name as a prefix
2428 73 : const char *prefix = this->GetName();
2429 73 : const char *fprefix = "_field_";
2430 :
2431 73 : osVarName = CPLString(prefix) + CPLString(fprefix) + osVarName;
2432 : }
2433 :
2434 174 : bool vCDFHas = false;
2435 174 : if (!m_bLegacyCreateMode)
2436 : {
2437 73 : vCDFHas = layerVID.virtualVarNameDefined(osVarName);
2438 : }
2439 :
2440 : // Also check the real
2441 174 : status = nc_inq_varid(m_nLayerCDFId, osVarName, &nVarID);
2442 174 : 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 174 : const char *pszVarName = osVarName.c_str();
2459 :
2460 : NCDFNoDataUnion nodata;
2461 174 : memset(&nodata, 0, sizeof(nodata));
2462 :
2463 174 : const OGRFieldType eType = poFieldDefn->GetType();
2464 174 : const OGRFieldSubType eSubType = poFieldDefn->GetSubType();
2465 174 : nc_type nType = NC_NAT;
2466 174 : int nDimCount = 1;
2467 :
2468 : // Find which is the dimension that this variable should be indexed against
2469 174 : int nMainDimId = m_nRecordDimID;
2470 174 : 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 176 : 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 1 : return OGRERR_FAILURE;
2504 : }
2505 : }
2506 :
2507 : try
2508 : {
2509 173 : switch (eType)
2510 : {
2511 74 : case OFTString:
2512 : case OFTStringList:
2513 : case OFTIntegerList:
2514 : case OFTRealList:
2515 : {
2516 74 : if (poFieldDefn->GetWidth() == 1)
2517 : {
2518 2 : nType = NC_CHAR;
2519 2 : nVarID =
2520 2 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2521 : }
2522 72 : else if (m_poDS->eFormat == NCDF_FORMAT_NC4 &&
2523 13 : m_bUseStringInNC4)
2524 : {
2525 9 : nType = NC_STRING;
2526 9 : nVarID =
2527 9 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2528 : }
2529 : else
2530 : {
2531 63 : if (poFieldDefn->GetWidth() == 0 && !m_bAutoGrowStrings)
2532 : {
2533 1 : if (m_nDefaultMaxWidthDimId < 0)
2534 : {
2535 1 : m_nDefaultMaxWidthDimId = layerVID.nc_def_vdim(
2536 1 : "string_default_max_width", m_nDefaultWidth);
2537 : }
2538 :
2539 1 : nSecDimId = m_nDefaultMaxWidthDimId;
2540 : }
2541 : else
2542 : {
2543 62 : size_t nDim = poFieldDefn->GetWidth() == 0
2544 68 : ? m_nDefaultWidth
2545 6 : : poFieldDefn->GetWidth();
2546 :
2547 : std::string ndimname =
2548 124 : std::string(pszVarName) + std::string("_max_width");
2549 : nSecDimId =
2550 62 : layerVID.nc_def_vdim(ndimname.c_str(), nDim);
2551 : }
2552 :
2553 63 : nDimCount = 2;
2554 63 : int anDims[2] = {nMainDimId, nSecDimId};
2555 63 : nType = NC_CHAR;
2556 63 : nVarID = layerVID.nc_def_vvar(pszVarName, nType, 2, anDims);
2557 : }
2558 :
2559 74 : break;
2560 : }
2561 :
2562 49 : case OFTInteger:
2563 : {
2564 92 : nType = eSubType == OFSTBoolean ? NC_BYTE
2565 43 : : (eSubType == OFSTInt16) ? NC_SHORT
2566 : : NC_INT;
2567 :
2568 49 : if (nType == NC_BYTE)
2569 6 : nodata.chVal = NC_FILL_BYTE;
2570 43 : else if (nType == NC_SHORT)
2571 7 : nodata.sVal = NC_FILL_SHORT;
2572 36 : else if (nType == NC_INT)
2573 36 : nodata.nVal = NC_FILL_INT;
2574 :
2575 49 : nVarID =
2576 49 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2577 :
2578 49 : if (eSubType == OFSTBoolean)
2579 : {
2580 6 : if (m_bLegacyCreateMode)
2581 : {
2582 6 : signed char anRange[2] = {0, 1};
2583 6 : nc_put_att_schar(m_nLayerCDFId, nVarID, "valid_range",
2584 : NC_BYTE, 2, anRange);
2585 : }
2586 : }
2587 :
2588 49 : break;
2589 : }
2590 :
2591 11 : case OFTInteger64:
2592 : {
2593 11 : nType = NC_DOUBLE;
2594 11 : nodata.dfVal = NC_FILL_DOUBLE;
2595 11 : if (m_poDS->eFormat == NCDF_FORMAT_NC4)
2596 : {
2597 5 : nType = NC_INT64;
2598 5 : nodata.nVal64 = NC_FILL_INT64;
2599 : }
2600 :
2601 11 : nVarID =
2602 11 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2603 11 : break;
2604 : }
2605 :
2606 30 : case OFTReal:
2607 : {
2608 30 : nType = (eSubType == OFSTFloat32) ? NC_FLOAT : NC_DOUBLE;
2609 30 : if (eSubType == OFSTFloat32)
2610 7 : nodata.fVal = NC_FILL_FLOAT;
2611 : else
2612 23 : nodata.dfVal = NC_FILL_DOUBLE;
2613 :
2614 30 : nVarID =
2615 30 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2616 30 : break;
2617 : }
2618 :
2619 3 : case OFTDate:
2620 : {
2621 3 : nType = NC_INT;
2622 :
2623 3 : nVarID =
2624 3 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2625 3 : nodata.nVal = NC_FILL_INT;
2626 :
2627 3 : layerVID.nc_put_vatt_text(nVarID, CF_UNITS,
2628 : "days since 1970-1-1");
2629 3 : break;
2630 : }
2631 :
2632 6 : case OFTDateTime:
2633 : {
2634 6 : nType = NC_DOUBLE;
2635 6 : nVarID =
2636 6 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2637 :
2638 6 : nodata.dfVal = NC_FILL_DOUBLE;
2639 :
2640 6 : layerVID.nc_put_vatt_text(nVarID, CF_UNITS,
2641 : "seconds since 1970-1-1 0:0:0");
2642 6 : break;
2643 : }
2644 :
2645 0 : default:
2646 0 : return OGRERR_FAILURE;
2647 : }
2648 :
2649 : FieldDesc fieldDesc;
2650 173 : fieldDesc.uNoData = nodata;
2651 173 : fieldDesc.nType = nType;
2652 173 : fieldDesc.nVarId = nVarID;
2653 173 : fieldDesc.nDimCount = nDimCount;
2654 173 : fieldDesc.nMainDimId = nMainDimId;
2655 173 : fieldDesc.nSecDimId = nSecDimId;
2656 173 : fieldDesc.bHasWarnedAboutTruncation = false;
2657 173 : fieldDesc.bIsDays = (eType == OFTDate);
2658 173 : m_aoFieldDesc.push_back(fieldDesc);
2659 :
2660 : // If we have an alternative name that is compatible with the
2661 : // standard_name attribute, then put it in it.
2662 : // http://cfconventions.org/Data/cf-standard-names/docs/guidelines.html:
2663 : // "Standard names consist of lower-letters, digits and underscores,
2664 : // and begin with a letter. Upper case is not used."
2665 : // Otherwise use it as the long_name, unless we have a comment
2666 173 : bool bAlternativeNameCompatibleOfStandardName = false;
2667 173 : const char *pszAlternativeName = poFieldDefn->GetAlternativeNameRef();
2668 173 : if (pszAlternativeName[0] >= 'a' && pszAlternativeName[0] <= 'z')
2669 : {
2670 2 : bAlternativeNameCompatibleOfStandardName = true;
2671 15 : for (const char *chPtr = pszAlternativeName; *chPtr; ++chPtr)
2672 : {
2673 14 : if (!((*chPtr >= 'a' && *chPtr <= 'z') || *chPtr == '_' ||
2674 1 : (*chPtr >= '0' && *chPtr <= '9')))
2675 : {
2676 1 : bAlternativeNameCompatibleOfStandardName = false;
2677 1 : break;
2678 : }
2679 : }
2680 : }
2681 :
2682 173 : if (!poFieldDefn->GetComment().empty())
2683 : {
2684 2 : layerVID.nc_put_vatt_text(nVarID, CF_LNG_NAME,
2685 2 : poFieldDefn->GetComment().c_str());
2686 : }
2687 171 : else if (pszAlternativeName[0] &&
2688 1 : !bAlternativeNameCompatibleOfStandardName)
2689 : {
2690 1 : layerVID.nc_put_vatt_text(nVarID, CF_LNG_NAME, pszAlternativeName);
2691 : }
2692 : else
2693 : {
2694 : const char *pszLongName =
2695 170 : CPLSPrintf("Field %s", poFieldDefn->GetNameRef());
2696 :
2697 170 : layerVID.nc_put_vatt_text(nVarID, CF_LNG_NAME, pszLongName);
2698 : }
2699 :
2700 173 : if (bAlternativeNameCompatibleOfStandardName)
2701 : {
2702 1 : layerVID.nc_put_vatt_text(nVarID, CF_STD_NAME, pszAlternativeName);
2703 : }
2704 :
2705 173 : if (!m_bLegacyCreateMode)
2706 : {
2707 146 : std::string ct_name(m_layerSGDefn.get_containerName());
2708 73 : layerVID.nc_put_vatt_text(nVarID, CF_SG_GEOMETRY, ct_name.c_str());
2709 : }
2710 :
2711 173 : if (m_bWriteGDALTags)
2712 : {
2713 150 : layerVID.nc_put_vatt_text(nVarID, "ogr_field_name",
2714 : poFieldDefn->GetNameRef());
2715 :
2716 150 : const char *pszType = OGRFieldDefn::GetFieldTypeName(eType);
2717 150 : if (eSubType != OFSTNone)
2718 : {
2719 : pszType =
2720 14 : CPLSPrintf("%s(%s)", pszType,
2721 : OGRFieldDefn::GetFieldSubTypeName(eSubType));
2722 : }
2723 :
2724 150 : layerVID.nc_put_vatt_text(nVarID, "ogr_field_type", pszType);
2725 :
2726 150 : const int nWidth = poFieldDefn->GetWidth();
2727 150 : if (nWidth || nType == NC_CHAR)
2728 : {
2729 70 : layerVID.nc_put_vatt_int(nVarID, "ogr_field_width", &nWidth);
2730 :
2731 70 : const int nPrecision = poFieldDefn->GetPrecision();
2732 70 : if (nPrecision)
2733 : {
2734 3 : layerVID.nc_put_vatt_int(nVarID, "ogr_field_precision",
2735 : &nPrecision);
2736 : }
2737 : }
2738 : }
2739 :
2740 : // nc_put_att_text(m_nLayerCDFId, nVarID, CF_UNITS,
2741 : // strlen("none"), "none");
2742 :
2743 173 : if (!m_osGridMapping.empty() && nMainDimId == m_nRecordDimID)
2744 : {
2745 138 : layerVID.nc_put_vatt_text(nVarID, CF_GRD_MAPPING,
2746 : m_osGridMapping.c_str());
2747 : }
2748 :
2749 173 : if (!m_osCoordinatesValue.empty() && nMainDimId == m_nRecordDimID)
2750 : {
2751 83 : layerVID.nc_put_vatt_text(nVarID, CF_COORDINATES,
2752 : m_osCoordinatesValue.c_str());
2753 : }
2754 :
2755 173 : if (poConfig != nullptr)
2756 : {
2757 2 : netCDFWriteAttributesFromConf(m_nLayerCDFId, nVarID,
2758 2 : poConfig->m_aoAttributes);
2759 : }
2760 : }
2761 :
2762 0 : catch (nccfdriver::SG_Exception &e)
2763 : {
2764 0 : CPLError(CE_Failure, CPLE_FileIO, "%s", e.get_err_msg());
2765 0 : return OGRERR_FAILURE;
2766 : }
2767 :
2768 173 : m_poFeatureDefn->AddFieldDefn(poFieldDefn);
2769 173 : return OGRERR_NONE;
2770 : }
2771 :
2772 : /************************************************************************/
2773 : /* GetFeatureCount() */
2774 : /************************************************************************/
2775 :
2776 52 : GIntBig netCDFLayer::GetFeatureCount(int bForce)
2777 : {
2778 52 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
2779 : {
2780 46 : if (!m_bLegacyCreateMode)
2781 : {
2782 32 : return m_simpleGeometryReader->get_geometry_count();
2783 : }
2784 :
2785 : size_t nDimLen;
2786 14 : nc_inq_dimlen(m_nLayerCDFId, m_nRecordDimID, &nDimLen);
2787 14 : return static_cast<GIntBig>(nDimLen);
2788 : }
2789 6 : return OGRLayer::GetFeatureCount(bForce);
2790 : }
2791 :
2792 : /************************************************************************/
2793 : /* TestCapability() */
2794 : /************************************************************************/
2795 :
2796 341 : int netCDFLayer::TestCapability(const char *pszCap) const
2797 : {
2798 341 : if (EQUAL(pszCap, OLCSequentialWrite))
2799 1 : return m_poDS->GetAccess() == GA_Update;
2800 340 : if (EQUAL(pszCap, OLCCreateField))
2801 2 : return m_poDS->GetAccess() == GA_Update;
2802 338 : if (EQUAL(pszCap, OLCFastFeatureCount))
2803 0 : return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
2804 338 : if (EQUAL(pszCap, OLCZGeometries))
2805 54 : return true;
2806 284 : return false;
2807 : }
2808 :
2809 : /************************************************************************/
2810 : /* GetDataset() */
2811 : /************************************************************************/
2812 :
2813 2 : GDALDataset *netCDFLayer::GetDataset()
2814 : {
2815 2 : return m_poDS;
2816 : }
|