Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: netCDF read/write Driver
4 : * Author: Even Rouault <even.rouault at spatialys.com>
5 : *
6 : ******************************************************************************
7 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
8 : *
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #include <algorithm>
13 : #include <cinttypes>
14 : #include <limits>
15 : #include <map>
16 :
17 : #include "netcdfdataset.h"
18 : #include "netcdfdrivercore.h"
19 :
20 : #include "netcdf_mem.h"
21 :
22 : /************************************************************************/
23 : /* netCDFSharedResources */
24 : /************************************************************************/
25 :
26 : class netCDFSharedResources
27 : {
28 : friend class netCDFDataset;
29 :
30 : bool m_bImappIsInElements = true;
31 : bool m_bReadOnly = true;
32 : bool m_bIsNC4 = false;
33 : int m_cdfid = 0;
34 : #ifdef ENABLE_NCDUMP
35 : bool m_bFileToDestroyAtClosing = false;
36 : #endif
37 : CPLString m_osFilename{};
38 : #ifdef ENABLE_UFFD
39 : cpl_uffd_context *m_pUffdCtx = nullptr;
40 : #endif
41 : VSILFILE *m_fpVSIMEM = nullptr;
42 : bool m_bDefineMode = false;
43 : std::map<int, int> m_oMapDimIdToGroupId{};
44 : bool m_bIsInIndexingVariable = false;
45 : std::shared_ptr<GDALPamMultiDim> m_poPAM{};
46 : std::map<int, std::weak_ptr<GDALDimension>> m_oCachedDimensions{};
47 :
48 : public:
49 : explicit netCDFSharedResources(const std::string &osFilename);
50 : ~netCDFSharedResources();
51 :
52 583 : inline int GetCDFId() const
53 : {
54 583 : return m_cdfid;
55 : }
56 :
57 1870 : inline bool IsReadOnly() const
58 : {
59 1870 : return m_bReadOnly;
60 : }
61 :
62 161 : inline bool IsNC4() const
63 : {
64 161 : return m_bIsNC4;
65 : }
66 :
67 : bool SetDefineMode(bool bNewDefineMode);
68 : int GetBelongingGroupOfDim(int startgid, int dimid);
69 :
70 7 : inline bool GetImappIsInElements() const
71 : {
72 7 : return m_bImappIsInElements;
73 : }
74 :
75 1112 : void SetIsInGetIndexingVariable(bool b)
76 : {
77 1112 : m_bIsInIndexingVariable = b;
78 1112 : }
79 :
80 1731 : bool GetIsInIndexingVariable() const
81 : {
82 1731 : return m_bIsInIndexingVariable;
83 : }
84 :
85 595 : const std::string &GetFilename() const
86 : {
87 595 : return m_osFilename;
88 : }
89 :
90 1650 : const std::shared_ptr<GDALPamMultiDim> &GetPAM()
91 : {
92 1650 : return m_poPAM;
93 : }
94 :
95 1578 : void CacheDimension(int dimid, const std::shared_ptr<GDALDimension> &poDim)
96 : {
97 1578 : m_oCachedDimensions[dimid] = poDim;
98 1578 : }
99 :
100 2343 : std::shared_ptr<GDALDimension> GetCachedDimension(int dimid) const
101 : {
102 2343 : auto oIter = m_oCachedDimensions.find(dimid);
103 2343 : if (oIter == m_oCachedDimensions.end())
104 402 : return nullptr;
105 1941 : return oIter->second.lock();
106 : }
107 : };
108 :
109 : /************************************************************************/
110 : /* netCDFSharedResources() */
111 : /************************************************************************/
112 :
113 228 : netCDFSharedResources::netCDFSharedResources(const std::string &osFilename)
114 : : m_bImappIsInElements(false), m_osFilename(osFilename),
115 228 : m_poPAM(std::make_shared<GDALPamMultiDim>(osFilename))
116 : {
117 : // netcdf >= 4.4 uses imapp argument of nc_get/put_varm as a stride in
118 : // elements, whereas earlier versions use bytes.
119 : CPLStringList aosVersionNumbers(
120 456 : CSLTokenizeString2(nc_inq_libvers(), ".", 0));
121 228 : m_bImappIsInElements = false;
122 228 : if (aosVersionNumbers.size() >= 3)
123 : {
124 228 : m_bImappIsInElements =
125 228 : (atoi(aosVersionNumbers[0]) > 4 || atoi(aosVersionNumbers[1]) >= 4);
126 : }
127 228 : }
128 :
129 : /************************************************************************/
130 : /* GetBelongingGroupOfDim() */
131 : /************************************************************************/
132 :
133 1520 : int netCDFSharedResources::GetBelongingGroupOfDim(int startgid, int dimid)
134 : {
135 : // Am I missing a netCDF API to do this directly ?
136 1520 : auto oIter = m_oMapDimIdToGroupId.find(dimid);
137 1520 : if (oIter != m_oMapDimIdToGroupId.end())
138 1219 : return oIter->second;
139 :
140 301 : int gid = startgid;
141 : while (true)
142 : {
143 316 : int nbDims = 0;
144 316 : NCDF_ERR(nc_inq_ndims(gid, &nbDims));
145 316 : if (nbDims > 0)
146 : {
147 301 : std::vector<int> dimids(nbDims);
148 301 : NCDF_ERR(nc_inq_dimids(gid, &nbDims, &dimids[0], FALSE));
149 650 : for (int i = 0; i < nbDims; i++)
150 : {
151 650 : m_oMapDimIdToGroupId[dimid] = gid;
152 650 : if (dimids[i] == dimid)
153 301 : return gid;
154 : }
155 : }
156 15 : int nParentGID = 0;
157 15 : if (nc_inq_grp_parent(gid, &nParentGID) != NC_NOERR)
158 0 : return startgid;
159 15 : gid = nParentGID;
160 15 : }
161 : }
162 :
163 : /************************************************************************/
164 : /* SetDefineMode() */
165 : /************************************************************************/
166 :
167 964 : bool netCDFSharedResources::SetDefineMode(bool bNewDefineMode)
168 : {
169 : // Do nothing if already in new define mode
170 : // or if dataset is in read-only mode or if dataset is NC4 format.
171 964 : if (m_bDefineMode == bNewDefineMode || m_bReadOnly || m_bIsNC4)
172 950 : return true;
173 :
174 14 : CPLDebug("GDAL_netCDF", "SetDefineMode(%d) new=%d, old=%d", m_cdfid,
175 14 : static_cast<int>(bNewDefineMode), static_cast<int>(m_bDefineMode));
176 :
177 14 : m_bDefineMode = bNewDefineMode;
178 :
179 : int status;
180 14 : if (m_bDefineMode)
181 8 : status = nc_redef(m_cdfid);
182 : else
183 6 : status = nc_enddef(m_cdfid);
184 :
185 14 : NCDF_ERR(status);
186 14 : return status == NC_NOERR;
187 : }
188 :
189 : /************************************************************************/
190 : /* netCDFAttributeHolder */
191 : /************************************************************************/
192 :
193 : class netCDFAttributeHolder CPL_NON_FINAL
194 : {
195 : protected:
196 : std::map<std::string, GDALAttribute *> m_oMapAttributes{};
197 :
198 : public:
199 660 : void RegisterAttribute(GDALAttribute *poAttr)
200 : {
201 660 : m_oMapAttributes[poAttr->GetName()] = poAttr;
202 660 : }
203 :
204 596 : void UnRegisterAttribute(GDALAttribute *poAttr)
205 : {
206 596 : m_oMapAttributes.erase(poAttr->GetName());
207 596 : }
208 : };
209 :
210 : /************************************************************************/
211 : /* netCDFGroup */
212 : /************************************************************************/
213 :
214 : class netCDFGroup final : public GDALGroup, public netCDFAttributeHolder
215 : {
216 : std::shared_ptr<netCDFSharedResources> m_poShared;
217 : int m_gid = 0;
218 : CPLStringList m_aosStructuralInfo{};
219 : std::weak_ptr<netCDFGroup> m_poParent{};
220 : std::set<GDALGroup *> m_oSetGroups{};
221 : std::set<GDALDimension *> m_oSetDimensions{};
222 : std::set<GDALMDArray *> m_oSetArrays{};
223 :
224 583 : static std::string retrieveName(int gid)
225 : {
226 583 : CPLMutexHolderD(&hNCMutex);
227 583 : char szName[NC_MAX_NAME + 1] = {};
228 583 : NCDF_ERR(nc_inq_grpname(gid, szName));
229 1166 : return szName;
230 : }
231 :
232 48 : void RegisterSubGroup(GDALGroup *poSubGroup)
233 : {
234 48 : m_oSetGroups.insert(poSubGroup);
235 48 : }
236 :
237 32 : void UnRegisterSubGroup(GDALGroup *poSubGroup)
238 : {
239 32 : m_oSetGroups.erase(poSubGroup);
240 32 : }
241 :
242 : protected:
243 : friend class netCDFDimension;
244 :
245 509 : void RegisterDimension(GDALDimension *poDim)
246 : {
247 509 : m_oSetDimensions.insert(poDim);
248 509 : }
249 :
250 229 : void UnRegisterDimension(GDALDimension *poDim)
251 : {
252 229 : m_oSetDimensions.erase(poDim);
253 229 : }
254 :
255 : friend class netCDFVariable;
256 :
257 533 : void RegisterArray(GDALMDArray *poArray)
258 : {
259 533 : m_oSetArrays.insert(poArray);
260 533 : }
261 :
262 322 : void UnRegisterArray(GDALMDArray *poArray)
263 : {
264 322 : m_oSetArrays.erase(poArray);
265 322 : }
266 :
267 : void NotifyChildrenOfRenaming() override;
268 :
269 : netCDFGroup(const std::shared_ptr<netCDFSharedResources> &poShared,
270 : int gid);
271 :
272 : public:
273 : ~netCDFGroup();
274 :
275 : static std::shared_ptr<netCDFGroup>
276 : Create(const std::shared_ptr<netCDFSharedResources> &poShared, int cdfid);
277 :
278 : static std::shared_ptr<netCDFGroup>
279 : Create(const std::shared_ptr<netCDFSharedResources> &poShared,
280 : const std::shared_ptr<netCDFGroup> &poParent, int nSubGroupId);
281 :
282 : std::vector<std::string>
283 : GetGroupNames(CSLConstList papszOptions) const override;
284 : std::shared_ptr<GDALGroup>
285 : OpenGroup(const std::string &osName,
286 : CSLConstList papszOptions = nullptr) const override;
287 :
288 : std::vector<std::string>
289 : GetMDArrayNames(CSLConstList papszOptions) const override;
290 : std::shared_ptr<GDALMDArray>
291 : OpenMDArray(const std::string &osName,
292 : CSLConstList papszOptions) const override;
293 :
294 : std::vector<std::shared_ptr<GDALDimension>>
295 : GetDimensions(CSLConstList papszOptions) const override;
296 :
297 : std::shared_ptr<GDALAttribute>
298 : GetAttribute(const std::string &osName) const override;
299 :
300 : std::vector<std::shared_ptr<GDALAttribute>>
301 : GetAttributes(CSLConstList papszOptions) const override;
302 :
303 : std::shared_ptr<GDALGroup> CreateGroup(const std::string &osName,
304 : CSLConstList papszOptions) override;
305 :
306 : std::shared_ptr<GDALDimension>
307 : CreateDimension(const std::string &osName, const std::string &osType,
308 : const std::string &osDirection, GUInt64 nSize,
309 : CSLConstList papszOptions) override;
310 :
311 : std::shared_ptr<GDALMDArray> CreateMDArray(
312 : const std::string &osName,
313 : const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
314 : const GDALExtendedDataType &oDataType,
315 : CSLConstList papszOptions) override;
316 :
317 : std::shared_ptr<GDALAttribute>
318 : CreateAttribute(const std::string &osName,
319 : const std::vector<GUInt64> &anDimensions,
320 : const GDALExtendedDataType &oDataType,
321 : CSLConstList papszOptions) override;
322 :
323 : bool DeleteAttribute(const std::string &osName,
324 : CSLConstList papszOptions) override;
325 :
326 : CSLConstList GetStructuralInfo() const override;
327 :
328 : void ClearStatistics() override;
329 :
330 : bool Rename(const std::string &osNewName) override;
331 : };
332 :
333 : /************************************************************************/
334 : /* netCDFVirtualGroupBySameDimension */
335 : /************************************************************************/
336 :
337 : class netCDFVirtualGroupBySameDimension final : public GDALGroup
338 : {
339 : // the real group to which we derived this virtual group from
340 : std::shared_ptr<netCDFGroup> m_poGroup;
341 : std::string m_osDimName{};
342 :
343 : protected:
344 : netCDFVirtualGroupBySameDimension(
345 : const std::shared_ptr<netCDFGroup> &poGroup,
346 : const std::string &osDimName);
347 :
348 : public:
349 : static std::shared_ptr<netCDFVirtualGroupBySameDimension>
350 : Create(const std::shared_ptr<netCDFGroup> &poGroup,
351 : const std::string &osDimName);
352 :
353 : std::vector<std::string>
354 : GetMDArrayNames(CSLConstList papszOptions) const override;
355 : std::shared_ptr<GDALMDArray>
356 : OpenMDArray(const std::string &osName,
357 : CSLConstList papszOptions) const override;
358 : };
359 :
360 : /************************************************************************/
361 : /* netCDFDimension */
362 : /************************************************************************/
363 :
364 : class netCDFDimension final : public GDALDimension
365 : {
366 : std::shared_ptr<netCDFSharedResources> m_poShared;
367 : int m_gid = 0;
368 : int m_dimid = 0;
369 : std::weak_ptr<netCDFGroup> m_poParent{};
370 :
371 1715 : static std::string retrieveName(int cfid, int dimid)
372 : {
373 1715 : CPLMutexHolderD(&hNCMutex);
374 1715 : char szName[NC_MAX_NAME + 1] = {};
375 1715 : NCDF_ERR(nc_inq_dimname(cfid, dimid, szName));
376 3430 : return szName;
377 : }
378 :
379 2036 : static GUInt64 retrieveSize(int cfid, int dimid)
380 : {
381 2036 : CPLMutexHolderD(&hNCMutex);
382 2036 : size_t nDimLen = 0;
383 2036 : NCDF_ERR(nc_inq_dimlen(cfid, dimid, &nDimLen));
384 4072 : return nDimLen;
385 : }
386 :
387 : public:
388 : netCDFDimension(const std::shared_ptr<netCDFSharedResources> &poShared,
389 : int cfid, int dimid, size_t nForcedSize,
390 : const std::string &osType);
391 :
392 : ~netCDFDimension();
393 :
394 : static std::shared_ptr<netCDFDimension>
395 : Create(const std::shared_ptr<netCDFSharedResources> &poShared,
396 : const std::shared_ptr<netCDFGroup> &poParent, int cfid, int dimid,
397 : size_t nForcedSize, const std::string &osType);
398 :
399 : std::shared_ptr<GDALMDArray> GetIndexingVariable() const override;
400 :
401 199 : int GetId() const
402 : {
403 199 : return m_dimid;
404 : }
405 :
406 454 : GUInt64 GetActualSize() const
407 : {
408 454 : return retrieveSize(m_gid, m_dimid);
409 : }
410 :
411 3 : void SetSize(GUInt64 nNewSize)
412 : {
413 3 : m_nSize = nNewSize;
414 3 : }
415 :
416 : bool Rename(const std::string &osNewName) override;
417 : };
418 :
419 : /************************************************************************/
420 : /* netCDFAttribute */
421 : /************************************************************************/
422 :
423 : class netCDFVariable;
424 :
425 : class netCDFAttribute final : public GDALAttribute
426 : {
427 : std::shared_ptr<netCDFSharedResources> m_poShared;
428 : std::weak_ptr<netCDFAttributeHolder> m_poParent;
429 : int m_gid = 0;
430 : int m_varid = 0;
431 : size_t m_nTextLength = 0;
432 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
433 : nc_type m_nAttType = NC_NAT;
434 : mutable std::unique_ptr<GDALExtendedDataType> m_dt;
435 : mutable bool m_bPerfectDataTypeMatch = false;
436 :
437 : protected:
438 : netCDFAttribute(const std::shared_ptr<netCDFSharedResources> &poShared,
439 : int gid, int varid, const std::string &name);
440 :
441 : netCDFAttribute(const std::shared_ptr<netCDFSharedResources> &poShared,
442 : int gid, int varid, const std::string &osName,
443 : const std::vector<GUInt64> &anDimensions,
444 : const GDALExtendedDataType &oDataType,
445 : CSLConstList papszOptions);
446 :
447 : bool
448 : IRead(const GUInt64 *arrayStartIdx, // array of size GetDimensionCount()
449 : const size_t *count, // array of size GetDimensionCount()
450 : const GInt64 *arrayStep, // step in elements
451 : const GPtrDiff_t *bufferStride, // stride in elements
452 : const GDALExtendedDataType &bufferDataType,
453 : void *pDstBuffer) const override;
454 :
455 : bool
456 : IWrite(const GUInt64 *arrayStartIdx, // array of size GetDimensionCount()
457 : const size_t *count, // array of size GetDimensionCount()
458 : const GInt64 *arrayStep, // step in elements
459 : const GPtrDiff_t *bufferStride, // stride in elements
460 : const GDALExtendedDataType &bufferDataType,
461 : const void *pSrcBuffer) override;
462 :
463 : public:
464 : ~netCDFAttribute() override;
465 :
466 : static std::shared_ptr<netCDFAttribute>
467 : Create(const std::shared_ptr<netCDFSharedResources> &poShared,
468 : const std::shared_ptr<netCDFAttributeHolder> &poParent, int gid,
469 : int varid, const std::string &name);
470 :
471 : static std::shared_ptr<netCDFAttribute>
472 : Create(const std::shared_ptr<netCDFSharedResources> &poShared,
473 : const std::shared_ptr<netCDFAttributeHolder> &poParent, int gid,
474 : int varid, const std::string &osName,
475 : const std::vector<GUInt64> &anDimensions,
476 : const GDALExtendedDataType &oDataType, CSLConstList papszOptions);
477 :
478 : const std::vector<std::shared_ptr<GDALDimension>> &
479 2469 : GetDimensions() const override
480 : {
481 2469 : return m_dims;
482 : }
483 :
484 : const GDALExtendedDataType &GetDataType() const override;
485 :
486 : bool Rename(const std::string &osNewName) override;
487 : };
488 :
489 : /************************************************************************/
490 : /* netCDFVariable */
491 : /************************************************************************/
492 :
493 : class netCDFVariable final : public GDALPamMDArray, public netCDFAttributeHolder
494 : {
495 : std::shared_ptr<netCDFSharedResources> m_poShared;
496 : std::weak_ptr<netCDFGroup> m_poParent{};
497 : int m_gid = 0;
498 : int m_varid = 0;
499 : int m_nDims = 0;
500 : mutable std::vector<std::shared_ptr<GDALDimension>> m_dims{};
501 : mutable nc_type m_nVarType = NC_NAT;
502 : mutable std::unique_ptr<GDALExtendedDataType> m_dt;
503 : mutable bool m_bPerfectDataTypeMatch = false;
504 : mutable std::vector<GByte> m_abyNoData{};
505 : mutable bool m_bGetRawNoDataValueHasRun = false;
506 : bool m_bHasWrittenData = true;
507 : bool m_bUseDefaultFillAsNoData = false;
508 : std::string m_osUnit{};
509 : CPLStringList m_aosStructuralInfo{};
510 : mutable bool m_bSRSRead = false;
511 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
512 : bool m_bWriteGDALTags = true;
513 : size_t m_nTextLength = 0;
514 : mutable std::vector<GUInt64> m_cachedArrayStartIdx{};
515 : mutable std::vector<size_t> m_cachedCount{};
516 : mutable std::shared_ptr<GDALMDArray> m_poCachedArray{};
517 :
518 : void ConvertNCToGDAL(GByte *) const;
519 : void ConvertGDALToNC(GByte *) const;
520 :
521 : bool ReadOneElement(const GDALExtendedDataType &src_datatype,
522 : const GDALExtendedDataType &bufferDataType,
523 : const size_t *array_idx, void *pDstBuffer) const;
524 :
525 : bool WriteOneElement(const GDALExtendedDataType &dst_datatype,
526 : const GDALExtendedDataType &bufferDataType,
527 : const size_t *array_idx, const void *pSrcBuffer) const;
528 :
529 : template <typename BufferType, typename NCGetPutVar1FuncType,
530 : typename ReadOrWriteOneElementType>
531 : bool
532 : IReadWriteGeneric(const size_t *arrayStartIdx, const size_t *count,
533 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
534 : const GDALExtendedDataType &bufferDataType,
535 : BufferType buffer, NCGetPutVar1FuncType NCGetPutVar1Func,
536 : ReadOrWriteOneElementType ReadOrWriteOneElement) const;
537 :
538 : template <typename BufferType, typename NCGetPutVar1FuncType,
539 : typename NCGetPutVaraFuncType, typename NCGetPutVarmFuncType,
540 : typename ReadOrWriteOneElementType>
541 : bool IReadWrite(const bool bIsRead, const GUInt64 *arrayStartIdx,
542 : const size_t *count, const GInt64 *arrayStep,
543 : const GPtrDiff_t *bufferStride,
544 : const GDALExtendedDataType &bufferDataType,
545 : BufferType buffer, NCGetPutVar1FuncType NCGetPutVar1Func,
546 : NCGetPutVaraFuncType NCGetPutVaraFunc,
547 : NCGetPutVarmFuncType NCGetPutVarmFunc,
548 : ReadOrWriteOneElementType ReadOrWriteOneElement) const;
549 :
550 : protected:
551 : netCDFVariable(const std::shared_ptr<netCDFSharedResources> &poShared,
552 : int gid, int varid,
553 : const std::vector<std::shared_ptr<GDALDimension>> &dims,
554 : CSLConstList papszOptions);
555 :
556 : bool
557 : IRead(const GUInt64 *arrayStartIdx, // array of size GetDimensionCount()
558 : const size_t *count, // array of size GetDimensionCount()
559 : const GInt64 *arrayStep, // step in elements
560 : const GPtrDiff_t *bufferStride, // stride in elements
561 : const GDALExtendedDataType &bufferDataType,
562 : void *pDstBuffer) const override;
563 :
564 : bool
565 : IWrite(const GUInt64 *arrayStartIdx, // array of size GetDimensionCount()
566 : const size_t *count, // array of size GetDimensionCount()
567 : const GInt64 *arrayStep, // step in elements
568 : const GPtrDiff_t *bufferStride, // stride in elements
569 : const GDALExtendedDataType &bufferDataType,
570 : const void *pSrcBuffer) override;
571 :
572 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
573 : CSLConstList papszOptions) const override;
574 :
575 : void NotifyChildrenOfRenaming() override;
576 :
577 : bool SetStatistics(bool bApproxStats, double dfMin, double dfMax,
578 : double dfMean, double dfStdDev, GUInt64 nValidCount,
579 : CSLConstList papszOptions) override;
580 :
581 : public:
582 : static std::shared_ptr<netCDFVariable>
583 1649 : Create(const std::shared_ptr<netCDFSharedResources> &poShared,
584 : const std::shared_ptr<netCDFGroup> &poParent, int gid, int varid,
585 : const std::vector<std::shared_ptr<GDALDimension>> &dims,
586 : CSLConstList papszOptions, bool bCreate)
587 : {
588 : auto var(std::shared_ptr<netCDFVariable>(
589 1649 : new netCDFVariable(poShared, gid, varid, dims, papszOptions)));
590 1649 : var->SetSelf(var);
591 1649 : var->m_poParent = poParent;
592 1649 : if (poParent)
593 533 : poParent->RegisterArray(var.get());
594 1649 : var->m_bHasWrittenData = !bCreate;
595 1649 : return var;
596 : }
597 :
598 : ~netCDFVariable() override;
599 :
600 1246 : void SetUseDefaultFillAsNoData(bool b)
601 : {
602 1246 : m_bUseDefaultFillAsNoData = b;
603 1246 : }
604 :
605 36 : bool IsWritable() const override
606 : {
607 36 : return !m_poShared->IsReadOnly();
608 : }
609 :
610 595 : const std::string &GetFilename() const override
611 : {
612 595 : return m_poShared->GetFilename();
613 : }
614 :
615 : const std::vector<std::shared_ptr<GDALDimension>> &
616 : GetDimensions() const override;
617 :
618 : const GDALExtendedDataType &GetDataType() const override;
619 :
620 : std::shared_ptr<GDALAttribute>
621 : GetAttribute(const std::string &osName) const override;
622 :
623 : std::vector<std::shared_ptr<GDALAttribute>>
624 : GetAttributes(CSLConstList papszOptions) const override;
625 :
626 : std::shared_ptr<GDALAttribute>
627 : CreateAttribute(const std::string &osName,
628 : const std::vector<GUInt64> &anDimensions,
629 : const GDALExtendedDataType &oDataType,
630 : CSLConstList papszOptions) override;
631 :
632 : bool DeleteAttribute(const std::string &osName,
633 : CSLConstList papszOptions) override;
634 :
635 : const void *GetRawNoDataValue() const override;
636 :
637 : bool SetRawNoDataValue(const void *) override;
638 :
639 : std::vector<GUInt64> GetBlockSize() const override;
640 :
641 : CSLConstList GetStructuralInfo() const override;
642 :
643 77 : const std::string &GetUnit() const override
644 : {
645 77 : return m_osUnit;
646 : }
647 :
648 : bool SetUnit(const std::string &osUnit) override;
649 :
650 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override;
651 :
652 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override;
653 :
654 : double GetOffset(bool *pbHasOffset,
655 : GDALDataType *peStorageType) const override;
656 :
657 : double GetScale(bool *pbHasScale,
658 : GDALDataType *peStorageType) const override;
659 :
660 : bool SetOffset(double dfOffset, GDALDataType eStorageType) override;
661 :
662 : bool SetScale(double dfScale, GDALDataType eStorageType) override;
663 :
664 : std::vector<std::shared_ptr<GDALMDArray>>
665 : GetCoordinateVariables() const override;
666 :
667 : bool Resize(const std::vector<GUInt64> &anNewDimSizes,
668 : CSLConstList) override;
669 :
670 251 : int GetGroupId() const
671 : {
672 251 : return m_gid;
673 : }
674 :
675 227 : int GetVarId() const
676 : {
677 227 : return m_varid;
678 : }
679 :
680 5002 : static std::string retrieveName(int gid, int varid)
681 : {
682 5002 : CPLMutexHolderD(&hNCMutex);
683 5002 : char szName[NC_MAX_NAME + 1] = {};
684 5002 : NCDF_ERR(nc_inq_varname(gid, varid, szName));
685 10004 : return szName;
686 : }
687 :
688 : bool Rename(const std::string &osNewName) override;
689 :
690 8 : std::shared_ptr<GDALGroup> GetRootGroup() const override
691 : {
692 8 : return netCDFGroup::Create(m_poShared, nullptr, m_gid);
693 : }
694 : };
695 :
696 : /************************************************************************/
697 : /* ~netCDFSharedResources() */
698 : /************************************************************************/
699 :
700 228 : netCDFSharedResources::~netCDFSharedResources()
701 : {
702 456 : CPLMutexHolderD(&hNCMutex);
703 :
704 228 : if (m_cdfid > 0)
705 : {
706 : #ifdef NCDF_DEBUG
707 : CPLDebug("GDAL_netCDF", "calling nc_close( %d)", m_cdfid);
708 : #endif
709 225 : int status = GDAL_nc_close(m_cdfid);
710 225 : NCDF_ERR(status);
711 : }
712 :
713 : #ifdef ENABLE_UFFD
714 228 : if (m_pUffdCtx)
715 : {
716 1 : NETCDF_UFFD_UNMAP(m_pUffdCtx);
717 : }
718 : #endif
719 :
720 228 : if (m_fpVSIMEM)
721 4 : VSIFCloseL(m_fpVSIMEM);
722 :
723 : #ifdef ENABLE_NCDUMP
724 228 : if (m_bFileToDestroyAtClosing)
725 0 : VSIUnlink(m_osFilename);
726 : #endif
727 228 : }
728 :
729 : /************************************************************************/
730 : /* NCDFGetParentGroupName() */
731 : /************************************************************************/
732 :
733 583 : static CPLString NCDFGetParentGroupName(int gid)
734 : {
735 583 : int nParentGID = 0;
736 583 : if (nc_inq_grp_parent(gid, &nParentGID) != NC_NOERR)
737 1066 : return std::string();
738 50 : return NCDFGetGroupFullName(nParentGID);
739 : }
740 :
741 : /************************************************************************/
742 : /* netCDFGroup() */
743 : /************************************************************************/
744 :
745 583 : netCDFGroup::netCDFGroup(const std::shared_ptr<netCDFSharedResources> &poShared,
746 583 : int gid)
747 1166 : : GDALGroup(NCDFGetParentGroupName(gid), retrieveName(gid)),
748 1749 : m_poShared(poShared), m_gid(gid)
749 : {
750 583 : if (m_gid == m_poShared->GetCDFId())
751 : {
752 533 : int nFormat = 0;
753 533 : NCDF_ERR(nc_inq_format(m_gid, &nFormat));
754 533 : if (nFormat == NC_FORMAT_CLASSIC)
755 : {
756 144 : m_aosStructuralInfo.SetNameValue("NC_FORMAT", "CLASSIC");
757 : }
758 : #ifdef NC_FORMAT_64BIT_OFFSET
759 389 : else if (nFormat == NC_FORMAT_64BIT_OFFSET)
760 : {
761 0 : m_aosStructuralInfo.SetNameValue("NC_FORMAT", "64BIT_OFFSET");
762 : }
763 : #endif
764 : #ifdef NC_FORMAT_CDF5
765 389 : else if (nFormat == NC_FORMAT_CDF5)
766 : {
767 0 : m_aosStructuralInfo.SetNameValue("NC_FORMAT", "CDF5");
768 : }
769 : #endif
770 389 : else if (nFormat == NC_FORMAT_NETCDF4)
771 : {
772 388 : m_aosStructuralInfo.SetNameValue("NC_FORMAT", "NETCDF4");
773 : }
774 1 : else if (nFormat == NC_FORMAT_NETCDF4_CLASSIC)
775 : {
776 1 : m_aosStructuralInfo.SetNameValue("NC_FORMAT", "NETCDF4_CLASSIC");
777 : }
778 : }
779 583 : }
780 :
781 : /************************************************************************/
782 : /* ~netCDFGroup() */
783 : /************************************************************************/
784 :
785 871 : netCDFGroup::~netCDFGroup()
786 : {
787 1166 : auto poParent = m_poParent.lock();
788 583 : if (poParent)
789 32 : poParent->UnRegisterSubGroup(this);
790 871 : }
791 :
792 : /************************************************************************/
793 : /* Create() */
794 : /************************************************************************/
795 :
796 : /* static */
797 : std::shared_ptr<netCDFGroup>
798 288 : netCDFGroup::Create(const std::shared_ptr<netCDFSharedResources> &poShared,
799 : int cdfid)
800 : {
801 : auto poGroup =
802 288 : std::shared_ptr<netCDFGroup>(new netCDFGroup(poShared, cdfid));
803 288 : poGroup->SetSelf(poGroup);
804 288 : return poGroup;
805 : }
806 :
807 : /************************************************************************/
808 : /* Create() */
809 : /************************************************************************/
810 :
811 : /* static */
812 : std::shared_ptr<netCDFGroup>
813 117 : netCDFGroup::Create(const std::shared_ptr<netCDFSharedResources> &poShared,
814 : const std::shared_ptr<netCDFGroup> &poParent,
815 : int nSubGroupId)
816 : {
817 117 : auto poSubGroup = netCDFGroup::Create(poShared, nSubGroupId);
818 117 : poSubGroup->m_poParent = poParent;
819 117 : if (poParent)
820 48 : poParent->RegisterSubGroup(poSubGroup.get());
821 117 : return poSubGroup;
822 : }
823 :
824 : /************************************************************************/
825 : /* CreateGroup() */
826 : /************************************************************************/
827 :
828 : std::shared_ptr<GDALGroup>
829 16 : netCDFGroup::CreateGroup(const std::string &osName,
830 : CSLConstList /*papszOptions*/)
831 : {
832 16 : if (osName.empty())
833 : {
834 1 : CPLError(CE_Failure, CPLE_NotSupported,
835 : "Empty group name not supported");
836 1 : return nullptr;
837 : }
838 30 : CPLMutexHolderD(&hNCMutex);
839 15 : m_poShared->SetDefineMode(true);
840 15 : int nSubGroupId = -1;
841 15 : int ret = nc_def_grp(m_gid, osName.c_str(), &nSubGroupId);
842 15 : NCDF_ERR(ret);
843 15 : if (ret != NC_NOERR)
844 3 : return nullptr;
845 12 : return netCDFGroup::Create(
846 24 : m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
847 12 : nSubGroupId);
848 : }
849 :
850 : /************************************************************************/
851 : /* CreateDimension() */
852 : /************************************************************************/
853 :
854 : std::shared_ptr<GDALDimension>
855 138 : netCDFGroup::CreateDimension(const std::string &osName,
856 : const std::string &osType, const std::string &,
857 : GUInt64 nSize, CSLConstList papszOptions)
858 : {
859 : const bool bUnlimited =
860 138 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "UNLIMITED", "FALSE"));
861 : if (static_cast<size_t>(nSize) != nSize)
862 : {
863 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid size");
864 : return nullptr;
865 : }
866 276 : CPLMutexHolderD(&hNCMutex);
867 138 : m_poShared->SetDefineMode(true);
868 138 : int nDimId = -1;
869 138 : NCDF_ERR(nc_def_dim(m_gid, osName.c_str(),
870 : static_cast<size_t>(bUnlimited ? 0 : nSize), &nDimId));
871 138 : if (nDimId < 0)
872 3 : return nullptr;
873 135 : return netCDFDimension::Create(
874 270 : m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
875 135 : m_gid, nDimId, static_cast<size_t>(nSize), osType);
876 : }
877 :
878 : /************************************************************************/
879 : /* CreateOrGetComplexDataType() */
880 : /************************************************************************/
881 :
882 7 : static int CreateOrGetComplexDataType(int gid, GDALDataType eDT)
883 : {
884 7 : const char *pszName = "";
885 7 : int nSubTypeId = NC_NAT;
886 7 : switch (eDT)
887 : {
888 1 : case GDT_CInt16:
889 1 : pszName = "ComplexInt16";
890 1 : nSubTypeId = NC_SHORT;
891 1 : break;
892 1 : case GDT_CInt32:
893 1 : pszName = "ComplexInt32";
894 1 : nSubTypeId = NC_INT;
895 1 : break;
896 2 : case GDT_CFloat32:
897 2 : pszName = "ComplexFloat32";
898 2 : nSubTypeId = NC_FLOAT;
899 2 : break;
900 3 : case GDT_CFloat64:
901 3 : pszName = "ComplexFloat64";
902 3 : nSubTypeId = NC_DOUBLE;
903 3 : break;
904 0 : default:
905 0 : CPLAssert(false);
906 : break;
907 : }
908 7 : int nTypeId = NC_NAT;
909 7 : if (nc_inq_typeid(gid, pszName, &nTypeId) == NC_NOERR)
910 : {
911 : // We could check that the type definition is really the one we want
912 1 : return nTypeId;
913 : }
914 6 : const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
915 6 : NCDF_ERR(nc_def_compound(gid, nDTSize, pszName, &nTypeId));
916 6 : if (nTypeId != NC_NAT)
917 : {
918 6 : NCDF_ERR(nc_insert_compound(gid, nTypeId, "real", 0, nSubTypeId));
919 6 : NCDF_ERR(
920 : nc_insert_compound(gid, nTypeId, "imag", nDTSize / 2, nSubTypeId));
921 : }
922 6 : return nTypeId;
923 : }
924 :
925 : /************************************************************************/
926 : /* CreateOrGetCompoundDataType() */
927 : /************************************************************************/
928 :
929 : static int CreateOrGetType(int gid, const GDALExtendedDataType &oType);
930 :
931 3 : static int CreateOrGetCompoundDataType(int gid,
932 : const GDALExtendedDataType &oType)
933 : {
934 3 : int nTypeId = NC_NAT;
935 3 : if (nc_inq_typeid(gid, oType.GetName().c_str(), &nTypeId) == NC_NOERR)
936 : {
937 : // We could check that the type definition is really the one we want
938 2 : return nTypeId;
939 : }
940 1 : NCDF_ERR(nc_def_compound(gid, oType.GetSize(), oType.GetName().c_str(),
941 : &nTypeId));
942 1 : if (nTypeId != NC_NAT)
943 : {
944 3 : for (const auto &comp : oType.GetComponents())
945 : {
946 2 : int nSubTypeId = CreateOrGetType(gid, comp->GetType());
947 2 : if (nSubTypeId == NC_NAT)
948 0 : return NC_NAT;
949 2 : NCDF_ERR(nc_insert_compound(gid, nTypeId, comp->GetName().c_str(),
950 : comp->GetOffset(), nSubTypeId));
951 : }
952 : }
953 1 : return nTypeId;
954 : }
955 :
956 : /************************************************************************/
957 : /* CreateOrGetType() */
958 : /************************************************************************/
959 :
960 290 : static int CreateOrGetType(int gid, const GDALExtendedDataType &oType)
961 : {
962 290 : int nTypeId = NC_NAT;
963 290 : const auto typeClass = oType.GetClass();
964 290 : if (typeClass == GEDTC_NUMERIC)
965 : {
966 149 : switch (oType.GetNumericDataType())
967 : {
968 36 : case GDT_Byte:
969 36 : nTypeId = NC_UBYTE;
970 36 : break;
971 1 : case GDT_Int8:
972 1 : nTypeId = NC_BYTE;
973 1 : break;
974 11 : case GDT_UInt16:
975 11 : nTypeId = NC_USHORT;
976 11 : break;
977 10 : case GDT_Int16:
978 10 : nTypeId = NC_SHORT;
979 10 : break;
980 5 : case GDT_UInt32:
981 5 : nTypeId = NC_UINT;
982 5 : break;
983 2 : case GDT_Int32:
984 2 : nTypeId = NC_INT;
985 2 : break;
986 1 : case GDT_UInt64:
987 1 : nTypeId = NC_UINT64;
988 1 : break;
989 1 : case GDT_Int64:
990 1 : nTypeId = NC_INT64;
991 1 : break;
992 4 : case GDT_Float32:
993 4 : nTypeId = NC_FLOAT;
994 4 : break;
995 71 : case GDT_Float64:
996 71 : nTypeId = NC_DOUBLE;
997 71 : break;
998 7 : case GDT_CInt16:
999 : case GDT_CInt32:
1000 : case GDT_CFloat32:
1001 : case GDT_CFloat64:
1002 : nTypeId =
1003 7 : CreateOrGetComplexDataType(gid, oType.GetNumericDataType());
1004 7 : break;
1005 0 : default:
1006 0 : break;
1007 : }
1008 : }
1009 141 : else if (typeClass == GEDTC_STRING)
1010 : {
1011 138 : nTypeId = NC_STRING;
1012 : }
1013 3 : else if (typeClass == GEDTC_COMPOUND)
1014 : {
1015 3 : nTypeId = CreateOrGetCompoundDataType(gid, oType);
1016 : }
1017 290 : return nTypeId;
1018 : }
1019 :
1020 : /************************************************************************/
1021 : /* CreateMDArray() */
1022 : /************************************************************************/
1023 :
1024 127 : std::shared_ptr<GDALMDArray> netCDFGroup::CreateMDArray(
1025 : const std::string &osName,
1026 : const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
1027 : const GDALExtendedDataType &oType, CSLConstList papszOptions)
1028 : {
1029 127 : if (osName.empty())
1030 : {
1031 0 : CPLError(CE_Failure, CPLE_NotSupported,
1032 : "Empty array name not supported");
1033 0 : return nullptr;
1034 : }
1035 254 : CPLMutexHolderD(&hNCMutex);
1036 127 : m_poShared->SetDefineMode(true);
1037 127 : int nVarId = -1;
1038 254 : std::vector<int> anDimIds;
1039 254 : std::vector<std::shared_ptr<GDALDimension>> dims;
1040 321 : for (const auto &dim : aoDimensions)
1041 : {
1042 194 : int nDimId = -1;
1043 194 : auto netCDFDim = std::dynamic_pointer_cast<netCDFDimension>(dim);
1044 194 : if (netCDFDim)
1045 : {
1046 191 : nDimId = netCDFDim->GetId();
1047 : }
1048 : else
1049 : {
1050 3 : if (nc_inq_dimid(m_gid, dim->GetName().c_str(), &nDimId) ==
1051 : NC_NOERR)
1052 : {
1053 4 : netCDFDim = netCDFDimension::Create(
1054 2 : m_poShared,
1055 4 : std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
1056 2 : m_gid, nDimId, 0, dim->GetType());
1057 2 : if (netCDFDim->GetSize() != dim->GetSize())
1058 : {
1059 1 : CPLError(CE_Warning, CPLE_AppDefined,
1060 : "Dimension %s already exists, "
1061 : "but with a size of " CPL_FRMT_GUIB,
1062 1 : dim->GetName().c_str(),
1063 1 : static_cast<GUIntBig>(netCDFDim->GetSize()));
1064 : }
1065 : }
1066 : else
1067 : {
1068 : netCDFDim =
1069 2 : std::dynamic_pointer_cast<netCDFDimension>(CreateDimension(
1070 : dim->GetName(), dim->GetType(), dim->GetDirection(),
1071 1 : dim->GetSize(), nullptr));
1072 1 : if (!netCDFDim)
1073 0 : return nullptr;
1074 1 : nDimId = netCDFDim->GetId();
1075 : }
1076 : }
1077 194 : anDimIds.push_back(nDimId);
1078 194 : dims.emplace_back(netCDFDim);
1079 : }
1080 127 : int nTypeId = CreateOrGetType(m_gid, oType);
1081 127 : if (nTypeId == NC_NAT)
1082 : {
1083 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unhandled data type");
1084 0 : return nullptr;
1085 : }
1086 127 : const char *pszType = CSLFetchNameValueDef(papszOptions, "NC_TYPE", "");
1087 184 : if ((EQUAL(pszType, "") || EQUAL(pszType, "NC_CHAR")) && dims.size() == 1 &&
1088 311 : oType.GetClass() == GEDTC_STRING && oType.GetMaxStringLength() > 0)
1089 : {
1090 2 : nTypeId = NC_CHAR;
1091 : auto dimLength =
1092 4 : std::dynamic_pointer_cast<netCDFDimension>(CreateDimension(
1093 4 : aoDimensions[0]->GetName() + "_length", std::string(),
1094 4 : std::string(), oType.GetMaxStringLength(), nullptr));
1095 2 : if (!dimLength)
1096 0 : return nullptr;
1097 2 : anDimIds.push_back(dimLength->GetId());
1098 : }
1099 125 : else if (EQUAL(pszType, "NC_BYTE"))
1100 1 : nTypeId = NC_BYTE;
1101 124 : else if (EQUAL(pszType, "NC_INT64"))
1102 1 : nTypeId = NC_INT64;
1103 123 : else if (EQUAL(pszType, "NC_UINT64"))
1104 1 : nTypeId = NC_UINT64;
1105 127 : NCDF_ERR(nc_def_var(m_gid, osName.c_str(), nTypeId,
1106 : static_cast<int>(anDimIds.size()),
1107 : anDimIds.empty() ? nullptr : anDimIds.data(), &nVarId));
1108 127 : if (nVarId < 0)
1109 3 : return nullptr;
1110 :
1111 124 : const char *pszBlockSize = CSLFetchNameValue(papszOptions, "BLOCKSIZE");
1112 134 : if (pszBlockSize &&
1113 : /* ignore for now BLOCKSIZE for 1-dim string variables created as 2-dim
1114 : */
1115 10 : anDimIds.size() == aoDimensions.size())
1116 : {
1117 10 : auto aszTokens(CPLStringList(CSLTokenizeString2(pszBlockSize, ",", 0)));
1118 10 : if (static_cast<size_t>(aszTokens.size()) != aoDimensions.size())
1119 : {
1120 0 : CPLError(CE_Failure, CPLE_AppDefined,
1121 : "Invalid number of values in BLOCKSIZE");
1122 0 : return nullptr;
1123 : }
1124 10 : if (!aoDimensions.empty())
1125 : {
1126 9 : std::vector<size_t> anChunkSize(aoDimensions.size());
1127 27 : for (size_t i = 0; i < anChunkSize.size(); ++i)
1128 : {
1129 18 : anChunkSize[i] =
1130 18 : static_cast<size_t>(CPLAtoGIntBig(aszTokens[i]));
1131 : }
1132 : int ret =
1133 9 : nc_def_var_chunking(m_gid, nVarId, NC_CHUNKED, &anChunkSize[0]);
1134 9 : NCDF_ERR(ret);
1135 9 : if (ret != NC_NOERR)
1136 0 : return nullptr;
1137 : }
1138 : }
1139 :
1140 124 : const char *pszCompress = CSLFetchNameValue(papszOptions, "COMPRESS");
1141 124 : if (pszCompress && EQUAL(pszCompress, "DEFLATE"))
1142 : {
1143 30 : int nZLevel = NCDF_DEFLATE_LEVEL;
1144 30 : const char *pszZLevel = CSLFetchNameValue(papszOptions, "ZLEVEL");
1145 30 : if (pszZLevel != nullptr)
1146 : {
1147 2 : nZLevel = atoi(pszZLevel);
1148 2 : if (!(nZLevel >= 1 && nZLevel <= 9))
1149 : {
1150 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1151 : "ZLEVEL=%s value not recognised, ignoring.",
1152 : pszZLevel);
1153 0 : nZLevel = NCDF_DEFLATE_LEVEL;
1154 : }
1155 : }
1156 30 : int ret = nc_def_var_deflate(m_gid, nVarId, TRUE /* shuffle */,
1157 : TRUE /* deflate on */, nZLevel);
1158 30 : NCDF_ERR(ret);
1159 30 : if (ret != NC_NOERR)
1160 0 : return nullptr;
1161 : }
1162 :
1163 124 : const char *pszFilter = CSLFetchNameValue(papszOptions, "FILTER");
1164 124 : if (pszFilter)
1165 : {
1166 : #ifdef NC_EFILTER
1167 : const auto aosTokens(
1168 0 : CPLStringList(CSLTokenizeString2(pszFilter, ",", 0)));
1169 0 : if (!aosTokens.empty())
1170 : {
1171 : const unsigned nFilterId =
1172 0 : static_cast<unsigned>(CPLAtoGIntBig(aosTokens[0]));
1173 0 : std::vector<unsigned> anParams;
1174 0 : for (int i = 1; i < aosTokens.size(); ++i)
1175 : {
1176 0 : anParams.push_back(
1177 0 : static_cast<unsigned>(CPLAtoGIntBig(aosTokens[i])));
1178 : }
1179 0 : int ret = nc_def_var_filter(m_gid, nVarId, nFilterId,
1180 0 : anParams.size(), anParams.data());
1181 0 : NCDF_ERR(ret);
1182 0 : if (ret != NC_NOERR)
1183 0 : return nullptr;
1184 : }
1185 : #else
1186 : CPLError(CE_Failure, CPLE_NotSupported,
1187 : "netCDF 4.6 or later needed for FILTER option");
1188 : return nullptr;
1189 : #endif
1190 : }
1191 :
1192 : const bool bChecksum =
1193 124 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "CHECKSUM", "FALSE"));
1194 124 : if (bChecksum)
1195 : {
1196 1 : int ret = nc_def_var_fletcher32(m_gid, nVarId, TRUE);
1197 1 : NCDF_ERR(ret);
1198 1 : if (ret != NC_NOERR)
1199 0 : return nullptr;
1200 : }
1201 :
1202 124 : return netCDFVariable::Create(
1203 248 : m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
1204 124 : m_gid, nVarId, dims, papszOptions, true);
1205 : }
1206 :
1207 : /************************************************************************/
1208 : /* CreateAttribute() */
1209 : /************************************************************************/
1210 :
1211 76 : std::shared_ptr<GDALAttribute> netCDFGroup::CreateAttribute(
1212 : const std::string &osName, const std::vector<GUInt64> &anDimensions,
1213 : const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
1214 : {
1215 152 : return netCDFAttribute::Create(
1216 228 : m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
1217 152 : m_gid, NC_GLOBAL, osName, anDimensions, oDataType, papszOptions);
1218 : }
1219 :
1220 : /************************************************************************/
1221 : /* DeleteAttribute() */
1222 : /************************************************************************/
1223 :
1224 2 : bool netCDFGroup::DeleteAttribute(const std::string &osName,
1225 : CSLConstList /*papszOptions*/)
1226 : {
1227 4 : CPLMutexHolderD(&hNCMutex);
1228 2 : m_poShared->SetDefineMode(true);
1229 :
1230 2 : int ret = nc_del_att(m_gid, NC_GLOBAL, osName.c_str());
1231 2 : NCDF_ERR(ret);
1232 2 : if (ret != NC_NOERR)
1233 1 : return false;
1234 :
1235 1 : auto it = m_oMapAttributes.find(osName);
1236 1 : if (it != m_oMapAttributes.end())
1237 : {
1238 1 : it->second->Deleted();
1239 1 : m_oMapAttributes.erase(it);
1240 : }
1241 :
1242 1 : return true;
1243 : }
1244 :
1245 : /************************************************************************/
1246 : /* GetGroupNames() */
1247 : /************************************************************************/
1248 :
1249 : std::vector<std::string>
1250 22 : netCDFGroup::GetGroupNames(CSLConstList papszOptions) const
1251 : {
1252 44 : CPLMutexHolderD(&hNCMutex);
1253 22 : int nSubGroups = 0;
1254 22 : NCDF_ERR(nc_inq_grps(m_gid, &nSubGroups, nullptr));
1255 22 : if (nSubGroups == 0)
1256 : {
1257 16 : if (EQUAL(CSLFetchNameValueDef(papszOptions, "GROUP_BY", ""),
1258 : "SAME_DIMENSION"))
1259 : {
1260 4 : std::vector<std::string> names;
1261 4 : std::set<std::string> oSetDimNames;
1262 30 : for (const auto &osArrayName : GetMDArrayNames(nullptr))
1263 : {
1264 56 : const auto poArray = OpenMDArray(osArrayName, nullptr);
1265 28 : const auto &apoDims = poArray->GetDimensions();
1266 28 : if (apoDims.size() == 1)
1267 : {
1268 28 : const auto &osDimName = apoDims[0]->GetName();
1269 28 : if (oSetDimNames.find(osDimName) == oSetDimNames.end())
1270 : {
1271 6 : oSetDimNames.insert(osDimName);
1272 6 : names.emplace_back(osDimName);
1273 : }
1274 : }
1275 : }
1276 2 : return names;
1277 : }
1278 14 : return {};
1279 : }
1280 12 : std::vector<int> anSubGroupdsIds(nSubGroups);
1281 6 : NCDF_ERR(nc_inq_grps(m_gid, nullptr, &anSubGroupdsIds[0]));
1282 12 : std::vector<std::string> names;
1283 6 : names.reserve(nSubGroups);
1284 15 : for (const auto &subgid : anSubGroupdsIds)
1285 : {
1286 9 : char szName[NC_MAX_NAME + 1] = {};
1287 9 : NCDF_ERR(nc_inq_grpname(subgid, szName));
1288 9 : if (GetFullName() == "/" && EQUAL(szName, "METADATA"))
1289 : {
1290 4 : const auto poMetadata = OpenGroup(szName);
1291 2 : if (poMetadata && poMetadata->OpenGroup("ISO_METADATA"))
1292 2 : continue;
1293 : }
1294 7 : names.emplace_back(szName);
1295 : }
1296 6 : return names;
1297 : }
1298 :
1299 : /************************************************************************/
1300 : /* OpenGroup() */
1301 : /************************************************************************/
1302 :
1303 : std::shared_ptr<GDALGroup>
1304 85 : netCDFGroup::OpenGroup(const std::string &osName,
1305 : CSLConstList papszOptions) const
1306 : {
1307 170 : CPLMutexHolderD(&hNCMutex);
1308 85 : int nSubGroups = 0;
1309 : // This is weird but nc_inq_grp_ncid() succeeds on a single group file.
1310 85 : NCDF_ERR(nc_inq_grps(m_gid, &nSubGroups, nullptr));
1311 85 : if (nSubGroups == 0)
1312 : {
1313 20 : if (EQUAL(CSLFetchNameValueDef(papszOptions, "GROUP_BY", ""),
1314 : "SAME_DIMENSION"))
1315 : {
1316 1 : const auto oCandidateGroupNames = GetGroupNames(papszOptions);
1317 1 : for (const auto &osCandidateGroupName : oCandidateGroupNames)
1318 : {
1319 1 : if (osCandidateGroupName == osName)
1320 : {
1321 1 : auto poThisGroup = netCDFGroup::Create(m_poShared, m_gid);
1322 2 : return netCDFVirtualGroupBySameDimension::Create(
1323 1 : poThisGroup, osName);
1324 : }
1325 : }
1326 : }
1327 19 : return nullptr;
1328 : }
1329 65 : int nSubGroupId = 0;
1330 101 : if (nc_inq_grp_ncid(m_gid, osName.c_str(), &nSubGroupId) != NC_NOERR ||
1331 36 : nSubGroupId <= 0)
1332 29 : return nullptr;
1333 36 : return netCDFGroup::Create(
1334 72 : m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
1335 36 : nSubGroupId);
1336 : }
1337 :
1338 : /************************************************************************/
1339 : /* GetMDArrayNames() */
1340 : /************************************************************************/
1341 :
1342 : std::vector<std::string>
1343 333 : netCDFGroup::GetMDArrayNames(CSLConstList papszOptions) const
1344 : {
1345 666 : CPLMutexHolderD(&hNCMutex);
1346 333 : int nVars = 0;
1347 333 : NCDF_ERR(nc_inq_nvars(m_gid, &nVars));
1348 333 : if (nVars == 0)
1349 11 : return {};
1350 644 : std::vector<int> anVarIds(nVars);
1351 322 : NCDF_ERR(nc_inq_varids(m_gid, nullptr, &anVarIds[0]));
1352 644 : std::vector<std::string> names;
1353 322 : names.reserve(nVars);
1354 : const bool bAll =
1355 322 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_ALL", "NO"));
1356 : const bool bZeroDim =
1357 643 : bAll ||
1358 321 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_ZERO_DIM", "NO"));
1359 : const bool bCoordinates =
1360 322 : bAll || CPLTestBool(CSLFetchNameValueDef(papszOptions,
1361 322 : "SHOW_COORDINATES", "YES"));
1362 : const bool bBounds =
1363 643 : bAll ||
1364 321 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_BOUNDS", "YES"));
1365 : const bool bIndexing =
1366 643 : bAll ||
1367 321 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_INDEXING", "YES"));
1368 : const bool bTime =
1369 643 : bAll ||
1370 321 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_TIME", "YES"));
1371 644 : std::set<std::string> ignoreList;
1372 322 : if (!bCoordinates || !bBounds)
1373 : {
1374 12 : for (const auto &varid : anVarIds)
1375 : {
1376 9 : char **papszTokens = nullptr;
1377 9 : if (!bCoordinates)
1378 : {
1379 5 : char *pszTemp = nullptr;
1380 5 : if (NCDFGetAttr(m_gid, varid, "coordinates", &pszTemp) ==
1381 : CE_None)
1382 1 : papszTokens = NCDFTokenizeCoordinatesAttribute(pszTemp);
1383 5 : CPLFree(pszTemp);
1384 : }
1385 9 : if (!bBounds)
1386 : {
1387 4 : char *pszTemp = nullptr;
1388 4 : if (NCDFGetAttr(m_gid, varid, "bounds", &pszTemp) == CE_None &&
1389 4 : pszTemp != nullptr && !EQUAL(pszTemp, ""))
1390 2 : papszTokens = CSLAddString(papszTokens, pszTemp);
1391 4 : CPLFree(pszTemp);
1392 : }
1393 15 : for (char **iter = papszTokens; iter && iter[0]; ++iter)
1394 6 : ignoreList.insert(*iter);
1395 9 : CSLDestroy(papszTokens);
1396 : }
1397 : }
1398 :
1399 322 : const bool bGroupBySameDimension = EQUAL(
1400 : CSLFetchNameValueDef(papszOptions, "GROUP_BY", ""), "SAME_DIMENSION");
1401 1586 : for (const auto &varid : anVarIds)
1402 : {
1403 1264 : int nVarDims = 0;
1404 1264 : NCDF_ERR(nc_inq_varndims(m_gid, varid, &nVarDims));
1405 1264 : if (nVarDims == 0 && !bZeroDim)
1406 : {
1407 145 : continue;
1408 : }
1409 1137 : if (nVarDims == 1 && bGroupBySameDimension)
1410 : {
1411 14 : continue;
1412 : }
1413 :
1414 1123 : char szName[NC_MAX_NAME + 1] = {};
1415 1123 : NCDF_ERR(nc_inq_varname(m_gid, varid, szName));
1416 1123 : if (!bIndexing && nVarDims == 1)
1417 : {
1418 1 : int nDimId = 0;
1419 1 : NCDF_ERR(nc_inq_vardimid(m_gid, varid, &nDimId));
1420 1 : char szDimName[NC_MAX_NAME + 1] = {};
1421 1 : NCDF_ERR(nc_inq_dimname(m_gid, nDimId, szDimName));
1422 1 : if (strcmp(szDimName, szName) == 0)
1423 : {
1424 1 : continue;
1425 : }
1426 : }
1427 :
1428 1122 : if (!bTime)
1429 : {
1430 14 : char *pszTemp = nullptr;
1431 14 : bool bSkip = false;
1432 14 : if (NCDFGetAttr(m_gid, varid, "standard_name", &pszTemp) == CE_None)
1433 : {
1434 10 : bSkip = pszTemp && strcmp(pszTemp, "time") == 0;
1435 : }
1436 14 : CPLFree(pszTemp);
1437 14 : if (bSkip)
1438 : {
1439 3 : continue;
1440 : }
1441 : }
1442 :
1443 1119 : if (ignoreList.find(szName) == ignoreList.end())
1444 : {
1445 1113 : names.emplace_back(szName);
1446 : }
1447 : }
1448 322 : return names;
1449 : }
1450 :
1451 : /************************************************************************/
1452 : /* Rename() */
1453 : /************************************************************************/
1454 :
1455 5 : bool netCDFGroup::Rename(const std::string &osNewName)
1456 : {
1457 5 : if (m_poShared->IsReadOnly())
1458 : {
1459 1 : CPLError(CE_Failure, CPLE_AppDefined,
1460 : "Rename() not supported on read-only file");
1461 1 : return false;
1462 : }
1463 4 : if (osNewName.empty())
1464 : {
1465 1 : CPLError(CE_Failure, CPLE_NotSupported, "Empty name not supported");
1466 1 : return false;
1467 : }
1468 3 : if (m_osName == "/")
1469 : {
1470 1 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot rename root group");
1471 1 : return false;
1472 : }
1473 :
1474 4 : CPLMutexHolderD(&hNCMutex);
1475 2 : m_poShared->SetDefineMode(true);
1476 :
1477 2 : int ret = nc_rename_grp(m_gid, osNewName.c_str());
1478 2 : NCDF_ERR(ret);
1479 2 : if (ret != NC_NOERR)
1480 1 : return false;
1481 :
1482 1 : BaseRename(osNewName);
1483 :
1484 1 : return true;
1485 : }
1486 :
1487 : /************************************************************************/
1488 : /* NotifyChildrenOfRenaming() */
1489 : /************************************************************************/
1490 :
1491 2 : void netCDFGroup::NotifyChildrenOfRenaming()
1492 : {
1493 3 : for (const auto poSubGroup : m_oSetGroups)
1494 1 : poSubGroup->ParentRenamed(m_osFullName);
1495 :
1496 3 : for (const auto poDim : m_oSetDimensions)
1497 1 : poDim->ParentRenamed(m_osFullName);
1498 :
1499 4 : for (const auto poArray : m_oSetArrays)
1500 2 : poArray->ParentRenamed(m_osFullName);
1501 :
1502 4 : for (const auto &iter : m_oMapAttributes)
1503 2 : iter.second->ParentRenamed(m_osFullName);
1504 2 : }
1505 :
1506 : /************************************************************************/
1507 : /* OpenMDArray() */
1508 : /************************************************************************/
1509 :
1510 : std::shared_ptr<GDALMDArray>
1511 1271 : netCDFGroup::OpenMDArray(const std::string &osName,
1512 : CSLConstList papszOptions) const
1513 : {
1514 2542 : CPLMutexHolderD(&hNCMutex);
1515 1271 : int nVarId = 0;
1516 1271 : if (nc_inq_varid(m_gid, osName.c_str(), &nVarId) != NC_NOERR)
1517 25 : return nullptr;
1518 :
1519 : auto poVar = netCDFVariable::Create(
1520 2492 : m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
1521 1246 : m_gid, nVarId, std::vector<std::shared_ptr<GDALDimension>>(),
1522 3738 : papszOptions, false);
1523 1246 : if (poVar)
1524 : {
1525 1246 : poVar->SetUseDefaultFillAsNoData(CPLTestBool(CSLFetchNameValueDef(
1526 : papszOptions, "USE_DEFAULT_FILL_AS_NODATA", "NO")));
1527 : }
1528 1246 : return poVar;
1529 : }
1530 :
1531 : /************************************************************************/
1532 : /* GetDimensions() */
1533 : /************************************************************************/
1534 :
1535 : std::vector<std::shared_ptr<GDALDimension>>
1536 32 : netCDFGroup::GetDimensions(CSLConstList) const
1537 : {
1538 64 : CPLMutexHolderD(&hNCMutex);
1539 32 : int nbDims = 0;
1540 32 : NCDF_ERR(nc_inq_ndims(m_gid, &nbDims));
1541 32 : if (nbDims == 0)
1542 1 : return {};
1543 62 : std::vector<int> dimids(nbDims);
1544 31 : NCDF_ERR(nc_inq_dimids(m_gid, &nbDims, &dimids[0], FALSE));
1545 62 : std::vector<std::shared_ptr<GDALDimension>> res;
1546 96 : for (int i = 0; i < nbDims; i++)
1547 : {
1548 130 : auto poCachedDim = m_poShared->GetCachedDimension(dimids[i]);
1549 65 : if (poCachedDim == nullptr)
1550 : {
1551 58 : poCachedDim = netCDFDimension::Create(
1552 58 : m_poShared,
1553 116 : std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()), m_gid,
1554 174 : dimids[i], 0, std::string());
1555 58 : m_poShared->CacheDimension(dimids[i], poCachedDim);
1556 : }
1557 65 : res.emplace_back(poCachedDim);
1558 : }
1559 31 : return res;
1560 : }
1561 :
1562 : /************************************************************************/
1563 : /* GetAttribute() */
1564 : /************************************************************************/
1565 :
1566 : static const char *const apszJSONMDKeys[] = {
1567 : "ISO_METADATA", "ESA_METADATA", "EOP_METADATA",
1568 : "QA_STATISTICS", "GRANULE_DESCRIPTION", "ALGORITHM_SETTINGS"};
1569 :
1570 : std::shared_ptr<GDALAttribute>
1571 14 : netCDFGroup::GetAttribute(const std::string &osName) const
1572 : {
1573 28 : CPLMutexHolderD(&hNCMutex);
1574 14 : int nAttId = -1;
1575 14 : if (nc_inq_attid(m_gid, NC_GLOBAL, osName.c_str(), &nAttId) != NC_NOERR)
1576 : {
1577 2 : if (GetFullName() == "/")
1578 : {
1579 8 : for (const char *key : apszJSONMDKeys)
1580 : {
1581 7 : if (osName == key)
1582 : {
1583 2 : auto poMetadata = OpenGroup("METADATA");
1584 1 : if (poMetadata)
1585 : {
1586 : auto poSubMetadata =
1587 : std::dynamic_pointer_cast<netCDFGroup>(
1588 2 : poMetadata->OpenGroup(key));
1589 1 : if (poSubMetadata)
1590 : {
1591 : const auto osJson =
1592 1 : NCDFReadMetadataAsJson(poSubMetadata->m_gid);
1593 2 : return std::make_shared<GDALAttributeString>(
1594 3 : GetFullName(), key, osJson, GEDTST_JSON);
1595 : }
1596 : }
1597 0 : break;
1598 : }
1599 : }
1600 : }
1601 1 : return nullptr;
1602 : }
1603 24 : return netCDFAttribute::Create(
1604 36 : m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
1605 24 : m_gid, NC_GLOBAL, osName);
1606 : }
1607 :
1608 : /************************************************************************/
1609 : /* GetAttributes() */
1610 : /************************************************************************/
1611 :
1612 : std::vector<std::shared_ptr<GDALAttribute>>
1613 37 : netCDFGroup::GetAttributes(CSLConstList) const
1614 : {
1615 74 : CPLMutexHolderD(&hNCMutex);
1616 37 : std::vector<std::shared_ptr<GDALAttribute>> res;
1617 37 : int nbAttr = 0;
1618 37 : NCDF_ERR(nc_inq_varnatts(m_gid, NC_GLOBAL, &nbAttr));
1619 37 : res.reserve(nbAttr);
1620 147 : for (int i = 0; i < nbAttr; i++)
1621 : {
1622 : char szAttrName[NC_MAX_NAME + 1];
1623 110 : szAttrName[0] = 0;
1624 110 : NCDF_ERR(nc_inq_attname(m_gid, NC_GLOBAL, i, szAttrName));
1625 110 : if (!EQUAL(szAttrName, "_NCProperties"))
1626 : {
1627 440 : res.emplace_back(netCDFAttribute::Create(
1628 110 : m_poShared,
1629 220 : std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()), m_gid,
1630 110 : NC_GLOBAL, szAttrName));
1631 : }
1632 : }
1633 :
1634 37 : if (GetFullName() == "/")
1635 : {
1636 96 : auto poMetadata = OpenGroup("METADATA");
1637 32 : if (poMetadata)
1638 : {
1639 14 : for (const char *key : apszJSONMDKeys)
1640 : {
1641 : auto poSubMetadata = std::dynamic_pointer_cast<netCDFGroup>(
1642 36 : poMetadata->OpenGroup(key));
1643 12 : if (poSubMetadata)
1644 : {
1645 : const auto osJson =
1646 2 : NCDFReadMetadataAsJson(poSubMetadata->m_gid);
1647 4 : res.emplace_back(std::make_shared<GDALAttributeString>(
1648 6 : GetFullName(), key, osJson, GEDTST_JSON));
1649 : }
1650 : }
1651 : }
1652 : }
1653 :
1654 74 : return res;
1655 : }
1656 :
1657 : /************************************************************************/
1658 : /* GetStructuralInfo() */
1659 : /************************************************************************/
1660 :
1661 9 : CSLConstList netCDFGroup::GetStructuralInfo() const
1662 : {
1663 9 : return m_aosStructuralInfo.List();
1664 : }
1665 :
1666 : /************************************************************************/
1667 : /* ClearStatistics() */
1668 : /************************************************************************/
1669 :
1670 1 : void netCDFGroup::ClearStatistics()
1671 : {
1672 1 : m_poShared->GetPAM()->ClearStatistics();
1673 1 : }
1674 :
1675 : /************************************************************************/
1676 : /* netCDFVirtualGroupBySameDimension() */
1677 : /************************************************************************/
1678 :
1679 1 : netCDFVirtualGroupBySameDimension::netCDFVirtualGroupBySameDimension(
1680 1 : const std::shared_ptr<netCDFGroup> &poGroup, const std::string &osDimName)
1681 1 : : GDALGroup(poGroup->GetName(), osDimName), m_poGroup(poGroup),
1682 2 : m_osDimName(osDimName)
1683 : {
1684 1 : }
1685 :
1686 : /************************************************************************/
1687 : /* Create() */
1688 : /************************************************************************/
1689 :
1690 : /* static */ std::shared_ptr<netCDFVirtualGroupBySameDimension>
1691 1 : netCDFVirtualGroupBySameDimension::Create(
1692 : const std::shared_ptr<netCDFGroup> &poGroup, const std::string &osDimName)
1693 : {
1694 : auto poNewGroup = std::shared_ptr<netCDFVirtualGroupBySameDimension>(
1695 1 : new netCDFVirtualGroupBySameDimension(poGroup, osDimName));
1696 1 : poNewGroup->SetSelf(poNewGroup);
1697 1 : return poNewGroup;
1698 : }
1699 :
1700 : /************************************************************************/
1701 : /* GetMDArrayNames() */
1702 : /************************************************************************/
1703 :
1704 : std::vector<std::string>
1705 1 : netCDFVirtualGroupBySameDimension::GetMDArrayNames(CSLConstList) const
1706 : {
1707 2 : const auto srcNames = m_poGroup->GetMDArrayNames(nullptr);
1708 1 : std::vector<std::string> names;
1709 15 : for (const auto &srcName : srcNames)
1710 : {
1711 28 : auto poArray = m_poGroup->OpenMDArray(srcName, nullptr);
1712 14 : if (poArray)
1713 : {
1714 14 : const auto &apoArrayDims = poArray->GetDimensions();
1715 28 : if (apoArrayDims.size() == 1 &&
1716 14 : apoArrayDims[0]->GetName() == m_osDimName)
1717 : {
1718 7 : names.emplace_back(srcName);
1719 : }
1720 : }
1721 : }
1722 2 : return names;
1723 : }
1724 :
1725 : /************************************************************************/
1726 : /* OpenMDArray() */
1727 : /************************************************************************/
1728 :
1729 : std::shared_ptr<GDALMDArray>
1730 7 : netCDFVirtualGroupBySameDimension::OpenMDArray(const std::string &osName,
1731 : CSLConstList papszOptions) const
1732 : {
1733 7 : return m_poGroup->OpenMDArray(osName, papszOptions);
1734 : }
1735 :
1736 : /************************************************************************/
1737 : /* netCDFDimension() */
1738 : /************************************************************************/
1739 :
1740 1715 : netCDFDimension::netCDFDimension(
1741 : const std::shared_ptr<netCDFSharedResources> &poShared, int cfid, int dimid,
1742 1715 : size_t nForcedSize, const std::string &osType)
1743 3430 : : GDALDimension(NCDFGetGroupFullName(cfid), retrieveName(cfid, dimid),
1744 : osType, // type
1745 3430 : std::string(), // direction
1746 1582 : nForcedSize ? nForcedSize : retrieveSize(cfid, dimid)),
1747 5145 : m_poShared(poShared), m_gid(cfid), m_dimid(dimid)
1748 : {
1749 1715 : if (m_osType.empty() && nForcedSize == 0)
1750 : {
1751 : auto var =
1752 3164 : std::dynamic_pointer_cast<netCDFVariable>(GetIndexingVariable());
1753 1582 : if (var)
1754 : {
1755 227 : const auto gid = var->GetGroupId();
1756 227 : const auto varid = var->GetVarId();
1757 227 : const auto varname = var->GetName().c_str();
1758 441 : if (NCDFIsVarLongitude(gid, varid, varname) ||
1759 214 : NCDFIsVarProjectionX(gid, varid, varname))
1760 : {
1761 38 : m_osType = GDAL_DIM_TYPE_HORIZONTAL_X;
1762 114 : auto attrPositive = var->GetAttribute(CF_UNITS);
1763 38 : if (attrPositive)
1764 : {
1765 35 : const auto val = attrPositive->ReadAsString();
1766 35 : if (val)
1767 : {
1768 35 : if (EQUAL(val, CF_DEGREES_EAST))
1769 : {
1770 10 : m_osDirection = "EAST";
1771 : }
1772 : }
1773 : }
1774 : }
1775 363 : else if (NCDFIsVarLatitude(gid, varid, varname) ||
1776 174 : NCDFIsVarProjectionY(gid, varid, varname))
1777 : {
1778 39 : m_osType = GDAL_DIM_TYPE_HORIZONTAL_Y;
1779 117 : auto attrPositive = var->GetAttribute(CF_UNITS);
1780 39 : if (attrPositive)
1781 : {
1782 36 : const auto val = attrPositive->ReadAsString();
1783 36 : if (val)
1784 : {
1785 36 : if (EQUAL(val, CF_DEGREES_NORTH))
1786 : {
1787 12 : m_osDirection = "NORTH";
1788 : }
1789 : }
1790 : }
1791 : }
1792 150 : else if (NCDFIsVarVerticalCoord(gid, varid, varname))
1793 : {
1794 9 : m_osType = GDAL_DIM_TYPE_VERTICAL;
1795 27 : auto attrPositive = var->GetAttribute("positive");
1796 9 : if (attrPositive)
1797 : {
1798 0 : const auto val = attrPositive->ReadAsString();
1799 0 : if (val)
1800 : {
1801 0 : if (EQUAL(val, "up"))
1802 : {
1803 0 : m_osDirection = "UP";
1804 : }
1805 0 : else if (EQUAL(val, "down"))
1806 : {
1807 0 : m_osDirection = "DOWN";
1808 : }
1809 : }
1810 : }
1811 : }
1812 141 : else if (NCDFIsVarTimeCoord(gid, varid, varname))
1813 : {
1814 49 : m_osType = GDAL_DIM_TYPE_TEMPORAL;
1815 : }
1816 : }
1817 : }
1818 1715 : }
1819 :
1820 : /************************************************************************/
1821 : /* ~netCDFDimension() */
1822 : /************************************************************************/
1823 :
1824 1715 : netCDFDimension::~netCDFDimension()
1825 : {
1826 3430 : auto poParent = m_poParent.lock();
1827 1715 : if (poParent)
1828 229 : poParent->UnRegisterDimension(this);
1829 1715 : }
1830 :
1831 : /************************************************************************/
1832 : /* Create() */
1833 : /************************************************************************/
1834 :
1835 : /* static */
1836 : std::shared_ptr<netCDFDimension>
1837 1715 : netCDFDimension::Create(const std::shared_ptr<netCDFSharedResources> &poShared,
1838 : const std::shared_ptr<netCDFGroup> &poParent, int cfid,
1839 : int dimid, size_t nForcedSize,
1840 : const std::string &osType)
1841 : {
1842 : auto poDim(std::make_shared<netCDFDimension>(poShared, cfid, dimid,
1843 1715 : nForcedSize, osType));
1844 1715 : poDim->m_poParent = poParent;
1845 1715 : if (poParent)
1846 509 : poParent->RegisterDimension(poDim.get());
1847 1715 : return poDim;
1848 : }
1849 :
1850 : /************************************************************************/
1851 : /* GetIndexingVariable() */
1852 : /************************************************************************/
1853 :
1854 : namespace
1855 : {
1856 : struct SetIsInGetIndexingVariable
1857 : {
1858 : netCDFSharedResources *m_poShared;
1859 :
1860 556 : explicit SetIsInGetIndexingVariable(
1861 : netCDFSharedResources *poSharedResources)
1862 556 : : m_poShared(poSharedResources)
1863 : {
1864 556 : m_poShared->SetIsInGetIndexingVariable(true);
1865 556 : }
1866 :
1867 556 : ~SetIsInGetIndexingVariable()
1868 556 : {
1869 556 : m_poShared->SetIsInGetIndexingVariable(false);
1870 556 : }
1871 : };
1872 : } // namespace
1873 :
1874 1731 : std::shared_ptr<GDALMDArray> netCDFDimension::GetIndexingVariable() const
1875 : {
1876 1731 : if (m_poShared->GetIsInIndexingVariable())
1877 1175 : return nullptr;
1878 :
1879 1112 : SetIsInGetIndexingVariable setterIsInGetIndexingVariable(m_poShared.get());
1880 :
1881 1112 : CPLMutexHolderD(&hNCMutex);
1882 :
1883 : // First try to find a variable in this group with the same name as the
1884 : // dimension
1885 556 : int nVarId = 0;
1886 556 : if (nc_inq_varid(m_gid, GetName().c_str(), &nVarId) == NC_NOERR)
1887 : {
1888 261 : int nDims = 0;
1889 261 : NCDF_ERR(nc_inq_varndims(m_gid, nVarId, &nDims));
1890 261 : int nVarType = NC_NAT;
1891 261 : NCDF_ERR(nc_inq_vartype(m_gid, nVarId, &nVarType));
1892 261 : if (nDims == 1 || (nDims == 2 && nVarType == NC_CHAR))
1893 : {
1894 261 : int anDimIds[2] = {};
1895 261 : NCDF_ERR(nc_inq_vardimid(m_gid, nVarId, anDimIds));
1896 261 : if (anDimIds[0] == m_dimid)
1897 : {
1898 261 : if (nDims == 2)
1899 : {
1900 : // Check that there is no variable with the same of the
1901 : // second dimension.
1902 2 : char szExtraDim[NC_MAX_NAME + 1] = {};
1903 2 : NCDF_ERR(nc_inq_dimname(m_gid, anDimIds[1], szExtraDim));
1904 : int nUnused;
1905 2 : if (nc_inq_varid(m_gid, szExtraDim, &nUnused) == NC_NOERR)
1906 0 : return nullptr;
1907 : }
1908 :
1909 261 : return netCDFVariable::Create(
1910 522 : m_poShared, m_poParent.lock(), m_gid, nVarId,
1911 522 : std::vector<std::shared_ptr<GDALDimension>>(), nullptr,
1912 261 : false);
1913 : }
1914 : }
1915 : }
1916 :
1917 : // Otherwise explore the variables in this group to find one that has a
1918 : // "coordinates" attribute that references this dimension. If so, let's
1919 : // return the variable pointed by the value of "coordinates" as the indexing
1920 : // variable. This assumes that there is no other variable that would use
1921 : // another variable for the matching dimension of its "coordinates".
1922 590 : netCDFGroup oGroup(m_poShared, m_gid);
1923 590 : const auto arrayNames = oGroup.GetMDArrayNames(nullptr);
1924 295 : std::shared_ptr<GDALMDArray> candidateIndexingVariable;
1925 1188 : for (const auto &arrayName : arrayNames)
1926 : {
1927 913 : const auto poArray = oGroup.OpenMDArray(arrayName, nullptr);
1928 : const auto poArrayNC =
1929 913 : std::dynamic_pointer_cast<netCDFVariable>(poArray);
1930 913 : if (!poArrayNC)
1931 0 : continue;
1932 :
1933 913 : const auto &apoArrayDims = poArray->GetDimensions();
1934 913 : if (apoArrayDims.size() == 1)
1935 : {
1936 176 : const auto &poArrayDim = apoArrayDims[0];
1937 : const auto poArrayDimNC =
1938 176 : std::dynamic_pointer_cast<netCDFDimension>(poArrayDim);
1939 352 : if (poArrayDimNC && poArrayDimNC->m_gid == m_gid &&
1940 176 : poArrayDimNC->m_dimid == m_dimid)
1941 : {
1942 : // If the array doesn't have a coordinates variable, but is a 1D
1943 : // array indexed by our dimension, then use it as the indexing
1944 : // variable, provided it is the only such variable.
1945 59 : if (!candidateIndexingVariable)
1946 : {
1947 51 : candidateIndexingVariable = poArray;
1948 : }
1949 : else
1950 : {
1951 8 : return nullptr;
1952 : }
1953 51 : continue;
1954 : }
1955 : }
1956 :
1957 1708 : const auto poCoordinates = poArray->GetAttribute("coordinates");
1958 854 : if (!(poCoordinates &&
1959 854 : poCoordinates->GetDataType().GetClass() == GEDTC_STRING))
1960 : {
1961 842 : continue;
1962 : }
1963 :
1964 : // Check that the arrays has as many dimensions as its coordinates
1965 : // attribute
1966 : const CPLStringList aosCoordinates(
1967 12 : NCDFTokenizeCoordinatesAttribute(poCoordinates->ReadAsString()));
1968 12 : if (apoArrayDims.size() != static_cast<size_t>(aosCoordinates.size()))
1969 0 : continue;
1970 :
1971 26 : for (size_t i = 0; i < apoArrayDims.size(); ++i)
1972 : {
1973 26 : const auto &poArrayDim = apoArrayDims[i];
1974 : const auto poArrayDimNC =
1975 26 : std::dynamic_pointer_cast<netCDFDimension>(poArrayDim);
1976 :
1977 : // Check if the array is indexed by the current dimension
1978 52 : if (!(poArrayDimNC && poArrayDimNC->m_gid == m_gid &&
1979 26 : poArrayDimNC->m_dimid == m_dimid))
1980 : {
1981 14 : continue;
1982 : }
1983 :
1984 : // Caution: some datasets have their coordinates variables in the
1985 : // same order than dimensions (i.e. from slowest varying to
1986 : // fastest varying), while others have the coordinates variables
1987 : // in the opposite order.
1988 : // Assume same order by default, but if we find the first variable
1989 : // to be of longitude/X type, then assume the opposite order.
1990 12 : bool coordinatesInSameOrderThanDimensions = true;
1991 12 : if (aosCoordinates.size() > 1)
1992 : {
1993 12 : int bFirstGroupId = -1;
1994 12 : int nFirstVarId = -1;
1995 12 : if (NCDFResolveVar(poArrayNC->GetGroupId(), aosCoordinates[0],
1996 24 : &bFirstGroupId, &nVarId, false) == CE_None &&
1997 12 : (NCDFIsVarLongitude(bFirstGroupId, nFirstVarId,
1998 4 : aosCoordinates[0]) ||
1999 4 : NCDFIsVarProjectionX(bFirstGroupId, nFirstVarId,
2000 : aosCoordinates[0])))
2001 : {
2002 8 : coordinatesInSameOrderThanDimensions = false;
2003 : }
2004 : }
2005 :
2006 12 : int nIndexingVarGroupId = -1;
2007 12 : int nIndexingVarId = -1;
2008 : const size_t nIdxCoordinate = coordinatesInSameOrderThanDimensions
2009 12 : ? i
2010 8 : : aosCoordinates.size() - 1 - i;
2011 12 : if (NCDFResolveVar(
2012 : poArrayNC->GetGroupId(), aosCoordinates[nIdxCoordinate],
2013 12 : &nIndexingVarGroupId, &nIndexingVarId, false) == CE_None)
2014 : {
2015 12 : return netCDFVariable::Create(
2016 24 : m_poShared, m_poParent.lock(), nIndexingVarGroupId,
2017 : nIndexingVarId,
2018 24 : std::vector<std::shared_ptr<GDALDimension>>(), nullptr,
2019 12 : false);
2020 : }
2021 : }
2022 : }
2023 :
2024 275 : return candidateIndexingVariable;
2025 : }
2026 :
2027 : /************************************************************************/
2028 : /* Rename() */
2029 : /************************************************************************/
2030 :
2031 4 : bool netCDFDimension::Rename(const std::string &osNewName)
2032 : {
2033 4 : if (m_poShared->IsReadOnly())
2034 : {
2035 1 : CPLError(CE_Failure, CPLE_AppDefined,
2036 : "Rename() not supported on read-only file");
2037 1 : return false;
2038 : }
2039 3 : if (osNewName.empty())
2040 : {
2041 1 : CPLError(CE_Failure, CPLE_NotSupported, "Empty name not supported");
2042 1 : return false;
2043 : }
2044 4 : CPLMutexHolderD(&hNCMutex);
2045 2 : m_poShared->SetDefineMode(true);
2046 :
2047 2 : int ret = nc_rename_dim(m_gid, m_dimid, osNewName.c_str());
2048 2 : NCDF_ERR(ret);
2049 2 : if (ret != NC_NOERR)
2050 1 : return false;
2051 :
2052 1 : BaseRename(osNewName);
2053 :
2054 1 : return true;
2055 : }
2056 :
2057 : /************************************************************************/
2058 : /* netCDFVariable() */
2059 : /************************************************************************/
2060 :
2061 1649 : netCDFVariable::netCDFVariable(
2062 : const std::shared_ptr<netCDFSharedResources> &poShared, int gid, int varid,
2063 : const std::vector<std::shared_ptr<GDALDimension>> &dims,
2064 1649 : CSLConstList papszOptions)
2065 3298 : : GDALAbstractMDArray(NCDFGetGroupFullName(gid), retrieveName(gid, varid)),
2066 3298 : GDALPamMDArray(NCDFGetGroupFullName(gid), retrieveName(gid, varid),
2067 : poShared->GetPAM()),
2068 8245 : m_poShared(poShared), m_gid(gid), m_varid(varid), m_dims(dims)
2069 : {
2070 : {
2071 : // Cf https://docs.unidata.ucar.edu/netcdf-c/current/group__variables.html#gae6b59e92d1140b5fec56481b0f41b610
2072 1649 : size_t nRawDataChunkCacheSize = 0;
2073 1649 : size_t nChunkSlots = 0;
2074 1649 : float fPreemption = 0.0f;
2075 : int ret =
2076 1649 : nc_get_var_chunk_cache(m_gid, m_varid, &nRawDataChunkCacheSize,
2077 : &nChunkSlots, &fPreemption);
2078 1649 : if (ret == NC_NOERR)
2079 : {
2080 1336 : if (const char *pszVar = CSLFetchNameValue(
2081 : papszOptions, "RAW_DATA_CHUNK_CACHE_SIZE"))
2082 : {
2083 1 : nRawDataChunkCacheSize =
2084 1 : static_cast<size_t>(std::min<unsigned long long>(
2085 3 : std::strtoull(pszVar, nullptr, 10),
2086 1 : std::numeric_limits<size_t>::max()));
2087 : }
2088 1336 : if (const char *pszVar =
2089 1336 : CSLFetchNameValue(papszOptions, "CHUNK_SLOTS"))
2090 : {
2091 1 : nChunkSlots = static_cast<size_t>(std::min<unsigned long long>(
2092 3 : std::strtoull(pszVar, nullptr, 10),
2093 1 : std::numeric_limits<size_t>::max()));
2094 : }
2095 1336 : if (const char *pszVar =
2096 1336 : CSLFetchNameValue(papszOptions, "PREEMPTION"))
2097 : {
2098 1 : fPreemption = std::max(
2099 1 : 0.0f, std::min(1.0f, static_cast<float>(CPLAtof(pszVar))));
2100 : }
2101 1336 : NCDF_ERR(nc_set_var_chunk_cache(m_gid, m_varid,
2102 : nRawDataChunkCacheSize, nChunkSlots,
2103 : fPreemption));
2104 : }
2105 313 : else if (ret != NC_ENOTNC4)
2106 : {
2107 0 : CPLError(CE_Warning, CPLE_AppDefined,
2108 : "netcdf error #%d : %s .\nat (%s,%s,%d)\n", ret,
2109 : nc_strerror(ret), __FILE__, __FUNCTION__, __LINE__);
2110 : }
2111 : }
2112 :
2113 1649 : NCDF_ERR(nc_inq_varndims(m_gid, m_varid, &m_nDims));
2114 1649 : NCDF_ERR(nc_inq_vartype(m_gid, m_varid, &m_nVarType));
2115 1649 : if (m_nDims == 2 && m_nVarType == NC_CHAR)
2116 : {
2117 35 : int anDimIds[2] = {};
2118 35 : NCDF_ERR(nc_inq_vardimid(m_gid, m_varid, &anDimIds[0]));
2119 :
2120 : // Check that there is no variable with the same of the
2121 : // second dimension.
2122 35 : char szExtraDim[NC_MAX_NAME + 1] = {};
2123 35 : NCDF_ERR(nc_inq_dimname(m_gid, anDimIds[1], szExtraDim));
2124 : int nUnused;
2125 35 : if (nc_inq_varid(m_gid, szExtraDim, &nUnused) != NC_NOERR)
2126 : {
2127 13 : NCDF_ERR(nc_inq_dimlen(m_gid, anDimIds[1], &m_nTextLength));
2128 : }
2129 : }
2130 :
2131 1649 : int nShuffle = 0;
2132 1649 : int nDeflate = 0;
2133 1649 : int nDeflateLevel = 0;
2134 1649 : if (nc_inq_var_deflate(m_gid, m_varid, &nShuffle, &nDeflate,
2135 1649 : &nDeflateLevel) == NC_NOERR)
2136 : {
2137 1649 : if (nDeflate)
2138 : {
2139 106 : m_aosStructuralInfo.SetNameValue("COMPRESS", "DEFLATE");
2140 : }
2141 : }
2142 4947 : auto unit = netCDFVariable::GetAttribute(CF_UNITS);
2143 1649 : if (unit && unit->GetDataType().GetClass() == GEDTC_STRING)
2144 : {
2145 388 : const char *pszVal = unit->ReadAsString();
2146 388 : if (pszVal)
2147 388 : m_osUnit = pszVal;
2148 : }
2149 1649 : m_bWriteGDALTags = CPLTestBool(
2150 : CSLFetchNameValueDef(papszOptions, "WRITE_GDAL_TAGS", "YES"));
2151 :
2152 : // Non-documented option. Only used for test purposes.
2153 1649 : if (CPLTestBool(CSLFetchNameValueDef(
2154 : papszOptions, "INCLUDE_CHUNK_CACHE_PARAMETERS_IN_STRUCTURAL_INFO",
2155 : "NO")))
2156 : {
2157 1 : size_t nRawDataChunkCacheSize = 0;
2158 1 : size_t nChunkSlots = 0;
2159 1 : float fPreemption = 0.0f;
2160 1 : NCDF_ERR(nc_get_var_chunk_cache(m_gid, m_varid, &nRawDataChunkCacheSize,
2161 : &nChunkSlots, &fPreemption));
2162 : m_aosStructuralInfo.SetNameValue(
2163 : "RAW_DATA_CHUNK_CACHE_SIZE",
2164 : CPLSPrintf("%" PRIu64,
2165 1 : static_cast<uint64_t>(nRawDataChunkCacheSize)));
2166 : m_aosStructuralInfo.SetNameValue(
2167 : "CHUNK_SLOTS",
2168 1 : CPLSPrintf("%" PRIu64, static_cast<uint64_t>(nChunkSlots)));
2169 : m_aosStructuralInfo.SetNameValue("PREEMPTION",
2170 1 : CPLSPrintf("%f", fPreemption));
2171 : }
2172 1649 : }
2173 :
2174 : /************************************************************************/
2175 : /* ~netCDFVariable() */
2176 : /************************************************************************/
2177 :
2178 3298 : netCDFVariable::~netCDFVariable()
2179 : {
2180 3298 : auto poParent = m_poParent.lock();
2181 1649 : if (poParent)
2182 322 : poParent->UnRegisterArray(this);
2183 :
2184 1649 : if (!m_poShared->IsReadOnly() && !m_dims.empty())
2185 : {
2186 254 : bool bNeedToWriteDummy = false;
2187 705 : for (auto &poDim : m_dims)
2188 : {
2189 454 : auto netCDFDim = std::dynamic_pointer_cast<netCDFDimension>(poDim);
2190 454 : CPLAssert(netCDFDim);
2191 454 : if (netCDFDim->GetSize() > netCDFDim->GetActualSize())
2192 : {
2193 3 : bNeedToWriteDummy = true;
2194 3 : break;
2195 : }
2196 : }
2197 254 : if (bNeedToWriteDummy)
2198 : {
2199 3 : CPLDebug("netCDF", "Extending array %s to new dimension sizes",
2200 3 : GetName().c_str());
2201 3 : m_bGetRawNoDataValueHasRun = false;
2202 3 : m_bUseDefaultFillAsNoData = true;
2203 3 : const void *pNoData = GetRawNoDataValue();
2204 6 : std::vector<GByte> abyDummy(GetDataType().GetSize());
2205 3 : if (pNoData == nullptr)
2206 0 : pNoData = abyDummy.data();
2207 3 : const auto nDimCount = m_dims.size();
2208 6 : std::vector<GUInt64> arrayStartIdx(nDimCount);
2209 6 : std::vector<size_t> count(nDimCount, 1);
2210 6 : std::vector<GInt64> arrayStep(nDimCount, 0);
2211 6 : std::vector<GPtrDiff_t> bufferStride(nDimCount, 0);
2212 9 : for (size_t i = 0; i < nDimCount; ++i)
2213 : {
2214 6 : arrayStartIdx[i] = m_dims[i]->GetSize() - 1;
2215 : }
2216 6 : Write(arrayStartIdx.data(), count.data(), arrayStep.data(),
2217 3 : bufferStride.data(), GetDataType(), pNoData);
2218 : }
2219 : }
2220 3298 : }
2221 :
2222 : /************************************************************************/
2223 : /* GetDimensions() */
2224 : /************************************************************************/
2225 :
2226 : const std::vector<std::shared_ptr<GDALDimension>> &
2227 4024 : netCDFVariable::GetDimensions() const
2228 : {
2229 4024 : if (m_nDims == 0 || !m_dims.empty())
2230 2818 : return m_dims;
2231 2412 : CPLMutexHolderD(&hNCMutex);
2232 1206 : std::vector<int> anDimIds(m_nDims);
2233 1206 : NCDF_ERR(nc_inq_vardimid(m_gid, m_varid, &anDimIds[0]));
2234 1206 : if (m_nDims == 2 && m_nVarType == NC_CHAR && m_nTextLength > 0)
2235 9 : anDimIds.resize(1);
2236 1206 : m_dims.reserve(m_nDims);
2237 3484 : for (const auto &dimid : anDimIds)
2238 : {
2239 4556 : auto poCachedDim = m_poShared->GetCachedDimension(dimid);
2240 2278 : if (poCachedDim == nullptr)
2241 : {
2242 : const int groupDim =
2243 1520 : m_poShared->GetBelongingGroupOfDim(m_gid, dimid);
2244 : poCachedDim =
2245 3040 : netCDFDimension::Create(m_poShared, m_poParent.lock(), groupDim,
2246 4560 : dimid, 0, std::string());
2247 1520 : m_poShared->CacheDimension(dimid, poCachedDim);
2248 : }
2249 2278 : m_dims.emplace_back(poCachedDim);
2250 : }
2251 1206 : return m_dims;
2252 : }
2253 :
2254 : /************************************************************************/
2255 : /* GetStructuralInfo() */
2256 : /************************************************************************/
2257 :
2258 15 : CSLConstList netCDFVariable::GetStructuralInfo() const
2259 : {
2260 15 : return m_aosStructuralInfo.List();
2261 : }
2262 :
2263 : /************************************************************************/
2264 : /* GetComplexDataType() */
2265 : /************************************************************************/
2266 :
2267 23 : static GDALDataType GetComplexDataType(int gid, int nVarType)
2268 : {
2269 : // First enquire and check that the number of fields is 2
2270 23 : size_t nfields = 0;
2271 23 : size_t compoundsize = 0;
2272 23 : char szName[NC_MAX_NAME + 1] = {};
2273 23 : if (nc_inq_compound(gid, nVarType, szName, &compoundsize, &nfields) !=
2274 : NC_NOERR)
2275 : {
2276 0 : return GDT_Unknown;
2277 : }
2278 :
2279 23 : if (nfields != 2 || !STARTS_WITH_CI(szName, "complex"))
2280 : {
2281 8 : return GDT_Unknown;
2282 : }
2283 :
2284 : // Now check that that two types are the same in the struct.
2285 : nc_type field_type1, field_type2;
2286 : int field_dims1, field_dims2;
2287 15 : if (nc_inq_compound_field(gid, nVarType, 0, nullptr, nullptr, &field_type1,
2288 15 : &field_dims1, nullptr) != NC_NOERR)
2289 : {
2290 0 : return GDT_Unknown;
2291 : }
2292 :
2293 15 : if (nc_inq_compound_field(gid, nVarType, 0, nullptr, nullptr, &field_type2,
2294 15 : &field_dims2, nullptr) != NC_NOERR)
2295 : {
2296 0 : return GDT_Unknown;
2297 : }
2298 :
2299 15 : if ((field_type1 != field_type2) || (field_dims1 != field_dims2) ||
2300 15 : (field_dims1 != 0))
2301 : {
2302 0 : return GDT_Unknown;
2303 : }
2304 :
2305 15 : if (field_type1 == NC_SHORT)
2306 : {
2307 3 : return GDT_CInt16;
2308 : }
2309 12 : else if (field_type1 == NC_INT)
2310 : {
2311 2 : return GDT_CInt32;
2312 : }
2313 10 : else if (field_type1 == NC_FLOAT)
2314 : {
2315 5 : return GDT_CFloat32;
2316 : }
2317 5 : else if (field_type1 == NC_DOUBLE)
2318 : {
2319 5 : return GDT_CFloat64;
2320 : }
2321 :
2322 0 : return GDT_Unknown;
2323 : }
2324 :
2325 : /************************************************************************/
2326 : /* GetCompoundDataType() */
2327 : /************************************************************************/
2328 :
2329 : static bool BuildDataType(int gid, int varid, int nVarType,
2330 : std::unique_ptr<GDALExtendedDataType> &dt,
2331 : bool &bPerfectDataTypeMatch);
2332 :
2333 8 : static bool GetCompoundDataType(int gid, int nVarType,
2334 : std::unique_ptr<GDALExtendedDataType> &dt,
2335 : bool &bPerfectDataTypeMatch)
2336 : {
2337 8 : size_t nfields = 0;
2338 8 : size_t compoundsize = 0;
2339 8 : char szName[NC_MAX_NAME + 1] = {};
2340 8 : if (nc_inq_compound(gid, nVarType, szName, &compoundsize, &nfields) !=
2341 : NC_NOERR)
2342 : {
2343 0 : return false;
2344 : }
2345 8 : bPerfectDataTypeMatch = true;
2346 16 : std::vector<std::unique_ptr<GDALEDTComponent>> comps;
2347 25 : for (size_t i = 0; i < nfields; i++)
2348 : {
2349 17 : nc_type field_type = 0;
2350 17 : int field_dims = 0;
2351 17 : size_t field_offset = 0;
2352 17 : char field_name[NC_MAX_NAME + 1] = {};
2353 17 : if (nc_inq_compound_field(gid, nVarType, static_cast<int>(i),
2354 : field_name, &field_offset, &field_type,
2355 17 : &field_dims, nullptr) != NC_NOERR)
2356 : {
2357 0 : return false;
2358 : }
2359 17 : if (field_dims != 0)
2360 : {
2361 : // We don't support that
2362 0 : return false;
2363 : }
2364 0 : std::unique_ptr<GDALExtendedDataType> subDt;
2365 17 : bool bSubPerfectDataTypeMatch = false;
2366 17 : if (!BuildDataType(gid, -1, field_type, subDt,
2367 : bSubPerfectDataTypeMatch))
2368 : {
2369 0 : return false;
2370 : }
2371 17 : if (!bSubPerfectDataTypeMatch)
2372 : {
2373 0 : CPLError(
2374 : CE_Failure, CPLE_NotSupported,
2375 : "Non native GDAL type found in a component of a compound type");
2376 0 : return false;
2377 : }
2378 : auto comp = std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
2379 51 : std::string(field_name), field_offset, *subDt));
2380 17 : comps.emplace_back(std::move(comp));
2381 : }
2382 16 : dt.reset(new GDALExtendedDataType(
2383 8 : GDALExtendedDataType::Create(szName, compoundsize, std::move(comps))));
2384 :
2385 8 : return dt->GetClass() == GEDTC_COMPOUND;
2386 : }
2387 :
2388 : /************************************************************************/
2389 : /* BuildDataType() */
2390 : /************************************************************************/
2391 :
2392 432 : static bool BuildDataType(int gid, int varid, int nVarType,
2393 : std::unique_ptr<GDALExtendedDataType> &dt,
2394 : bool &bPerfectDataTypeMatch)
2395 : {
2396 432 : GDALDataType eDataType = GDT_Unknown;
2397 432 : bPerfectDataTypeMatch = false;
2398 432 : if (NCDFIsUserDefinedType(gid, nVarType))
2399 : {
2400 25 : nc_type nBaseType = NC_NAT;
2401 25 : int eClass = 0;
2402 25 : nc_inq_user_type(gid, nVarType, nullptr, nullptr, &nBaseType, nullptr,
2403 : &eClass);
2404 25 : if (eClass == NC_COMPOUND)
2405 : {
2406 23 : eDataType = GetComplexDataType(gid, nVarType);
2407 23 : if (eDataType != GDT_Unknown)
2408 : {
2409 15 : bPerfectDataTypeMatch = true;
2410 15 : dt.reset(new GDALExtendedDataType(
2411 15 : GDALExtendedDataType::Create(eDataType)));
2412 23 : return true;
2413 : }
2414 8 : else if (GetCompoundDataType(gid, nVarType, dt,
2415 : bPerfectDataTypeMatch))
2416 : {
2417 8 : return true;
2418 : }
2419 : else
2420 : {
2421 0 : CPLError(CE_Failure, CPLE_NotSupported,
2422 : "Unsupported netCDF compound data type encountered.");
2423 0 : return false;
2424 : }
2425 : }
2426 2 : else if (eClass == NC_ENUM)
2427 : {
2428 2 : nVarType = nBaseType;
2429 : }
2430 0 : else if (eClass == NC_VLEN)
2431 : {
2432 0 : CPLError(CE_Failure, CPLE_NotSupported,
2433 : "VLen data type not supported");
2434 0 : return false;
2435 : }
2436 0 : else if (eClass == NC_OPAQUE)
2437 : {
2438 0 : CPLError(CE_Failure, CPLE_NotSupported,
2439 : "Opaque data type not supported");
2440 0 : return false;
2441 : }
2442 : else
2443 : {
2444 0 : CPLError(CE_Failure, CPLE_NotSupported,
2445 : "Unsupported netCDF data type encountered.");
2446 0 : return false;
2447 : }
2448 : }
2449 :
2450 409 : if (nVarType == NC_STRING)
2451 : {
2452 9 : bPerfectDataTypeMatch = true;
2453 9 : dt.reset(
2454 9 : new GDALExtendedDataType(GDALExtendedDataType::CreateString()));
2455 9 : return true;
2456 : }
2457 : else
2458 : {
2459 400 : if (nVarType == NC_BYTE)
2460 : {
2461 57 : char *pszTemp = nullptr;
2462 57 : bool bSignedData = true;
2463 112 : if (varid >= 0 &&
2464 55 : NCDFGetAttr(gid, varid, "_Unsigned", &pszTemp) == CE_None)
2465 : {
2466 45 : if (EQUAL(pszTemp, "true"))
2467 44 : bSignedData = false;
2468 1 : else if (EQUAL(pszTemp, "false"))
2469 1 : bSignedData = true;
2470 45 : CPLFree(pszTemp);
2471 : }
2472 57 : if (!bSignedData)
2473 : {
2474 44 : eDataType = GDT_Byte;
2475 44 : bPerfectDataTypeMatch = true;
2476 : }
2477 : else
2478 : {
2479 13 : eDataType = GDT_Int8;
2480 13 : bPerfectDataTypeMatch = true;
2481 : }
2482 : }
2483 343 : else if (nVarType == NC_CHAR)
2484 : {
2485 : // Not sure of this
2486 1 : bPerfectDataTypeMatch = true;
2487 1 : eDataType = GDT_Byte;
2488 : }
2489 342 : else if (nVarType == NC_SHORT)
2490 : {
2491 50 : bPerfectDataTypeMatch = true;
2492 50 : char *pszTemp = nullptr;
2493 50 : bool bSignedData = true;
2494 93 : if (varid >= 0 &&
2495 43 : NCDFGetAttr(gid, varid, "_Unsigned", &pszTemp) == CE_None)
2496 : {
2497 30 : if (EQUAL(pszTemp, "true"))
2498 30 : bSignedData = false;
2499 0 : else if (EQUAL(pszTemp, "false"))
2500 0 : bSignedData = true;
2501 30 : CPLFree(pszTemp);
2502 : }
2503 50 : if (!bSignedData)
2504 : {
2505 30 : eDataType = GDT_UInt16;
2506 : }
2507 : else
2508 : {
2509 20 : eDataType = GDT_Int16;
2510 : }
2511 : }
2512 292 : else if (nVarType == NC_INT)
2513 : {
2514 58 : bPerfectDataTypeMatch = true;
2515 58 : eDataType = GDT_Int32;
2516 : }
2517 234 : else if (nVarType == NC_FLOAT)
2518 : {
2519 48 : bPerfectDataTypeMatch = true;
2520 48 : eDataType = GDT_Float32;
2521 : }
2522 186 : else if (nVarType == NC_DOUBLE)
2523 : {
2524 91 : bPerfectDataTypeMatch = true;
2525 91 : eDataType = GDT_Float64;
2526 : }
2527 95 : else if (nVarType == NC_UBYTE)
2528 : {
2529 65 : bPerfectDataTypeMatch = true;
2530 65 : eDataType = GDT_Byte;
2531 : }
2532 30 : else if (nVarType == NC_USHORT)
2533 : {
2534 9 : bPerfectDataTypeMatch = true;
2535 9 : eDataType = GDT_UInt16;
2536 : }
2537 21 : else if (nVarType == NC_UINT)
2538 : {
2539 7 : bPerfectDataTypeMatch = true;
2540 7 : eDataType = GDT_UInt32;
2541 : }
2542 14 : else if (nVarType == NC_INT64)
2543 : {
2544 8 : bPerfectDataTypeMatch = true;
2545 8 : eDataType = GDT_Int64;
2546 : }
2547 6 : else if (nVarType == NC_UINT64)
2548 : {
2549 6 : bPerfectDataTypeMatch = true;
2550 6 : eDataType = GDT_UInt64;
2551 : }
2552 : else
2553 : {
2554 0 : CPLError(CE_Failure, CPLE_NotSupported,
2555 : "Unsupported netCDF data type encountered.");
2556 0 : return false;
2557 : }
2558 : }
2559 400 : dt.reset(new GDALExtendedDataType(GDALExtendedDataType::Create(eDataType)));
2560 400 : return true;
2561 : }
2562 :
2563 : /************************************************************************/
2564 : /* GetDataType() */
2565 : /************************************************************************/
2566 :
2567 2167 : const GDALExtendedDataType &netCDFVariable::GetDataType() const
2568 : {
2569 2167 : if (m_dt)
2570 1868 : return *m_dt;
2571 598 : CPLMutexHolderD(&hNCMutex);
2572 :
2573 299 : if (m_nDims == 2 && m_nVarType == NC_CHAR && m_nTextLength > 0)
2574 : {
2575 7 : m_bPerfectDataTypeMatch = true;
2576 7 : m_dt.reset(new GDALExtendedDataType(
2577 7 : GDALExtendedDataType::CreateString(m_nTextLength)));
2578 : }
2579 : else
2580 : {
2581 292 : m_dt.reset(new GDALExtendedDataType(
2582 292 : GDALExtendedDataType::Create(GDT_Unknown)));
2583 :
2584 292 : BuildDataType(m_gid, m_varid, m_nVarType, m_dt,
2585 292 : m_bPerfectDataTypeMatch);
2586 : }
2587 299 : return *m_dt;
2588 : }
2589 :
2590 : /************************************************************************/
2591 : /* SetUnit() */
2592 : /************************************************************************/
2593 :
2594 1 : bool netCDFVariable::SetUnit(const std::string &osUnit)
2595 : {
2596 1 : if (osUnit.empty())
2597 : {
2598 0 : nc_del_att(m_gid, m_varid, CF_UNITS);
2599 0 : return true;
2600 : }
2601 3 : auto poUnits(GetAttribute(CF_UNITS));
2602 1 : if (!poUnits)
2603 : {
2604 2 : poUnits = CreateAttribute(
2605 3 : CF_UNITS, {}, GDALExtendedDataType::CreateString(), nullptr);
2606 1 : if (!poUnits)
2607 0 : return false;
2608 : }
2609 1 : return poUnits->Write(osUnit.c_str());
2610 : }
2611 :
2612 : /************************************************************************/
2613 : /* GetSpatialRef() */
2614 : /************************************************************************/
2615 :
2616 111 : std::shared_ptr<OGRSpatialReference> netCDFVariable::GetSpatialRef() const
2617 : {
2618 111 : if (m_bSRSRead)
2619 50 : return m_poSRS;
2620 :
2621 61 : m_bSRSRead = true;
2622 122 : netCDFDataset poDS;
2623 61 : poDS.ReadAttributes(m_gid, m_varid);
2624 61 : int iDimX = 0;
2625 61 : int iDimY = 0;
2626 61 : int iCount = 1;
2627 179 : for (const auto &poDim : GetDimensions())
2628 : {
2629 118 : if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X)
2630 20 : iDimX = iCount;
2631 98 : else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y)
2632 28 : iDimY = iCount;
2633 118 : poDS.papszDimName.AddString(poDim->GetName().c_str());
2634 118 : iCount++;
2635 : }
2636 61 : if ((iDimX == 0 || iDimY == 0) && GetDimensionCount() >= 2)
2637 : {
2638 26 : iDimX = static_cast<int>(GetDimensionCount());
2639 26 : iDimY = iDimX - 1;
2640 : }
2641 61 : poDS.SetProjectionFromVar(m_gid, m_varid, true);
2642 61 : auto poSRS = poDS.GetSpatialRef();
2643 61 : if (poSRS)
2644 : {
2645 21 : m_poSRS.reset(poSRS->Clone());
2646 21 : if (iDimX > 0 && iDimY > 0)
2647 : {
2648 21 : if (m_poSRS->GetDataAxisToSRSAxisMapping() ==
2649 42 : std::vector<int>{2, 1})
2650 5 : m_poSRS->SetDataAxisToSRSAxisMapping({iDimY, iDimX});
2651 : else
2652 16 : m_poSRS->SetDataAxisToSRSAxisMapping({iDimX, iDimY});
2653 : }
2654 : }
2655 :
2656 61 : return m_poSRS;
2657 : }
2658 :
2659 : /************************************************************************/
2660 : /* SetSpatialRef() */
2661 : /************************************************************************/
2662 :
2663 72 : static void WriteDimAttr(std::shared_ptr<GDALMDArray> &poVar,
2664 : const char *pszAttrName, const char *pszAttrValue)
2665 : {
2666 216 : auto poAttr = poVar->GetAttribute(pszAttrName);
2667 72 : if (poAttr)
2668 : {
2669 24 : const char *pszVal = poAttr->ReadAsString();
2670 24 : if (pszVal && !EQUAL(pszVal, pszAttrValue))
2671 : {
2672 0 : CPLError(CE_Warning, CPLE_AppDefined,
2673 : "Variable %s has a %s which is %s and not %s",
2674 0 : poVar->GetName().c_str(), pszAttrName, pszVal,
2675 : pszAttrValue);
2676 : }
2677 : }
2678 : else
2679 : {
2680 144 : poAttr = poVar->CreateAttribute(
2681 144 : pszAttrName, {}, GDALExtendedDataType::CreateString(), nullptr);
2682 48 : if (poAttr)
2683 48 : poAttr->Write(pszAttrValue);
2684 : }
2685 72 : }
2686 :
2687 28 : static void WriteDimAttrs(const std::shared_ptr<GDALDimension> &dim,
2688 : const char *pszStandardName, const char *pszLongName,
2689 : const char *pszUnits)
2690 : {
2691 56 : auto poVar = dim->GetIndexingVariable();
2692 28 : if (poVar)
2693 : {
2694 24 : WriteDimAttr(poVar, CF_STD_NAME, pszStandardName);
2695 24 : WriteDimAttr(poVar, CF_LNG_NAME, pszLongName);
2696 24 : WriteDimAttr(poVar, CF_UNITS, pszUnits);
2697 : }
2698 : else
2699 : {
2700 4 : CPLError(CE_Warning, CPLE_AppDefined,
2701 : "Dimension %s lacks a indexing variable",
2702 4 : dim->GetName().c_str());
2703 : }
2704 28 : }
2705 :
2706 18 : bool netCDFVariable::SetSpatialRef(const OGRSpatialReference *poSRS)
2707 : {
2708 18 : m_bSRSRead = false;
2709 18 : m_poSRS.reset();
2710 :
2711 36 : CPLMutexHolderD(&hNCMutex);
2712 18 : m_poShared->SetDefineMode(true);
2713 :
2714 18 : if (poSRS == nullptr)
2715 : {
2716 2 : nc_del_att(m_gid, m_varid, CF_GRD_MAPPING);
2717 2 : return true;
2718 : }
2719 :
2720 16 : char *pszCFProjection = nullptr;
2721 : int nSRSVarId =
2722 16 : NCDFWriteSRSVariable(m_gid, poSRS, &pszCFProjection, m_bWriteGDALTags);
2723 16 : if (nSRSVarId < 0 || pszCFProjection == nullptr)
2724 0 : return false;
2725 :
2726 16 : NCDF_ERR(nc_put_att_text(m_gid, m_varid, CF_GRD_MAPPING,
2727 : strlen(pszCFProjection), pszCFProjection));
2728 16 : CPLFree(pszCFProjection);
2729 :
2730 16 : auto apoDims = GetDimensions();
2731 16 : if (poSRS->IsProjected())
2732 : {
2733 11 : bool bWriteX = false;
2734 11 : bool bWriteY = false;
2735 22 : const std::string osUnits = NCDFGetProjectedCFUnit(poSRS);
2736 33 : for (const auto &poDim : apoDims)
2737 : {
2738 22 : const char *pszStandardName = nullptr;
2739 22 : const char *pszLongName = nullptr;
2740 39 : if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X ||
2741 17 : EQUAL(poDim->GetName().c_str(), CF_PROJ_X_VAR_NAME))
2742 : {
2743 8 : pszStandardName = CF_PROJ_X_COORD;
2744 8 : pszLongName = CF_PROJ_X_COORD_LONG_NAME;
2745 8 : bWriteX = true;
2746 : }
2747 23 : else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y ||
2748 9 : EQUAL(poDim->GetName().c_str(), CF_PROJ_Y_VAR_NAME))
2749 : {
2750 8 : pszStandardName = CF_PROJ_Y_COORD;
2751 8 : pszLongName = CF_PROJ_Y_COORD_LONG_NAME;
2752 8 : bWriteY = true;
2753 : }
2754 22 : if (pszStandardName && pszLongName)
2755 : {
2756 16 : WriteDimAttrs(poDim, pszStandardName, pszLongName,
2757 : osUnits.c_str());
2758 : }
2759 : }
2760 6 : if (!bWriteX && !bWriteY && apoDims.size() >= 2 &&
2761 6 : apoDims[apoDims.size() - 2]->GetType().empty() &&
2762 6 : apoDims[apoDims.size() - 1]->GetType().empty() &&
2763 28 : apoDims[apoDims.size() - 2]->GetIndexingVariable() &&
2764 12 : apoDims[apoDims.size() - 1]->GetIndexingVariable())
2765 : {
2766 1 : CPLError(CE_Warning, CPLE_AppDefined,
2767 : "Dimensions of variable %s have no type declared. "
2768 : "Assuming the last one is X, and the preceding one Y",
2769 1 : GetName().c_str());
2770 1 : WriteDimAttrs(apoDims[apoDims.size() - 1], CF_PROJ_X_COORD,
2771 : CF_PROJ_X_COORD_LONG_NAME, osUnits.c_str());
2772 1 : WriteDimAttrs(apoDims[apoDims.size() - 2], CF_PROJ_Y_COORD,
2773 : CF_PROJ_Y_COORD_LONG_NAME, osUnits.c_str());
2774 : }
2775 : }
2776 5 : else if (poSRS->IsGeographic())
2777 : {
2778 5 : bool bWriteX = false;
2779 5 : bool bWriteY = false;
2780 15 : for (const auto &poDim : apoDims)
2781 : {
2782 10 : const char *pszStandardName = nullptr;
2783 10 : const char *pszLongName = nullptr;
2784 10 : const char *pszUnits = "";
2785 16 : if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X ||
2786 6 : EQUAL(poDim->GetName().c_str(), CF_LONGITUDE_VAR_NAME))
2787 : {
2788 4 : pszStandardName = CF_LONGITUDE_STD_NAME;
2789 4 : pszLongName = CF_LONGITUDE_LNG_NAME;
2790 4 : pszUnits = CF_DEGREES_EAST;
2791 4 : bWriteX = true;
2792 : }
2793 8 : else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y ||
2794 2 : EQUAL(poDim->GetName().c_str(), CF_LATITUDE_VAR_NAME))
2795 : {
2796 4 : pszStandardName = CF_LATITUDE_STD_NAME;
2797 4 : pszLongName = CF_LATITUDE_LNG_NAME;
2798 4 : pszUnits = CF_DEGREES_NORTH;
2799 4 : bWriteY = true;
2800 : }
2801 10 : if (pszStandardName && pszLongName)
2802 : {
2803 8 : WriteDimAttrs(poDim, pszStandardName, pszLongName, pszUnits);
2804 : }
2805 : }
2806 2 : if (!bWriteX && !bWriteY && apoDims.size() >= 2 &&
2807 2 : apoDims[apoDims.size() - 2]->GetType().empty() &&
2808 2 : apoDims[apoDims.size() - 1]->GetType().empty() &&
2809 12 : apoDims[apoDims.size() - 2]->GetIndexingVariable() &&
2810 6 : apoDims[apoDims.size() - 1]->GetIndexingVariable())
2811 : {
2812 1 : CPLError(CE_Warning, CPLE_AppDefined,
2813 : "Dimensions of variable %s have no type declared. "
2814 : "Assuming the last one is longitude, "
2815 : "and the preceding one latitude",
2816 1 : GetName().c_str());
2817 1 : WriteDimAttrs(apoDims[apoDims.size() - 1], CF_LONGITUDE_STD_NAME,
2818 : CF_LONGITUDE_LNG_NAME, CF_DEGREES_EAST);
2819 1 : WriteDimAttrs(apoDims[apoDims.size() - 2], CF_LATITUDE_STD_NAME,
2820 : CF_LATITUDE_LNG_NAME, CF_DEGREES_NORTH);
2821 : }
2822 : }
2823 :
2824 16 : return true;
2825 : }
2826 :
2827 : /************************************************************************/
2828 : /* SetStatistics() */
2829 : /************************************************************************/
2830 :
2831 2 : bool netCDFVariable::SetStatistics(bool bApproxStats, double dfMin,
2832 : double dfMax, double dfMean, double dfStdDev,
2833 : GUInt64 nValidCount,
2834 : CSLConstList papszOptions)
2835 : {
2836 3 : if (!bApproxStats && !m_poShared->IsReadOnly() &&
2837 1 : CPLTestBool(
2838 : CSLFetchNameValueDef(papszOptions, "UPDATE_METADATA", "NO")))
2839 : {
2840 3 : auto poAttr = GetAttribute("actual_range");
2841 1 : if (!poAttr)
2842 : {
2843 : poAttr =
2844 1 : CreateAttribute("actual_range", {2}, GetDataType(), nullptr);
2845 : }
2846 1 : if (poAttr)
2847 : {
2848 2 : std::vector<GUInt64> startIdx = {0};
2849 2 : std::vector<size_t> count = {2};
2850 1 : std::vector<double> values = {dfMin, dfMax};
2851 2 : poAttr->Write(startIdx.data(), count.data(), nullptr, nullptr,
2852 2 : GDALExtendedDataType::Create(GDT_Float64),
2853 1 : values.data(), nullptr, 0);
2854 : }
2855 : }
2856 2 : return GDALPamMDArray::SetStatistics(bApproxStats, dfMin, dfMax, dfMean,
2857 2 : dfStdDev, nValidCount, papszOptions);
2858 : }
2859 :
2860 : /************************************************************************/
2861 : /* GetNCTypeSize() */
2862 : /************************************************************************/
2863 :
2864 657 : static size_t GetNCTypeSize(const GDALExtendedDataType &dt,
2865 : bool bPerfectDataTypeMatch, int nAttType)
2866 : {
2867 657 : auto nElementSize = dt.GetSize();
2868 657 : if (!bPerfectDataTypeMatch)
2869 : {
2870 10 : if (nAttType == NC_BYTE)
2871 : {
2872 4 : CPLAssert(dt.GetNumericDataType() == GDT_Int16);
2873 4 : nElementSize = sizeof(signed char);
2874 : }
2875 6 : else if (nAttType == NC_INT64)
2876 : {
2877 4 : CPLAssert(dt.GetNumericDataType() == GDT_Float64);
2878 4 : nElementSize = sizeof(GInt64);
2879 : }
2880 2 : else if (nAttType == NC_UINT64)
2881 : {
2882 2 : CPLAssert(dt.GetNumericDataType() == GDT_Float64);
2883 2 : nElementSize = sizeof(GUInt64);
2884 : }
2885 : else
2886 : {
2887 0 : CPLAssert(false);
2888 : }
2889 : }
2890 657 : return nElementSize;
2891 : }
2892 :
2893 : /************************************************************************/
2894 : /* ConvertNCStringsToCPLStrings() */
2895 : /************************************************************************/
2896 :
2897 71 : static void ConvertNCStringsToCPLStrings(GByte *pBuffer,
2898 : const GDALExtendedDataType &dt)
2899 : {
2900 71 : switch (dt.GetClass())
2901 : {
2902 0 : case GEDTC_STRING:
2903 : {
2904 : char *pszStr;
2905 : // cppcheck-suppress pointerSize
2906 0 : memcpy(&pszStr, pBuffer, sizeof(char *));
2907 0 : if (pszStr)
2908 : {
2909 0 : char *pszNewStr = VSIStrdup(pszStr);
2910 0 : nc_free_string(1, &pszStr);
2911 : // cppcheck-suppress pointerSize
2912 0 : memcpy(pBuffer, &pszNewStr, sizeof(char *));
2913 : }
2914 0 : break;
2915 : }
2916 :
2917 67 : case GEDTC_NUMERIC:
2918 : {
2919 67 : break;
2920 : }
2921 :
2922 4 : case GEDTC_COMPOUND:
2923 : {
2924 4 : const auto &comps = dt.GetComponents();
2925 12 : for (const auto &comp : comps)
2926 : {
2927 8 : ConvertNCStringsToCPLStrings(pBuffer + comp->GetOffset(),
2928 : comp->GetType());
2929 : }
2930 4 : break;
2931 : }
2932 : }
2933 71 : }
2934 :
2935 : /************************************************************************/
2936 : /* FreeNCStrings() */
2937 : /************************************************************************/
2938 :
2939 147 : static void FreeNCStrings(GByte *pBuffer, const GDALExtendedDataType &dt)
2940 : {
2941 147 : switch (dt.GetClass())
2942 : {
2943 0 : case GEDTC_STRING:
2944 : {
2945 : char *pszStr;
2946 : // cppcheck-suppress pointerSize
2947 0 : memcpy(&pszStr, pBuffer, sizeof(char *));
2948 0 : if (pszStr)
2949 : {
2950 0 : nc_free_string(1, &pszStr);
2951 : }
2952 0 : break;
2953 : }
2954 :
2955 147 : case GEDTC_NUMERIC:
2956 : {
2957 147 : break;
2958 : }
2959 :
2960 0 : case GEDTC_COMPOUND:
2961 : {
2962 0 : const auto &comps = dt.GetComponents();
2963 0 : for (const auto &comp : comps)
2964 : {
2965 0 : FreeNCStrings(pBuffer + comp->GetOffset(), comp->GetType());
2966 : }
2967 0 : break;
2968 : }
2969 : }
2970 147 : }
2971 :
2972 : /************************************************************************/
2973 : /* IReadWriteGeneric() */
2974 : /************************************************************************/
2975 :
2976 : namespace
2977 : {
2978 : template <typename T> struct GetGByteType
2979 : {
2980 : };
2981 :
2982 : template <> struct GetGByteType<void *>
2983 : {
2984 : typedef GByte *type;
2985 : };
2986 :
2987 : template <> struct GetGByteType<const void *>
2988 : {
2989 : typedef const GByte *type;
2990 : };
2991 : } // namespace
2992 :
2993 : template <typename BufferType, typename NCGetPutVar1FuncType,
2994 : typename ReadOrWriteOneElementType>
2995 15 : bool netCDFVariable::IReadWriteGeneric(
2996 : const size_t *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
2997 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
2998 : BufferType buffer, NCGetPutVar1FuncType NCGetPutVar1Func,
2999 : ReadOrWriteOneElementType ReadOrWriteOneElement) const
3000 : {
3001 15 : CPLAssert(m_nDims > 0);
3002 30 : std::vector<size_t> array_idx(m_nDims);
3003 30 : std::vector<size_t> stack_count_iters(m_nDims - 1);
3004 : typedef typename GetGByteType<BufferType>::type GBytePtrType;
3005 30 : std::vector<GBytePtrType> stack_ptr(m_nDims);
3006 30 : std::vector<GPtrDiff_t> ptr_inc;
3007 15 : ptr_inc.reserve(m_nDims);
3008 15 : const auto &eArrayEDT = GetDataType();
3009 15 : const bool bSameDT = m_bPerfectDataTypeMatch && eArrayEDT == bufferDataType;
3010 15 : const auto nBufferDTSize = bufferDataType.GetSize();
3011 42 : for (int i = 0; i < m_nDims; i++)
3012 : {
3013 27 : ptr_inc.push_back(bufferStride[i] * nBufferDTSize);
3014 : }
3015 15 : const auto nDimsMinus1 = m_nDims - 1;
3016 15 : stack_ptr[0] = static_cast<GBytePtrType>(buffer);
3017 :
3018 4579 : auto lambdaLastDim = [&](GBytePtrType ptr)
3019 : {
3020 60 : array_idx[nDimsMinus1] = arrayStartIdx[nDimsMinus1];
3021 60 : size_t nIters = count[nDimsMinus1];
3022 520 : while (true)
3023 : {
3024 580 : if (bSameDT)
3025 : {
3026 : int ret =
3027 8 : NCGetPutVar1Func(m_gid, m_varid, array_idx.data(), ptr);
3028 8 : NCDF_ERR(ret);
3029 8 : if (ret != NC_NOERR)
3030 0 : return false;
3031 : }
3032 : else
3033 : {
3034 1144 : if (!(this->*ReadOrWriteOneElement)(eArrayEDT, bufferDataType,
3035 572 : array_idx.data(), ptr))
3036 0 : return false;
3037 : }
3038 580 : if ((--nIters) == 0)
3039 60 : break;
3040 520 : ptr += ptr_inc[nDimsMinus1];
3041 : // CPLUnsanitizedAdd needed as arrayStep[] might be negative, and
3042 : // thus automatic conversion from negative to big unsigned might
3043 : // occur
3044 520 : array_idx[nDimsMinus1] = CPLUnsanitizedAdd<size_t>(
3045 : array_idx[nDimsMinus1],
3046 520 : static_cast<GPtrDiff_t>(arrayStep[nDimsMinus1]));
3047 : }
3048 60 : return true;
3049 : };
3050 :
3051 15 : if (m_nDims == 1)
3052 : {
3053 6 : return lambdaLastDim(stack_ptr[0]);
3054 : }
3055 9 : else if (m_nDims == 2)
3056 : {
3057 7 : auto nIters = count[0];
3058 7 : array_idx[0] = arrayStartIdx[0];
3059 : while (true)
3060 : {
3061 36 : if (!lambdaLastDim(stack_ptr[0]))
3062 0 : return false;
3063 36 : if ((--nIters) == 0)
3064 7 : break;
3065 29 : stack_ptr[0] += ptr_inc[0];
3066 : // CPLUnsanitizedAdd needed as arrayStep[] might be negative, and
3067 : // thus automatic conversion from negative to big unsigned might
3068 : // occur
3069 29 : array_idx[0] = CPLUnsanitizedAdd<size_t>(
3070 : array_idx[0], static_cast<GPtrDiff_t>(arrayStep[0]));
3071 : }
3072 : }
3073 2 : else if (m_nDims == 3)
3074 : {
3075 1 : stack_count_iters[0] = count[0];
3076 1 : array_idx[0] = arrayStartIdx[0];
3077 2 : while (true)
3078 : {
3079 3 : auto nIters = count[1];
3080 3 : array_idx[1] = arrayStartIdx[1];
3081 3 : stack_ptr[1] = stack_ptr[0];
3082 : while (true)
3083 : {
3084 6 : if (!lambdaLastDim(stack_ptr[1]))
3085 0 : return false;
3086 6 : if ((--nIters) == 0)
3087 3 : break;
3088 3 : stack_ptr[1] += ptr_inc[1];
3089 : // CPLUnsanitizedAdd needed as arrayStep[] might be negative,
3090 : // and thus automatic conversion from negative to big unsigned
3091 : // might occur
3092 3 : array_idx[1] = CPLUnsanitizedAdd<size_t>(
3093 3 : array_idx[1], static_cast<GPtrDiff_t>(arrayStep[1]));
3094 : }
3095 3 : if ((--stack_count_iters[0]) == 0)
3096 1 : break;
3097 2 : stack_ptr[0] += ptr_inc[0];
3098 : // CPLUnsanitizedAdd needed as arrayStep[] might be negative, and
3099 : // thus automatic conversion from negative to big unsigned might
3100 : // occur
3101 2 : array_idx[0] = CPLUnsanitizedAdd<size_t>(
3102 : array_idx[0], static_cast<GPtrDiff_t>(arrayStep[0]));
3103 : }
3104 : }
3105 : else
3106 : {
3107 : // Implementation valid for nDims >= 3
3108 :
3109 1 : int dimIdx = 0;
3110 : // Non-recursive implementation. Hence the gotos
3111 : // It might be possible to rewrite this without gotos, but I find they
3112 : // make it clearer to understand the recursive nature of the code
3113 9 : lbl_start:
3114 9 : if (dimIdx == nDimsMinus1 - 1)
3115 : {
3116 6 : array_idx[dimIdx] = arrayStartIdx[dimIdx];
3117 6 : auto nIters = count[dimIdx];
3118 : while (true)
3119 : {
3120 12 : if (!(lambdaLastDim(stack_ptr[dimIdx])))
3121 0 : return false;
3122 12 : if ((--nIters) == 0)
3123 6 : break;
3124 6 : stack_ptr[dimIdx] += ptr_inc[dimIdx];
3125 : // CPLUnsanitizedAdd needed as arrayStep[] might be negative,
3126 : // and thus automatic conversion from negative to big unsigned
3127 : // might occur
3128 6 : array_idx[dimIdx] = CPLUnsanitizedAdd<size_t>(
3129 : array_idx[dimIdx],
3130 6 : static_cast<GPtrDiff_t>(arrayStep[dimIdx]));
3131 : }
3132 : // If there was a test if( dimIdx > 0 ), that would be valid for
3133 : // nDims == 2
3134 6 : goto lbl_return_to_caller;
3135 : }
3136 : else
3137 : {
3138 3 : array_idx[dimIdx] = arrayStartIdx[dimIdx];
3139 3 : stack_count_iters[dimIdx] = count[dimIdx];
3140 : while (true)
3141 : {
3142 : // Simulate a recursive call to the next dimension
3143 : // Implicitly save back count and ptr
3144 8 : dimIdx++;
3145 8 : stack_ptr[dimIdx] = stack_ptr[dimIdx - 1];
3146 8 : goto lbl_start;
3147 8 : lbl_return_to_caller:
3148 8 : dimIdx--;
3149 8 : if ((--stack_count_iters[dimIdx]) == 0)
3150 3 : break;
3151 5 : stack_ptr[dimIdx] += ptr_inc[dimIdx];
3152 : // CPLUnsanitizedAdd needed as arrayStep[] might be negative,
3153 : // and thus automatic conversion from negative to big unsigned
3154 : // might occur
3155 5 : array_idx[dimIdx] = CPLUnsanitizedAdd<size_t>(
3156 : array_idx[dimIdx],
3157 5 : static_cast<GPtrDiff_t>(arrayStep[dimIdx]));
3158 : }
3159 3 : if (dimIdx > 0)
3160 2 : goto lbl_return_to_caller;
3161 : }
3162 : }
3163 :
3164 9 : return true;
3165 : }
3166 :
3167 : /************************************************************************/
3168 : /* CheckNumericDataType() */
3169 : /************************************************************************/
3170 :
3171 935 : static bool CheckNumericDataType(const GDALExtendedDataType &dt)
3172 : {
3173 935 : const auto klass = dt.GetClass();
3174 935 : if (klass == GEDTC_NUMERIC)
3175 925 : return dt.GetNumericDataType() != GDT_Unknown;
3176 10 : if (klass == GEDTC_STRING)
3177 0 : return false;
3178 10 : CPLAssert(klass == GEDTC_COMPOUND);
3179 10 : const auto &comps = dt.GetComponents();
3180 31 : for (const auto &comp : comps)
3181 : {
3182 21 : if (!CheckNumericDataType(comp->GetType()))
3183 0 : return false;
3184 : }
3185 10 : return true;
3186 : }
3187 :
3188 : /************************************************************************/
3189 : /* IReadWrite() */
3190 : /************************************************************************/
3191 :
3192 : template <typename BufferType, typename NCGetPutVar1FuncType,
3193 : typename NCGetPutVaraFuncType, typename NCGetPutVarmFuncType,
3194 : typename ReadOrWriteOneElementType>
3195 462 : bool netCDFVariable::IReadWrite(
3196 : const bool bIsRead, const GUInt64 *arrayStartIdx, const size_t *count,
3197 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
3198 : const GDALExtendedDataType &bufferDataType, BufferType buffer,
3199 : NCGetPutVar1FuncType NCGetPutVar1Func,
3200 : NCGetPutVaraFuncType NCGetPutVaraFunc,
3201 : NCGetPutVarmFuncType NCGetPutVarmFunc,
3202 : ReadOrWriteOneElementType ReadOrWriteOneElement) const
3203 : {
3204 924 : CPLMutexHolderD(&hNCMutex);
3205 462 : m_poShared->SetDefineMode(false);
3206 :
3207 462 : const auto &eDT = GetDataType();
3208 924 : std::vector<size_t> startp;
3209 462 : startp.reserve(m_nDims);
3210 462 : bool bUseSlowPath =
3211 462 : !m_bPerfectDataTypeMatch &&
3212 0 : !(bIsRead && bufferDataType.GetClass() == GEDTC_NUMERIC &&
3213 0 : eDT.GetClass() == GEDTC_NUMERIC &&
3214 0 : bufferDataType.GetSize() >= eDT.GetSize());
3215 1353 : for (int i = 0; i < m_nDims; i++)
3216 : {
3217 : #if SIZEOF_VOIDP == 4
3218 : if (arrayStartIdx[i] > std::numeric_limits<size_t>::max())
3219 : return false;
3220 : #endif
3221 891 : startp.push_back(static_cast<size_t>(arrayStartIdx[i]));
3222 :
3223 : #if SIZEOF_VOIDP == 4
3224 : if (arrayStep[i] < std::numeric_limits<ptrdiff_t>::min() ||
3225 : arrayStep[i] > std::numeric_limits<ptrdiff_t>::max())
3226 : {
3227 : return false;
3228 : }
3229 : #endif
3230 :
3231 891 : if (count[i] != 1 && arrayStep[i] <= 0)
3232 3 : bUseSlowPath = true; // netCDF rejects negative or NULL strides
3233 :
3234 891 : if (bufferStride[i] < 0)
3235 0 : bUseSlowPath =
3236 : true; // and it seems to silently cast to size_t imapp
3237 : }
3238 :
3239 467 : if (eDT.GetClass() == GEDTC_STRING &&
3240 467 : bufferDataType.GetClass() == GEDTC_STRING && m_nVarType == NC_STRING)
3241 : {
3242 5 : if (m_nDims == 0)
3243 : {
3244 2 : return (this->*ReadOrWriteOneElement)(eDT, bufferDataType, nullptr,
3245 2 : buffer);
3246 : }
3247 :
3248 3 : return IReadWriteGeneric(startp.data(), count, arrayStep, bufferStride,
3249 : bufferDataType, buffer, NCGetPutVar1Func,
3250 3 : ReadOrWriteOneElement);
3251 : }
3252 :
3253 457 : if (!CheckNumericDataType(eDT))
3254 0 : return false;
3255 457 : if (!CheckNumericDataType(bufferDataType))
3256 0 : return false;
3257 :
3258 457 : if (m_nDims == 0)
3259 : {
3260 14 : return (this->*ReadOrWriteOneElement)(eDT, bufferDataType, nullptr,
3261 14 : buffer);
3262 : }
3263 :
3264 883 : if (!bUseSlowPath &&
3265 870 : ((GDALDataTypeIsComplex(bufferDataType.GetNumericDataType()) ||
3266 430 : bufferDataType.GetClass() == GEDTC_COMPOUND) &&
3267 15 : bufferDataType == eDT))
3268 : {
3269 : // nc_get_varm() not supported for non-atomic types.
3270 14 : ptrdiff_t nExpectedBufferStride = 1;
3271 40 : for (int i = m_nDims; i != 0;)
3272 : {
3273 26 : --i;
3274 26 : if (count[i] != 1 &&
3275 17 : (arrayStep[i] != 1 || bufferStride[i] != nExpectedBufferStride))
3276 : {
3277 0 : bUseSlowPath = true;
3278 0 : break;
3279 : }
3280 26 : nExpectedBufferStride *= count[i];
3281 : }
3282 14 : if (!bUseSlowPath)
3283 : {
3284 : int ret =
3285 14 : NCGetPutVaraFunc(m_gid, m_varid, startp.data(), count, buffer);
3286 14 : NCDF_ERR(ret);
3287 14 : return ret == NC_NOERR;
3288 : }
3289 : }
3290 :
3291 851 : if (bUseSlowPath || bufferDataType.GetClass() == GEDTC_COMPOUND ||
3292 425 : eDT.GetClass() == GEDTC_COMPOUND ||
3293 425 : (!bIsRead &&
3294 1227 : bufferDataType.GetNumericDataType() != eDT.GetNumericDataType()) ||
3295 372 : (bIsRead && bufferDataType.GetSize() < eDT.GetSize()))
3296 : {
3297 8 : return IReadWriteGeneric(startp.data(), count, arrayStep, bufferStride,
3298 : bufferDataType, buffer, NCGetPutVar1Func,
3299 8 : ReadOrWriteOneElement);
3300 : }
3301 :
3302 421 : bUseSlowPath = false;
3303 421 : ptrdiff_t nExpectedBufferStride = 1;
3304 1249 : for (int i = m_nDims; i != 0;)
3305 : {
3306 839 : --i;
3307 839 : if (count[i] != 1 &&
3308 495 : (arrayStep[i] != 1 || bufferStride[i] != nExpectedBufferStride))
3309 : {
3310 11 : bUseSlowPath = true;
3311 11 : break;
3312 : }
3313 828 : nExpectedBufferStride *= count[i];
3314 : }
3315 421 : if (!bUseSlowPath)
3316 : {
3317 : // nc_get_varm() is terribly inefficient, so use nc_get_vara()
3318 : // when possible.
3319 : int ret =
3320 410 : NCGetPutVaraFunc(m_gid, m_varid, startp.data(), count, buffer);
3321 410 : if (ret != NC_NOERR)
3322 : {
3323 1 : NCDF_ERR(ret);
3324 1 : return false;
3325 : }
3326 767 : if (bIsRead &&
3327 358 : (!m_bPerfectDataTypeMatch ||
3328 358 : bufferDataType.GetNumericDataType() != eDT.GetNumericDataType()))
3329 : {
3330 : // If the buffer data type is "larger" or of the same size as the
3331 : // native data type, we can do a in-place conversion
3332 16 : GByte *pabyBuffer =
3333 : static_cast<GByte *>(const_cast<void *>(buffer));
3334 16 : CPLAssert(bufferDataType.GetSize() >= eDT.GetSize());
3335 16 : const auto nDTSize = eDT.GetSize();
3336 16 : const auto nBufferDTSize = bufferDataType.GetSize();
3337 16 : if (!m_bPerfectDataTypeMatch &&
3338 0 : (m_nVarType == NC_CHAR || m_nVarType == NC_BYTE))
3339 : {
3340 : // native NC type translates into GDAL data type of larger size
3341 0 : for (ptrdiff_t i = nExpectedBufferStride - 1; i >= 0; --i)
3342 : {
3343 : GByte abySrc[sizeof(
3344 : double)]; // 2 is enough here, but sizeof(double) make
3345 : // MSVC happy
3346 0 : abySrc[0] = *(pabyBuffer + i);
3347 0 : ConvertNCToGDAL(&abySrc[0]);
3348 0 : GDALExtendedDataType::CopyValue(
3349 0 : &abySrc[0], eDT, pabyBuffer + i * nBufferDTSize,
3350 : bufferDataType);
3351 0 : }
3352 : }
3353 16 : else if (!m_bPerfectDataTypeMatch)
3354 : {
3355 : // native NC type translates into GDAL data type of same size
3356 0 : CPLAssert(m_nVarType == NC_INT64 || m_nVarType == NC_UINT64);
3357 0 : for (ptrdiff_t i = nExpectedBufferStride - 1; i >= 0; --i)
3358 : {
3359 0 : ConvertNCToGDAL(pabyBuffer + i * nDTSize);
3360 0 : GDALExtendedDataType::CopyValue(
3361 0 : pabyBuffer + i * nDTSize, eDT,
3362 0 : pabyBuffer + i * nBufferDTSize, bufferDataType);
3363 : }
3364 : }
3365 : else
3366 : {
3367 2196 : for (ptrdiff_t i = nExpectedBufferStride - 1; i >= 0; --i)
3368 : {
3369 2180 : GDALExtendedDataType::CopyValue(
3370 2180 : pabyBuffer + i * nDTSize, eDT,
3371 2180 : pabyBuffer + i * nBufferDTSize, bufferDataType);
3372 : }
3373 : }
3374 : }
3375 409 : return true;
3376 : }
3377 : else
3378 : {
3379 11 : if (bufferDataType.GetNumericDataType() != eDT.GetNumericDataType())
3380 : {
3381 4 : return IReadWriteGeneric(startp.data(), count, arrayStep,
3382 : bufferStride, bufferDataType, buffer,
3383 4 : NCGetPutVar1Func, ReadOrWriteOneElement);
3384 : }
3385 14 : std::vector<ptrdiff_t> stridep;
3386 7 : stridep.reserve(m_nDims);
3387 7 : std::vector<ptrdiff_t> imapp;
3388 7 : imapp.reserve(m_nDims);
3389 25 : for (int i = 0; i < m_nDims; i++)
3390 : {
3391 18 : stridep.push_back(
3392 18 : static_cast<ptrdiff_t>(count[i] == 1 ? 1 : arrayStep[i]));
3393 18 : imapp.push_back(static_cast<ptrdiff_t>(bufferStride[i]));
3394 : }
3395 :
3396 7 : if (!m_poShared->GetImappIsInElements())
3397 : {
3398 : const size_t nMul =
3399 0 : GetNCTypeSize(eDT, m_bPerfectDataTypeMatch, m_nVarType);
3400 0 : for (int i = 0; i < m_nDims; ++i)
3401 : {
3402 0 : imapp[i] = static_cast<ptrdiff_t>(imapp[i] * nMul);
3403 : }
3404 : }
3405 7 : int ret = NCGetPutVarmFunc(m_gid, m_varid, startp.data(), count,
3406 7 : stridep.data(), imapp.data(), buffer);
3407 7 : NCDF_ERR(ret);
3408 7 : return ret == NC_NOERR;
3409 : }
3410 : }
3411 :
3412 : /************************************************************************/
3413 : /* ConvertNCToGDAL() */
3414 : /************************************************************************/
3415 :
3416 579 : void netCDFVariable::ConvertNCToGDAL(GByte *buffer) const
3417 : {
3418 579 : if (!m_bPerfectDataTypeMatch)
3419 : {
3420 0 : if (m_nVarType == NC_CHAR || m_nVarType == NC_BYTE)
3421 : {
3422 0 : short s = reinterpret_cast<signed char *>(buffer)[0];
3423 0 : memcpy(buffer, &s, sizeof(s));
3424 : }
3425 0 : else if (m_nVarType == NC_INT64)
3426 : {
3427 0 : double v =
3428 0 : static_cast<double>(reinterpret_cast<GInt64 *>(buffer)[0]);
3429 0 : memcpy(buffer, &v, sizeof(v));
3430 : }
3431 0 : else if (m_nVarType == NC_UINT64)
3432 : {
3433 0 : double v =
3434 0 : static_cast<double>(reinterpret_cast<GUInt64 *>(buffer)[0]);
3435 0 : memcpy(buffer, &v, sizeof(v));
3436 : }
3437 : }
3438 579 : }
3439 :
3440 : /************************************************************************/
3441 : /* ReadOneElement() */
3442 : /************************************************************************/
3443 :
3444 580 : bool netCDFVariable::ReadOneElement(const GDALExtendedDataType &src_datatype,
3445 : const GDALExtendedDataType &bufferDataType,
3446 : const size_t *array_idx,
3447 : void *pDstBuffer) const
3448 : {
3449 580 : if (src_datatype.GetClass() == GEDTC_STRING)
3450 : {
3451 1 : char *pszStr = nullptr;
3452 1 : int ret = nc_get_var1_string(m_gid, m_varid, array_idx, &pszStr);
3453 1 : NCDF_ERR(ret);
3454 1 : if (ret != NC_NOERR)
3455 0 : return false;
3456 1 : GDALExtendedDataType::CopyValue(&pszStr, src_datatype, pDstBuffer,
3457 : bufferDataType);
3458 1 : nc_free_string(1, &pszStr);
3459 1 : return true;
3460 : }
3461 :
3462 : std::vector<GByte> abySrc(std::max(
3463 0 : src_datatype.GetSize(),
3464 1158 : GetNCTypeSize(src_datatype, m_bPerfectDataTypeMatch, m_nVarType)));
3465 :
3466 579 : int ret = nc_get_var1(m_gid, m_varid, array_idx, &abySrc[0]);
3467 579 : NCDF_ERR(ret);
3468 579 : if (ret != NC_NOERR)
3469 0 : return false;
3470 :
3471 579 : ConvertNCToGDAL(&abySrc[0]);
3472 :
3473 579 : GDALExtendedDataType::CopyValue(&abySrc[0], src_datatype, pDstBuffer,
3474 : bufferDataType);
3475 579 : return true;
3476 : }
3477 :
3478 : /************************************************************************/
3479 : /* IRead() */
3480 : /************************************************************************/
3481 :
3482 490 : bool netCDFVariable::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
3483 : const GInt64 *arrayStep,
3484 : const GPtrDiff_t *bufferStride,
3485 : const GDALExtendedDataType &bufferDataType,
3486 : void *pDstBuffer) const
3487 : {
3488 490 : if (m_nDims == 2 && m_nVarType == NC_CHAR && GetDimensions().size() == 1)
3489 : {
3490 8 : CPLMutexHolderD(&hNCMutex);
3491 4 : m_poShared->SetDefineMode(false);
3492 :
3493 4 : if (bufferDataType.GetClass() != GEDTC_STRING)
3494 0 : return false;
3495 4 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
3496 4 : size_t array_idx[2] = {static_cast<size_t>(arrayStartIdx[0]), 0};
3497 4 : size_t array_count[2] = {1, m_nTextLength};
3498 8 : std::string osTmp(m_nTextLength, 0);
3499 4 : const char *pszTmp = osTmp.c_str();
3500 12 : for (size_t i = 0; i < count[0]; i++)
3501 : {
3502 : int ret =
3503 8 : nc_get_vara(m_gid, m_varid, array_idx, array_count, &osTmp[0]);
3504 8 : NCDF_ERR(ret);
3505 8 : if (ret != NC_NOERR)
3506 0 : return false;
3507 : // coverity[use_after_free]
3508 8 : GDALExtendedDataType::CopyValue(&pszTmp, GetDataType(),
3509 : pabyDstBuffer, GetDataType());
3510 8 : array_idx[0] = static_cast<size_t>(array_idx[0] + arrayStep[0]);
3511 8 : pabyDstBuffer += bufferStride[0] * sizeof(char *);
3512 : }
3513 4 : return true;
3514 : }
3515 :
3516 486 : if (m_poCachedArray)
3517 : {
3518 5 : const auto nDims = GetDimensionCount();
3519 5 : std::vector<GUInt64> modifiedArrayStartIdx(nDims);
3520 5 : bool canUseCache = true;
3521 13 : for (size_t i = 0; i < nDims; i++)
3522 : {
3523 17 : if (arrayStartIdx[i] >= m_cachedArrayStartIdx[i] &&
3524 8 : arrayStartIdx[i] + (count[i] - 1) * arrayStep[i] <=
3525 8 : m_cachedArrayStartIdx[i] + m_cachedCount[i] - 1)
3526 : {
3527 8 : modifiedArrayStartIdx[i] =
3528 8 : arrayStartIdx[i] - m_cachedArrayStartIdx[i];
3529 : }
3530 : else
3531 : {
3532 1 : canUseCache = false;
3533 1 : break;
3534 : }
3535 : }
3536 5 : if (canUseCache)
3537 : {
3538 4 : return m_poCachedArray->Read(modifiedArrayStartIdx.data(), count,
3539 : arrayStep, bufferStride,
3540 4 : bufferDataType, pDstBuffer);
3541 : }
3542 : }
3543 :
3544 482 : if (IsTransposedRequest(count, bufferStride))
3545 : {
3546 84 : return ReadForTransposedRequest(arrayStartIdx, count, arrayStep,
3547 : bufferStride, bufferDataType,
3548 84 : pDstBuffer);
3549 : }
3550 :
3551 398 : return IReadWrite(true, arrayStartIdx, count, arrayStep, bufferStride,
3552 : bufferDataType, pDstBuffer, nc_get_var1, nc_get_vara,
3553 398 : nc_get_varm, &netCDFVariable::ReadOneElement);
3554 : }
3555 :
3556 : /************************************************************************/
3557 : /* IAdviseRead() */
3558 : /************************************************************************/
3559 :
3560 4 : bool netCDFVariable::IAdviseRead(const GUInt64 *arrayStartIdx,
3561 : const size_t *count,
3562 : CSLConstList /* papszOptions */) const
3563 : {
3564 4 : const auto nDims = GetDimensionCount();
3565 4 : if (nDims == 0)
3566 0 : return true;
3567 4 : const auto &eDT = GetDataType();
3568 4 : if (eDT.GetClass() != GEDTC_NUMERIC)
3569 0 : return false;
3570 :
3571 4 : auto poMemDriver = static_cast<GDALDriver *>(GDALGetDriverByName("MEM"));
3572 4 : if (poMemDriver == nullptr)
3573 0 : return false;
3574 :
3575 4 : m_poCachedArray.reset();
3576 :
3577 4 : size_t nElts = 1;
3578 12 : for (size_t i = 0; i < nDims; i++)
3579 8 : nElts *= count[i];
3580 :
3581 4 : void *pData = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
3582 4 : if (pData == nullptr)
3583 0 : return false;
3584 :
3585 4 : if (!Read(arrayStartIdx, count, nullptr, nullptr, eDT, pData))
3586 : {
3587 0 : VSIFree(pData);
3588 0 : return false;
3589 : }
3590 :
3591 4 : auto poDS = poMemDriver->CreateMultiDimensional("", nullptr, nullptr);
3592 8 : auto poGroup = poDS->GetRootGroup();
3593 4 : delete poDS;
3594 :
3595 4 : std::vector<std::shared_ptr<GDALDimension>> apoMemDims;
3596 4 : const auto &poDims = GetDimensions();
3597 12 : for (size_t i = 0; i < nDims; i++)
3598 : {
3599 : apoMemDims.emplace_back(
3600 24 : poGroup->CreateDimension(poDims[i]->GetName(), std::string(),
3601 24 : std::string(), count[i], nullptr));
3602 : }
3603 : m_poCachedArray =
3604 4 : poGroup->CreateMDArray(GetName(), apoMemDims, eDT, nullptr);
3605 4 : m_poCachedArray->Write(std::vector<GUInt64>(nDims).data(), count, nullptr,
3606 : nullptr, eDT, pData);
3607 4 : m_cachedArrayStartIdx.resize(nDims);
3608 4 : memcpy(&m_cachedArrayStartIdx[0], arrayStartIdx, nDims * sizeof(GUInt64));
3609 4 : m_cachedCount.resize(nDims);
3610 4 : memcpy(&m_cachedCount[0], count, nDims * sizeof(size_t));
3611 4 : VSIFree(pData);
3612 4 : return true;
3613 : }
3614 :
3615 : /************************************************************************/
3616 : /* ConvertGDALToNC() */
3617 : /************************************************************************/
3618 :
3619 21 : void netCDFVariable::ConvertGDALToNC(GByte *buffer) const
3620 : {
3621 21 : if (!m_bPerfectDataTypeMatch)
3622 : {
3623 0 : if (m_nVarType == NC_CHAR || m_nVarType == NC_BYTE)
3624 : {
3625 0 : const auto c =
3626 0 : static_cast<signed char>(reinterpret_cast<short *>(buffer)[0]);
3627 0 : memcpy(buffer, &c, sizeof(c));
3628 : }
3629 0 : else if (m_nVarType == NC_INT64)
3630 : {
3631 0 : const auto v =
3632 0 : static_cast<GInt64>(reinterpret_cast<double *>(buffer)[0]);
3633 0 : memcpy(buffer, &v, sizeof(v));
3634 : }
3635 0 : else if (m_nVarType == NC_UINT64)
3636 : {
3637 0 : const auto v =
3638 0 : static_cast<GUInt64>(reinterpret_cast<double *>(buffer)[0]);
3639 0 : memcpy(buffer, &v, sizeof(v));
3640 : }
3641 : }
3642 21 : }
3643 :
3644 : /************************************************************************/
3645 : /* WriteOneElement() */
3646 : /************************************************************************/
3647 :
3648 8 : bool netCDFVariable::WriteOneElement(const GDALExtendedDataType &dst_datatype,
3649 : const GDALExtendedDataType &bufferDataType,
3650 : const size_t *array_idx,
3651 : const void *pSrcBuffer) const
3652 : {
3653 8 : if (dst_datatype.GetClass() == GEDTC_STRING)
3654 : {
3655 1 : const char *pszStr = (static_cast<const char *const *>(pSrcBuffer))[0];
3656 1 : int ret = nc_put_var1_string(m_gid, m_varid, array_idx, &pszStr);
3657 1 : NCDF_ERR(ret);
3658 1 : return ret == NC_NOERR;
3659 : }
3660 :
3661 7 : std::vector<GByte> abyTmp(dst_datatype.GetSize());
3662 7 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &abyTmp[0],
3663 : dst_datatype);
3664 :
3665 7 : ConvertGDALToNC(&abyTmp[0]);
3666 :
3667 7 : int ret = nc_put_var1(m_gid, m_varid, array_idx, &abyTmp[0]);
3668 7 : NCDF_ERR(ret);
3669 7 : return ret == NC_NOERR;
3670 : }
3671 :
3672 : /************************************************************************/
3673 : /* IWrite() */
3674 : /************************************************************************/
3675 :
3676 67 : bool netCDFVariable::IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
3677 : const GInt64 *arrayStep,
3678 : const GPtrDiff_t *bufferStride,
3679 : const GDALExtendedDataType &bufferDataType,
3680 : const void *pSrcBuffer)
3681 : {
3682 67 : m_bHasWrittenData = true;
3683 :
3684 67 : m_poCachedArray.reset();
3685 :
3686 67 : if (m_nDims == 2 && m_nVarType == NC_CHAR && GetDimensions().size() == 1)
3687 : {
3688 6 : CPLMutexHolderD(&hNCMutex);
3689 3 : m_poShared->SetDefineMode(false);
3690 :
3691 3 : if (bufferDataType.GetClass() != GEDTC_STRING)
3692 0 : return false;
3693 3 : const char *const *ppszSrcBuffer =
3694 : static_cast<const char *const *>(pSrcBuffer);
3695 3 : size_t array_idx[2] = {static_cast<size_t>(arrayStartIdx[0]), 0};
3696 3 : size_t array_count[2] = {1, m_nTextLength};
3697 6 : std::string osTmp(m_nTextLength, 0);
3698 8 : for (size_t i = 0; i < count[0]; i++)
3699 : {
3700 5 : const char *pszStr = *ppszSrcBuffer;
3701 5 : memset(&osTmp[0], 0, m_nTextLength);
3702 5 : if (pszStr)
3703 : {
3704 5 : size_t nLen = strlen(pszStr);
3705 5 : memcpy(&osTmp[0], pszStr, std::min(m_nTextLength, nLen));
3706 : }
3707 : int ret =
3708 5 : nc_put_vara(m_gid, m_varid, array_idx, array_count, &osTmp[0]);
3709 5 : NCDF_ERR(ret);
3710 5 : if (ret != NC_NOERR)
3711 0 : return false;
3712 5 : array_idx[0] = static_cast<size_t>(array_idx[0] + arrayStep[0]);
3713 5 : ppszSrcBuffer += bufferStride[0];
3714 : }
3715 3 : return true;
3716 : }
3717 :
3718 64 : return IReadWrite(false, arrayStartIdx, count, arrayStep, bufferStride,
3719 : bufferDataType, pSrcBuffer, nc_put_var1, nc_put_vara,
3720 64 : nc_put_varm, &netCDFVariable::WriteOneElement);
3721 : }
3722 :
3723 : /************************************************************************/
3724 : /* GetRawNoDataValue() */
3725 : /************************************************************************/
3726 :
3727 172 : const void *netCDFVariable::GetRawNoDataValue() const
3728 : {
3729 172 : const auto &dt = GetDataType();
3730 172 : if (dt.GetClass() != GEDTC_NUMERIC)
3731 2 : return nullptr;
3732 :
3733 170 : if (m_bGetRawNoDataValueHasRun)
3734 : {
3735 86 : return m_abyNoData.empty() ? nullptr : m_abyNoData.data();
3736 : }
3737 :
3738 84 : m_bGetRawNoDataValueHasRun = true;
3739 :
3740 84 : const char *pszAttrName = NCDF_FillValue;
3741 252 : auto poAttr = GetAttribute(pszAttrName);
3742 84 : if (!poAttr)
3743 : {
3744 54 : pszAttrName = "missing_value";
3745 54 : poAttr = GetAttribute(pszAttrName);
3746 : }
3747 84 : if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
3748 : {
3749 33 : auto oRawResult = poAttr->ReadAsRaw();
3750 33 : if (oRawResult.data())
3751 : {
3752 : // Round-trip attribute value to target data type and back
3753 : // to attribute data type to ensure there is no loss
3754 : // Normally _FillValue data type should be the same
3755 : // as the array one, but this is not always the case.
3756 : // For example NASA GEDI L2B products have Float64
3757 : // _FillValue for Float32 variables.
3758 33 : m_abyNoData.resize(dt.GetSize());
3759 33 : GDALExtendedDataType::CopyValue(oRawResult.data(),
3760 33 : poAttr->GetDataType(),
3761 33 : m_abyNoData.data(), dt);
3762 66 : std::vector<GByte> abyTmp(poAttr->GetDataType().GetSize());
3763 33 : GDALExtendedDataType::CopyValue(
3764 33 : m_abyNoData.data(), dt, abyTmp.data(), poAttr->GetDataType());
3765 66 : std::vector<GByte> abyOri;
3766 33 : abyOri.assign(oRawResult.data(),
3767 33 : oRawResult.data() + oRawResult.size());
3768 33 : if (abyOri == abyTmp)
3769 32 : return m_abyNoData.data();
3770 1 : m_abyNoData.clear();
3771 1 : char *pszVal = nullptr;
3772 1 : GDALExtendedDataType::CopyValue(
3773 1 : oRawResult.data(), poAttr->GetDataType(), &pszVal,
3774 2 : GDALExtendedDataType::CreateString());
3775 1 : CPLError(CE_Warning, CPLE_AppDefined,
3776 : "%s attribute value (%s) is not in the range of the "
3777 : "variable data type",
3778 1 : pszAttrName, pszVal ? pszVal : "(null)");
3779 1 : CPLFree(pszVal);
3780 1 : return nullptr;
3781 : }
3782 : }
3783 51 : else if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
3784 : {
3785 3 : const char *pszVal = poAttr->ReadAsString();
3786 3 : if (pszVal)
3787 : {
3788 : // Round-trip attribute value to target data type and back
3789 : // to attribute data type to ensure there is no loss
3790 3 : m_abyNoData.resize(dt.GetSize());
3791 3 : GDALExtendedDataType::CopyValue(&pszVal, poAttr->GetDataType(),
3792 3 : m_abyNoData.data(), dt);
3793 3 : char *pszTmpVal = nullptr;
3794 3 : GDALExtendedDataType::CopyValue(m_abyNoData.data(), dt, &pszTmpVal,
3795 3 : poAttr->GetDataType());
3796 3 : if (pszTmpVal)
3797 : {
3798 3 : const bool bSame = strcmp(pszVal, pszTmpVal) == 0;
3799 3 : CPLFree(pszTmpVal);
3800 3 : if (bSame)
3801 3 : return m_abyNoData.data();
3802 2 : CPLError(CE_Warning, CPLE_AppDefined,
3803 : "%s attribute value ('%s') is not in the range of the "
3804 : "variable data type",
3805 : pszAttrName, pszVal);
3806 2 : m_abyNoData.clear();
3807 2 : return nullptr;
3808 : }
3809 : }
3810 : }
3811 :
3812 60 : if (m_bUseDefaultFillAsNoData && m_abyNoData.empty() &&
3813 12 : (m_nVarType == NC_SHORT || m_nVarType == NC_USHORT ||
3814 8 : m_nVarType == NC_INT || m_nVarType == NC_UINT ||
3815 6 : m_nVarType == NC_FLOAT || m_nVarType == NC_DOUBLE))
3816 : {
3817 9 : bool bGotNoData = false;
3818 : double dfNoData =
3819 9 : NCDFGetDefaultNoDataValue(m_gid, m_varid, m_nVarType, bGotNoData);
3820 9 : m_abyNoData.resize(dt.GetSize());
3821 9 : GDALCopyWords(&dfNoData, GDT_Float64, 0, &m_abyNoData[0],
3822 : dt.GetNumericDataType(), 0, 1);
3823 : }
3824 42 : else if (m_bUseDefaultFillAsNoData && m_abyNoData.empty() &&
3825 3 : m_nVarType == NC_INT64)
3826 : {
3827 1 : bool bGotNoData = false;
3828 : const auto nNoData =
3829 1 : NCDFGetDefaultNoDataValueAsInt64(m_gid, m_varid, bGotNoData);
3830 1 : m_abyNoData.resize(dt.GetSize());
3831 1 : memcpy(&m_abyNoData[0], &nNoData, sizeof(nNoData));
3832 : }
3833 40 : else if (m_bUseDefaultFillAsNoData && m_abyNoData.empty() &&
3834 2 : m_nVarType == NC_UINT64)
3835 : {
3836 1 : bool bGotNoData = false;
3837 : const auto nNoData =
3838 1 : NCDFGetDefaultNoDataValueAsUInt64(m_gid, m_varid, bGotNoData);
3839 1 : m_abyNoData.resize(dt.GetSize());
3840 1 : memcpy(&m_abyNoData[0], &nNoData, sizeof(nNoData));
3841 : }
3842 :
3843 48 : return m_abyNoData.empty() ? nullptr : m_abyNoData.data();
3844 : }
3845 :
3846 : /************************************************************************/
3847 : /* SetRawNoDataValue() */
3848 : /************************************************************************/
3849 :
3850 17 : bool netCDFVariable::SetRawNoDataValue(const void *pNoData)
3851 : {
3852 17 : GetDataType();
3853 17 : if (m_nVarType == NC_STRING)
3854 0 : return false;
3855 :
3856 17 : m_bGetRawNoDataValueHasRun = false;
3857 34 : CPLMutexHolderD(&hNCMutex);
3858 17 : m_poShared->SetDefineMode(true);
3859 : int ret;
3860 17 : if (pNoData == nullptr)
3861 : {
3862 3 : m_abyNoData.clear();
3863 3 : nc_type atttype = NC_NAT;
3864 3 : size_t attlen = 0;
3865 3 : if (nc_inq_att(m_gid, m_varid, NCDF_FillValue, &atttype, &attlen) ==
3866 : NC_NOERR)
3867 2 : ret = nc_del_att(m_gid, m_varid, NCDF_FillValue);
3868 : else
3869 1 : ret = NC_NOERR;
3870 3 : if (nc_inq_att(m_gid, m_varid, "missing_value", &atttype, &attlen) ==
3871 : NC_NOERR)
3872 : {
3873 0 : int ret2 = nc_del_att(m_gid, m_varid, "missing_value");
3874 0 : if (ret2 != NC_NOERR)
3875 0 : ret = ret2;
3876 : }
3877 : }
3878 : else
3879 : {
3880 14 : const auto nSize = GetDataType().GetSize();
3881 14 : m_abyNoData.resize(nSize);
3882 14 : memcpy(&m_abyNoData[0], pNoData, nSize);
3883 :
3884 14 : std::vector<GByte> abyTmp(nSize);
3885 14 : memcpy(&abyTmp[0], pNoData, nSize);
3886 14 : ConvertGDALToNC(&abyTmp[0]);
3887 :
3888 14 : if (!m_bHasWrittenData)
3889 : {
3890 12 : ret = nc_def_var_fill(m_gid, m_varid, NC_FILL, &abyTmp[0]);
3891 12 : NCDF_ERR(ret);
3892 : }
3893 :
3894 14 : nc_type atttype = NC_NAT;
3895 14 : size_t attlen = 0;
3896 14 : if (nc_inq_att(m_gid, m_varid, "missing_value", &atttype, &attlen) ==
3897 : NC_NOERR)
3898 : {
3899 2 : if (nc_inq_att(m_gid, m_varid, NCDF_FillValue, &atttype, &attlen) ==
3900 : NC_NOERR)
3901 : {
3902 1 : CPLError(CE_Failure, CPLE_NotSupported,
3903 : "Cannot change nodata when missing_value and "
3904 : "_FillValue both exist");
3905 1 : return false;
3906 : }
3907 1 : ret = nc_put_att(m_gid, m_varid, "missing_value", m_nVarType, 1,
3908 1 : &abyTmp[0]);
3909 : }
3910 : else
3911 : {
3912 12 : ret = nc_put_att(m_gid, m_varid, NCDF_FillValue, m_nVarType, 1,
3913 12 : &abyTmp[0]);
3914 : }
3915 : }
3916 16 : NCDF_ERR(ret);
3917 16 : if (ret == NC_NOERR)
3918 16 : m_bGetRawNoDataValueHasRun = true;
3919 16 : return ret == NC_NOERR;
3920 : }
3921 :
3922 : /************************************************************************/
3923 : /* SetScale() */
3924 : /************************************************************************/
3925 :
3926 3 : bool netCDFVariable::SetScale(double dfScale, GDALDataType eStorageType)
3927 : {
3928 9 : auto poAttr = GetAttribute(CF_SCALE_FACTOR);
3929 3 : if (!poAttr)
3930 : {
3931 4 : poAttr = CreateAttribute(
3932 : CF_SCALE_FACTOR, {},
3933 4 : GDALExtendedDataType::Create(
3934 : eStorageType == GDT_Unknown ? GDT_Float64 : eStorageType),
3935 2 : nullptr);
3936 : }
3937 3 : if (!poAttr)
3938 0 : return false;
3939 3 : return poAttr->Write(dfScale);
3940 : }
3941 :
3942 : /************************************************************************/
3943 : /* SetOffset() */
3944 : /************************************************************************/
3945 :
3946 3 : bool netCDFVariable::SetOffset(double dfOffset, GDALDataType eStorageType)
3947 : {
3948 9 : auto poAttr = GetAttribute(CF_ADD_OFFSET);
3949 3 : if (!poAttr)
3950 : {
3951 4 : poAttr = CreateAttribute(
3952 : CF_ADD_OFFSET, {},
3953 4 : GDALExtendedDataType::Create(
3954 : eStorageType == GDT_Unknown ? GDT_Float64 : eStorageType),
3955 2 : nullptr);
3956 : }
3957 3 : if (!poAttr)
3958 0 : return false;
3959 3 : return poAttr->Write(dfOffset);
3960 : }
3961 :
3962 : /************************************************************************/
3963 : /* GetScale() */
3964 : /************************************************************************/
3965 :
3966 59 : double netCDFVariable::GetScale(bool *pbHasScale,
3967 : GDALDataType *peStorageType) const
3968 : {
3969 177 : auto poAttr = GetAttribute(CF_SCALE_FACTOR);
3970 59 : if (!poAttr || poAttr->GetDataType().GetClass() != GEDTC_NUMERIC)
3971 : {
3972 54 : if (pbHasScale)
3973 53 : *pbHasScale = false;
3974 54 : return 1.0;
3975 : }
3976 5 : if (pbHasScale)
3977 5 : *pbHasScale = true;
3978 5 : if (peStorageType)
3979 2 : *peStorageType = poAttr->GetDataType().GetNumericDataType();
3980 5 : return poAttr->ReadAsDouble();
3981 : }
3982 :
3983 : /************************************************************************/
3984 : /* GetOffset() */
3985 : /************************************************************************/
3986 :
3987 59 : double netCDFVariable::GetOffset(bool *pbHasOffset,
3988 : GDALDataType *peStorageType) const
3989 : {
3990 177 : auto poAttr = GetAttribute(CF_ADD_OFFSET);
3991 59 : if (!poAttr || poAttr->GetDataType().GetClass() != GEDTC_NUMERIC)
3992 : {
3993 54 : if (pbHasOffset)
3994 53 : *pbHasOffset = false;
3995 54 : return 0.0;
3996 : }
3997 5 : if (pbHasOffset)
3998 5 : *pbHasOffset = true;
3999 5 : if (peStorageType)
4000 2 : *peStorageType = poAttr->GetDataType().GetNumericDataType();
4001 5 : return poAttr->ReadAsDouble();
4002 : }
4003 :
4004 : /************************************************************************/
4005 : /* GetBlockSize() */
4006 : /************************************************************************/
4007 :
4008 101 : std::vector<GUInt64> netCDFVariable::GetBlockSize() const
4009 : {
4010 101 : const auto nDimCount = GetDimensionCount();
4011 101 : std::vector<GUInt64> res(nDimCount);
4012 101 : if (res.empty())
4013 0 : return res;
4014 101 : int nStorageType = 0;
4015 : // We add 1 to the dimension count, for 2D char variables that we
4016 : // expose as a 1D variable.
4017 202 : std::vector<size_t> anTemp(1 + nDimCount);
4018 202 : CPLMutexHolderD(&hNCMutex);
4019 101 : nc_inq_var_chunking(m_gid, m_varid, &nStorageType, &anTemp[0]);
4020 101 : if (nStorageType == NC_CHUNKED)
4021 : {
4022 33 : for (size_t i = 0; i < res.size(); ++i)
4023 21 : res[i] = anTemp[i];
4024 : }
4025 101 : return res;
4026 : }
4027 :
4028 : /************************************************************************/
4029 : /* GetAttribute() */
4030 : /************************************************************************/
4031 :
4032 : std::shared_ptr<GDALAttribute>
4033 3064 : netCDFVariable::GetAttribute(const std::string &osName) const
4034 : {
4035 6128 : CPLMutexHolderD(&hNCMutex);
4036 3064 : int nAttId = -1;
4037 3064 : if (nc_inq_attid(m_gid, m_varid, osName.c_str(), &nAttId) != NC_NOERR)
4038 2457 : return nullptr;
4039 1214 : return netCDFAttribute::Create(
4040 1821 : m_poShared, std::dynamic_pointer_cast<netCDFVariable>(m_pSelf.lock()),
4041 1214 : m_gid, m_varid, osName);
4042 : }
4043 :
4044 : /************************************************************************/
4045 : /* GetAttributes() */
4046 : /************************************************************************/
4047 :
4048 : std::vector<std::shared_ptr<GDALAttribute>>
4049 88 : netCDFVariable::GetAttributes(CSLConstList papszOptions) const
4050 : {
4051 176 : CPLMutexHolderD(&hNCMutex);
4052 88 : std::vector<std::shared_ptr<GDALAttribute>> res;
4053 88 : int nbAttr = 0;
4054 88 : NCDF_ERR(nc_inq_varnatts(m_gid, m_varid, &nbAttr));
4055 88 : res.reserve(nbAttr);
4056 : const bool bShowAll =
4057 88 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_ALL", "NO"));
4058 375 : for (int i = 0; i < nbAttr; i++)
4059 : {
4060 : char szAttrName[NC_MAX_NAME + 1];
4061 287 : szAttrName[0] = 0;
4062 287 : NCDF_ERR(nc_inq_attname(m_gid, m_varid, i, szAttrName));
4063 287 : if (bShowAll || (!EQUAL(szAttrName, NCDF_FillValue) &&
4064 255 : !EQUAL(szAttrName, "missing_value") &&
4065 255 : !EQUAL(szAttrName, CF_UNITS) &&
4066 218 : !EQUAL(szAttrName, CF_SCALE_FACTOR) &&
4067 218 : !EQUAL(szAttrName, CF_ADD_OFFSET) &&
4068 218 : !EQUAL(szAttrName, CF_GRD_MAPPING) &&
4069 187 : !(EQUAL(szAttrName, "_Unsigned") &&
4070 29 : (m_nVarType == NC_BYTE || m_nVarType == NC_SHORT))))
4071 : {
4072 632 : res.emplace_back(netCDFAttribute::Create(
4073 158 : m_poShared,
4074 316 : std::dynamic_pointer_cast<netCDFVariable>(m_pSelf.lock()),
4075 316 : m_gid, m_varid, szAttrName));
4076 : }
4077 : }
4078 176 : return res;
4079 : }
4080 :
4081 : /************************************************************************/
4082 : /* CreateAttribute() */
4083 : /************************************************************************/
4084 :
4085 88 : std::shared_ptr<GDALAttribute> netCDFVariable::CreateAttribute(
4086 : const std::string &osName, const std::vector<GUInt64> &anDimensions,
4087 : const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
4088 : {
4089 176 : return netCDFAttribute::Create(
4090 264 : m_poShared, std::dynamic_pointer_cast<netCDFVariable>(m_pSelf.lock()),
4091 176 : m_gid, m_varid, osName, anDimensions, oDataType, papszOptions);
4092 : }
4093 :
4094 : /************************************************************************/
4095 : /* DeleteAttribute() */
4096 : /************************************************************************/
4097 :
4098 2 : bool netCDFVariable::DeleteAttribute(const std::string &osName,
4099 : CSLConstList /*papszOptions*/)
4100 : {
4101 4 : CPLMutexHolderD(&hNCMutex);
4102 2 : m_poShared->SetDefineMode(true);
4103 :
4104 2 : int ret = nc_del_att(m_gid, m_varid, osName.c_str());
4105 2 : NCDF_ERR(ret);
4106 2 : if (ret != NC_NOERR)
4107 1 : return false;
4108 :
4109 1 : auto it = m_oMapAttributes.find(osName);
4110 1 : if (it != m_oMapAttributes.end())
4111 : {
4112 1 : it->second->Deleted();
4113 1 : m_oMapAttributes.erase(it);
4114 : }
4115 :
4116 1 : return true;
4117 : }
4118 :
4119 : /************************************************************************/
4120 : /* GetCoordinateVariables() */
4121 : /************************************************************************/
4122 :
4123 : std::vector<std::shared_ptr<GDALMDArray>>
4124 12 : netCDFVariable::GetCoordinateVariables() const
4125 : {
4126 24 : std::vector<std::shared_ptr<GDALMDArray>> ret;
4127 :
4128 36 : const auto poCoordinates = GetAttribute("coordinates");
4129 3 : if (poCoordinates &&
4130 15 : poCoordinates->GetDataType().GetClass() == GEDTC_STRING &&
4131 3 : poCoordinates->GetDimensionCount() == 0)
4132 : {
4133 3 : const char *pszCoordinates = poCoordinates->ReadAsString();
4134 3 : if (pszCoordinates)
4135 : {
4136 : const CPLStringList aosNames(
4137 6 : NCDFTokenizeCoordinatesAttribute(pszCoordinates));
4138 6 : CPLMutexHolderD(&hNCMutex);
4139 9 : for (int i = 0; i < aosNames.size(); i++)
4140 : {
4141 6 : int nVarId = 0;
4142 6 : if (nc_inq_varid(m_gid, aosNames[i], &nVarId) == NC_NOERR)
4143 : {
4144 6 : ret.emplace_back(netCDFVariable::Create(
4145 12 : m_poShared, m_poParent.lock(), m_gid, nVarId,
4146 12 : std::vector<std::shared_ptr<GDALDimension>>(), nullptr,
4147 6 : false));
4148 : }
4149 : else
4150 : {
4151 0 : CPLError(
4152 : CE_Warning, CPLE_AppDefined,
4153 : "Cannot find variable corresponding to coordinate %s",
4154 : aosNames[i]);
4155 : }
4156 : }
4157 : }
4158 : }
4159 :
4160 : // Special case for NASA EMIT datasets
4161 24 : auto apoDims = GetDimensions();
4162 21 : if ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
4163 6 : apoDims[1]->GetName() == "crosstrack" &&
4164 33 : apoDims[2]->GetName() == "bands") ||
4165 18 : (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
4166 3 : apoDims[1]->GetName() == "crosstrack"))
4167 : {
4168 6 : auto poRootGroup = netCDFGroup::Create(m_poShared, nullptr, m_gid);
4169 6 : if (poRootGroup)
4170 : {
4171 12 : auto poLocationGroup = poRootGroup->OpenGroup("location");
4172 6 : if (poLocationGroup)
4173 : {
4174 12 : auto poLon = poLocationGroup->OpenMDArray("lon");
4175 12 : auto poLat = poLocationGroup->OpenMDArray("lat");
4176 6 : if (poLon && poLat)
4177 : {
4178 18 : return {std::move(poLon), std::move(poLat)};
4179 : }
4180 : }
4181 : }
4182 : }
4183 :
4184 6 : return ret;
4185 : }
4186 :
4187 : /************************************************************************/
4188 : /* Resize() */
4189 : /************************************************************************/
4190 :
4191 9 : bool netCDFVariable::Resize(const std::vector<GUInt64> &anNewDimSizes,
4192 : CSLConstList /* papszOptions */)
4193 : {
4194 9 : if (!IsWritable())
4195 : {
4196 1 : CPLError(CE_Failure, CPLE_AppDefined,
4197 : "Resize() not supported on read-only file");
4198 1 : return false;
4199 : }
4200 :
4201 8 : const auto nDimCount = GetDimensionCount();
4202 8 : if (anNewDimSizes.size() != nDimCount)
4203 : {
4204 0 : CPLError(CE_Failure, CPLE_IllegalArg,
4205 : "Not expected number of values in anNewDimSizes.");
4206 0 : return false;
4207 : }
4208 :
4209 8 : auto &dims = GetDimensions();
4210 16 : std::vector<size_t> anGrownDimIdx;
4211 16 : std::map<GDALDimension *, GUInt64> oMapDimToSize;
4212 18 : for (size_t i = 0; i < nDimCount; ++i)
4213 : {
4214 14 : auto oIter = oMapDimToSize.find(dims[i].get());
4215 14 : if (oIter != oMapDimToSize.end() && oIter->second != anNewDimSizes[i])
4216 : {
4217 2 : CPLError(CE_Failure, CPLE_AppDefined,
4218 : "Cannot resize a dimension referenced several times "
4219 : "to different sizes");
4220 4 : return false;
4221 : }
4222 12 : if (anNewDimSizes[i] != dims[i]->GetSize())
4223 : {
4224 9 : if (anNewDimSizes[i] < dims[i]->GetSize())
4225 : {
4226 2 : CPLError(CE_Failure, CPLE_NotSupported,
4227 : "Resize() does not support shrinking the array.");
4228 2 : return false;
4229 : }
4230 :
4231 7 : oMapDimToSize[dims[i].get()] = anNewDimSizes[i];
4232 7 : anGrownDimIdx.push_back(i);
4233 : }
4234 : else
4235 : {
4236 3 : oMapDimToSize[dims[i].get()] = dims[i]->GetSize();
4237 : }
4238 : }
4239 :
4240 4 : if (!anGrownDimIdx.empty())
4241 : {
4242 4 : CPLMutexHolderD(&hNCMutex);
4243 : // Query which netCDF dimensions have unlimited size
4244 4 : int nUnlimitedDimIds = 0;
4245 4 : nc_inq_unlimdims(m_gid, &nUnlimitedDimIds, nullptr);
4246 4 : std::vector<int> anUnlimitedDimIds(nUnlimitedDimIds);
4247 4 : nc_inq_unlimdims(m_gid, &nUnlimitedDimIds, anUnlimitedDimIds.data());
4248 4 : std::set<int> oSetUnlimitedDimId;
4249 8 : for (int idx : anUnlimitedDimIds)
4250 4 : oSetUnlimitedDimId.insert(idx);
4251 :
4252 : // Check that dimensions that need to grow are of unlimited size
4253 8 : for (size_t dimIdx : anGrownDimIdx)
4254 : {
4255 : auto netCDFDim =
4256 5 : std::dynamic_pointer_cast<netCDFDimension>(dims[dimIdx]);
4257 5 : if (!netCDFDim)
4258 : {
4259 0 : CPLAssert(false);
4260 : }
4261 5 : else if (oSetUnlimitedDimId.find(netCDFDim->GetId()) ==
4262 10 : oSetUnlimitedDimId.end())
4263 : {
4264 1 : CPLError(CE_Failure, CPLE_NotSupported,
4265 : "Resize() cannot grow dimension %d (%s) "
4266 : "as it is not created as UNLIMITED.",
4267 : static_cast<int>(dimIdx),
4268 1 : netCDFDim->GetName().c_str());
4269 1 : return false;
4270 : }
4271 : }
4272 9 : for (size_t i = 0; i < nDimCount; ++i)
4273 : {
4274 6 : if (anNewDimSizes[i] > dims[i]->GetSize())
4275 : {
4276 : auto netCDFDim =
4277 6 : std::dynamic_pointer_cast<netCDFDimension>(dims[i]);
4278 3 : if (!netCDFDim)
4279 : {
4280 0 : CPLAssert(false);
4281 : }
4282 : else
4283 : {
4284 3 : netCDFDim->SetSize(anNewDimSizes[i]);
4285 : }
4286 : }
4287 : }
4288 : }
4289 3 : return true;
4290 : }
4291 :
4292 : /************************************************************************/
4293 : /* Rename() */
4294 : /************************************************************************/
4295 :
4296 4 : bool netCDFVariable::Rename(const std::string &osNewName)
4297 : {
4298 4 : if (m_poShared->IsReadOnly())
4299 : {
4300 1 : CPLError(CE_Failure, CPLE_AppDefined,
4301 : "Rename() not supported on read-only file");
4302 1 : return false;
4303 : }
4304 3 : if (osNewName.empty())
4305 : {
4306 1 : CPLError(CE_Failure, CPLE_NotSupported, "Empty name not supported");
4307 1 : return false;
4308 : }
4309 :
4310 4 : CPLMutexHolderD(&hNCMutex);
4311 2 : m_poShared->SetDefineMode(true);
4312 :
4313 2 : int ret = nc_rename_var(m_gid, m_varid, osNewName.c_str());
4314 2 : NCDF_ERR(ret);
4315 2 : if (ret != NC_NOERR)
4316 1 : return false;
4317 :
4318 1 : BaseRename(osNewName);
4319 :
4320 1 : return true;
4321 : }
4322 :
4323 : /************************************************************************/
4324 : /* NotifyChildrenOfRenaming() */
4325 : /************************************************************************/
4326 :
4327 3 : void netCDFVariable::NotifyChildrenOfRenaming()
4328 : {
4329 6 : for (const auto &iter : m_oMapAttributes)
4330 3 : iter.second->ParentRenamed(m_osFullName);
4331 3 : }
4332 :
4333 : /************************************************************************/
4334 : /* retrieveAttributeParentName() */
4335 : /************************************************************************/
4336 :
4337 2096 : static CPLString retrieveAttributeParentName(int gid, int varid)
4338 : {
4339 4192 : auto groupName(NCDFGetGroupFullName(gid));
4340 2096 : if (varid == NC_GLOBAL)
4341 : {
4342 392 : if (groupName == "/")
4343 366 : return "/_GLOBAL_";
4344 52 : return groupName + "/_GLOBAL_";
4345 : }
4346 :
4347 3408 : return groupName + "/" + netCDFVariable::retrieveName(gid, varid);
4348 : }
4349 :
4350 : /************************************************************************/
4351 : /* netCDFAttribute() */
4352 : /************************************************************************/
4353 :
4354 887 : netCDFAttribute::netCDFAttribute(
4355 : const std::shared_ptr<netCDFSharedResources> &poShared, int gid, int varid,
4356 887 : const std::string &name)
4357 887 : : GDALAbstractMDArray(retrieveAttributeParentName(gid, varid), name),
4358 887 : GDALAttribute(retrieveAttributeParentName(gid, varid), name),
4359 2661 : m_poShared(poShared), m_gid(gid), m_varid(varid)
4360 : {
4361 1774 : CPLMutexHolderD(&hNCMutex);
4362 887 : size_t nLen = 0;
4363 887 : NCDF_ERR(nc_inq_atttype(m_gid, m_varid, GetName().c_str(), &m_nAttType));
4364 887 : NCDF_ERR(nc_inq_attlen(m_gid, m_varid, GetName().c_str(), &nLen));
4365 887 : if (m_nAttType == NC_CHAR)
4366 : {
4367 692 : m_nTextLength = nLen;
4368 : }
4369 195 : else if (nLen > 1)
4370 : {
4371 168 : m_dims.emplace_back(std::make_shared<GDALDimension>(
4372 252 : std::string(), "length", std::string(), std::string(), nLen));
4373 : }
4374 887 : }
4375 :
4376 : /************************************************************************/
4377 : /* netCDFAttribute() */
4378 : /************************************************************************/
4379 :
4380 161 : netCDFAttribute::netCDFAttribute(
4381 : const std::shared_ptr<netCDFSharedResources> &poShared, int gid, int varid,
4382 : const std::string &osName, const std::vector<GUInt64> &anDimensions,
4383 161 : const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
4384 161 : : GDALAbstractMDArray(retrieveAttributeParentName(gid, varid), osName),
4385 161 : GDALAttribute(retrieveAttributeParentName(gid, varid), osName),
4386 483 : m_poShared(poShared), m_gid(gid), m_varid(varid)
4387 : {
4388 322 : CPLMutexHolderD(&hNCMutex);
4389 161 : m_bPerfectDataTypeMatch = true;
4390 161 : m_nAttType = CreateOrGetType(gid, oDataType);
4391 161 : m_dt.reset(new GDALExtendedDataType(oDataType));
4392 161 : if (!anDimensions.empty())
4393 : {
4394 20 : m_dims.emplace_back(std::make_shared<GDALDimension>(
4395 20 : std::string(), "length", std::string(), std::string(),
4396 20 : anDimensions[0]));
4397 : }
4398 :
4399 161 : const char *pszType = CSLFetchNameValueDef(papszOptions, "NC_TYPE", "");
4400 293 : if (oDataType.GetClass() == GEDTC_STRING && anDimensions.empty() &&
4401 132 : (EQUAL(pszType, "") || EQUAL(pszType, "NC_CHAR")))
4402 : {
4403 131 : m_nAttType = NC_CHAR;
4404 : }
4405 40 : else if (oDataType.GetNumericDataType() == GDT_Byte &&
4406 10 : EQUAL(CSLFetchNameValueDef(papszOptions, "NC_TYPE", ""),
4407 : "NC_BYTE"))
4408 : {
4409 1 : m_nAttType = NC_BYTE;
4410 : }
4411 31 : else if (oDataType.GetNumericDataType() == GDT_Int16 &&
4412 2 : EQUAL(CSLFetchNameValueDef(papszOptions, "NC_TYPE", ""),
4413 : "NC_BYTE"))
4414 : {
4415 2 : m_bPerfectDataTypeMatch = false;
4416 2 : m_nAttType = NC_BYTE;
4417 : }
4418 27 : else if (oDataType.GetNumericDataType() == GDT_Float64)
4419 : {
4420 9 : if (EQUAL(pszType, "NC_INT64"))
4421 : {
4422 2 : m_bPerfectDataTypeMatch = false;
4423 2 : m_nAttType = NC_INT64;
4424 : }
4425 7 : else if (EQUAL(pszType, "NC_UINT64"))
4426 : {
4427 1 : m_bPerfectDataTypeMatch = false;
4428 1 : m_nAttType = NC_UINT64;
4429 : }
4430 : }
4431 161 : }
4432 :
4433 : /************************************************************************/
4434 : /* ~netCDFAttribute() */
4435 : /************************************************************************/
4436 :
4437 2096 : netCDFAttribute::~netCDFAttribute()
4438 : {
4439 1048 : if (m_bValid)
4440 : {
4441 1642 : if (auto poParent = m_poParent.lock())
4442 596 : poParent->UnRegisterAttribute(this);
4443 : }
4444 2096 : }
4445 :
4446 : /************************************************************************/
4447 : /* Create() */
4448 : /************************************************************************/
4449 :
4450 : std::shared_ptr<netCDFAttribute>
4451 887 : netCDFAttribute::Create(const std::shared_ptr<netCDFSharedResources> &poShared,
4452 : const std::shared_ptr<netCDFAttributeHolder> &poParent,
4453 : int gid, int varid, const std::string &name)
4454 : {
4455 : auto attr(std::shared_ptr<netCDFAttribute>(
4456 887 : new netCDFAttribute(poShared, gid, varid, name)));
4457 887 : attr->SetSelf(attr);
4458 887 : attr->m_poParent = poParent;
4459 887 : if (poParent)
4460 499 : poParent->RegisterAttribute(attr.get());
4461 887 : return attr;
4462 : }
4463 :
4464 164 : std::shared_ptr<netCDFAttribute> netCDFAttribute::Create(
4465 : const std::shared_ptr<netCDFSharedResources> &poShared,
4466 : const std::shared_ptr<netCDFAttributeHolder> &poParent, int gid, int varid,
4467 : const std::string &osName, const std::vector<GUInt64> &anDimensions,
4468 : const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
4469 : {
4470 164 : if (poShared->IsReadOnly())
4471 : {
4472 2 : CPLError(CE_Failure, CPLE_AppDefined,
4473 : "CreateAttribute() not supported on read-only file");
4474 2 : return nullptr;
4475 : }
4476 162 : if (anDimensions.size() > 1)
4477 : {
4478 1 : CPLError(CE_Failure, CPLE_NotSupported,
4479 : "Only 0 or 1-dimensional attribute are supported");
4480 1 : return nullptr;
4481 : }
4482 :
4483 161 : const char *apszOptions[2] = {nullptr, nullptr};
4484 176 : if (!poShared->IsNC4() && oDataType.GetClass() == GEDTC_NUMERIC &&
4485 176 : oDataType.GetNumericDataType() == GDT_Byte && !papszOptions)
4486 : {
4487 : // GDT_Byte would map to a NC_UBYTE datatype, which is not available in
4488 : // NC3 datasets
4489 1 : apszOptions[0] = "NC_TYPE=NC_BYTE";
4490 1 : papszOptions = apszOptions;
4491 : }
4492 :
4493 : auto attr(std::shared_ptr<netCDFAttribute>(new netCDFAttribute(
4494 322 : poShared, gid, varid, osName, anDimensions, oDataType, papszOptions)));
4495 161 : if (attr->m_nAttType == NC_NAT)
4496 0 : return nullptr;
4497 161 : attr->SetSelf(attr);
4498 161 : attr->m_poParent = poParent;
4499 161 : if (poParent)
4500 161 : poParent->RegisterAttribute(attr.get());
4501 161 : return attr;
4502 : }
4503 :
4504 : /************************************************************************/
4505 : /* GetDataType() */
4506 : /************************************************************************/
4507 :
4508 3348 : const GDALExtendedDataType &netCDFAttribute::GetDataType() const
4509 : {
4510 3348 : if (m_dt)
4511 2604 : return *m_dt;
4512 1488 : CPLMutexHolderD(&hNCMutex);
4513 :
4514 744 : if (m_nAttType == NC_CHAR)
4515 : {
4516 621 : m_dt.reset(
4517 621 : new GDALExtendedDataType(GDALExtendedDataType::CreateString()));
4518 : }
4519 : else
4520 : {
4521 123 : m_dt.reset(new GDALExtendedDataType(
4522 123 : GDALExtendedDataType::Create(GDT_Unknown)));
4523 123 : BuildDataType(m_gid, m_varid, m_nAttType, m_dt,
4524 123 : m_bPerfectDataTypeMatch);
4525 : }
4526 :
4527 744 : return *m_dt;
4528 : }
4529 :
4530 : /************************************************************************/
4531 : /* IRead() */
4532 : /************************************************************************/
4533 :
4534 762 : bool netCDFAttribute::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
4535 : const GInt64 *arrayStep,
4536 : const GPtrDiff_t *bufferStride,
4537 : const GDALExtendedDataType &bufferDataType,
4538 : void *pDstBuffer) const
4539 : {
4540 762 : if (!CheckValidAndErrorOutIfNot())
4541 0 : return false;
4542 1524 : CPLMutexHolderD(&hNCMutex);
4543 :
4544 762 : if (m_nAttType == NC_STRING)
4545 : {
4546 8 : CPLAssert(GetDataType().GetClass() == GEDTC_STRING);
4547 : std::vector<char *> apszStrings(
4548 16 : static_cast<size_t>(GetTotalElementsCount()));
4549 8 : int ret = nc_get_att_string(m_gid, m_varid, GetName().c_str(),
4550 8 : &apszStrings[0]);
4551 8 : NCDF_ERR(ret);
4552 8 : if (ret != NC_NOERR)
4553 0 : return false;
4554 8 : if (m_dims.empty())
4555 : {
4556 3 : const char *pszStr = apszStrings[0];
4557 3 : GDALExtendedDataType::CopyValue(&pszStr, GetDataType(), pDstBuffer,
4558 : bufferDataType);
4559 : }
4560 : else
4561 : {
4562 5 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4563 14 : for (size_t i = 0; i < count[0]; i++)
4564 : {
4565 9 : auto srcIdx =
4566 9 : static_cast<size_t>(arrayStartIdx[0] + arrayStep[0] * i);
4567 9 : const char *pszStr = apszStrings[srcIdx];
4568 9 : GDALExtendedDataType::CopyValue(&pszStr, GetDataType(),
4569 : pabyDstBuffer, bufferDataType);
4570 9 : pabyDstBuffer += sizeof(char *) * bufferStride[0];
4571 : }
4572 : }
4573 8 : nc_free_string(apszStrings.size(), &apszStrings[0]);
4574 8 : return true;
4575 : }
4576 :
4577 754 : if (m_nAttType == NC_CHAR)
4578 : {
4579 627 : CPLAssert(GetDataType().GetClass() == GEDTC_STRING);
4580 627 : CPLAssert(m_dims.empty());
4581 627 : if (bufferDataType != GetDataType())
4582 : {
4583 0 : std::string osStr;
4584 0 : osStr.resize(m_nTextLength);
4585 : int ret =
4586 0 : nc_get_att_text(m_gid, m_varid, GetName().c_str(), &osStr[0]);
4587 0 : NCDF_ERR(ret);
4588 0 : if (ret != NC_NOERR)
4589 0 : return false;
4590 0 : const char *pszStr = osStr.c_str();
4591 0 : GDALExtendedDataType::CopyValue(&pszStr, GetDataType(), pDstBuffer,
4592 : bufferDataType);
4593 : }
4594 : else
4595 : {
4596 627 : char *pszStr = static_cast<char *>(CPLCalloc(1, m_nTextLength + 1));
4597 : int ret =
4598 627 : nc_get_att_text(m_gid, m_varid, GetName().c_str(), pszStr);
4599 627 : NCDF_ERR(ret);
4600 627 : if (ret != NC_NOERR)
4601 : {
4602 1 : CPLFree(pszStr);
4603 1 : return false;
4604 : }
4605 626 : *static_cast<char **>(pDstBuffer) = pszStr;
4606 : }
4607 626 : return true;
4608 : }
4609 :
4610 127 : const auto &dt(GetDataType());
4611 250 : if (dt.GetClass() == GEDTC_NUMERIC &&
4612 123 : dt.GetNumericDataType() == GDT_Unknown)
4613 : {
4614 0 : return false;
4615 : }
4616 :
4617 127 : CPLAssert(dt.GetClass() != GEDTC_STRING);
4618 231 : const bool bFastPath = ((m_dims.size() == 1 && arrayStartIdx[0] == 0 &&
4619 52 : count[0] == m_dims[0]->GetSize() &&
4620 127 : arrayStep[0] == 1 && bufferStride[0] == 1) ||
4621 76 : m_dims.empty()) &&
4622 317 : m_bPerfectDataTypeMatch && bufferDataType == dt &&
4623 63 : dt.GetSize() > 0;
4624 127 : if (bFastPath)
4625 : {
4626 63 : int ret = nc_get_att(m_gid, m_varid, GetName().c_str(), pDstBuffer);
4627 63 : NCDF_ERR(ret);
4628 63 : if (ret == NC_NOERR)
4629 : {
4630 63 : ConvertNCStringsToCPLStrings(static_cast<GByte *>(pDstBuffer), dt);
4631 : }
4632 63 : return ret == NC_NOERR;
4633 : }
4634 :
4635 : const auto nElementSize =
4636 64 : GetNCTypeSize(dt, m_bPerfectDataTypeMatch, m_nAttType);
4637 64 : if (nElementSize == 0)
4638 0 : return false;
4639 64 : const auto nOutputDTSize = bufferDataType.GetSize();
4640 64 : std::vector<GByte> abyBuffer(static_cast<size_t>(GetTotalElementsCount()) *
4641 192 : nElementSize);
4642 64 : int ret = nc_get_att(m_gid, m_varid, GetName().c_str(), &abyBuffer[0]);
4643 64 : NCDF_ERR(ret);
4644 64 : if (ret != NC_NOERR)
4645 0 : return false;
4646 :
4647 : GByte *pabySrcBuffer =
4648 64 : m_dims.empty()
4649 64 : ? abyBuffer.data()
4650 40 : : abyBuffer.data() +
4651 40 : static_cast<size_t>(arrayStartIdx[0]) * nElementSize;
4652 64 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
4653 211 : for (size_t i = 0; i < (m_dims.empty() ? 1 : count[0]); i++)
4654 : {
4655 : GByte abyTmpBuffer[sizeof(double)];
4656 147 : const GByte *pabySrcElement = pabySrcBuffer;
4657 147 : if (!m_bPerfectDataTypeMatch)
4658 : {
4659 7 : if (m_nAttType == NC_BYTE)
4660 : {
4661 3 : short s =
4662 3 : reinterpret_cast<const signed char *>(pabySrcBuffer)[0];
4663 3 : memcpy(abyTmpBuffer, &s, sizeof(s));
4664 3 : pabySrcElement = abyTmpBuffer;
4665 : }
4666 4 : else if (m_nAttType == NC_INT64)
4667 : {
4668 3 : double v = static_cast<double>(
4669 3 : reinterpret_cast<const GInt64 *>(pabySrcBuffer)[0]);
4670 3 : memcpy(abyTmpBuffer, &v, sizeof(v));
4671 3 : pabySrcElement = abyTmpBuffer;
4672 : }
4673 1 : else if (m_nAttType == NC_UINT64)
4674 : {
4675 1 : double v = static_cast<double>(
4676 1 : reinterpret_cast<const GUInt64 *>(pabySrcBuffer)[0]);
4677 1 : memcpy(abyTmpBuffer, &v, sizeof(v));
4678 1 : pabySrcElement = abyTmpBuffer;
4679 : }
4680 : else
4681 : {
4682 0 : CPLAssert(false);
4683 : }
4684 : }
4685 147 : GDALExtendedDataType::CopyValue(pabySrcElement, dt, pabyDstBuffer,
4686 : bufferDataType);
4687 147 : FreeNCStrings(pabySrcBuffer, dt);
4688 147 : if (!m_dims.empty())
4689 : {
4690 123 : pabySrcBuffer +=
4691 123 : static_cast<std::ptrdiff_t>(arrayStep[0] * nElementSize);
4692 123 : pabyDstBuffer += nOutputDTSize * bufferStride[0];
4693 : }
4694 : }
4695 :
4696 64 : return true;
4697 : }
4698 :
4699 : /************************************************************************/
4700 : /* IWrite() */
4701 : /************************************************************************/
4702 :
4703 166 : bool netCDFAttribute::IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
4704 : const GInt64 *arrayStep,
4705 : const GPtrDiff_t *bufferStride,
4706 : const GDALExtendedDataType &bufferDataType,
4707 : const void *pSrcBuffer)
4708 : {
4709 166 : if (!CheckValidAndErrorOutIfNot())
4710 0 : return false;
4711 332 : CPLMutexHolderD(&hNCMutex);
4712 :
4713 177 : if (m_dims.size() == 1 &&
4714 11 : (arrayStartIdx[0] != 0 || count[0] != m_dims[0]->GetSize() ||
4715 11 : arrayStep[0] != 1))
4716 : {
4717 0 : CPLError(CE_Failure, CPLE_NotSupported,
4718 : "Only contiguous writing of attribute values supported");
4719 0 : return false;
4720 : }
4721 :
4722 166 : m_poShared->SetDefineMode(true);
4723 :
4724 166 : const auto &dt(GetDataType());
4725 166 : if (m_nAttType == NC_STRING)
4726 : {
4727 4 : CPLAssert(dt.GetClass() == GEDTC_STRING);
4728 4 : if (m_dims.empty())
4729 : {
4730 2 : char *pszStr = nullptr;
4731 : const char *pszStrConst;
4732 2 : if (bufferDataType != dt)
4733 : {
4734 1 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType,
4735 : &pszStr, dt);
4736 1 : pszStrConst = pszStr;
4737 : }
4738 : else
4739 : {
4740 1 : memcpy(&pszStrConst, pSrcBuffer, sizeof(const char *));
4741 : }
4742 2 : int ret = nc_put_att_string(m_gid, m_varid, GetName().c_str(), 1,
4743 : &pszStrConst);
4744 2 : CPLFree(pszStr);
4745 2 : NCDF_ERR(ret);
4746 2 : if (ret != NC_NOERR)
4747 0 : return false;
4748 2 : return true;
4749 : }
4750 :
4751 : int ret;
4752 2 : if (bufferDataType != dt)
4753 : {
4754 2 : std::vector<char *> apszStrings(count[0]);
4755 1 : const auto nInputDTSize = bufferDataType.GetSize();
4756 1 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4757 3 : for (size_t i = 0; i < count[0]; i++)
4758 : {
4759 2 : GDALExtendedDataType::CopyValue(pabySrcBuffer, bufferDataType,
4760 2 : &apszStrings[i], dt);
4761 2 : pabySrcBuffer += nInputDTSize * bufferStride[0];
4762 : }
4763 1 : ret = nc_put_att_string(m_gid, m_varid, GetName().c_str(), count[0],
4764 1 : const_cast<const char **>(&apszStrings[0]));
4765 3 : for (size_t i = 0; i < count[0]; i++)
4766 : {
4767 2 : CPLFree(apszStrings[i]);
4768 : }
4769 : }
4770 : else
4771 : {
4772 : const char **ppszStr;
4773 1 : memcpy(&ppszStr, &pSrcBuffer, sizeof(const char **));
4774 1 : ret = nc_put_att_string(m_gid, m_varid, GetName().c_str(), count[0],
4775 : ppszStr);
4776 : }
4777 2 : NCDF_ERR(ret);
4778 2 : if (ret != NC_NOERR)
4779 0 : return false;
4780 2 : return true;
4781 : }
4782 :
4783 162 : if (m_nAttType == NC_CHAR)
4784 : {
4785 137 : CPLAssert(dt.GetClass() == GEDTC_STRING);
4786 137 : CPLAssert(m_dims.empty());
4787 137 : char *pszStr = nullptr;
4788 : const char *pszStrConst;
4789 137 : if (bufferDataType != dt)
4790 : {
4791 1 : GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &pszStr,
4792 : dt);
4793 1 : pszStrConst = pszStr;
4794 : }
4795 : else
4796 : {
4797 136 : memcpy(&pszStrConst, pSrcBuffer, sizeof(const char *));
4798 : }
4799 137 : m_nTextLength = pszStrConst ? strlen(pszStrConst) : 0;
4800 137 : int ret = nc_put_att_text(m_gid, m_varid, GetName().c_str(),
4801 : m_nTextLength, pszStrConst);
4802 137 : CPLFree(pszStr);
4803 137 : NCDF_ERR(ret);
4804 137 : if (ret != NC_NOERR)
4805 1 : return false;
4806 136 : return true;
4807 : }
4808 :
4809 49 : if (dt.GetClass() == GEDTC_NUMERIC &&
4810 24 : dt.GetNumericDataType() == GDT_Unknown)
4811 : {
4812 0 : return false;
4813 : }
4814 :
4815 25 : CPLAssert(dt.GetClass() != GEDTC_STRING);
4816 : const bool bFastPath =
4817 50 : ((m_dims.size() == 1 && bufferStride[0] == 1) || m_dims.empty()) &&
4818 50 : m_bPerfectDataTypeMatch && bufferDataType == dt && dt.GetSize() > 0;
4819 25 : if (bFastPath)
4820 : {
4821 11 : int ret = nc_put_att(m_gid, m_varid, GetName().c_str(), m_nAttType,
4822 11 : m_dims.empty() ? 1 : count[0], pSrcBuffer);
4823 11 : NCDF_ERR(ret);
4824 11 : return ret == NC_NOERR;
4825 : }
4826 :
4827 : const auto nElementSize =
4828 14 : GetNCTypeSize(dt, m_bPerfectDataTypeMatch, m_nAttType);
4829 14 : if (nElementSize == 0)
4830 0 : return false;
4831 14 : const auto nInputDTSize = bufferDataType.GetSize();
4832 14 : std::vector<GByte> abyBuffer(static_cast<size_t>(GetTotalElementsCount()) *
4833 28 : nElementSize);
4834 :
4835 14 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
4836 14 : auto pabyDstBuffer = &abyBuffer[0];
4837 31 : for (size_t i = 0; i < (m_dims.empty() ? 1 : count[0]); i++)
4838 : {
4839 17 : if (!m_bPerfectDataTypeMatch)
4840 : {
4841 7 : if (m_nAttType == NC_BYTE)
4842 : {
4843 : short s;
4844 3 : GDALExtendedDataType::CopyValue(pabySrcBuffer, bufferDataType,
4845 : &s, dt);
4846 3 : signed char c = static_cast<signed char>(s);
4847 3 : memcpy(pabyDstBuffer, &c, sizeof(c));
4848 : }
4849 4 : else if (m_nAttType == NC_INT64)
4850 : {
4851 : double d;
4852 3 : GDALExtendedDataType::CopyValue(pabySrcBuffer, bufferDataType,
4853 : &d, dt);
4854 3 : GInt64 v = static_cast<GInt64>(d);
4855 3 : memcpy(pabyDstBuffer, &v, sizeof(v));
4856 : }
4857 1 : else if (m_nAttType == NC_UINT64)
4858 : {
4859 : double d;
4860 1 : GDALExtendedDataType::CopyValue(pabySrcBuffer, bufferDataType,
4861 : &d, dt);
4862 1 : GUInt64 v = static_cast<GUInt64>(d);
4863 1 : memcpy(pabyDstBuffer, &v, sizeof(v));
4864 : }
4865 : else
4866 : {
4867 0 : CPLAssert(false);
4868 : }
4869 : }
4870 : else
4871 : {
4872 10 : GDALExtendedDataType::CopyValue(pabySrcBuffer, bufferDataType,
4873 : pabyDstBuffer, dt);
4874 : }
4875 :
4876 17 : if (!m_dims.empty())
4877 : {
4878 7 : pabySrcBuffer += nInputDTSize * bufferStride[0];
4879 7 : pabyDstBuffer += nElementSize;
4880 : }
4881 : }
4882 :
4883 14 : int ret = nc_put_att(m_gid, m_varid, GetName().c_str(), m_nAttType,
4884 14 : m_dims.empty() ? 1 : count[0], &abyBuffer[0]);
4885 14 : NCDF_ERR(ret);
4886 14 : return ret == NC_NOERR;
4887 : }
4888 :
4889 : /************************************************************************/
4890 : /* Rename() */
4891 : /************************************************************************/
4892 :
4893 8 : bool netCDFAttribute::Rename(const std::string &osNewName)
4894 : {
4895 8 : if (!CheckValidAndErrorOutIfNot())
4896 2 : return false;
4897 6 : if (m_poShared->IsReadOnly())
4898 : {
4899 1 : CPLError(CE_Failure, CPLE_AppDefined,
4900 : "Rename() not supported on read-only file");
4901 1 : return false;
4902 : }
4903 5 : if (osNewName.empty())
4904 : {
4905 1 : CPLError(CE_Failure, CPLE_NotSupported, "Empty name not supported");
4906 1 : return false;
4907 : }
4908 8 : CPLMutexHolderD(&hNCMutex);
4909 4 : m_poShared->SetDefineMode(true);
4910 :
4911 : int ret =
4912 4 : nc_rename_att(m_gid, m_varid, m_osName.c_str(), osNewName.c_str());
4913 4 : NCDF_ERR(ret);
4914 4 : if (ret != NC_NOERR)
4915 2 : return false;
4916 :
4917 2 : BaseRename(osNewName);
4918 :
4919 2 : return true;
4920 : }
4921 :
4922 : /************************************************************************/
4923 : /* OpenMultiDim() */
4924 : /************************************************************************/
4925 :
4926 173 : GDALDataset *netCDFDataset::OpenMultiDim(GDALOpenInfo *poOpenInfo)
4927 : {
4928 :
4929 346 : CPLMutexHolderD(&hNCMutex);
4930 :
4931 173 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with
4932 : // GDALDataset own mutex.
4933 173 : netCDFDataset *poDS = new netCDFDataset();
4934 173 : CPLAcquireMutex(hNCMutex, 1000.0);
4935 :
4936 346 : std::string osFilename;
4937 :
4938 : // For example to open DAP datasets
4939 173 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "NETCDF:"))
4940 : {
4941 0 : osFilename = poOpenInfo->pszFilename + strlen("NETCDF:");
4942 0 : if (!osFilename.empty() && osFilename[0] == '"' &&
4943 0 : osFilename.back() == '"')
4944 : {
4945 0 : osFilename = osFilename.substr(1, osFilename.size() - 2);
4946 : }
4947 : }
4948 : else
4949 : {
4950 173 : osFilename = poOpenInfo->pszFilename;
4951 173 : poDS->eFormat =
4952 173 : netCDFIdentifyFormat(poOpenInfo, /* bCheckExt = */ true);
4953 : }
4954 :
4955 173 : poDS->SetDescription(poOpenInfo->pszFilename);
4956 173 : poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
4957 :
4958 : #ifdef ENABLE_NCDUMP
4959 173 : bool bFileToDestroyAtClosing = false;
4960 173 : const char *pszHeader =
4961 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
4962 173 : if (poOpenInfo->fpL != nullptr && STARTS_WITH(pszHeader, "netcdf ") &&
4963 1 : strstr(pszHeader, "dimensions:") && strstr(pszHeader, "variables:"))
4964 : {
4965 : // By default create a temporary file that will be destroyed,
4966 : // unless NETCDF_TMP_FILE is defined. Can be useful to see which
4967 : // netCDF file has been generated from a potential fuzzed input.
4968 1 : osFilename = CPLGetConfigOption("NETCDF_TMP_FILE", "");
4969 1 : if (osFilename.empty())
4970 : {
4971 1 : bFileToDestroyAtClosing = true;
4972 1 : osFilename = CPLGenerateTempFilename("netcdf_tmp");
4973 : }
4974 1 : if (!netCDFDatasetCreateTempFile(NCDF_FORMAT_NC4, osFilename.c_str(),
4975 : poOpenInfo->fpL))
4976 : {
4977 0 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll
4978 : // deadlock with GDALDataset own mutex.
4979 0 : delete poDS;
4980 0 : CPLAcquireMutex(hNCMutex, 1000.0);
4981 0 : return nullptr;
4982 : }
4983 1 : poDS->eFormat = NCDF_FORMAT_NC4;
4984 : }
4985 : #endif
4986 :
4987 : // Try opening the dataset.
4988 : #if defined(NCDF_DEBUG) && defined(ENABLE_UFFD)
4989 : CPLDebug("GDAL_netCDF", "calling nc_open_mem(%s)", osFilename.c_str());
4990 : #elif defined(NCDF_DEBUG) && !defined(ENABLE_UFFD)
4991 : CPLDebug("GDAL_netCDF", "calling nc_open(%s)", osFilename.c_str());
4992 : #endif
4993 173 : int cdfid = -1;
4994 173 : const int nMode =
4995 173 : (poOpenInfo->nOpenFlags & GDAL_OF_UPDATE) != 0 ? NC_WRITE : NC_NOWRITE;
4996 346 : CPLString osFilenameForNCOpen(osFilename);
4997 : #ifdef _WIN32
4998 : if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
4999 : {
5000 : char *pszTemp = CPLRecode(osFilenameForNCOpen, CPL_ENC_UTF8, "CP_ACP");
5001 : osFilenameForNCOpen = pszTemp;
5002 : CPLFree(pszTemp);
5003 : }
5004 : #endif
5005 173 : int status2 = -1;
5006 :
5007 346 : auto poSharedResources(std::make_shared<netCDFSharedResources>(osFilename));
5008 : #ifdef ENABLE_NCDUMP
5009 173 : poSharedResources->m_bFileToDestroyAtClosing = bFileToDestroyAtClosing;
5010 : #endif
5011 :
5012 177 : if (STARTS_WITH(osFilenameForNCOpen, "/vsimem/") &&
5013 4 : poOpenInfo->eAccess == GA_ReadOnly)
5014 : {
5015 4 : vsi_l_offset nLength = 0;
5016 4 : poDS->fpVSIMEM = VSIFOpenL(osFilenameForNCOpen, "rb");
5017 4 : if (poDS->fpVSIMEM)
5018 : {
5019 : // We assume that the file will not be modified. If it is, then
5020 : // pabyBuffer might become invalid.
5021 : GByte *pabyBuffer =
5022 4 : VSIGetMemFileBuffer(osFilenameForNCOpen, &nLength, false);
5023 4 : if (pabyBuffer)
5024 : {
5025 4 : status2 = nc_open_mem(CPLGetFilename(osFilenameForNCOpen),
5026 : nMode, static_cast<size_t>(nLength),
5027 : pabyBuffer, &cdfid);
5028 : }
5029 : }
5030 : }
5031 : else
5032 : {
5033 : #ifdef ENABLE_UFFD
5034 169 : bool bVsiFile = !strncmp(osFilenameForNCOpen, "/vsi", strlen("/vsi"));
5035 169 : bool bReadOnly = (poOpenInfo->eAccess == GA_ReadOnly);
5036 169 : void *pVma = nullptr;
5037 169 : uint64_t nVmaSize = 0;
5038 169 : cpl_uffd_context *pCtx = nullptr;
5039 :
5040 169 : if (bVsiFile && bReadOnly && CPLIsUserFaultMappingSupported())
5041 1 : pCtx = CPLCreateUserFaultMapping(osFilenameForNCOpen, &pVma,
5042 : &nVmaSize);
5043 169 : if (pCtx != nullptr && pVma != nullptr && nVmaSize > 0)
5044 : {
5045 : // netCDF code, at least for netCDF 4.7.0, is confused by filenames
5046 : // like /vsicurl/http[s]://example.com/foo.nc, so just pass the
5047 : // final part
5048 1 : status2 = nc_open_mem(CPLGetFilename(osFilenameForNCOpen), nMode,
5049 : static_cast<size_t>(nVmaSize), pVma, &cdfid);
5050 : }
5051 : else
5052 168 : status2 = GDAL_nc_open(osFilenameForNCOpen, nMode, &cdfid);
5053 169 : poSharedResources->m_pUffdCtx = pCtx;
5054 : #else
5055 : status2 = GDAL_nc_open(osFilenameForNCOpen, nMode, &cdfid);
5056 : #endif
5057 : }
5058 173 : if (status2 != NC_NOERR)
5059 : {
5060 : #ifdef NCDF_DEBUG
5061 : CPLDebug("GDAL_netCDF", "error opening");
5062 : #endif
5063 3 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock
5064 : // with GDALDataset own mutex.
5065 3 : delete poDS;
5066 3 : CPLAcquireMutex(hNCMutex, 1000.0);
5067 3 : return nullptr;
5068 : }
5069 : #ifdef NCDF_DEBUG
5070 : CPLDebug("GDAL_netCDF", "got cdfid=%d", cdfid);
5071 : #endif
5072 :
5073 : #if defined(ENABLE_NCDUMP) && !defined(_WIN32)
5074 : // Try to destroy the temporary file right now on Unix
5075 170 : if (poSharedResources->m_bFileToDestroyAtClosing)
5076 : {
5077 1 : if (VSIUnlink(poSharedResources->m_osFilename) == 0)
5078 : {
5079 1 : poSharedResources->m_bFileToDestroyAtClosing = false;
5080 : }
5081 : }
5082 : #endif
5083 170 : poSharedResources->m_bReadOnly = nMode == NC_NOWRITE;
5084 170 : poSharedResources->m_bIsNC4 =
5085 170 : poDS->eFormat == NCDF_FORMAT_NC4 || poDS->eFormat == NCDF_FORMAT_NC4C;
5086 170 : poSharedResources->m_cdfid = cdfid;
5087 170 : poSharedResources->m_fpVSIMEM = poDS->fpVSIMEM;
5088 170 : poDS->fpVSIMEM = nullptr;
5089 :
5090 : // Is this a real netCDF file?
5091 : int ndims;
5092 : int ngatts;
5093 : int nvars;
5094 : int unlimdimid;
5095 170 : int status = nc_inq(cdfid, &ndims, &nvars, &ngatts, &unlimdimid);
5096 170 : if (status != NC_NOERR)
5097 : {
5098 0 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock
5099 : // with GDALDataset own mutex.
5100 0 : delete poDS;
5101 0 : CPLAcquireMutex(hNCMutex, 1000.0);
5102 0 : return nullptr;
5103 : }
5104 :
5105 170 : poDS->m_poRootGroup = netCDFGroup::Create(poSharedResources, cdfid);
5106 :
5107 170 : poDS->TryLoadXML();
5108 :
5109 170 : return poDS;
5110 : }
5111 :
5112 : /************************************************************************/
5113 : /* GetRootGroup() */
5114 : /************************************************************************/
5115 :
5116 292 : std::shared_ptr<GDALGroup> netCDFDataset::GetRootGroup() const
5117 : {
5118 292 : return m_poRootGroup;
5119 : }
5120 :
5121 : /************************************************************************/
5122 : /* CreateMultiDimensional() */
5123 : /************************************************************************/
5124 :
5125 : GDALDataset *
5126 57 : netCDFDataset::CreateMultiDimensional(const char *pszFilename,
5127 : CSLConstList /* papszRootGroupOptions */,
5128 : CSLConstList papszOptions)
5129 : {
5130 114 : CPLMutexHolderD(&hNCMutex);
5131 :
5132 57 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with
5133 : // GDALDataset own mutex.
5134 57 : netCDFDataset *poDS = new netCDFDataset();
5135 57 : CPLAcquireMutex(hNCMutex, 1000.0);
5136 57 : poDS->eAccess = GA_Update;
5137 57 : poDS->osFilename = pszFilename;
5138 :
5139 : // process options.
5140 57 : poDS->papszCreationOptions = CSLDuplicate(papszOptions);
5141 57 : if (CSLFetchNameValue(papszOptions, "FORMAT") == nullptr)
5142 : {
5143 56 : poDS->papszCreationOptions =
5144 56 : CSLSetNameValue(poDS->papszCreationOptions, "FORMAT", "NC4");
5145 : }
5146 57 : poDS->ProcessCreationOptions();
5147 :
5148 : // Create the dataset.
5149 114 : CPLString osFilenameForNCCreate(pszFilename);
5150 : #ifdef _WIN32
5151 : if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
5152 : {
5153 : char *pszTemp =
5154 : CPLRecode(osFilenameForNCCreate, CPL_ENC_UTF8, "CP_ACP");
5155 : osFilenameForNCCreate = pszTemp;
5156 : CPLFree(pszTemp);
5157 : }
5158 : #endif
5159 57 : int cdfid = 0;
5160 57 : int status = nc_create(osFilenameForNCCreate, poDS->nCreateMode, &cdfid);
5161 57 : if (status != NC_NOERR)
5162 : {
5163 2 : CPLError(CE_Failure, CPLE_OpenFailed,
5164 : "Unable to create netCDF file %s (Error code %d): %s .",
5165 : pszFilename, status, nc_strerror(status));
5166 2 : CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock
5167 : // with GDALDataset own mutex.
5168 2 : delete poDS;
5169 2 : CPLAcquireMutex(hNCMutex, 1000.0);
5170 2 : return nullptr;
5171 : }
5172 :
5173 : auto poSharedResources(
5174 55 : std::make_shared<netCDFSharedResources>(pszFilename));
5175 55 : poSharedResources->m_cdfid = cdfid;
5176 55 : poSharedResources->m_bReadOnly = false;
5177 55 : poSharedResources->m_bDefineMode = true;
5178 55 : poSharedResources->m_bIsNC4 =
5179 55 : poDS->eFormat == NCDF_FORMAT_NC4 || poDS->eFormat == NCDF_FORMAT_NC4C;
5180 : poDS->m_poRootGroup =
5181 55 : netCDFGroup::Create(poSharedResources, nullptr, cdfid);
5182 55 : const char *pszConventions = CSLFetchNameValueDef(
5183 : papszOptions, "CONVENTIONS", NCDF_CONVENTIONS_CF_V1_6);
5184 55 : if (!EQUAL(pszConventions, ""))
5185 : {
5186 55 : auto poAttr = poDS->m_poRootGroup->CreateAttribute(
5187 165 : NCDF_CONVENTIONS, {}, GDALExtendedDataType::CreateString());
5188 55 : if (poAttr)
5189 55 : poAttr->Write(pszConventions);
5190 : }
5191 :
5192 55 : return poDS;
5193 : }
|