Coverage Report

Created: 2025-07-11 06:33

/src/bag/api/bag_surfacecorrections.cpp
Line
Count
Source (jump to first uncovered line)
1
2
#include "bag_dataset.h"
3
#include "bag_private.h"
4
#include "bag_simplelayer.h"
5
#include "bag_surfacecorrections.h"
6
#include "bag_surfacecorrectionsdescriptor.h"
7
8
#include <array>
9
#include <cmath>
10
#include <cstring>  // memset
11
#include <memory>
12
#include <H5Cpp.h>
13
14
15
namespace BAG {
16
17
//! The maximum length of the list of datums.
18
constexpr uint16_t kMaxDatumsLength = 256;
19
//! The radius to use while searching for nearest neighbours.
20
constexpr int32_t kSearchRadius = 3;
21
22
namespace {
23
24
//! Get the HDF5 CompType specified by the surface corrections descriptor.
25
/*!
26
\param descriptor
27
    The surface corrections descriptor.
28
29
\return
30
    The HDF5 CompType as specified by the descriptor.
31
*/
32
::H5::CompType getCompoundType(
33
    const SurfaceCorrectionsDescriptor& descriptor)
34
0
{
35
0
    const ::H5::CompType h5memDataType{
36
0
        static_cast<size_t>(descriptor.getElementSize())};
37
38
0
    size_t offset = 0;
39
40
0
    if (descriptor.getSurfaceType() == BAG_SURFACE_IRREGULARLY_SPACED)
41
0
    {
42
0
        h5memDataType.insertMember("x", offset, ::H5::PredType::NATIVE_DOUBLE);
43
0
        offset += sizeof(double);
44
45
0
        h5memDataType.insertMember("y", offset, ::H5::PredType::NATIVE_DOUBLE);
46
0
        offset += sizeof(double);
47
0
    }
48
49
0
    const std::array<hsize_t, kRank> zDims{1, descriptor.getNumCorrectors()};
50
0
    ::H5::ArrayType h5zDataType{::H5::PredType::NATIVE_FLOAT, kRank, zDims.data()};
51
52
0
    h5memDataType.insertMember("z", offset, h5zDataType);
53
54
0
    return h5memDataType;
55
0
}
56
57
}  // namespace
58
59
//! Constructor.
60
/*!
61
\param dataset
62
    The BAG Dataset this layer belongs to.
63
\param descriptor
64
    The descriptor of this layer.
65
\param pH5dataSet
66
    The HDF5 DataSet that stores this interleaved layer.
67
*/
68
SurfaceCorrections::SurfaceCorrections(
69
    Dataset& dataset,
70
    SurfaceCorrectionsDescriptor& descriptor,
71
    std::unique_ptr<::H5::DataSet, DeleteH5dataSet> pH5dataSet)
72
26
    : Layer(dataset, descriptor)
73
26
    , m_pH5dataSet(std::move(pH5dataSet))
74
26
{
75
26
}
76
77
//! Create a surface corrections layer.
78
/*!
79
\param dataset
80
    The BAG Dataset this layer belongs to.
81
\param type
82
    The type of surface correction topography.
83
\param numCorrectors
84
    The number of correctors provided.
85
    Valid range is 1-10.
86
\param chunkSize
87
    The chunk size the HDF5 DataSet will use.
88
\param compressionLevel
89
    The compression level the HDF5 DataSet will use.
90
91
\return
92
    The new surface corrections layer.
93
*/
94
std::shared_ptr<SurfaceCorrections> SurfaceCorrections::create(
95
    Dataset& dataset,
96
    BAG_SURFACE_CORRECTION_TOPOGRAPHY type,
97
    uint8_t numCorrectors,
98
    uint64_t chunkSize,
99
    int compressionLevel)
100
0
{
101
0
    auto descriptor = SurfaceCorrectionsDescriptor::create(dataset, type,
102
0
        numCorrectors, chunkSize, compressionLevel);
103
104
0
    auto h5dataSet = SurfaceCorrections::createH5dataSet(dataset, *descriptor);
105
106
0
    return std::make_shared<SurfaceCorrections>(dataset,
107
0
        *descriptor, std::move(h5dataSet));
108
0
}
109
110
//! Open an existing surface corrections layer.
111
/*!
112
\param dataset
113
    The BAG Dataset this layer belongs to.
114
\param descriptor
115
    The descriptor of this layer.
116
117
\return
118
    The new surface corrections layer.
119
*/
120
std::shared_ptr<SurfaceCorrections> SurfaceCorrections::open(
121
    Dataset& dataset,
122
    SurfaceCorrectionsDescriptor& descriptor)
123
26
{
124
26
    const auto& h5file = dataset.getH5file();
125
26
    auto h5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
126
26
        new ::H5::DataSet{h5file.openDataSet(descriptor.getInternalPath())},
127
26
        DeleteH5dataSet{});
128
129
26
    return std::make_shared<SurfaceCorrections>(dataset,
130
26
        descriptor, std::move(h5dataSet));
131
26
}
132
133
134
//! Create the HDF5 DataSet.
135
/*!
136
\param dataset
137
    The BAG Dataset this layer belongs to.
138
\param descriptor
139
    The descriptor of this layer.
140
141
\return
142
    The new HDF5 DataSet.
143
*/
144
std::unique_ptr<::H5::DataSet, DeleteH5dataSet>
145
SurfaceCorrections::createH5dataSet(
146
    const Dataset& dataset,
147
    const SurfaceCorrectionsDescriptor& descriptor)
