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