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