148
0
{
149
0
    std::array<hsize_t, kRank> fileDims{0, 0};
150
0
    const std::array<hsize_t, kRank> kMaxFileDims{H5S_UNLIMITED, H5S_UNLIMITED};
151
0
    const ::H5::DataSpace h5fileDataSpace{kRank, fileDims.data(), kMaxFileDims.data()};
152
153
    // Use chunk size and compression level from the descriptor.
154
0
    const auto compressionLevel = descriptor.getCompressionLevel();
155
156
    // Create the creation property list.
157
0
    const ::H5::DSetCreatPropList h5createPropList{};
158
159
0
    const auto chunkSize = descriptor.getChunkSize();
160
0
    if (chunkSize > 0)
161
0
    {
162
0
      const std::array<hsize_t, kRank> chunkDims{chunkSize, chunkSize};
163
0
        h5createPropList.setChunk(kRank, chunkDims.data());
164
165
0
        if (compressionLevel > 0 && compressionLevel <= kMaxCompressionLevel)
166
0
            h5createPropList.setDeflate(compressionLevel);
167
0
    }
168
0
    else if (compressionLevel > 0)
169
0
        throw CompressionNeedsChunkingSet{};
170
0
    else
171
0
        throw LayerRequiresChunkingSet{};
172
173
0
    h5createPropList.setFillTime(H5D_FILL_TIME_ALLOC);
174
175
0
    const auto h5memDataType = getCompoundType(descriptor);
176
177
    // Create the DataSet using the above.
178
0
    const auto& h5file = dataset.getH5file();
179
180
0
    const auto h5dataSet = h5file.createDataSet(VERT_DATUM_CORR_PATH,
181
0
        h5memDataType, h5fileDataSpace, h5createPropList);
182
183
    // Create any attributes.
184
0
    const ::H5::DataSpace kScalarDataSpace{};
185
0
    auto att = h5dataSet.createAttribute(VERT_DATUM_CORR_SURFACE_TYPE,
186
0
        ::H5::PredType::NATIVE_UINT8, kScalarDataSpace);
187
188
0
    const auto surfaceType = descriptor.getSurfaceType();
189
0
    const auto tmpSurfaceType = static_cast<uint8_t>(surfaceType);
190
0
    att.write(::H5::PredType::NATIVE_UINT8, &tmpSurfaceType);
191
192
0
    constexpr hsize_t xmlLength = 0;
193
0
    constexpr hsize_t kMaxSize = kMaxDatumsLength;
194
0
    const ::H5::DataSpace kVerticalDatumDataSpace{1, &xmlLength, &kMaxSize};
195
196
0
    att = h5dataSet.createAttribute(VERT_DATUM_CORR_VERTICAL_DATUM,
197
0
        ::H5::PredType::C_S1, kVerticalDatumDataSpace);
198
199
    // Create any optional attributes.
200
0
    if (surfaceType == BAG_SURFACE_GRID_EXTENTS)
201
0
    {
202
0
        constexpr double junk = 0.;
203
204
0
        att = h5dataSet.createAttribute(VERT_DATUM_CORR_SWX,
205
0
            ::H5::PredType::NATIVE_DOUBLE, kScalarDataSpace);
206
0
        att.write(::H5::PredType::NATIVE_DOUBLE, &junk);
207
208
0
        att = h5dataSet.createAttribute(VERT_DATUM_CORR_SWY,
209
0
            ::H5::PredType::NATIVE_DOUBLE, kScalarDataSpace);
210
0
        att.write(::H5::PredType::NATIVE_DOUBLE, &junk);
211
212
0
        att = h5dataSet.createAttribute(VERT_DATUM_CORR_NSX,
213
0
            ::H5::PredType::NATIVE_DOUBLE, kScalarDataSpace);
214
0
        att.write(::H5::PredType::NATIVE_DOUBLE, &junk);
215
216
0
        att = h5dataSet.createAttribute(VERT_DATUM_CORR_NSY,
217
0
            ::H5::PredType::NATIVE_DOUBLE, kScalarDataSpace);
218
0
        att.write(::H5::PredType::NATIVE_DOUBLE, &junk);
219
0
    }
220
221
0
    return std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
222
0
        new ::H5::DataSet{h5dataSet}, DeleteH5dataSet{});
