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