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 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 306 : return true;
1325 : }
1326 :
1327 : /************************************************************************/
1328 : /* GetNextFeature() */
1329 : /************************************************************************/
1330 :
1331 776 : OGRFeature *netCDFLayer::GetNextFeature()
1332 : {
1333 : while (true)
1334 : {
1335 776 : OGRFeature *poFeature = GetNextRawFeature();
1336 776 : if (poFeature == nullptr)
1337 60 : return nullptr;
1338 :
1339 1465 : if ((m_poFilterGeom == nullptr ||
1340 1413 : FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
1341 697 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
1342 673 : return poFeature;
1343 :
1344 43 : delete poFeature;
1345 43 : }
1346 : }
1347 :
1348 : /************************************************************************/
1349 : /* GetLayerDefn() */
1350 : /************************************************************************/
1351 :
1352 4090 : OGRFeatureDefn *netCDFLayer::GetLayerDefn()
1353 : {
1354 4090 : return m_poFeatureDefn;
1355 : }
1356 :
1357 : /************************************************************************/
1358 : /* ICreateFeature() */
1359 : /************************************************************************/
1360 :
1361 509 : OGRErr netCDFLayer::ICreateFeature(OGRFeature *poFeature)
1362 : {
1363 509 : m_poDS->SetDefineMode(false);
1364 :
1365 509 : size_t nFeatureIdx = 0;
1366 509 : nc_inq_dimlen(m_nLayerCDFId, m_nRecordDimID, &nFeatureIdx);
1367 :
1368 509 : if (!m_bLegacyCreateMode)
1369 : {
1370 : // Detects: append mode
1371 446 : 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 509 : 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 509 : if (!FillVarFromFeature(poFeature, m_nRecordDimID, nFeatureIdx))
1468 0 : return OGRERR_FAILURE;
1469 :
1470 509 : poFeature->SetFID(nFeatureIdx + 1);
1471 :
1472 509 : return OGRERR_NONE;
1473 : }
1474 :
1475 : /************************************************************************/
1476 : /* FillVarFromFeature() */
1477 : /************************************************************************/
1478 :
1479 517 : bool netCDFLayer::FillVarFromFeature(OGRFeature *poFeature, int nMainDimId,
1480 : size_t nIndex)
1481 : {
1482 : size_t anIndex[2];
1483 517 : anIndex[0] = nIndex;
1484 517 : anIndex[1] = 0;
1485 :
1486 2715 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
1487 : {
1488 2198 : if (m_aoFieldDesc[i].nMainDimId != nMainDimId)
1489 46 : continue;
1490 :
1491 2152 : 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 2025 : int status = NC_NOERR;
1504 2025 : switch (m_aoFieldDesc[i].nType)
1505 : {
1506 963 : case NC_CHAR:
1507 : {
1508 963 : const char *pszVal = poFeature->GetFieldAsString(i);
1509 963 : 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 956 : anCount[0] = 1;
1539 956 : anCount[1] = strlen(pszVal);
1540 956 : size_t nWidth = 0;
1541 :
1542 956 : if (m_bLegacyCreateMode)
1543 : {
1544 72 : nc_inq_dimlen(m_nLayerCDFId, m_aoFieldDesc[i].nSecDimId,
1545 : &nWidth);
1546 : }
1547 : else
1548 : {
1549 884 : nWidth =
1550 884 : layerVID.virtualDIDToDim(m_aoFieldDesc[i].nSecDimId)
1551 884 : .getLen();
1552 : }
1553 :
1554 956 : 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 956 : if (m_poDS->HasInfiniteRecordDim())
1605 : {
1606 72 : status = nc_put_vara_text(m_nLayerCDFId,
1607 72 : m_aoFieldDesc[i].nVarId,
1608 : anIndex, anCount, pszVal);
1609 : }
1610 : else
1611 : {
1612 884 : m_poDS->FieldScribe.enqueue_transaction(
1613 1768 : nccfdriver::MTPtr(
1614 : new nccfdriver::OGR_SGFS_NC_CharA_Transaction(
1615 884 : m_aoFieldDesc[i].nVarId, pszVal)));
1616 : }
1617 : }
1618 963 : 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 286 : case NC_INT:
1725 : {
1726 : int nVal;
1727 286 : 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 281 : nVal = poFeature->GetFieldAsInteger(i);
1754 : }
1755 :
1756 286 : if (m_poDS->HasInfiniteRecordDim())
1757 : {
1758 70 : status = nc_put_var1_int(
1759 70 : m_nLayerCDFId, m_aoFieldDesc[i].nVarId, anIndex, &nVal);
1760 : }
1761 : else
1762 : {
1763 216 : m_poDS->FieldScribe.enqueue_transaction(nccfdriver::MTPtr(
1764 : new nccfdriver::OGR_SGFS_NC_Int_Transaction(
1765 216 : m_aoFieldDesc[i].nVarId, nVal)));
1766 : }
1767 :
1768 286 : 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 2025 : NCDF_ERR(status);
1903 2025 : if (status != NC_NOERR)
1904 : {
1905 0 : return false;
1906 : }
1907 : }
1908 :
1909 517 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1910 599 : if (wkbFlatten(m_poFeatureDefn->GetGeomType()) == wkbPoint &&
1911 79 : poGeom != nullptr &&
1912 678 : wkbFlatten(poGeom->getGeometryType()) == wkbPoint &&
1913 79 : m_bLegacyCreateMode)
1914 : {
1915 42 : if (m_osProfileDimName.empty() || nMainDimId == m_nProfileDimID)
1916 : {
1917 22 : auto poPoint = poGeom->toPoint();
1918 22 : double dfX = poPoint->getX();
1919 22 : double dfY = poPoint->getY();
1920 :
1921 : int status;
1922 :
1923 22 : if (m_nXVarNCDFType == NC_DOUBLE)
1924 : status =
1925 22 : 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 22 : NCDF_ERR(status);
1933 22 : if (status != NC_NOERR)
1934 : {
1935 0 : return false;
1936 : }
1937 :
1938 22 : if (m_nYVarNCDFType == NC_DOUBLE)
1939 : status =
1940 22 : 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 22 : NCDF_ERR(status);
1948 22 : if (status != NC_NOERR)
1949 : {
1950 0 : return false;
1951 : }
1952 : }
1953 :
1954 112 : if (m_poFeatureDefn->GetGeomType() == wkbPoint25D &&
1955 70 : (m_osProfileDimName.empty() || nMainDimId == m_nRecordDimID))
1956 : {
1957 : int status;
1958 34 : double dfZ = poGeom->toPoint()->getZ();
1959 34 : if (m_nZVarNCDFType == NC_DOUBLE)
1960 : status =
1961 34 : 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 34 : NCDF_ERR(status);
1969 34 : if (status != NC_NOERR)
1970 : {
1971 0 : return false;
1972 : }
1973 : }
1974 : }
1975 948 : else if (m_poFeatureDefn->GetGeomType() != wkbNone && m_nWKTVarID >= 0 &&
1976 948 : poGeom != nullptr && m_bLegacyCreateMode)
1977 : {
1978 21 : char *pszWKT = nullptr;
1979 21 : poGeom->exportToWkt(&pszWKT, wkbVariantIso);
1980 : int status;
1981 21 : if (m_nWKTNCDFType == NC_STRING)
1982 : {
1983 10 : const char *pszWKTConst = pszWKT;
1984 10 : status = nc_put_var1_string(m_nLayerCDFId, m_nWKTVarID, anIndex,
1985 : &pszWKTConst);
1986 : }
1987 : else
1988 : {
1989 : size_t anCount[2];
1990 11 : anCount[0] = 1;
1991 11 : anCount[1] = strlen(pszWKT);
1992 11 : if (anCount[1] > static_cast<unsigned int>(m_nWKTMaxWidth))
1993 : {
1994 4 : if (m_bAutoGrowStrings)
1995 : {
1996 3 : size_t nNewSize = anCount[1] + anCount[1] / 3;
1997 :
1998 3 : CPLDebug("GDAL_netCDF", "Growing %s from %u to %u",
1999 : m_osWKTVarName.c_str(),
2000 3 : static_cast<unsigned>(m_nWKTMaxWidth),
2001 : static_cast<unsigned>(nNewSize));
2002 3 : m_poDS->GrowDim(m_nLayerCDFId, m_nWKTMaxWidthDimId,
2003 : nNewSize);
2004 :
2005 3 : m_nWKTMaxWidth = static_cast<int>(nNewSize);
2006 :
2007 3 : status = nc_put_vara_text(m_nLayerCDFId, m_nWKTVarID,
2008 : anIndex, anCount, pszWKT);
2009 : }
2010 : else
2011 : {
2012 1 : CPLError(CE_Failure, CPLE_AppDefined,
2013 : "Cannot write geometry as WKT. Would require %d "
2014 : "characters but field width is %d",
2015 1 : static_cast<int>(anCount[1]), m_nWKTMaxWidth);
2016 1 : status = NC_NOERR;
2017 : }
2018 : }
2019 : else
2020 : {
2021 7 : status = nc_put_vara_text(m_nLayerCDFId, m_nWKTVarID, anIndex,
2022 : anCount, pszWKT);
2023 : }
2024 : }
2025 21 : CPLFree(pszWKT);
2026 21 : NCDF_ERR(status);
2027 21 : if (status != NC_NOERR)
2028 : {
2029 0 : return false;
2030 : }
2031 : }
2032 906 : else if (m_poFeatureDefn->GetGeomType() != wkbNone && m_nWKTVarID >= 0 &&
2033 3 : poGeom == nullptr && m_nWKTNCDFType == NC_STRING &&
2034 906 : m_bNCDumpCompat && m_bLegacyCreateMode)
2035 : {
2036 1 : const char *pszWKTConst = "";
2037 1 : int status = nc_put_var1_string(m_nLayerCDFId, m_nWKTVarID, anIndex,
2038 : &pszWKTConst);
2039 1 : NCDF_ERR(status);
2040 : }
2041 :
2042 : try
2043 : {
2044 : // CF 1.8 simple geometry, only
2045 517 : if (!m_bLegacyCreateMode && poGeom != nullptr)
2046 : {
2047 892 : nccfdriver::SGeometry_Feature featWithMetaData(*poFeature);
2048 :
2049 : // Check if ready to dump buffer to LOG
2050 446 : if (m_poDS->bufManager.isOverQuota())
2051 : {
2052 180 : m_poDS->SGLogPendingTransaction();
2053 : }
2054 :
2055 : // Finally, "write" the feature
2056 446 : m_layerSGDefn.writeSGeometryFeature(featWithMetaData);
2057 : }
2058 : }
2059 :
2060 0 : catch (nccfdriver::SG_Exception &sge)
2061 : {
2062 0 : CPLError(CE_Failure, CPLE_AppDefined,
2063 : "An error occurred while attempting to write a feature to the "
2064 : "target netCDF file.\n%s",
2065 0 : sge.get_err_msg());
2066 0 : return false;
2067 : }
2068 :
2069 517 : return true;
2070 : }
2071 :
2072 : /************************************************************************/
2073 : /* AddField() */
2074 : /************************************************************************/
2075 :
2076 633 : bool netCDFLayer::AddField(int nVarID)
2077 : {
2078 633 : if (nVarID == m_nWKTVarID)
2079 9 : return false;
2080 :
2081 : char szName[NC_MAX_NAME + 1];
2082 624 : szName[0] = '\0';
2083 624 : CPL_IGNORE_RET_VAL(nc_inq_varname(m_nLayerCDFId, nVarID, szName));
2084 :
2085 624 : nc_type vartype = NC_NAT;
2086 624 : nc_inq_vartype(m_nLayerCDFId, nVarID, &vartype);
2087 :
2088 624 : OGRFieldType eType = OFTString;
2089 624 : OGRFieldSubType eSubType = OFSTNone;
2090 624 : int nWidth = 0;
2091 :
2092 : NCDFNoDataUnion nodata;
2093 624 : memset(&nodata, 0, sizeof(nodata));
2094 624 : int nDimCount = 1;
2095 624 : nc_inq_varndims(m_nLayerCDFId, nVarID, &nDimCount);
2096 624 : int anDimIds[2] = {-1, -1};
2097 624 : if ((vartype == NC_CHAR && nDimCount <= 2) ||
2098 482 : (vartype != NC_CHAR && nDimCount == 1))
2099 : {
2100 624 : nc_inq_vardimid(m_nLayerCDFId, nVarID, anDimIds);
2101 : }
2102 : else
2103 : {
2104 0 : return false;
2105 : }
2106 :
2107 624 : switch (vartype)
2108 : {
2109 47 : case NC_BYTE:
2110 : {
2111 47 : eType = OFTInteger;
2112 47 : char *pszValue = nullptr;
2113 47 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2114 11 : nodata.chVal = static_cast<signed char>(atoi(pszValue));
2115 : else
2116 36 : nodata.chVal = NC_FILL_BYTE;
2117 47 : CPLFree(pszValue);
2118 47 : break;
2119 : }
2120 :
2121 12 : case NC_UBYTE:
2122 : {
2123 12 : eType = OFTInteger;
2124 12 : char *pszValue = nullptr;
2125 12 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2126 6 : nodata.uchVal = static_cast<unsigned char>(atoi(pszValue));
2127 : else
2128 6 : nodata.uchVal = NC_FILL_UBYTE;
2129 12 : CPLFree(pszValue);
2130 12 : break;
2131 : }
2132 :
2133 142 : case NC_CHAR:
2134 : {
2135 142 : eType = OFTString;
2136 142 : if (nDimCount == 1)
2137 : {
2138 9 : nWidth = 1;
2139 : }
2140 133 : else if (nDimCount == 2)
2141 : {
2142 133 : size_t nDimLen = 0;
2143 133 : nc_inq_dimlen(m_nLayerCDFId, anDimIds[1], &nDimLen);
2144 133 : nWidth = static_cast<int>(nDimLen);
2145 : }
2146 142 : break;
2147 : }
2148 :
2149 20 : case NC_STRING:
2150 : {
2151 20 : eType = OFTString;
2152 20 : break;
2153 : }
2154 :
2155 38 : case NC_SHORT:
2156 : {
2157 38 : eType = OFTInteger;
2158 38 : eSubType = OFSTInt16;
2159 38 : char *pszValue = nullptr;
2160 38 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2161 11 : nodata.sVal = static_cast<short>(atoi(pszValue));
2162 : else
2163 27 : nodata.sVal = NC_FILL_SHORT;
2164 38 : CPLFree(pszValue);
2165 38 : break;
2166 : }
2167 :
2168 12 : case NC_USHORT:
2169 : {
2170 12 : eType = OFTInteger;
2171 12 : char *pszValue = nullptr;
2172 12 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2173 6 : nodata.usVal = static_cast<unsigned short>(atoi(pszValue));
2174 : else
2175 6 : nodata.usVal = NC_FILL_USHORT;
2176 12 : CPLFree(pszValue);
2177 12 : break;
2178 : }
2179 :
2180 115 : case NC_INT:
2181 : {
2182 115 : eType = OFTInteger;
2183 115 : char *pszValue = nullptr;
2184 115 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2185 13 : nodata.nVal = atoi(pszValue);
2186 : else
2187 102 : nodata.nVal = NC_FILL_INT;
2188 115 : CPLFree(pszValue);
2189 115 : break;
2190 : }
2191 :
2192 12 : case NC_UINT:
2193 : {
2194 12 : eType = OFTInteger64;
2195 12 : char *pszValue = nullptr;
2196 12 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2197 6 : nodata.unVal =
2198 6 : static_cast<unsigned int>(CPLAtoGIntBig(pszValue));
2199 : else
2200 6 : nodata.unVal = NC_FILL_UINT;
2201 12 : CPLFree(pszValue);
2202 12 : break;
2203 : }
2204 :
2205 25 : case NC_INT64:
2206 : {
2207 25 : eType = OFTInteger64;
2208 25 : char *pszValue = nullptr;
2209 25 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2210 6 : nodata.nVal64 = CPLAtoGIntBig(pszValue);
2211 : else
2212 19 : nodata.nVal64 = NC_FILL_INT64;
2213 25 : CPLFree(pszValue);
2214 25 : break;
2215 : }
2216 :
2217 12 : case NC_UINT64:
2218 : {
2219 12 : eType = OFTReal;
2220 12 : char *pszValue = nullptr;
2221 12 : if (GetFillValue(nVarID, &pszValue) == CE_None)
2222 : {
2223 6 : nodata.unVal64 = 0;
2224 126 : for (int i = 0; pszValue[i] != '\0'; i++)
2225 : {
2226 120 : nodata.unVal64 = nodata.unVal64 * 10 + (pszValue[i] - '0');
2227 : }
2228 : }
2229 : else
2230 6 : nodata.unVal64 = NC_FILL_UINT64;
2231 12 : CPLFree(pszValue);
2232 12 : break;
2233 : }
2234 :
2235 38 : case NC_FLOAT:
2236 : {
2237 38 : eType = OFTReal;
2238 38 : eSubType = OFSTFloat32;
2239 : double dfValue;
2240 38 : if (GetFillValue(nVarID, &dfValue) == CE_None)
2241 11 : nodata.fVal = static_cast<float>(dfValue);
2242 : else
2243 27 : nodata.fVal = NC_FILL_FLOAT;
2244 38 : break;
2245 : }
2246 :
2247 151 : case NC_DOUBLE:
2248 : {
2249 151 : eType = OFTReal;
2250 : double dfValue;
2251 151 : if (GetFillValue(nVarID, &dfValue) == CE_None)
2252 31 : nodata.dfVal = dfValue;
2253 : else
2254 120 : nodata.dfVal = NC_FILL_DOUBLE;
2255 151 : break;
2256 : }
2257 :
2258 0 : default:
2259 : {
2260 0 : CPLDebug("GDAL_netCDF",
2261 : "Variable %s has type %d, which is unhandled", szName,
2262 : vartype);
2263 0 : return false;
2264 : }
2265 : }
2266 :
2267 624 : bool bIsDays = false;
2268 :
2269 624 : char *pszValue = nullptr;
2270 624 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_type", &pszValue) ==
2271 : CE_None)
2272 : {
2273 434 : if ((eType == OFTInteger || eType == OFTReal) &&
2274 281 : EQUAL(pszValue, "Date"))
2275 : {
2276 17 : eType = OFTDate;
2277 : // cppcheck-suppress knownConditionTrueFalse
2278 17 : bIsDays = (eType == OFTInteger);
2279 : }
2280 417 : else if ((eType == OFTInteger || eType == OFTReal) &&
2281 264 : EQUAL(pszValue, "DateTime"))
2282 23 : eType = OFTDateTime;
2283 394 : else if (eType == OFTReal && EQUAL(pszValue, "Integer64"))
2284 20 : eType = OFTInteger64;
2285 374 : else if (eType == OFTInteger && EQUAL(pszValue, "Integer(Boolean)"))
2286 34 : eSubType = OFSTBoolean;
2287 : }
2288 624 : CPLFree(pszValue);
2289 :
2290 624 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "units", &pszValue) == CE_None)
2291 : {
2292 86 : if ((eType == OFTInteger || eType == OFTReal || eType == OFTDate) &&
2293 40 : (EQUAL(pszValue, "seconds since 1970-1-1 0:0:0") ||
2294 16 : EQUAL(pszValue, "seconds since 1970-01-01 00:00:00")))
2295 : {
2296 24 : if (eType != OFTDate)
2297 13 : eType = OFTDateTime;
2298 24 : bIsDays = false;
2299 : }
2300 62 : else if ((eType == OFTInteger || eType == OFTReal ||
2301 16 : eType == OFTDate) &&
2302 16 : (EQUAL(pszValue, "days since 1970-1-1") ||
2303 9 : EQUAL(pszValue, "days since 1970-01-01")))
2304 : {
2305 7 : eType = OFTDate;
2306 7 : bIsDays = true;
2307 : }
2308 : }
2309 624 : CPLFree(pszValue);
2310 :
2311 624 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_name", &pszValue) ==
2312 : CE_None)
2313 : {
2314 429 : snprintf(szName, sizeof(szName), "%s", pszValue);
2315 : }
2316 624 : CPLFree(pszValue);
2317 :
2318 624 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_width", &pszValue) ==
2319 : CE_None)
2320 : {
2321 151 : nWidth = atoi(pszValue);
2322 : }
2323 624 : CPLFree(pszValue);
2324 :
2325 624 : int nPrecision = 0;
2326 624 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, "ogr_field_precision", &pszValue) ==
2327 : CE_None)
2328 : {
2329 5 : nPrecision = atoi(pszValue);
2330 : }
2331 624 : CPLFree(pszValue);
2332 :
2333 624 : OGRFieldDefn oFieldDefn(szName, eType);
2334 624 : oFieldDefn.SetSubType(eSubType);
2335 624 : oFieldDefn.SetWidth(nWidth);
2336 624 : oFieldDefn.SetPrecision(nPrecision);
2337 :
2338 1078 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, CF_LNG_NAME, &pszValue) == CE_None &&
2339 1078 : pszValue != std::string("Field ") + szName)
2340 : {
2341 7 : oFieldDefn.SetComment(pszValue);
2342 : }
2343 624 : CPLFree(pszValue);
2344 :
2345 629 : if (NCDFGetAttr(m_nLayerCDFId, nVarID, CF_STD_NAME, &pszValue) == CE_None &&
2346 5 : strcmp(pszValue, szName) != 0)
2347 : {
2348 3 : oFieldDefn.SetAlternativeName(pszValue);
2349 : }
2350 624 : CPLFree(pszValue);
2351 :
2352 : FieldDesc fieldDesc;
2353 624 : fieldDesc.uNoData = nodata;
2354 624 : fieldDesc.nType = vartype;
2355 624 : fieldDesc.nVarId = nVarID;
2356 624 : fieldDesc.nDimCount = nDimCount;
2357 624 : fieldDesc.nMainDimId = anDimIds[0];
2358 624 : fieldDesc.nSecDimId = anDimIds[1];
2359 624 : fieldDesc.bHasWarnedAboutTruncation = false;
2360 624 : fieldDesc.bIsDays = bIsDays;
2361 624 : m_aoFieldDesc.push_back(fieldDesc);
2362 :
2363 624 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
2364 :
2365 624 : return true;
2366 : }
2367 :
2368 : /************************************************************************/
2369 : /* CreateField() */
2370 : /************************************************************************/
2371 :
2372 182 : OGRErr netCDFLayer::CreateField(const OGRFieldDefn *poFieldDefn,
2373 : int /* bApproxOK */)
2374 : {
2375 182 : int nSecDimId = -1;
2376 182 : int nVarID = -1;
2377 : int status;
2378 :
2379 182 : const netCDFWriterConfigField *poConfig = nullptr;
2380 182 : if (m_poDS->oWriterConfig.m_bIsValid)
2381 : {
2382 8 : std::map<CPLString, netCDFWriterConfigField>::const_iterator oIter;
2383 12 : if (m_poLayerConfig != nullptr &&
2384 4 : (oIter =
2385 12 : m_poLayerConfig->m_oFields.find(poFieldDefn->GetNameRef())) !=
2386 12 : m_poLayerConfig->m_oFields.end())
2387 : {
2388 1 : poConfig = &(oIter->second);
2389 : }
2390 7 : else if ((oIter = m_poDS->oWriterConfig.m_oFields.find(
2391 14 : poFieldDefn->GetNameRef())) !=
2392 14 : m_poDS->oWriterConfig.m_oFields.end())
2393 : {
2394 2 : poConfig = &(oIter->second);
2395 : }
2396 : }
2397 :
2398 182 : if (!m_osProfileDimName.empty() &&
2399 185 : EQUAL(poFieldDefn->GetNameRef(), m_osProfileDimName) &&
2400 3 : poFieldDefn->GetType() == OFTInteger)
2401 : {
2402 : FieldDesc fieldDesc;
2403 3 : fieldDesc.uNoData.nVal = NC_FILL_INT;
2404 3 : fieldDesc.nType = NC_INT;
2405 3 : fieldDesc.nVarId = m_nProfileVarID;
2406 3 : fieldDesc.nDimCount = 1;
2407 3 : fieldDesc.nMainDimId = m_nProfileDimID;
2408 3 : fieldDesc.nSecDimId = -1;
2409 3 : fieldDesc.bHasWarnedAboutTruncation = false;
2410 3 : fieldDesc.bIsDays = false;
2411 3 : m_aoFieldDesc.push_back(fieldDesc);
2412 3 : m_poFeatureDefn->AddFieldDefn(poFieldDefn);
2413 3 : return OGRERR_NONE;
2414 : }
2415 :
2416 179 : m_poDS->SetDefineMode(true);
2417 :
2418 : // Try to use the field name as variable name, but detects conflict first
2419 : CPLString osVarName(poConfig != nullptr
2420 3 : ? poConfig->m_osNetCDFName
2421 358 : : CPLString(poFieldDefn->GetNameRef()));
2422 :
2423 179 : if (!m_bLegacyCreateMode && m_bWriteGDALTags)
2424 : {
2425 : // To help avoid naming conflicts, append the layer name as a prefix
2426 77 : const char *prefix = this->GetName();
2427 77 : const char *fprefix = "_field_";
2428 :
2429 77 : osVarName = CPLString(prefix) + CPLString(fprefix) + osVarName;
2430 : }
2431 :
2432 179 : bool vCDFHas = false;
2433 179 : if (!m_bLegacyCreateMode)
2434 : {
2435 77 : vCDFHas = layerVID.virtualVarNameDefined(osVarName);
2436 : }
2437 :
2438 : // Also check the real
2439 179 : status = nc_inq_varid(m_nLayerCDFId, osVarName, &nVarID);
2440 179 : if (status == NC_NOERR || vCDFHas)
2441 : {
2442 3 : for (int i = 1; i <= 100; i++)
2443 : {
2444 3 : osVarName = CPLSPrintf("%s%d", poFieldDefn->GetNameRef(), i);
2445 3 : status = nc_inq_varid(m_nLayerCDFId, osVarName, &nVarID);
2446 3 : if (!m_bLegacyCreateMode)
2447 0 : vCDFHas = layerVID.virtualVarNameDefined(osVarName);
2448 3 : if (status != NC_NOERR && !vCDFHas)
2449 3 : break;
2450 : }
2451 :
2452 3 : CPLDebug("netCDF", "Field %s is written in variable %s",
2453 : poFieldDefn->GetNameRef(), osVarName.c_str());
2454 : }
2455 :
2456 179 : const char *pszVarName = osVarName.c_str();
2457 :
2458 : NCDFNoDataUnion nodata;
2459 179 : memset(&nodata, 0, sizeof(nodata));
2460 :
2461 179 : const OGRFieldType eType = poFieldDefn->GetType();
2462 179 : const OGRFieldSubType eSubType = poFieldDefn->GetSubType();
2463 179 : nc_type nType = NC_NAT;
2464 179 : int nDimCount = 1;
2465 :
2466 : // Find which is the dimension that this variable should be indexed against
2467 179 : int nMainDimId = m_nRecordDimID;
2468 179 : if (!m_osProfileVariables.empty())
2469 : {
2470 : char **papszTokens =
2471 3 : CSLTokenizeString2(m_osProfileVariables, ",", CSLT_HONOURSTRINGS);
2472 3 : if (CSLFindString(papszTokens, poFieldDefn->GetNameRef()) >= 0)
2473 1 : nMainDimId = m_nProfileDimID;
2474 3 : CSLDestroy(papszTokens);
2475 : }
2476 181 : if (poConfig != nullptr && !poConfig->m_osMainDim.empty() &&
2477 2 : m_bLegacyCreateMode)
2478 : {
2479 2 : int ndims = 0;
2480 2 : status = nc_inq_ndims(m_nLayerCDFId, &ndims);
2481 2 : NCDF_ERR(status);
2482 2 : bool bFound = false;
2483 4 : for (int idim = 0; idim < ndims; idim++)
2484 : {
2485 : char szDimName[NC_MAX_NAME + 1];
2486 3 : szDimName[0] = 0;
2487 3 : status = nc_inq_dimname(m_poDS->cdfid, idim, szDimName);
2488 3 : NCDF_ERR(status);
2489 3 : if (strcmp(poConfig->m_osMainDim, szDimName) == 0)
2490 : {
2491 1 : nMainDimId = idim;
2492 1 : bFound = true;
2493 1 : break;
2494 : }
2495 : }
2496 2 : if (!bFound)
2497 : {
2498 1 : CPLError(CE_Failure, CPLE_AppDefined,
2499 : "Dimension '%s' does not exist",
2500 : poConfig->m_osMainDim.c_str());
2501 : }
2502 : }
2503 :
2504 : try
2505 : {
2506 179 : switch (eType)
2507 : {
2508 78 : case OFTString:
2509 : case OFTStringList:
2510 : case OFTIntegerList:
2511 : case OFTRealList:
2512 : {
2513 78 : if (poFieldDefn->GetWidth() == 1)
2514 : {
2515 2 : nType = NC_CHAR;
2516 2 : nVarID =
2517 2 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2518 : }
2519 76 : else if (m_poDS->eFormat == NCDF_FORMAT_NC4 &&
2520 13 : m_bUseStringInNC4)
2521 : {
2522 9 : nType = NC_STRING;
2523 9 : nVarID =
2524 9 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2525 : }
2526 : else
2527 : {
2528 67 : if (poFieldDefn->GetWidth() == 0 && !m_bAutoGrowStrings)
2529 : {
2530 1 : if (m_nDefaultMaxWidthDimId < 0)
2531 : {
2532 1 : m_nDefaultMaxWidthDimId = layerVID.nc_def_vdim(
2533 1 : "string_default_max_width", m_nDefaultWidth);
2534 : }
2535 :
2536 1 : nSecDimId = m_nDefaultMaxWidthDimId;
2537 : }
2538 : else
2539 : {
2540 66 : size_t nDim = poFieldDefn->GetWidth() == 0
2541 72 : ? m_nDefaultWidth
2542 6 : : poFieldDefn->GetWidth();
2543 :
2544 : std::string ndimname =
2545 132 : std::string(pszVarName) + std::string("_max_width");
2546 : nSecDimId =
2547 66 : layerVID.nc_def_vdim(ndimname.c_str(), nDim);
2548 : }
2549 :
2550 67 : nDimCount = 2;
2551 67 : int anDims[2] = {nMainDimId, nSecDimId};
2552 67 : nType = NC_CHAR;
2553 67 : nVarID = layerVID.nc_def_vvar(pszVarName, nType, 2, anDims);
2554 : }
2555 :
2556 78 : break;
2557 : }
2558 :
2559 51 : case OFTInteger:
2560 : {
2561 96 : nType = eSubType == OFSTBoolean ? NC_BYTE
2562 45 : : (eSubType == OFSTInt16) ? NC_SHORT
2563 : : NC_INT;
2564 :
2565 51 : if (nType == NC_BYTE)
2566 6 : nodata.chVal = NC_FILL_BYTE;
2567 45 : else if (nType == NC_SHORT)
2568 7 : nodata.sVal = NC_FILL_SHORT;
2569 38 : else if (nType == NC_INT)
2570 38 : nodata.nVal = NC_FILL_INT;
2571 :
2572 51 : nVarID =
2573 51 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2574 :
2575 51 : if (eSubType == OFSTBoolean)
2576 : {
2577 6 : if (m_bLegacyCreateMode)
2578 : {
2579 6 : signed char anRange[2] = {0, 1};
2580 6 : nc_put_att_schar(m_nLayerCDFId, nVarID, "valid_range",
2581 : NC_BYTE, 2, anRange);
2582 : }
2583 : }
2584 :
2585 51 : break;
2586 : }
2587 :
2588 11 : case OFTInteger64:
2589 : {
2590 11 : nType = NC_DOUBLE;
2591 11 : nodata.dfVal = NC_FILL_DOUBLE;
2592 11 : if (m_poDS->eFormat == NCDF_FORMAT_NC4)
2593 : {
2594 5 : nType = NC_INT64;
2595 5 : nodata.nVal64 = NC_FILL_INT64;
2596 : }
2597 :
2598 11 : nVarID =
2599 11 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2600 11 : break;
2601 : }
2602 :
2603 30 : case OFTReal:
2604 : {
2605 30 : nType = (eSubType == OFSTFloat32) ? NC_FLOAT : NC_DOUBLE;
2606 30 : if (eSubType == OFSTFloat32)
2607 7 : nodata.fVal = NC_FILL_FLOAT;
2608 : else
2609 23 : nodata.dfVal = NC_FILL_DOUBLE;
2610 :
2611 30 : nVarID =
2612 30 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2613 30 : break;
2614 : }
2615 :
2616 3 : case OFTDate:
2617 : {
2618 3 : nType = NC_INT;
2619 :
2620 3 : nVarID =
2621 3 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2622 3 : nodata.nVal = NC_FILL_INT;
2623 :
2624 3 : layerVID.nc_put_vatt_text(nVarID, CF_UNITS,
2625 : "days since 1970-1-1");
2626 3 : break;
2627 : }
2628 :
2629 6 : case OFTDateTime:
2630 : {
2631 6 : nType = NC_DOUBLE;
2632 6 : nVarID =
2633 6 : layerVID.nc_def_vvar(pszVarName, nType, 1, &nMainDimId);
2634 :
2635 6 : nodata.dfVal = NC_FILL_DOUBLE;
2636 :
2637 6 : layerVID.nc_put_vatt_text(nVarID, CF_UNITS,
2638 : "seconds since 1970-1-1 0:0:0");
2639 6 : break;
2640 : }
2641 :
2642 0 : default:
2643 0 : return OGRERR_FAILURE;
2644 : }
2645 :
2646 : FieldDesc fieldDesc;
2647 179 : fieldDesc.uNoData = nodata;
2648 179 : fieldDesc.nType = nType;
2649 179 : fieldDesc.nVarId = nVarID;
2650 179 : fieldDesc.nDimCount = nDimCount;
2651 179 : fieldDesc.nMainDimId = nMainDimId;
2652 179 : fieldDesc.nSecDimId = nSecDimId;
2653 179 : fieldDesc.bHasWarnedAboutTruncation = false;
2654 179 : fieldDesc.bIsDays = (eType == OFTDate);
2655 179 : m_aoFieldDesc.push_back(fieldDesc);
2656 :
2657 : // If we have an alternative name that is compatible of the
2658 : // standard_name attribute, then put it in it.
2659 : // http://cfconventions.org/Data/cf-standard-names/docs/guidelines.html:
2660 : // "Standard names consist of lower-letters, digits and underscores,
2661 : // and begin with a letter. Upper case is not used."
2662 : // Otherwise use it as the long_name, unless we have a comment
2663 179 : bool bAlternativeNameCompatibleOfStandardName = false;
2664 179 : const char *pszAlternativeName = poFieldDefn->GetAlternativeNameRef();
2665 179 : if (pszAlternativeName[0] >= 'a' && pszAlternativeName[0] <= 'z')
2666 : {
2667 2 : bAlternativeNameCompatibleOfStandardName = true;
2668 15 : for (const char *chPtr = pszAlternativeName; *chPtr; ++chPtr)
2669 : {
2670 14 : if (!((*chPtr >= 'a' && *chPtr <= 'z') || *chPtr == '_' ||
2671 1 : (*chPtr >= '0' && *chPtr <= '9')))
2672 : {
2673 1 : bAlternativeNameCompatibleOfStandardName = false;
2674 1 : break;
2675 : }
2676 : }
2677 : }
2678 :
2679 179 : if (!poFieldDefn->GetComment().empty())
2680 : {
2681 2 : layerVID.nc_put_vatt_text(nVarID, CF_LNG_NAME,
2682 2 : poFieldDefn->GetComment().c_str());
2683 : }
2684 177 : else if (pszAlternativeName[0] &&
2685 1 : !bAlternativeNameCompatibleOfStandardName)
2686 : {
2687 1 : layerVID.nc_put_vatt_text(nVarID, CF_LNG_NAME, pszAlternativeName);
2688 : }
2689 : else
2690 : {
2691 : const char *pszLongName =
2692 176 : CPLSPrintf("Field %s", poFieldDefn->GetNameRef());
2693 :
2694 176 : layerVID.nc_put_vatt_text(nVarID, CF_LNG_NAME, pszLongName);
2695 : }
2696 :
2697 179 : if (bAlternativeNameCompatibleOfStandardName)
2698 : {
2699 1 : layerVID.nc_put_vatt_text(nVarID, CF_STD_NAME, pszAlternativeName);
2700 : }
2701 :
2702 179 : if (!m_bLegacyCreateMode)
2703 : {
2704 154 : std::string ct_name(m_layerSGDefn.get_containerName());
2705 77 : layerVID.nc_put_vatt_text(nVarID, CF_SG_GEOMETRY, ct_name.c_str());
2706 : }
2707 :
2708 179 : if (m_bWriteGDALTags)
2709 : {
2710 156 : layerVID.nc_put_vatt_text(nVarID, "ogr_field_name",
2711 : poFieldDefn->GetNameRef());
2712 :
2713 156 : const char *pszType = OGRFieldDefn::GetFieldTypeName(eType);
2714 156 : if (eSubType != OFSTNone)
2715 : {
2716 : pszType =
2717 14 : CPLSPrintf("%s(%s)", pszType,
2718 : OGRFieldDefn::GetFieldSubTypeName(eSubType));
2719 : }
2720 :
2721 156 : layerVID.nc_put_vatt_text(nVarID, "ogr_field_type", pszType);
2722 :
2723 156 : const int nWidth = poFieldDefn->GetWidth();
2724 156 : if (nWidth || nType == NC_CHAR)
2725 : {
2726 74 : layerVID.nc_put_vatt_int(nVarID, "ogr_field_width", &nWidth);
2727 :
2728 74 : const int nPrecision = poFieldDefn->GetPrecision();
2729 74 : if (nPrecision)
2730 : {
2731 3 : layerVID.nc_put_vatt_int(nVarID, "ogr_field_precision",
2732 : &nPrecision);
2733 : }
2734 : }
2735 : }
2736 :
2737 : // nc_put_att_text(m_nLayerCDFId, nVarID, CF_UNITS,
2738 : // strlen("none"), "none");
2739 :
2740 179 : if (!m_osGridMapping.empty() && nMainDimId == m_nRecordDimID)
2741 : {
2742 138 : layerVID.nc_put_vatt_text(nVarID, CF_GRD_MAPPING,
2743 : m_osGridMapping.c_str());
2744 : }
2745 :
2746 179 : if (!m_osCoordinatesValue.empty() && nMainDimId == m_nRecordDimID)
2747 : {
2748 85 : layerVID.nc_put_vatt_text(nVarID, CF_COORDINATES,
2749 : m_osCoordinatesValue.c_str());
2750 : }
2751 :
2752 179 : if (poConfig != nullptr)
2753 : {
2754 3 : netCDFWriteAttributesFromConf(m_nLayerCDFId, nVarID,
2755 3 : poConfig->m_aoAttributes);
2756 : }
2757 : }
2758 :
2759 0 : catch (nccfdriver::SG_Exception &e)
2760 : {
2761 0 : CPLError(CE_Failure, CPLE_FileIO, "%s", e.get_err_msg());
2762 0 : return OGRERR_FAILURE;
2763 : }
2764 :
2765 179 : m_poFeatureDefn->AddFieldDefn(poFieldDefn);
2766 179 : return OGRERR_NONE;
2767 : }
2768 :
2769 : /************************************************************************/
2770 : /* GetFeatureCount() */
2771 : /************************************************************************/
2772 :
2773 52 : GIntBig netCDFLayer::GetFeatureCount(int bForce)
2774 : {
2775 52 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
2776 : {
2777 46 : if (!m_bLegacyCreateMode)
2778 : {
2779 32 : return m_simpleGeometryReader->get_geometry_count();
2780 : }
2781 :
2782 : size_t nDimLen;
2783 14 : nc_inq_dimlen(m_nLayerCDFId, m_nRecordDimID, &nDimLen);
2784 14 : return static_cast<GIntBig>(nDimLen);
2785 : }
2786 6 : return OGRLayer::GetFeatureCount(bForce);
2787 : }
2788 :
2789 : /************************************************************************/
2790 : /* TestCapability() */
2791 : /************************************************************************/
2792 :
2793 245 : int netCDFLayer::TestCapability(const char *pszCap)
2794 : {
2795 245 : if (EQUAL(pszCap, OLCSequentialWrite))
2796 1 : return m_poDS->GetAccess() == GA_Update;
2797 244 : if (EQUAL(pszCap, OLCCreateField))
2798 2 : return m_poDS->GetAccess() == GA_Update;
2799 242 : if (EQUAL(pszCap, OLCFastFeatureCount))
2800 0 : return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
2801 242 : if (EQUAL(pszCap, OLCZGeometries))
2802 3 : return true;
2803 239 : return false;
2804 : }
2805 :
2806 : /************************************************************************/
2807 : /* GetDataset() */
2808 : /************************************************************************/
2809 :
2810 2 : GDALDataset *netCDFLayer::GetDataset()
2811 : {
2812 2 : return m_poDS;
2813 : }
|