223
0
}
224
225
//! Retrieve the HDF5 DataSet.
226
const ::H5::DataSet& SurfaceCorrections::getH5dataSet() const & noexcept
227
0
{
228
0
    return *m_pH5dataSet;
229
0
}
230
231
//! Retrieve the layer's descriptor. Note: this shadows BAG::Layer.getDescriptor()
232
/*!
233
\return
234
    The layer's descriptor.
235
    Will never be nullptr.
236
*/
237
std::shared_ptr<SurfaceCorrectionsDescriptor> SurfaceCorrections::getDescriptor() & noexcept
238
0
{
239
0
    return std::dynamic_pointer_cast<SurfaceCorrectionsDescriptor>(Layer::getDescriptor());
240
0
}
241
242
//! Retrieve the layer's descriptor. Note: this shadows BAG::Layer.getDescriptor()
243
/*!
244
\return
245
    The layer's descriptor.
246
    Will never be nullptr.
247
*/
248
0
std::shared_ptr<const SurfaceCorrectionsDescriptor> SurfaceCorrections::getDescriptor() const & noexcept {
249
0
    return std::dynamic_pointer_cast<const SurfaceCorrectionsDescriptor>(Layer::getDescriptor());
250
0
}
251
252
//! Read a corrected region from a simple layer using the specified corrector.
253
/*!
254
\param rowStart
255
    The starting row.
256
\param columnStart
257
    The starting column.
258
\param rowEnd
259
    The ending row (inclusive).
260
\param columnEnd
261
    The ending column (inclusive).
262
\param corrector
263
    The corrector to use when applying a correction.
264
    Valid values are 1-10.
265
\param layer
266
    The simple layer to correct.
267
268
\return
269
    The corrected date from the simple layer using the specified corrector.
270
*/
271
UInt8Array SurfaceCorrections::readCorrected(
272
    uint32_t rowStart,
273
    uint32_t columnStart,
274
    uint32_t rowEnd,
275
    uint32_t columnEnd,
276
    uint8_t corrector,
277
    const SimpleLayer& layer) const
