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