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