278
0
{
279
0
    auto pDescriptor =
280
0
        std::dynamic_pointer_cast<const SurfaceCorrectionsDescriptor>(
281
0
            this->getDescriptor());
282
0
    if (!pDescriptor)
283
0
        throw InvalidDescriptor{};
284
285
0
    auto weakDataset = this->getDataset();
286
0
    if (weakDataset.expired())
287
0
        throw DatasetNotFound{};
288
289
0
    auto dataset = weakDataset.lock();
290
291
0
    uint32_t ncols = 0, nrows = 0;
292
0
    std::tie(ncols, nrows) = dataset->getDescriptor().getDims();
293
294
0
    if (columnEnd >= ncols || rowEnd >= nrows || rowStart > rowEnd
295
0
        || columnStart > columnEnd)
296
0
        throw InvalidReadSize{};
297
298
0
    UInt8Array data{(columnEnd - columnStart + 1) * (rowEnd - rowStart + 1)
299
0
        * sizeof(float)};
300
301
0
    for (uint32_t f=0, i=0; i<nrows; ++i)
302
0
    {
303
0
        if (i >= rowStart && i <= rowEnd)
304
0
        {
305
            //! By row at a time, fill the whole buffer
306
0
            auto sepData = this->readCorrectedRow(i, columnStart, columnEnd,
307
0
                corrector, layer);
308
309
            //! For each column in the transfer set
310
0
            for (uint32_t j=0; j<columnEnd-columnStart + 1; ++j)
311
0
                data[(columnEnd - columnStart + 1) * f + j] = sepData[j];
312
313
0
            f++;
314
0
        }
315
0
    }
316
317
0
    return data;
318
0
}
319
320
//! Read a corrected row from a simple layer using the specified corrector.
321
/*!
322
\param row
323
    The row.
324
\param columnStart
325
    The starting column.
326
\param columnEnd
327
    The ending column (inclusive).
328
\param corrector
329
    The corrector to use when applying a correction.
330
    Valid values are 1-10.
331
\param layer
332
    The simple layer to correct.
333
334
\return
335
    The corrected date from the simple layer using the specified corrector.
336
*/
337
UInt8Array SurfaceCorrections::readCorrectedRow(
338
    uint32_t row,
339
    uint32_t columnStart,
340
    uint32_t columnEnd,
341
    uint8_t corrector,
342
    const SimpleLayer& layer) const
