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