Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim_array_transposed.cpp
4 : * Project: GDAL Core
5 : * Purpose: GDALMDArray::Transpose() implementation
6 : * Author: Even Rouault <even.rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "gdal_multidim.h"
15 : #include "gdal_pam_multidim.h"
16 : #include "ogr_spatialref.h"
17 :
18 : //! @cond Doxygen_Suppress
19 :
20 : /************************************************************************/
21 : /* CopyToFinalBufferSameDataType() */
22 : /************************************************************************/
23 :
24 : template <size_t N>
25 116 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
26 : size_t nDims, const size_t *count,
27 : const GPtrDiff_t *bufferStride)
28 : {
29 232 : std::vector<size_t> anStackCount(nDims);
30 232 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
31 116 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
32 : #if defined(__GNUC__)
33 : #pragma GCC diagnostic push
34 : #pragma GCC diagnostic ignored "-Wnull-dereference"
35 : #endif
36 116 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
37 : #if defined(__GNUC__)
38 : #pragma GCC diagnostic pop
39 : #endif
40 116 : size_t iDim = 0;
41 :
42 659 : lbl_next_depth:
43 659 : if (iDim == nDims - 1)
44 : {
45 543 : size_t n = count[iDim];
46 543 : GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
47 543 : const auto bufferStrideLastDim = bufferStride[iDim] * N;
48 18270 : while (n > 0)
49 : {
50 17727 : --n;
51 17727 : memcpy(pabyDstBuffer, pabySrcBuffer, N);
52 17727 : pabyDstBuffer += bufferStrideLastDim;
53 17727 : pabySrcBuffer += N;
54 : }
55 : }
56 : else
57 : {
58 116 : anStackCount[iDim] = count[iDim];
59 : while (true)
60 : {
61 543 : ++iDim;
62 543 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
63 543 : goto lbl_next_depth;
64 543 : lbl_return_to_caller_in_loop:
65 543 : --iDim;
66 543 : --anStackCount[iDim];
67 543 : if (anStackCount[iDim] == 0)
68 116 : break;
69 427 : pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
70 : }
71 : }
72 659 : if (iDim > 0)
73 543 : goto lbl_return_to_caller_in_loop;
74 116 : }
75 :
76 : /************************************************************************/
77 : /* CopyToFinalBuffer() */
78 : /************************************************************************/
79 :
80 364 : static void CopyToFinalBuffer(const void *pSrcBuffer,
81 : const GDALExtendedDataType &eSrcDataType,
82 : void *pDstBuffer,
83 : const GDALExtendedDataType &eDstDataType,
84 : size_t nDims, const size_t *count,
85 : const GPtrDiff_t *bufferStride)
86 : {
87 364 : const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
88 : // Use specialized implementation for well-known data types when no
89 : // type conversion is needed
90 364 : if (eSrcDataType == eDstDataType)
91 : {
92 169 : if (nSrcDataTypeSize == 1)
93 : {
94 77 : CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
95 : count, bufferStride);
96 116 : return;
97 : }
98 92 : else if (nSrcDataTypeSize == 2)
99 : {
100 1 : CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
101 : count, bufferStride);
102 1 : return;
103 : }
104 91 : else if (nSrcDataTypeSize == 4)
105 : {
106 18 : CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
107 : count, bufferStride);
108 18 : return;
109 : }
110 73 : else if (nSrcDataTypeSize == 8)
111 : {
112 20 : CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
113 : count, bufferStride);
114 20 : return;
115 : }
116 : }
117 :
118 248 : const size_t nDstDataTypeSize(eDstDataType.GetSize());
119 496 : std::vector<size_t> anStackCount(nDims);
120 496 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
121 248 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
122 248 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
123 248 : size_t iDim = 0;
124 :
125 517 : lbl_next_depth:
126 517 : if (iDim == nDims - 1)
127 : {
128 507 : GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
129 507 : pabyDstBufferStack[iDim], eDstDataType,
130 507 : bufferStride[iDim], count[iDim]);
131 507 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
132 : }
133 : else
134 : {
135 10 : anStackCount[iDim] = count[iDim];
136 : while (true)
137 : {
138 269 : ++iDim;
139 269 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
140 269 : goto lbl_next_depth;
141 269 : lbl_return_to_caller_in_loop:
142 269 : --iDim;
143 269 : --anStackCount[iDim];
144 269 : if (anStackCount[iDim] == 0)
145 10 : break;
146 259 : pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
147 : }
148 : }
149 517 : if (iDim > 0)
150 269 : goto lbl_return_to_caller_in_loop;
151 : }
152 :
153 : /************************************************************************/
154 : /* IsTransposedRequest() */
155 : /************************************************************************/
156 :
157 1575 : bool GDALMDArray::IsTransposedRequest(
158 : const size_t *count,
159 : const GPtrDiff_t *bufferStride) const // stride in elements
160 : {
161 : /*
162 : For example:
163 : count = [2,3,4]
164 : strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
165 : stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
166 : (axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
167 : (3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
168 : */
169 1575 : const size_t nDims(GetDimensionCount());
170 1575 : size_t nCurStrideForRowMajorStrides = 1;
171 1575 : bool bRowMajorStrides = true;
172 1575 : size_t nElts = 1;
173 1575 : size_t nLastIdx = 0;
174 3955 : for (size_t i = nDims; i > 0;)
175 : {
176 2380 : --i;
177 2380 : if (bufferStride[i] < 0)
178 0 : return false;
179 2380 : if (static_cast<size_t>(bufferStride[i]) !=
180 : nCurStrideForRowMajorStrides)
181 : {
182 496 : bRowMajorStrides = false;
183 : }
184 : // Integer overflows have already been checked in CheckReadWriteParams()
185 2380 : nCurStrideForRowMajorStrides *= count[i];
186 2380 : nElts *= count[i];
187 2380 : nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
188 : }
189 1575 : if (bRowMajorStrides)
190 1190 : return false;
191 385 : return nLastIdx == nElts - 1;
192 : }
193 :
194 : /************************************************************************/
195 : /* TransposeLast2Dims() */
196 : /************************************************************************/
197 :
198 20 : static bool TransposeLast2Dims(void *pDstBuffer,
199 : const GDALExtendedDataType &eDT,
200 : const size_t nDims, const size_t *count,
201 : const size_t nEltsNonLast2Dims)
202 : {
203 20 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
204 20 : const auto nDTSize = eDT.GetSize();
205 : void *pTempBufferForLast2DimsTranspose =
206 20 : VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
207 20 : if (pTempBufferForLast2DimsTranspose == nullptr)
208 0 : return false;
209 :
210 20 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
211 60 : for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
212 : {
213 40 : GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
214 : pTempBufferForLast2DimsTranspose,
215 40 : eDT.GetNumericDataType(), count[nDims - 1],
216 40 : count[nDims - 2]);
217 40 : memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
218 : nDTSize * nEltsLast2Dims);
219 40 : pabyDstBuffer += nDTSize * nEltsLast2Dims;
220 : }
221 :
222 20 : VSIFree(pTempBufferForLast2DimsTranspose);
223 :
224 20 : return true;
225 : }
226 :
227 : /************************************************************************/
228 : /* ReadForTransposedRequest() */
229 : /************************************************************************/
230 :
231 : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
232 : // transposed view yield to extremely poor/unusable performance. This fixes
233 : // this by using temporary memory to read in a contiguous buffer in a
234 : // row-major order, and then do the transposition to the final buffer.
235 :
236 384 : bool GDALMDArray::ReadForTransposedRequest(
237 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
238 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
239 : void *pDstBuffer) const
240 : {
241 384 : const size_t nDims(GetDimensionCount());
242 384 : if (nDims == 0)
243 : {
244 0 : CPLAssert(false);
245 : return false; // shouldn't happen
246 : }
247 384 : size_t nElts = 1;
248 919 : for (size_t i = 0; i < nDims; ++i)
249 535 : nElts *= count[i];
250 :
251 768 : std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
252 384 : tmpBufferStrides.back() = 1;
253 535 : for (size_t i = nDims - 1; i > 0;)
254 : {
255 151 : --i;
256 151 : tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
257 : }
258 :
259 384 : const auto &eDT = GetDataType();
260 384 : const auto nDTSize = eDT.GetSize();
261 573 : if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
262 589 : static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
263 16 : (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
264 : {
265 : // Optimization of the optimization if only the last 2 dims are
266 : // transposed that saves on temporary buffer allocation
267 24 : const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
268 24 : size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
269 24 : bool bRowMajorStridesForNonLast2Dims = true;
270 24 : size_t nEltsNonLast2Dims = 1;
271 41 : for (size_t i = nDims - 2; i > 0;)
272 : {
273 17 : --i;
274 17 : if (static_cast<size_t>(bufferStride[i]) !=
275 : nCurStrideForRowMajorStrides)
276 : {
277 4 : bRowMajorStridesForNonLast2Dims = false;
278 : }
279 : // Integer overflows have already been checked in
280 : // CheckReadWriteParams()
281 17 : nCurStrideForRowMajorStrides *= count[i];
282 17 : nEltsNonLast2Dims *= count[i];
283 : }
284 24 : if (bRowMajorStridesForNonLast2Dims)
285 : {
286 : // We read in the final buffer!
287 20 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
288 20 : eDT, pDstBuffer))
289 : {
290 0 : return false;
291 : }
292 :
293 20 : return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
294 20 : nEltsNonLast2Dims);
295 : }
296 : }
297 :
298 364 : void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
299 364 : if (pTempBuffer == nullptr)
300 0 : return false;
301 :
302 364 : if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
303 364 : pTempBuffer))
304 : {
305 0 : VSIFree(pTempBuffer);
306 0 : return false;
307 : }
308 364 : CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
309 : count, bufferStride);
310 :
311 364 : if (eDT.NeedsFreeDynamicMemory())
312 : {
313 237 : GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
314 474 : for (size_t i = 0; i < nElts; ++i)
315 : {
316 237 : eDT.FreeDynamicMemory(pabyPtr);
317 237 : pabyPtr += nDTSize;
318 : }
319 : }
320 :
321 364 : VSIFree(pTempBuffer);
322 364 : return true;
323 : }
324 :
325 : /************************************************************************/
326 : /* GDALMDArrayTransposed */
327 : /************************************************************************/
328 :
329 : class GDALMDArrayTransposed final : public GDALPamMDArray
330 : {
331 : private:
332 : std::shared_ptr<GDALMDArray> m_poParent{};
333 : std::vector<int> m_anMapNewAxisToOldAxis{};
334 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
335 :
336 : mutable std::vector<GUInt64> m_parentStart;
337 : mutable std::vector<size_t> m_parentCount;
338 : mutable std::vector<GInt64> m_parentStep;
339 : mutable std::vector<GPtrDiff_t> m_parentStride;
340 :
341 : void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
342 : const GInt64 *arrayStep,
343 : const GPtrDiff_t *bufferStride) const;
344 :
345 : static std::string
346 88 : MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
347 : {
348 88 : std::string ret;
349 88 : ret += '[';
350 324 : for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
351 : {
352 236 : if (i > 0)
353 148 : ret += ',';
354 236 : ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
355 : }
356 88 : ret += ']';
357 88 : return ret;
358 : }
359 :
360 : protected:
361 44 : GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
362 : const std::vector<int> &anMapNewAxisToOldAxis,
363 : std::vector<std::shared_ptr<GDALDimension>> &&dims)
364 88 : : GDALAbstractMDArray(std::string(),
365 88 : "Transposed view of " + poParent->GetFullName() +
366 88 : " along " +
367 44 : MappingToStr(anMapNewAxisToOldAxis)),
368 88 : GDALPamMDArray(std::string(),
369 88 : "Transposed view of " + poParent->GetFullName() +
370 176 : " along " + MappingToStr(anMapNewAxisToOldAxis),
371 88 : GDALPamMultiDim::GetPAM(poParent),
372 : poParent->GetContext()),
373 44 : m_poParent(std::move(poParent)),
374 : m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
375 44 : m_dims(std::move(dims)),
376 44 : m_parentStart(m_poParent->GetDimensionCount()),
377 44 : m_parentCount(m_poParent->GetDimensionCount()),
378 44 : m_parentStep(m_poParent->GetDimensionCount()),
379 352 : m_parentStride(m_poParent->GetDimensionCount())
380 : {
381 44 : }
382 :
383 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
384 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
385 : const GDALExtendedDataType &bufferDataType,
386 : void *pDstBuffer) const override;
387 :
388 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
389 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
390 : const GDALExtendedDataType &bufferDataType,
391 : const void *pSrcBuffer) override;
392 :
393 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
394 : CSLConstList papszOptions) const override;
395 :
396 : public:
397 : static std::shared_ptr<GDALMDArrayTransposed>
398 44 : Create(const std::shared_ptr<GDALMDArray> &poParent,
399 : const std::vector<int> &anMapNewAxisToOldAxis)
400 : {
401 44 : const auto &parentDims(poParent->GetDimensions());
402 88 : std::vector<std::shared_ptr<GDALDimension>> dims;
403 162 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
404 : {
405 118 : if (iOldAxis < 0)
406 : {
407 1 : dims.push_back(std::make_shared<GDALDimension>(
408 2 : std::string(), "newaxis", std::string(), std::string(), 1));
409 : }
410 : else
411 : {
412 117 : dims.emplace_back(parentDims[iOldAxis]);
413 : }
414 : }
415 :
416 : auto newAr(
417 : std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
418 44 : poParent, anMapNewAxisToOldAxis, std::move(dims))));
419 44 : newAr->SetSelf(newAr);
420 88 : return newAr;
421 : }
422 :
423 3 : bool IsWritable() const override
424 : {
425 3 : return m_poParent->IsWritable();
426 : }
427 :
428 99 : const std::string &GetFilename() const override
429 : {
430 99 : return m_poParent->GetFilename();
431 : }
432 :
433 : const std::vector<std::shared_ptr<GDALDimension>> &
434 373 : GetDimensions() const override
435 : {
436 373 : return m_dims;
437 : }
438 :
439 151 : const GDALExtendedDataType &GetDataType() const override
440 : {
441 151 : return m_poParent->GetDataType();
442 : }
443 :
444 4 : const std::string &GetUnit() const override
445 : {
446 4 : return m_poParent->GetUnit();
447 : }
448 :
449 5 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
450 : {
451 10 : auto poSrcSRS = m_poParent->GetSpatialRef();
452 5 : if (!poSrcSRS)
453 2 : return nullptr;
454 6 : auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
455 6 : std::vector<int> dstMapping;
456 9 : for (int srcAxis : srcMapping)
457 : {
458 6 : bool bFound = false;
459 14 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
460 : {
461 14 : if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
462 : {
463 6 : dstMapping.push_back(static_cast<int>(i) + 1);
464 6 : bFound = true;
465 6 : break;
466 : }
467 : }
468 6 : if (!bFound)
469 : {
470 0 : dstMapping.push_back(0);
471 : }
472 : }
473 6 : auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
474 3 : poClone->SetDataAxisToSRSAxisMapping(dstMapping);
475 3 : return poClone;
476 : }
477 :
478 7 : const void *GetRawNoDataValue() const override
479 : {
480 7 : return m_poParent->GetRawNoDataValue();
481 : }
482 :
483 : // bool SetRawNoDataValue(const void* pRawNoData) override { return
484 : // m_poParent->SetRawNoDataValue(pRawNoData); }
485 :
486 4 : double GetOffset(bool *pbHasOffset,
487 : GDALDataType *peStorageType) const override
488 : {
489 4 : return m_poParent->GetOffset(pbHasOffset, peStorageType);
490 : }
491 :
492 4 : double GetScale(bool *pbHasScale,
493 : GDALDataType *peStorageType) const override
494 : {
495 4 : return m_poParent->GetScale(pbHasScale, peStorageType);
496 : }
497 :
498 : // bool SetOffset(double dfOffset) override { return
499 : // m_poParent->SetOffset(dfOffset); }
500 :
501 : // bool SetScale(double dfScale) override { return
502 : // m_poParent->SetScale(dfScale); }
503 :
504 5 : std::vector<GUInt64> GetBlockSize() const override
505 : {
506 5 : std::vector<GUInt64> ret(GetDimensionCount());
507 10 : const auto parentBlockSize(m_poParent->GetBlockSize());
508 17 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
509 : {
510 12 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
511 12 : if (iOldAxis >= 0)
512 : {
513 11 : ret[i] = parentBlockSize[iOldAxis];
514 : }
515 : }
516 10 : return ret;
517 : }
518 :
519 : std::shared_ptr<GDALAttribute>
520 4 : GetAttribute(const std::string &osName) const override
521 : {
522 4 : return m_poParent->GetAttribute(osName);
523 : }
524 :
525 : std::vector<std::shared_ptr<GDALAttribute>>
526 8 : GetAttributes(CSLConstList papszOptions = nullptr) const override
527 : {
528 8 : return m_poParent->GetAttributes(papszOptions);
529 : }
530 : };
531 :
532 : /************************************************************************/
533 : /* PrepareParentArrays() */
534 : /************************************************************************/
535 :
536 48 : void GDALMDArrayTransposed::PrepareParentArrays(
537 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
538 : const GPtrDiff_t *bufferStride) const
539 : {
540 179 : for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
541 : {
542 131 : const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
543 131 : if (iOldAxis >= 0)
544 : {
545 130 : m_parentStart[iOldAxis] = arrayStartIdx[i];
546 130 : m_parentCount[iOldAxis] = count[i];
547 130 : if (arrayStep) // only null when called from IAdviseRead()
548 : {
549 128 : m_parentStep[iOldAxis] = arrayStep[i];
550 : }
551 130 : if (bufferStride) // only null when called from IAdviseRead()
552 : {
553 128 : m_parentStride[iOldAxis] = bufferStride[i];
554 : }
555 : }
556 : }
557 48 : }
558 :
559 : /************************************************************************/
560 : /* IRead() */
561 : /************************************************************************/
562 :
563 45 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
564 : const size_t *count, const GInt64 *arrayStep,
565 : const GPtrDiff_t *bufferStride,
566 : const GDALExtendedDataType &bufferDataType,
567 : void *pDstBuffer) const
568 : {
569 45 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
570 90 : return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
571 45 : m_parentStep.data(), m_parentStride.data(),
572 45 : bufferDataType, pDstBuffer);
573 : }
574 :
575 : /************************************************************************/
576 : /* IWrite() */
577 : /************************************************************************/
578 :
579 2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
580 : const size_t *count, const GInt64 *arrayStep,
581 : const GPtrDiff_t *bufferStride,
582 : const GDALExtendedDataType &bufferDataType,
583 : const void *pSrcBuffer)
584 : {
585 2 : PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
586 4 : return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
587 2 : m_parentStep.data(), m_parentStride.data(),
588 2 : bufferDataType, pSrcBuffer);
589 : }
590 :
591 : /************************************************************************/
592 : /* IAdviseRead() */
593 : /************************************************************************/
594 :
595 1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
596 : const size_t *count,
597 : CSLConstList papszOptions) const
598 : {
599 1 : PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
600 1 : return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
601 1 : papszOptions);
602 : }
603 :
604 : //! @endcond
605 :
606 : /************************************************************************/
607 : /* Transpose() */
608 : /************************************************************************/
609 :
610 : /** Return a view of the array whose axis have been reordered.
611 : *
612 : * The anMapNewAxisToOldAxis parameter should contain all the values between 0
613 : * and GetDimensionCount() - 1, and each only once.
614 : * -1 can be used as a special index value to ask for the insertion of a new
615 : * axis of size 1.
616 : * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
617 : * index of one of its dimension, it corresponds to the axis of index
618 : * anMapNewAxisToOldAxis[i] from the current array.
619 : *
620 : * This is similar to the numpy.transpose() method
621 : *
622 : * The returned array holds a reference to the original one, and thus is
623 : * a view of it (not a copy). If the content of the original array changes,
624 : * the content of the view array too. The view can be written if the underlying
625 : * array is writable.
626 : *
627 : * Note that I/O performance in such a transposed view might be poor.
628 : *
629 : * This is the same as the C function GDALMDArrayTranspose().
630 : *
631 : * @return a new array, that holds a reference to the original one, and thus is
632 : * a view of it (not a copy), or nullptr in case of error.
633 : */
634 : std::shared_ptr<GDALMDArray>
635 52 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
636 : {
637 104 : auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
638 52 : if (!self)
639 : {
640 0 : CPLError(CE_Failure, CPLE_AppDefined,
641 : "Driver implementation issue: m_pSelf not set !");
642 0 : return nullptr;
643 : }
644 52 : const int nDims = static_cast<int>(GetDimensionCount());
645 104 : std::vector<bool> alreadyUsedOldAxis(nDims, false);
646 52 : int nCountOldAxis = 0;
647 185 : for (const auto iOldAxis : anMapNewAxisToOldAxis)
648 : {
649 137 : if (iOldAxis < -1 || iOldAxis >= nDims)
650 : {
651 3 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
652 4 : return nullptr;
653 : }
654 134 : if (iOldAxis >= 0)
655 : {
656 132 : if (alreadyUsedOldAxis[iOldAxis])
657 : {
658 1 : CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
659 : iOldAxis);
660 1 : return nullptr;
661 : }
662 131 : alreadyUsedOldAxis[iOldAxis] = true;
663 131 : nCountOldAxis++;
664 : }
665 : }
666 48 : if (nCountOldAxis != nDims)
667 : {
668 4 : CPLError(CE_Failure, CPLE_AppDefined,
669 : "One or several original axis missing");
670 4 : return nullptr;
671 : }
672 44 : return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
673 : }
|