343
0
{
344
0
    auto pDescriptor =
345
0
        std::dynamic_pointer_cast<const SurfaceCorrectionsDescriptor>(
346
0
            this->getDescriptor());
347
0
    if (!pDescriptor)
348
0
        throw InvalidDescriptor{};
349
350
0
    const auto surfaceType = pDescriptor->getSurfaceType();  // aka topography
351
352
0
    if (surfaceType != BAG_SURFACE_GRID_EXTENTS)
353
0
        throw UnsupportedSurfaceType{};
354
355
0
    if (corrector < 1 || corrector > pDescriptor->getNumCorrectors())
356
0
        throw InvalidCorrector{};
357
358
0
    --corrector;  // This is 0 based when used.
359
360
0
    auto originalRow = layer.read(row, row, columnStart, columnEnd);
361
0
    auto* data = reinterpret_cast<float*>(originalRow.data());
362
363
    // Obtain cell resolution and SW origin (0,1,1,0).
364
0
    double swCornerX = 0., swCornerY = 0.;
365
0
    std::tie(swCornerX, swCornerY) = pDescriptor->getOrigin();
366
367
0
    double nodeSpacingX = 0., nodeSpacingY = 0.;
368
0
    std::tie(nodeSpacingX, nodeSpacingY) = pDescriptor->getSpacing();
369
370
0
    const auto resratio = nodeSpacingX / nodeSpacingY;
371
372
0
    uint32_t ncols = 0, nrows = 0;
373
0
    std::tie(nrows, ncols) = pDescriptor->getDims();
374
375
0
    std::array<int32_t, 2> lastP{-1, -1};
376
377
0
    auto weakDataset = this->getDataset();
378
0
    if (weakDataset.expired())
379
0
        throw DatasetNotFound{};
380
381
0
    auto dataset = weakDataset.lock();
382
383
0
    double swCornerXsimple = 0., swCornerYsimple = 0.;
384
0
    std::tie(swCornerXsimple, swCornerYsimple) =
385
0
        dataset->getDescriptor().getOrigin();
386
387
0
    double nodeSpacingXsimple = 0., nodeSpacingYsimple = 0.;
388
0
    std::tie(nodeSpacingXsimple, nodeSpacingYsimple) =
389
0
        dataset->getDescriptor().getGridSpacing();
390
391
0
    using std::floor;  using std::fabs;  using std::ceil;
392
393
    // Compute an SEP for each cell in the row.
394
0
    for (auto j=columnStart; j<=columnEnd; ++j)
395
0
    {
396
0
        const uint32_t rowIndex = j - columnStart;
397
398
0
        if (data[rowIndex] == BAG_NULL_GENERIC ||
399
0
            data[rowIndex] == BAG_NULL_ELEVATION ||
400
0
            data[rowIndex] == BAG_NULL_UNCERTAINTY)
401
0
            continue;
402
403
0
        std::array<uint32_t, 2> rowRange{0, 0};
404
0
        std::array<uint32_t, 2> colRange{0, 0};
405
406
        //! Determine the X and Y values of given cell.
407
0
        const double nodeX = swCornerXsimple + j * nodeSpacingXsimple;
408
0
        const double nodeY = swCornerYsimple + row * nodeSpacingYsimple;
409
410
#if 0  // Dead code (see logic above); saved from C code for now.
411
        if (surfaceType == BAG_SURFACE_IRREGULARLY_SPACED)
412
        {
413
            if (lastP[0] == -1 || lastP[1] == -1)
414
            {
415
                colRange[0] = 0;
416
                colRange[1] = ncols -1;
417
                rowRange[0] = 0;
418
                rowRange[1] = nrows -1;
419
            }
420
            else
421
            {
422
                colRange[0] = lastP[0] - kSearchRadius;
423
                colRange[1] = lastP[0] + kSearchRadius;
424
                rowRange[0] = lastP[1] - kSearchRadius;
425
                rowRange[1] = lastP[1] + kSearchRadius;
426
            }
427
        }
428
        else
429
#endif
430
0
        if (surfaceType == BAG_SURFACE_GRID_EXTENTS)
431
0
        {
432
            //! A simple calculation for 4 nearest corrector nodes.
433
0
            colRange[0] = static_cast<uint32_t>(fabs(floor((swCornerX - nodeX) / nodeSpacingX)));
434
0
            colRange[1] = static_cast<uint32_t>(fabs(ceil((swCornerX - nodeX) / nodeSpacingX)));
435
0
            rowRange[0] = static_cast<uint32_t>(fabs(floor((swCornerY - nodeY) / nodeSpacingY)));
436
0
            rowRange[1] = static_cast<uint32_t>(fabs(ceil((swCornerY - nodeY) / nodeSpacingY)));
437
0
        }
438
439
        //! Enforce dataset limits.
440
0
        if (colRange[0] > colRange[1])
441
0
            std::swap(colRange[0], colRange[1]);
442
443
0
        if (colRange[0] >= ncols)
444
0
            colRange[0] = ncols -1;
445
446
0
        if (colRange[1] >= ncols)
447
0
            colRange[1] = ncols -1;
448
449
0
        if (rowRange[0] > rowRange[1])
450
0
            std::swap(rowRange[0], rowRange[1]);
451
452
0
        if (rowRange[0] >= nrows)
453
0
            rowRange[0] = nrows -1;
454
455
0
        if (rowRange[1] >= nrows)
456
0
            rowRange[1] = nrows -1;
457
458
0
        if (colRange[1] == colRange[0])
459
0
        {
460
0
            if (colRange[0] > 0)
461
0
                --colRange[0];
462
463
0
            if ((colRange[1] + 1) < ncols)
464
0
                ++colRange[1];
465
0
        }
466
0
        if (rowRange[1] == rowRange[0])
467
0
        {
468
469
0
            if (rowRange[0] > 0)
470
0
                --rowRange[0];
471
472
0
            if ((rowRange[1] + 1) < nrows)
473
0
                ++rowRange[1];
474
0
        }
475
476
        //std::cerr << "INDX: " << rowIndex << " Row: " << row << " RC:  " << rowRange[0] << "  / "<< rowRange[1] << '\n';
477
478
0
        bool isZeroDistance = false;
479
0
        double sum_sep = 0.0;
480
0
        double sum = 0.0;
481
0
        double leastDistSq = std::numeric_limits<double>::max();
482
483
        // Look through the SEPs and calculate the weighted average between them and this position.
484
0
        for (auto q=rowRange[0]; !isZeroDistance && q <= rowRange[1]; ++q)
485
0
        {
486
            // The SEP should be accessed, up to entire row of all columns of Surface_Correction.
487
0
            const auto buffer = this->read(q, q, colRange[0], colRange[1]);
488
0
            const auto* readbuf = reinterpret_cast<const BagVerticalDatumCorrectionsGridded*>(buffer.data());
489
0
            const auto y1 = swCornerY + q * nodeSpacingY;
490
491
0
            for (auto u=colRange[0]; u<=colRange[1]; ++u)
492
0
            {
493
0
                const auto* vertCorr = readbuf + (u - colRange[0]);
494
495
0
                const auto z1 = vertCorr->z[corrector];
496
0
                const auto x1 = swCornerX + u * nodeSpacingX;
497
498
0
                double distSq = 0.;
499
500
0
                if (nodeX ==  x1 && nodeY == y1)
501
0
                {
502
0
                    isZeroDistance = true;
503
0
                    distSq = 1.0;
504
0
                    data[rowIndex] += z1;
505
506
0
                    break;
507
0
                }
508
509
                // Calculate distance weight between nodeX/nodeY and y1/x1
510
0
                distSq = std::pow(fabs(static_cast<double>(nodeX - x1)), 2.0) +
511
0
                    std::pow(resratio * fabs(static_cast<double>(nodeY - y1)), 2.0);
512
513
514
0
                if (leastDistSq > distSq)
515
0
                {
516
0
                    leastDistSq = distSq;
517
0
                    lastP[0] = u;
518
0
                    lastP[1] = q;
519
0
                }
520
521
                // Inverse distance calculation
522
0
                sum_sep += z1 / distSq;
523
0
                sum += 1.0 / distSq;
524
0
            }
525
0
        }
526
527
        //std::cerr << "sum sum " << sum_sep << " / " << sum << " =  " << sum_sep / sum << " : " << data[rowIndex] << '\n';
528
529
0
        if (!isZeroDistance)
530
0
        {
531
            // is not a constant SEP with one point?
532
0
            if (sum_sep != 0.0 && sum != 0.0)
533
0
                data[rowIndex] += static_cast<float>(sum_sep / sum);
534
0
            else
535
0
                data[rowIndex] = BAG_NULL_GENERIC;
536
0
        }
537
538
0
    }
539
540
0
    return originalRow;
541
0
}
542
543
//! \copydoc Layer::read
544
UInt8Array SurfaceCorrections::readProxy(
545
    uint32_t rowStart,
546
    uint32_t columnStart,
547
    uint32_t rowEnd,
548
    uint32_t columnEnd) const
