Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim_array_maths.cpp
4 : * Project: GDAL Core
5 : * Purpose: Mathematic operations on GDALMDArray
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 : #include <algorithm>
19 : #include <cmath>
20 : #include <functional>
21 : #include <limits>
22 :
23 : /************************************************************************/
24 : /* GDALMDArray::HasSameShapeAs() */
25 : /************************************************************************/
26 :
27 : /** Returns true if both arrays have the same shape.
28 : *
29 : * That is to say the same number of dimensions and the size of corresponding
30 : * dimensions is the same.
31 : *
32 : * @since 3.14
33 : */
34 161 : bool GDALMDArray::HasSameShapeAs(const GDALMDArray &other) const
35 : {
36 161 : bool bRet = (GetDimensionCount() == other.GetDimensionCount());
37 161 : if (bRet)
38 : {
39 161 : const auto &apoThisDims = GetDimensions();
40 161 : const auto &apoOtherDims = other.GetDimensions();
41 376 : for (size_t i = 0; bRet && i < apoThisDims.size(); ++i)
42 : {
43 215 : bRet = (apoThisDims[i]->GetSize() == apoOtherDims[i]->GetSize());
44 : }
45 : }
46 161 : return bRet;
47 : }
48 :
49 : /************************************************************************/
50 : /* GDALMathOperationMDArray */
51 : /************************************************************************/
52 :
53 : //! @cond Doxygen_Suppress
54 :
55 : class GDALMathOperationMDArray final : public GDALPamMDArray
56 : {
57 : public:
58 : enum class Operation
59 : {
60 : OP_ADD,
61 : OP_SUBTRACT,
62 : OP_MULTIPLY,
63 : OP_DIVIDE,
64 : };
65 :
66 290 : static const char *OperationToString(Operation op)
67 : {
68 290 : switch (op)
69 : {
70 92 : case Operation::OP_ADD:
71 92 : break;
72 84 : case Operation::OP_SUBTRACT:
73 84 : return "-";
74 65 : case Operation::OP_MULTIPLY:
75 65 : return "*";
76 49 : case Operation::OP_DIVIDE:
77 49 : return "/";
78 : }
79 92 : return "+";
80 : }
81 :
82 : static std::shared_ptr<GDALMathOperationMDArray>
83 : Create(const std::shared_ptr<GDALMDArray> &arrayLeft,
84 : const std::shared_ptr<GDALMDArray> &arrayRight, Operation op);
85 :
86 : protected:
87 284 : static std::string GetName(const std::shared_ptr<GDALMDArray> &arrayLeft,
88 : const std::shared_ptr<GDALMDArray> &arrayRight,
89 : Operation op)
90 : {
91 568 : return std::string(arrayLeft->GetFullName())
92 284 : .append(" ")
93 284 : .append(OperationToString(op))
94 284 : .append(" ")
95 568 : .append(arrayRight->GetFullName());
96 : }
97 :
98 142 : GDALMathOperationMDArray(const std::shared_ptr<GDALMDArray> &arrayLeft,
99 : const std::shared_ptr<GDALMDArray> &arrayRight,
100 : Operation op)
101 284 : : GDALAbstractMDArray(std::string(),
102 142 : GetName(arrayLeft, arrayRight, op)),
103 284 : GDALPamMDArray(std::string(), GetName(arrayLeft, arrayRight, op),
104 : // Arbitrary linking to arrayLeft for PAM purposes
105 284 : GDALPamMultiDim::GetPAM(arrayLeft),
106 : arrayLeft->GetContext()),
107 : m_arrayLeft(arrayLeft), m_arrayRight(arrayRight), m_op(op),
108 : m_dt(GDALExtendedDataType::Create(GDT_Float64)),
109 : m_dfNoDataLeft(
110 142 : m_arrayLeft->GetNoDataValueAsDouble(&m_bHasNoDataLeft)),
111 : m_dfNoDataRight(
112 852 : m_arrayRight->GetNoDataValueAsDouble(&m_bHasNoDataRight))
113 : {
114 142 : if (m_bHasNoDataLeft || m_bHasNoDataRight)
115 : {
116 50 : if (m_bHasNoDataLeft && m_bHasNoDataRight)
117 : {
118 37 : if (m_dfNoDataLeft == m_dfNoDataRight)
119 32 : m_dfNoData = m_dfNoDataLeft;
120 : }
121 13 : else if (m_bHasNoDataLeft)
122 7 : m_dfNoData = m_dfNoDataLeft;
123 : else
124 6 : m_dfNoData = m_dfNoDataRight;
125 50 : m_abyRawNoDataValue.resize(sizeof(double));
126 50 : memcpy(m_abyRawNoDataValue.data(), &m_dfNoData, sizeof(m_dfNoData));
127 : }
128 :
129 142 : const auto &leftUnit = m_arrayLeft->GetUnit();
130 142 : const auto &rightUnit = m_arrayRight->GetUnit();
131 142 : switch (m_op)
132 : {
133 88 : case Operation::OP_ADD:
134 : case Operation::OP_SUBTRACT:
135 : {
136 88 : if (leftUnit == rightUnit)
137 84 : m_osUnit = leftUnit;
138 88 : break;
139 : }
140 54 : case Operation::OP_MULTIPLY:
141 : case Operation::OP_DIVIDE:
142 : {
143 54 : if (!leftUnit.empty() && !rightUnit.empty())
144 : {
145 6 : m_osUnit = leftUnit;
146 6 : m_osUnit += ' ';
147 6 : m_osUnit += OperationToString(m_op);
148 6 : m_osUnit += ' ';
149 6 : m_osUnit += rightUnit;
150 : }
151 54 : break;
152 : }
153 : }
154 142 : }
155 :
156 1 : bool IsWritable() const override
157 : {
158 1 : return false;
159 : }
160 :
161 123 : const std::string &GetFilename() const override
162 : {
163 123 : const auto &filename1 = m_arrayLeft->GetFilename();
164 123 : const auto &filename2 = m_arrayRight->GetFilename();
165 123 : if (filename1 == filename2)
166 82 : return filename1;
167 41 : static std::string emptyString;
168 41 : return emptyString;
169 : }
170 :
171 : const std::vector<std::shared_ptr<GDALDimension>> &
172 862 : GetDimensions() const override
173 : {
174 862 : return m_arrayLeft->GetDimensions();
175 : }
176 :
177 : std::vector<std::shared_ptr<GDALMDArray>>
178 17 : GetCoordinateVariables() const override
179 : {
180 34 : auto left = m_arrayLeft->GetCoordinateVariables();
181 34 : auto right = m_arrayRight->GetCoordinateVariables();
182 17 : if (left == right)
183 17 : return left;
184 0 : return GDALMDArray::GetCoordinateVariables();
185 : }
186 :
187 410 : const GDALExtendedDataType &GetDataType() const override
188 : {
189 410 : return m_dt;
190 : }
191 :
192 71 : const void *GetRawNoDataValue() const override
193 : {
194 71 : return m_abyRawNoDataValue.empty() ? nullptr
195 71 : : m_abyRawNoDataValue.data();
196 : }
197 :
198 40 : const std::string &GetUnit() const override
199 : {
200 40 : return m_osUnit;
201 : }
202 :
203 20 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
204 : {
205 40 : auto leftSRS = m_arrayLeft->GetSpatialRef();
206 40 : auto rightSRS = m_arrayRight->GetSpatialRef();
207 20 : if (!leftSRS || !rightSRS || !leftSRS->IsSame(rightSRS.get()))
208 19 : return nullptr;
209 1 : return leftSRS;
210 : }
211 :
212 20 : double GetOffset(bool *pbHasOffset,
213 : GDALDataType *peStorageType) const override
214 : {
215 20 : bool bHasLeftOffset = false;
216 20 : GDALDataType leftStorageType = GDT_Unknown;
217 : const double dfLeftOffset =
218 20 : m_arrayLeft->GetOffset(&bHasLeftOffset, &leftStorageType);
219 :
220 20 : bool bHasRightOffset = false;
221 20 : GDALDataType rightStorageType = GDT_Unknown;
222 : const double dfRightOffset =
223 20 : m_arrayRight->GetOffset(&bHasRightOffset, &rightStorageType);
224 :
225 20 : bool bHasLeftScale = false;
226 20 : const double dfLeftScale = m_arrayLeft->GetScale(&bHasLeftScale);
227 :
228 20 : bool bHasRightScale = false;
229 20 : const double dfRightScale = m_arrayRight->GetScale(&bHasRightScale);
230 :
231 20 : bool bRet = false;
232 20 : double dfRes = 0.0;
233 :
234 20 : switch (m_op)
235 : {
236 7 : case Operation::OP_ADD:
237 : {
238 10 : bRet = bHasLeftOffset && bHasRightOffset &&
239 3 : (bHasLeftScale == bHasRightScale &&
240 3 : (!bHasLeftScale || dfLeftScale == dfRightScale));
241 7 : if (bRet)
242 2 : dfRes = dfLeftOffset + dfRightOffset;
243 7 : break;
244 : }
245 :
246 7 : case Operation::OP_SUBTRACT:
247 : {
248 10 : bRet = bHasLeftOffset && bHasRightOffset &&
249 3 : (bHasLeftScale == bHasRightScale &&
250 3 : (!bHasLeftScale || dfLeftScale == dfRightScale));
251 7 : if (bRet)
252 2 : dfRes = dfLeftOffset - dfRightOffset;
253 7 : break;
254 : }
255 :
256 6 : case Operation::OP_MULTIPLY:
257 : case Operation::OP_DIVIDE:
258 6 : break;
259 : }
260 :
261 20 : if (pbHasOffset)
262 20 : *pbHasOffset = bRet;
263 20 : if (bRet && peStorageType)
264 : {
265 0 : *peStorageType =
266 0 : GDALDataTypeUnion(leftStorageType, rightStorageType);
267 : }
268 :
269 20 : return dfRes;
270 : }
271 :
272 8 : double GetScale(bool *pbHasScale,
273 : GDALDataType *peStorageType) const override
274 : {
275 8 : bool bHasLeftOffset = false;
276 8 : const double dfLeftOffset = m_arrayLeft->GetOffset(&bHasLeftOffset);
277 :
278 8 : bool bHasRightOffset = false;
279 8 : const double dfRightOffset = m_arrayRight->GetOffset(&bHasRightOffset);
280 :
281 8 : bool bHasLeftScale = false;
282 8 : GDALDataType leftStorageType = GDT_Unknown;
283 : const double dfLeftScale =
284 8 : m_arrayLeft->GetScale(&bHasLeftScale, &leftStorageType);
285 :
286 8 : bool bHasRightScale = false;
287 8 : GDALDataType rightStorageType = GDT_Unknown;
288 : const double dfRightScale =
289 8 : m_arrayRight->GetScale(&bHasRightScale, &rightStorageType);
290 :
291 8 : bool bRet = false;
292 8 : double dfRes = 1.0;
293 :
294 8 : const bool bZeroOffset =
295 16 : (bHasLeftOffset == bHasRightOffset &&
296 8 : (!bHasLeftOffset ||
297 4 : (dfLeftOffset == dfRightOffset && dfLeftOffset == 0)));
298 :
299 8 : switch (m_op)
300 : {
301 4 : case Operation::OP_ADD:
302 : case Operation::OP_SUBTRACT:
303 : {
304 6 : bRet = bZeroOffset &&
305 2 : (bHasLeftScale == bHasRightScale &&
306 2 : (!bHasLeftScale || dfLeftScale == dfRightScale));
307 4 : if (bRet)
308 2 : dfRes = dfLeftScale;
309 4 : break;
310 : }
311 :
312 2 : case Operation::OP_MULTIPLY:
313 : {
314 2 : bRet = bZeroOffset && bHasLeftScale && bHasRightScale;
315 2 : if (bRet)
316 1 : dfRes = dfLeftScale * dfRightScale;
317 2 : break;
318 : }
319 :
320 2 : case Operation::OP_DIVIDE:
321 : {
322 2 : bRet = bZeroOffset && bHasLeftScale && bHasRightScale;
323 2 : if (bRet)
324 1 : dfRes = dfLeftScale / dfRightScale;
325 2 : break;
326 : }
327 : }
328 :
329 8 : if (pbHasScale)
330 8 : *pbHasScale = bRet;
331 8 : if (bRet && peStorageType)
332 : {
333 0 : *peStorageType =
334 0 : GDALDataTypeUnion(leftStorageType, rightStorageType);
335 : }
336 :
337 8 : return dfRes;
338 : }
339 :
340 32 : std::vector<GUInt64> GetBlockSize() const override
341 : {
342 64 : const auto leftBlockSize = m_arrayLeft->GetBlockSize();
343 64 : const auto rightBlockSize = m_arrayRight->GetBlockSize();
344 32 : if (leftBlockSize != rightBlockSize)
345 2 : return GDALMDArray::GetBlockSize();
346 30 : return leftBlockSize;
347 : }
348 :
349 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
350 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
351 : const GDALExtendedDataType &bufferDataType,
352 : void *pDstBuffer) const override;
353 :
354 : private:
355 : const std::shared_ptr<GDALMDArray> m_arrayLeft;
356 : const std::shared_ptr<GDALMDArray> m_arrayRight;
357 : const Operation m_op;
358 : const GDALExtendedDataType m_dt;
359 : bool m_bHasNoDataLeft = false;
360 : bool m_bHasNoDataRight = false;
361 : const double m_dfNoDataLeft;
362 : const double m_dfNoDataRight;
363 : double m_dfNoData = std::numeric_limits<double>::quiet_NaN();
364 : std::vector<GByte> m_abyRawNoDataValue{};
365 : std::string m_osUnit{};
366 : mutable std::vector<double> m_leftValues{};
367 : mutable std::vector<double> m_rightValues{};
368 :
369 896 : inline bool IsInvalid(double dfLeft) const
370 : {
371 1792 : return std::isnan(dfLeft) ||
372 1792 : (m_bHasNoDataLeft && m_dfNoDataLeft == dfLeft);
373 : }
374 :
375 899 : inline bool IsInvalidTuple(double dfLeft, double dfRight) const
376 : {
377 899 : return std::isnan(dfLeft) ||
378 1785 : (m_bHasNoDataLeft && m_dfNoDataLeft == dfLeft) ||
379 2684 : std::isnan(dfRight) ||
380 1785 : (m_bHasNoDataRight && m_dfNoDataRight == dfRight);
381 : }
382 :
383 : template <class Op>
384 : void PerformOperation(double *leftValues, const double *rightValues,
385 : size_t nElts, bool bIntegerAndAllValid) const;
386 : };
387 :
388 : /************************************************************************/
389 : /* GDALMathOperationMDArray::Create() */
390 : /************************************************************************/
391 :
392 : /* static */ std::shared_ptr<GDALMathOperationMDArray>
393 148 : GDALMathOperationMDArray::Create(const std::shared_ptr<GDALMDArray> &arrayLeft,
394 : const std::shared_ptr<GDALMDArray> &arrayRight,
395 : Operation op)
396 : {
397 148 : if (!arrayLeft)
398 : {
399 0 : CPLError(CE_Failure, CPLE_AppDefined, "arrayLeft is null");
400 0 : return nullptr;
401 : }
402 :
403 148 : if (!arrayRight)
404 : {
405 0 : CPLError(CE_Failure, CPLE_AppDefined, "arrayRight is null");
406 0 : return nullptr;
407 : }
408 :
409 148 : if (!arrayLeft->HasSameShapeAs(*arrayRight))
410 : {
411 4 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
412 8 : ("Arrays " + arrayLeft->GetFullName() + " and " +
413 12 : arrayRight->GetFullName() + " do not have the same shape")
414 : .c_str());
415 4 : return nullptr;
416 : }
417 :
418 717 : for (const auto &array : {arrayLeft, arrayRight})
419 : {
420 287 : if (array->GetDataType().GetClass() != GEDTC_NUMERIC)
421 : {
422 2 : CPLError(
423 : CE_Failure, CPLE_AppDefined, "%s",
424 4 : ("Array " + array->GetFullName() + " is not numeric").c_str());
425 2 : return nullptr;
426 : }
427 : }
428 :
429 : auto newAr(std::shared_ptr<GDALMathOperationMDArray>(
430 284 : new GDALMathOperationMDArray(arrayLeft, arrayRight, op)));
431 142 : newAr->SetSelf(newAr);
432 142 : return newAr;
433 : }
434 :
435 : /************************************************************************/
436 : /* GDALMathOperationMDArray::PerformOperation() */
437 : /************************************************************************/
438 :
439 : template <class Op>
440 : #if defined(__GNUC__) && !defined(__clang__)
441 : __attribute__((optimize("tree-vectorize")))
442 : #endif
443 107 : void GDALMathOperationMDArray::PerformOperation(double *leftValues,
444 : const double *rightValues,
445 : size_t nElts,
446 : bool bIntegerAndAllValid) const
447 : {
448 107 : if (bIntegerAndAllValid)
449 : {
450 28 : if (!rightValues)
451 : {
452 21 : for (size_t i = 0; i < nElts; ++i)
453 : {
454 18 : leftValues[i] = Op()(leftValues[i], leftValues[i]);
455 : }
456 : }
457 : else
458 : {
459 4467 : for (size_t i = 0; i < nElts; ++i)
460 : {
461 4442 : leftValues[i] = Op()(leftValues[i], rightValues[i]);
462 : }
463 : }
464 : }
465 : else
466 : {
467 79 : if (!rightValues)
468 : {
469 46 : if ((!m_bHasNoDataLeft || std::isnan(m_dfNoDataLeft)) &&
470 14 : std::isnan(m_dfNoData))
471 : {
472 998 : for (size_t i = 0; i < nElts; ++i)
473 : {
474 984 : leftValues[i] = Op()(leftValues[i], leftValues[i]);
475 : }
476 : }
477 : else
478 : {
479 914 : for (size_t i = 0; i < nElts; ++i)
480 : {
481 896 : if (IsInvalid(leftValues[i]))
482 8 : leftValues[i] = m_dfNoData;
483 : else
484 888 : leftValues[i] = Op()(leftValues[i], leftValues[i]);
485 : }
486 : }
487 : }
488 : else
489 : {
490 23 : if ((!m_bHasNoDataLeft || std::isnan(m_dfNoDataLeft)) &&
491 90 : (!m_bHasNoDataRight || std::isnan(m_dfNoDataRight)) &&
492 20 : std::isnan(m_dfNoData))
493 : {
494 364 : for (size_t i = 0; i < nElts; ++i)
495 : {
496 344 : leftValues[i] = Op()(leftValues[i], rightValues[i]);
497 : }
498 : }
499 : else
500 : {
501 926 : for (size_t i = 0; i < nElts; ++i)
502 : {
503 899 : if (IsInvalidTuple(leftValues[i], rightValues[i]))
504 26 : leftValues[i] = m_dfNoData;
505 : else
506 873 : leftValues[i] = Op()(leftValues[i], rightValues[i]);
507 : }
508 : }
509 : }
510 : }
511 107 : }
512 :
513 : /************************************************************************/
514 : /* GDALMathOperationMDArray::IRead() */
515 : /************************************************************************/
516 :
517 110 : bool GDALMathOperationMDArray::IRead(const GUInt64 *arrayStartIdx,
518 : const size_t *count,
519 : const GInt64 *arrayStep,
520 : const GPtrDiff_t *bufferStride,
521 : const GDALExtendedDataType &bufferDataType,
522 : void *pDstBuffer) const
523 : {
524 110 : if (bufferDataType.GetClass() != GEDTC_NUMERIC)
525 : {
526 0 : CPLError(CE_Failure, CPLE_AppDefined,
527 : "GDALMathOperationMDArray::IRead(): not supported with "
528 : "non-numeric buffer data type");
529 0 : return false;
530 : }
531 :
532 110 : const auto &apoDims = GetDimensions();
533 110 : const size_t nDims = apoDims.size();
534 :
535 : const bool bIntegerAndAllValid =
536 110 : m_abyRawNoDataValue.empty() &&
537 65 : GDALDataTypeIsInteger(
538 175 : m_arrayLeft->GetDataType().GetNumericDataType()) &&
539 31 : GDALDataTypeIsInteger(m_arrayRight->GetDataType().GetNumericDataType());
540 :
541 127 : if (bIntegerAndAllValid && m_op == Operation::OP_SUBTRACT &&
542 17 : m_arrayLeft == m_arrayRight)
543 : {
544 3 : CopyContiguousBufferToBuffer(nDims, count, nullptr,
545 6 : GDALExtendedDataType::Create(GDT_Unknown),
546 : pDstBuffer, bufferDataType, bufferStride);
547 3 : return true;
548 : }
549 :
550 : // Check if the output buffer is in contiguous row major order
551 : // If this is true and its type is also the type of the array, we can
552 : // directly read the left array into the output buffer and update it in
553 : // place.
554 107 : bool bRowMajorBufferStride = true;
555 107 : GPtrDiff_t nExpectedStrideValue = 1;
556 268 : for (size_t i = nDims; i > 0; /* decremented in loop */)
557 : {
558 161 : --i;
559 161 : if (bufferStride[i] != nExpectedStrideValue)
560 : {
561 0 : bRowMajorBufferStride = false;
562 0 : break;
563 : }
564 161 : nExpectedStrideValue = static_cast<GPtrDiff_t>(nExpectedStrideValue *
565 161 : apoDims[i]->GetSize());
566 : }
567 :
568 107 : size_t nElts = 1;
569 107 : bool bFullArrayRequested = true;
570 268 : for (size_t i = 0; i < nDims; ++i)
571 : {
572 161 : nElts *= count[i];
573 161 : if (bFullArrayRequested)
574 161 : bFullArrayRequested = (count[i] == apoDims[i]->GetSize());
575 : }
576 :
577 : const bool bLeftValuesAllocNeeded =
578 107 : !bRowMajorBufferStride || bufferDataType != m_dt;
579 :
580 : // We can skip both I/O and memory allocations if operating on the same
581 : // array.
582 107 : const bool bRightValuesAllocNeeded = m_arrayLeft != m_arrayRight;
583 :
584 : try
585 : {
586 107 : if (bLeftValuesAllocNeeded)
587 : {
588 2 : if (nElts > m_leftValues.size())
589 2 : m_leftValues.resize(nElts);
590 : }
591 107 : if (bRightValuesAllocNeeded)
592 : {
593 72 : if (nElts > m_rightValues.size())
594 72 : m_rightValues.resize(nElts);
595 : }
596 : }
597 0 : catch (const std::exception &)
598 : {
599 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
600 : "GDALMathOperationMDArray::IRead(): out of memory");
601 0 : return false;
602 : }
603 :
604 : double *leftValues = bLeftValuesAllocNeeded
605 107 : ? m_leftValues.data()
606 107 : : static_cast<double *>(pDstBuffer);
607 :
608 107 : const bool ret = m_arrayLeft->Read(arrayStartIdx, count, arrayStep, nullptr,
609 214 : m_dt, leftValues) &&
610 107 : (!bRightValuesAllocNeeded ||
611 144 : m_arrayRight->Read(arrayStartIdx, count, arrayStep,
612 72 : nullptr, m_dt, m_rightValues.data()));
613 :
614 : const double *rightValues =
615 107 : bRightValuesAllocNeeded ? m_rightValues.data() : nullptr;
616 :
617 107 : if (ret)
618 : {
619 107 : switch (m_op)
620 : {
621 19 : case Operation::OP_ADD:
622 : {
623 19 : PerformOperation<std::plus<double>>(leftValues, rightValues,
624 : nElts, bIntegerAndAllValid);
625 19 : break;
626 : }
627 44 : case Operation::OP_SUBTRACT:
628 : {
629 44 : PerformOperation<std::minus<double>>(
630 : leftValues, rightValues, nElts, bIntegerAndAllValid);
631 44 : break;
632 : }
633 28 : case Operation::OP_MULTIPLY:
634 : {
635 28 : PerformOperation<std::multiplies<double>>(
636 : leftValues, rightValues, nElts, bIntegerAndAllValid);
637 28 : break;
638 : }
639 16 : case Operation::OP_DIVIDE:
640 : {
641 16 : PerformOperation<std::divides<double>>(
642 : leftValues, rightValues, nElts, bIntegerAndAllValid);
643 16 : break;
644 : }
645 : }
646 :
647 107 : if (bLeftValuesAllocNeeded)
648 : {
649 2 : CopyContiguousBufferToBuffer(nDims, count, leftValues, m_dt,
650 : pDstBuffer, bufferDataType,
651 : bufferStride);
652 : }
653 : }
654 :
655 107 : if (bFullArrayRequested)
656 : {
657 107 : m_leftValues.clear();
658 107 : m_rightValues.clear();
659 : }
660 :
661 107 : return ret;
662 : }
663 :
664 : //! @endcond
665 :
666 : /************************************************************************/
667 : /* operator+() */
668 : /************************************************************************/
669 :
670 : /** Add this array with another one of the same shape.
671 : *
672 : * The resulting array is lazy evaluated.
673 : *
674 : * The resulting array type is Float64.
675 : *
676 : * The operation is nodata-aware.
677 : *
678 : * This is the same as C function GDALMDArrayBinaryOperation().
679 : *
680 : * @since 3.14
681 : * @return a new array, or nullptr in case of error
682 : */
683 : std::shared_ptr<GDALMDArray>
684 49 : GDALMDArray::operator+(const std::shared_ptr<GDALMDArray> &other) const
685 : {
686 98 : return GDALMathOperationMDArray::Create(
687 147 : GetSelf(), other, GDALMathOperationMDArray::Operation::OP_ADD);
688 : }
689 :
690 : /************************************************************************/
691 : /* operator-() */
692 : /************************************************************************/
693 :
694 : /** Subtract this array with another one of the same shape.
695 : *
696 : * The resulting array is lazy evaluated.
697 : *
698 : * The resulting array type is Float64.
699 : *
700 : * The operation is nodata-aware.
701 : *
702 : * This is the same as C function GDALMDArrayBinaryOperation().
703 : *
704 : * @since 3.14
705 : * @return a new array, or nullptr in case of error
706 : */
707 : std::shared_ptr<GDALMDArray>
708 43 : GDALMDArray::operator-(const std::shared_ptr<GDALMDArray> &other) const
709 : {
710 86 : return GDALMathOperationMDArray::Create(
711 129 : GetSelf(), other, GDALMathOperationMDArray::Operation::OP_SUBTRACT);
712 : }
713 :
714 : /************************************************************************/
715 : /* operator*() */
716 : /************************************************************************/
717 :
718 : /** Multiply this array with another one of the same shape.
719 : *
720 : * The resulting array is lazy evaluated.
721 : *
722 : * The resulting array type is Float64.
723 : *
724 : * The operation is nodata-aware.
725 : *
726 : * This is the same as C function GDALMDArrayBinaryOperation().
727 : *
728 : * @since 3.14
729 : * @return a new array, or nullptr in case of error
730 : */
731 : std::shared_ptr<GDALMDArray>
732 31 : GDALMDArray::operator*(const std::shared_ptr<GDALMDArray> &other) const
733 : {
734 62 : return GDALMathOperationMDArray::Create(
735 93 : GetSelf(), other, GDALMathOperationMDArray::Operation::OP_MULTIPLY);
736 : }
737 :
738 : /************************************************************************/
739 : /* operator/() */
740 : /************************************************************************/
741 :
742 : /** Divide this array by another one of the same shape.
743 : *
744 : * The resulting array is lazy evaluated.
745 : *
746 : * The resulting array type is Float64.
747 : *
748 : * The operation is nodata-aware.
749 : *
750 : * This is the same as C function GDALMDArrayBinaryOperation().
751 : *
752 : * @since 3.14
753 : * @return a new array, or nullptr in case of error
754 : */
755 : std::shared_ptr<GDALMDArray>
756 25 : GDALMDArray::operator/(const std::shared_ptr<GDALMDArray> &other) const
757 : {
758 50 : return GDALMathOperationMDArray::Create(
759 75 : GetSelf(), other, GDALMathOperationMDArray::Operation::OP_DIVIDE);
760 : }
|