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