549
0
{
550
    // Query the file for the specified rows and columns.
551
0
    const auto h5fileDataSpace = m_pH5dataSet->getSpace();
552
553
0
    const auto rows = (rowEnd - rowStart) + 1;
554
0
    const auto columns = (columnEnd - columnStart) + 1;
555
0
    const std::array<hsize_t, kRank> count{rows, columns};
556
0
    const std::array<hsize_t, kRank> offset{rowStart, columnStart};
557
558
0
    h5fileDataSpace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data());
559
560
0
    auto pDescriptor =
561
0
        std::dynamic_pointer_cast<const SurfaceCorrectionsDescriptor>(
562
0
            this->getDescriptor());
563
0
    if (!pDescriptor)
564
0
        throw InvalidLayerDescriptor{};
565
566
0
    const auto bufferSize = pDescriptor->getReadBufferSize(rows, columns);
567
0
    UInt8Array buffer{bufferSize};
568
569
0
    const ::H5::DataSpace h5memSpace{kRank, count.data(), count.data()};
570
571
0
    const auto h5memDataType = getCompoundType(*pDescriptor);
572
573
0
    m_pH5dataSet->read(buffer.data(), h5memDataType, h5memSpace, h5fileDataSpace);
574
575
0
    return buffer;
576
0
}
577
578
//! \copydoc Layer::writeAttributes
579
void SurfaceCorrections::writeAttributesProxy() const
580
0
{
581
0
    auto pDescriptor =
582
0
        std::dynamic_pointer_cast<const SurfaceCorrectionsDescriptor>(
583
0
            this->getDescriptor());
584
0
    if (!pDescriptor)
585
0
        throw InvalidDescriptor{};
586
587
    // Write any attributes, from the layer descriptor.
588
    // surface type
589
0
    auto att = m_pH5dataSet->openAttribute(VERT_DATUM_CORR_SURFACE_TYPE);
590
0
    const auto surfaceType = pDescriptor->getSurfaceType();
591
0
    const auto tmpSurfaceType = static_cast<uint8_t>(surfaceType);
592
0
    att.write(::H5::PredType::NATIVE_UINT8, &tmpSurfaceType);
593
594
    // vertical datums
595
0
    auto tmpDatums = pDescriptor->getVerticalDatums();
596
0
    if (tmpDatums.size() > kMaxDatumsLength)
597
0
        tmpDatums.resize(kMaxDatumsLength);
598
599
0
    att = m_pH5dataSet->openAttribute(VERT_DATUM_CORR_VERTICAL_DATUM);
600
0
    att.write(::H5::PredType::C_S1, tmpDatums);
601
602
    // Write any optional attributes.
603
0
    if (surfaceType == BAG_SURFACE_GRID_EXTENTS)
604
0
    {
605
        // sw corner x
606
0
        att = m_pH5dataSet->openAttribute(VERT_DATUM_CORR_SWX);
607
0
        const auto origin = pDescriptor->getOrigin();
608
0
        att.write(::H5::PredType::NATIVE_DOUBLE, &std::get<0>(origin));
609
610
        // sw corner y
611
0
        att = m_pH5dataSet->openAttribute(VERT_DATUM_CORR_SWY);
612
0
        att.write(::H5::PredType::NATIVE_DOUBLE, &std::get<1>(origin));
613
614
        // node spacing x
615
0
        const auto spacing = pDescriptor->getSpacing();
616
0
        att = m_pH5dataSet->openAttribute(VERT_DATUM_CORR_NSX);
617
0
        att.write(::H5::PredType::NATIVE_DOUBLE, &std::get<0>(spacing));
618
619
        // node spacing y
620
0
        att = m_pH5dataSet->openAttribute(VERT_DATUM_CORR_NSY);
621
0
        att.write(::H5::PredType::NATIVE_DOUBLE, &std::get<1>(spacing));
622
0
    }
623
0
}
624
625
//! \copydoc Layer::write
626
void SurfaceCorrections::writeProxy(
627
    uint32_t rowStart,
628
    uint32_t columnStart,
629
    uint32_t rowEnd,
630
    uint32_t columnEnd,
631
    const uint8_t* buffer)
