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