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