632
0
{
633
0
    auto pDescriptor = std::dynamic_pointer_cast<SurfaceCorrectionsDescriptor>(
634
0
        this->getDescriptor());
635
0
    if (!pDescriptor)
636
0
        throw InvalidDescriptor{};
637
638
0
    const auto rows = (rowEnd - rowStart) + 1;
639
0
    const auto columns = (columnEnd - columnStart) + 1;
640
0
    const std::array<hsize_t, kRank> count{rows, columns};
641
0
    const std::array<hsize_t, kRank> offset{rowStart, columnStart};
642
0
    const ::H5::DataSpace h5memDataSpace{kRank, count.data(), count.data()};
643
644
0
    ::H5::DataSpace h5fileDataSpace = m_pH5dataSet->getSpace();
645
646
    // Expand the file data space if needed.
647
0
    std::array<hsize_t, kRank> fileDims{};
648
0
    std::array<hsize_t, kRank> maxFileDims{};
649
0
    h5fileDataSpace.getSimpleExtentDims(fileDims.data(), maxFileDims.data());
650
651
0
    if ((fileDims[0] < (rowEnd + 1)) ||
652
0
        (fileDims[1] < (columnEnd + 1)))
653
0
    {
654
0
        const std::array<hsize_t, kRank> newDims{
655
0
            std::max<hsize_t>(fileDims[0], rowEnd + 1),
656
0
            std::max<hsize_t>(fileDims[1], columnEnd + 1)};
657
658
0
        m_pH5dataSet->extend(newDims.data());
659
660
0
        h5fileDataSpace = m_pH5dataSet->getSpace();
661
662
        // Update the dataset's dimensions.
663
0
        if (this->getDataset().expired())
664
0
            throw DatasetNotFound{};
665
666
0
        auto pDataset = this->getDataset().lock();
667
0
        pDataset->getDescriptor().setDims(static_cast<uint32_t>(newDims[0]),
668
0
            static_cast<uint32_t>(newDims[1]));
669
0
    }
670
671
0
    h5fileDataSpace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data());
672
673
0
    const auto h5memDataType = getCompoundType(*pDescriptor);
674
675
0
    m_pH5dataSet->write(buffer, h5memDataType, h5memDataSpace, h5fileDataSpace);
676
677
    // Update descriptor.
678
0
    const auto h5Space = m_pH5dataSet->getSpace();
679
680
0
    std::array<hsize_t, kRank> dims{};
681
0
    h5Space.getSimpleExtentDims(dims.data());
682
683
0
    pDescriptor->setDims(static_cast<uint32_t>(dims[0]),
684
0
        static_cast<uint32_t>(dims[1]));
685
0
}
686
687
}   //namespace BAG
688