meta.cpp 93.5 KB
Newer Older
1
// Copyright 2017-2021, Schlumberger
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "meta.h"
#include "types.h"
#include "file.h"
#include "cornerpoints.h"
#include "structaccess.h"
#include "transform.h"
#include "guid.h"
#include "logger.h"
23
#include "file_smallcache.h"
24
25
26
27
#include "../exception.h"

#include <iostream>
#include <algorithm>
28
#include <cmath>
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269

namespace InternalZGY {
#if 0
}
#endif

// This is to bring several operator<< methods into scope. Note that
// placing the declaration in the global namespace doesn't seem to
// work. Placing it here gives it a narrower scope and (I speculate)
// somewhat higher precedence. This problem is only for the <<
// overload. A hypothetical InternalZGY::Formatters::foo() would be
// found just fine either way.
using namespace InternalZGY::Formatters;

/** file: meta.cpp
 *
 * TODO-Low: Move most of this code to a new meta_storage.cpp/h.
 * This file should only implement ZgyInternalMeta.
 *
 * Most of the existing meta.cpp deals with version-specific layout on
 * the ZGY file. All that code should be private and moved to the new
 * source file. Exports from meta_storage.h would be the factories.
 * One for each header type. The interface returned by each factory is
 * defined in the existing meta.h. That would not change.
 *
 * I cannot do this today because ZgyInternalMeta::initFromScratch()
 * that creates a new file does not use factories. It doesn't really
 * need to since only the latest version of the file layout can be
 * created. I could of course just move the entire initFromScratch()
 * into a single factory method in the new meta_storage.h. But then
 * there would be almost nothing left in this file and I would be back
 * where I started. The current situation actually isn't that bad
 * since all the version-specific classes are defined here in the .cpp
 * source. So even today they aren't visible outside.
 */
namespace {
  template<typename T, std::size_t  N>
  std::ostream& operator<<(std::ostream& os, const std::array<T,N>& a)
  {
    os << "[";
    for (std::size_t ii=0; ii<N; ++ii)
      os << a[ii] << (ii == N-1 ? "" : ", ");
    os << "]";
    return os;
  }
}

namespace {
/**
 * Technically I should have mapped these format specific
 * codes to am enum defined in the impl layer. But since
 * the enum is really unlikely to change I'll just use casts
 * and expose the RawXXX enums to the layers above.
 * They will need to be remapped in the public api anyway.
 * RawGridDefinition and RawCoordType are even less of a
 * problem as those should not leave this layer.
 */
static RawDataType            DecodeDataType(std::uint8_t e)            { return static_cast<RawDataType>(e); }
static RawHorizontalDimension DecodeHorizontalDimension(std::uint8_t e) { return static_cast<RawHorizontalDimension>(e); }
static RawVerticalDimension   DecodeVerticalDimension(std::uint8_t e)   { return static_cast<RawVerticalDimension>(e); }
static RawCoordType           DecodeCoordType(std::uint8_t e)           { return static_cast<RawCoordType>(e); }
//static RawGridDefinition      DecodeGridDefinition(std::uint8_t e)      { return static_cast<RawGridDefinition>(e); }

/**
 * Compute the size of the file in bricks, for all LOD levels
 * and all 3 dimensions. Indirectly also compute the number
 * of LOD levels, since by definition the last LOD level
 * is the one that has size (1, 1, 1) i.e. the entire cube
 * is decimated enought to fit inside a single brick.
 */
static std::vector<std::array<std::int64_t,3>>
calcLodSizes(const std::array<std::int64_t,3>& size_in,
               const std::array<std::int64_t,3>& bricksize)
{
  std::array<std::int64_t,3> size = size_in;
  std::vector<std::array<std::int64_t,3>> result;
  if (size[0] < 1 || size[1] < 1 || size[2] < 1) {
    // Empty file is the only case where the highest lod isn't one brick.
    // There will be one lod and its size will be zero bricks.
    result.push_back(std::array<std::int64_t,3>{0,0,0});
  }
  else {
    // number of full and partial bricks:
    for (int ii=0; ii<3; ++ii)
      size[ii] = (size[ii] + bricksize[ii] - 1) / bricksize[ii];
    result.push_back(size);
    // Continue to add new levels until we get a level with just one brick.
    while (size[0] > 1 || size[1] > 1 || size[2] > 1) {
      for (int ii=0; ii<3; ++ii)
        size[ii] = (size[ii] + 1) / 2;
      result.push_back(size);
    }
  }
  return result;
}

/**
 * Compute the offset into the lookup tables by LOD level.
 * Return an array of offsets indexed by LOD. Also return
 * (appended to the end of the result) the lookup table size.
 * The result differs depending on whether this is the alpha
 * or brick LUT.
 * The highest LOD is at the start of each table so it by
 * definition has offset 0. Since the highest LOD level
 * just has a single brick, the second highest will start
 * at offset 1.
 */
static std::vector<std::int64_t>
calcLutOffsets(const std::vector<std::array<std::int64_t,3>>& lods_in,
               bool isalpha)
{
  std::vector<std::array<std::int64_t,3>> lods(lods_in);
  std::vector<std::int64_t> result;
  std::reverse(lods.begin(), lods.end());
  std::int64_t pos = 0;
  for (const auto& e : lods) {
    result.push_back(pos);
    pos += (e[0] * e[1] * (isalpha ? 1 : e[2]));
  }
  std::reverse(result.begin(), result.end());
  result.push_back(pos);
  return result;
}

static std::int64_t
calcEntriesInLut(const std::array<std::int64_t,3>& size,
                 const std::array<std::int64_t,3>& bricksize,
                 bool is_alpha)
{
  return calcLutOffsets(calcLodSizes(size, bricksize), is_alpha).back();
}

template<typename T>
static IHeaderAccess::podbytes_t
pod2bytes(const T& pod)
{
  IHeaderAccess::podbytes_t tmp(sizeof(pod));
  memcpy(tmp.data(), &pod, sizeof(pod));
  return tmp;
}

/**
 * Convert three arbitrary control points to 4 ordered corner points ordered
 * first il, first xl; last il, first xl, first il, last xl, last il, last xl.
 * Also return the same 4 corners in annotation- and ordinal coordinates.
 * The last two are redundant with size and annot but are provided as a
 * convenience for the api layer.
 *
 * The geometry stored in the ZGY file can be specified either as 4 ordered
 * corner points (_gdef = GridDefinition.FourPoint) or 3 arbitrary points
 * (_gdef = GridDefinition.ThreePoint) where both annotation and world
 * coordinates are given. There is also GridDefinition..Parametric which
 * specifies azimuth and spacing, but that is no longer supported. If it
 * ever was. With FourPoint the writer is still required to store the
 * annotation coordinates just in case there is some disagreement over how
 * the corners are ordered. So, we treat FourPoint the same as ThreePoint.
 * Ignore the last point (nominally this is the corner opposite the origin)
 * and treat the remaining 3 points as if they were arbitrary points
 * instead of the grid corners. V1 files are usually ThreePoint and V2 files
 * are usually FourPoint but don't count on that.
 *
 * TODO-Medium not fully implemented:
 * If the file contains world corners but no annotation, assume the writer
 * used GridDefinition.FourPoint but without storing the apparently redundant
 * annotation corners. This is contrary to spec but we will handle that case
 * for robustness.
 *
 * If the conversion fails for another reason then return all zeros because
 * in that case coordinate conversion will not be possible.
 */
static std::tuple<std::array<std::array<double,2>,4>,
                  std::array<std::array<double,2>,4>,
                  std::array<std::array<double,2>,4>>
calcOrderedCorners(const std::array<float,3>& orig,
                   const std::array<float,3>& inc,
                   const std::array<std::int64_t,3>& size,
                   const std::array<float,4>& gpiline,
                   const std::array<float,4>& gpxline,
                   const std::array<double,4>& gpx,
                   const std::array<double,4>& gpy)
{
  OrderedCornerPoints ocp(
      orig[0], inc[0], size[0], // il
      orig[1], inc[1], size[1], // xl
      gpiline[0], gpxline[0], gpx[0], gpy[0],
      gpiline[1], gpxline[1], gpx[1], gpy[1],
      gpiline[2], gpxline[2], gpx[2], gpy[2]);
  // Testing OrderedCornerPoints...
  std::array<std::array<double,2>,4> check_index{{
      { 0, 0 },
      { (double)size[0]-1, 0 },
      { 0, (double)size[1]-1 },
      { (double)size[0]-1, (double)size[1]-1}}};
  if (ocp.index_coords() != check_index)
    throw OpenZGY::Errors::ZgyInternalError("Messed up corner point computation.");
  return std::make_tuple(ocp.index_coords(),
                         ocp.annot_coords(),
                         ocp.world_coords());
}

#if 0 // TODO-Low: enable this.
/**
 * Alternative implementation. The generalTransform() method it calls
 * is a lot simpler than OrderedCornerPoints but I worry about testing.
 * OrderedCornerPoints was cribbed from the old ZGY accessor so it
 * should be ok... but I might still be using it incorrectly.
 * TODO-Test export both calcOrderedCorners() methods and run some
 * exhaustive tests. Then switch.
 */
static std::tuple<std::array<std::array<double,2>,4>,
                  std::array<std::array<double,2>,4>,
                  std::array<std::array<double,2>,4>>
calcOrderedCornersNew(const std::array<float,3>& orig,
                      const std::array<float,3>& inc,
                      const std::array<std::int64_t,3>& size,
                      const std::array<float,4>& gpiline,
                      const std::array<float,4>& gpxline,
                      const std::array<double,4>& gpx,
                      const std::array<double,4>& gpy)
{
  typedef std::array<std::array<double,2>,4> coords_t;
  const double last[2]{orig[0] + inc[0] * (size[0]-1),
                       orig[1] + inc[1] * (size[1]-1)};
  double I[4] {0,    (double)size[0]-1,                 0, (double)size[0]-1};
  double J[4] {0,                    0, (double)size[1]-1, (double)size[1]-1};
  double X[4] {orig[0], last[0], orig[0], last[0]};
  double Y[4] {orig[1], orig[1], last[1], last[1]};
  coords_t index{{{I[0],J[0]}, {I[1],J[1]}, {I[2],J[2]}, {I[3],J[3]}}};
  coords_t annot{{{X[0],Y[0]}, {X[1],Y[1]}, {X[2],Y[2]}, {X[3],Y[3]}}};
  generalTransform(gpiline[0], gpxline[0],
                   gpiline[1], gpxline[1],
                   gpiline[2], gpxline[2],
                   gpx[0], gpy[0],
                   gpx[1], gpy[1],
                   gpx[2], gpy[2],
                   &X[0], &Y[0], 4);
  coords_t world{{{X[0],Y[0]}, {X[1],Y[1]}, {X[2],Y[2]}, {X[3],Y[3]}}};
  return std::make_tuple(index, annot, world);
}
#endif

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/**
 * Sanity check. If the codingrange for an int cube is bad, silently
 * use a range that causes no conversion between storage and float.
 *
 * This avoids several corner cases both inside OpenZGY and in applications.
 *
 * Rationale: A non-finite range is always bad. A range with min==max
 * is technically valid when reading, as all storage values would map
 * to the same float value. But this is almost certainly not what the
 * writer intended. Similarly a range with min>max technically means
 * that increasing storage values correspond to decreasing float values.
 * Again, the values are more likely to be completely bogus.
 *
 * Leave the range alone if this is a float cube. There is no conversion
 * involved, and for float the codingrange is ignored by the API anyway.
 *
 * For files written by the OpenZGY library an exception wouls be thrown
 * on create. So the codingrange should always be ok for those.
 *
 * Note: The sanity check could also be applied to the histogram range.
 * That fix would also apply to float cubes. The histogram is less
 * important though, and it should be ok to let the application worry
 * about a bad histogram.
 */
static void
_fix_codingrange(float *lo, float *hi, std::uint8_t datatype)
{
  // The datatype codes used in the file map 1:1 to the RawDataType enum.
  const RawDataTypeDetails details(static_cast<RawDataType>(datatype));
  if (details.is_integer) {
    if (!std::isfinite(*lo) || !std::isfinite(*hi) || *hi <= *lo) {
      if (false)
        std::cout << "Bad codingrange "
                  << "(" << *lo << ", " << *hi << ") "
                  << "-> use "
                  << "(" << details.lowest << ", " << details.highest << ")"
                  << std::endl;
      *lo = static_cast<float>(details.lowest);
      *hi = static_cast<float>(details.highest);
    }
  }
}

313
314
315
316
317
318
319
320
} // namespace

/////////////////////////////////////////////////////////////////////////////
//    FileHeader   //////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

// Python layout is: <4s I
// Size in storage: 8 bytes
321
// Thread safety: None. The user of this type is responsible for that.
322
323
324
325
326
#pragma pack(1)
class FileHeaderPOD
{
public:
  std::uint8_t      _magic[4];      // Always VBS\0.
327
  std::uint32_t     _version;       // Current version is 3, or 4 if ZFP used.
328
329
330
};
#pragma pack()

331
// Thread safety: None. The user of this type is responsible for that.
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
class FileHeaderAccess : public IFileHeaderAccess
{
public:
  FileHeaderPOD _pod;
  FileHeaderAccess() { memset(&_pod, 0, sizeof(_pod)); }
  virtual podbytes_t podbytes() const override { return pod2bytes(_pod); }
  virtual void read(const std::shared_ptr<FileADT>& file, std::int64_t offset);
  virtual void byteswap();
  virtual void dump(std::ostream& out, const std::string& prefix = "") override;
public:
  virtual std::array<std::uint8_t,4> magic() const override { return ptr_to_array<std::uint8_t,4>(_pod._magic); }
  virtual std::uint32_t version() const override { return _pod._version; }
};

void
FileHeaderAccess::read(const std::shared_ptr<FileADT>& file, std::int64_t offset)
{
  file->xx_read(&this->_pod, offset, sizeof(this->_pod));
  byteswap();
}

void
FileHeaderAccess::byteswap()
{
  // byteswap not needed for std::uint8_t _magic[4] because of its type.
  byteswapT(&_pod._version);
}

void
FileHeaderAccess::dump(std::ostream& out, const std::string& prefix)
{
  out
    << prefix << "magic():            " << array_to_string<std::uint8_t,4>(magic()) << "\n"
    << prefix << "version():          " << version() << "\n"
  ;
}

std::shared_ptr<IFileHeaderAccess>
HeaderAccessFactory::createFileHeader()
{
  return std::shared_ptr<IFileHeaderAccess>(new FileHeaderAccess());
}

/////////////////////////////////////////////////////////////////////////////
//    OffsetHeader   ////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

379
// Thread safety: None. The user of this type is responsible for that.
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
class OffsetHeaderAccess : public IOffsetHeaderAccess
{
  virtual void dump(std::ostream& out, const std::string& prefix = "") override;
};

void
OffsetHeaderAccess::dump(std::ostream& out, const std::string& prefix)
{
  out
    << std::hex
    << prefix << "infoff():           " << infoff() << "\n"
    << prefix << "stroff():           " << "N/A\n"
    << prefix << "alphalupoff():      " << alphalupoff() << "\n"
    << prefix << "bricklupoff():      " << bricklupoff() << "\n"
    << prefix << "histoff():          " << histoff() << "\n"
    << std::dec
  ;
}

// Python layout is: <8I
// Size in storage: 32 bytes
#pragma pack(1)
402
// Thread safety: None. The user of this type is responsible for that.
403
404
405
406
407
408
409
410
411
412
413
414
/** \brief Physical layout of Offset Header version 1. */
class OffsetHeaderV1POD
{
public:
  std::int64_t      _infoff;        // InfoHeader position in file.
  //std::int64_t      _stroff;        // String table position, N/A in V1 and pulled from InfoHeader in V2.
  std::int64_t      _alphalupoff;   // Alpha tile lookup table position in file.
  std::int64_t      _bricklupoff;   // Brick data lookup table position in file.
  std::int64_t      _histoff;       // Histogram position in file.
};
#pragma pack()

415
// Thread safety: None. The user of this type is responsible for that.
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
class OffsetHeaderV1Access : public OffsetHeaderAccess
{
public:
  OffsetHeaderV1POD _pod;
  OffsetHeaderV1Access() { memset(&_pod, 0, sizeof(_pod)); }
  virtual podbytes_t podbytes() const override { return pod2bytes(_pod); }
  // Set by calculate().
  std::int64_t _derived_infsize;
  std::int64_t _derived_histsize;
  std::int64_t _derived_alphalupsize;
  std::int64_t _derived_bricklupsize;

  virtual void read(const std::shared_ptr<FileADT>& file, std::int64_t offset) override;
  virtual void byteswap() override;
  virtual void calculate(const std::shared_ptr<IInfoHeaderAccess>& ih) override;
public:
  virtual std::int64_t      infoff()      const override { return align(_pod._infoff); }
  virtual std::int64_t      stroff()      const override { return 0; }
  virtual std::int64_t      alphalupoff() const override { return align(_pod._alphalupoff); }
  virtual std::int64_t      bricklupoff() const override { return align(_pod._bricklupoff); }
  virtual std::int64_t      histoff()     const override { return align(_pod._histoff); }
  virtual std::int64_t      infsize()      const override { return _derived_infsize; }
  virtual std::int64_t      histsize()     const override { return _derived_histsize; }
  virtual std::int64_t      alphalupsize() const override { return _derived_alphalupsize; }
  virtual std::int64_t      bricklupsize() const override { return _derived_bricklupsize; }
};

void
OffsetHeaderV1Access::read(const std::shared_ptr<FileADT>& file, std::int64_t offset)
{
  // In V1, V2, V3 all the headers are fixed size, so we might as well check.
  if (sizeof(this->_pod) != 32)
    throw OpenZGY::Errors::ZgyFormatError("Wrong OffsetHeaderV1 size, expected 32 bytes");
  file->xx_read(&this->_pod, offset, sizeof(this->_pod));
  byteswap();
}

void
OffsetHeaderV1Access::byteswap()
{
  byteswapV1Long(&_pod._infoff);
  // byteswap not needed for std::int64_t _stroff because it is not stored.
  byteswapV1Long(&_pod._alphalupoff);
  byteswapV1Long(&_pod._bricklupoff);
  byteswapV1Long(&_pod._histoff);
}

// Python layout is: <B
// Size in storage: 1 bytes
#pragma pack(1)
466
// Thread safety: None. The user of this type is responsible for that.
467
/** \brief Physical layout of Offser Header version 2 and 3 and 4 (empty). */
468
469
470
471
472
473
474
475
476
477
478
class OffsetHeaderV2POD
{
public:
  //std::int64_t      _infoff;        // InfoHeader position in file.
  //std::int64_t      _stroff;        // String table position, N/A in V1 and pulled from InfoHeader in V2.
  //std::int64_t      _alphalupoff;   // Alpha tile lookup table position in file.
  //std::int64_t      _bricklupoff;   // Brick data lookup table position in file.
  //std::int64_t      _histoff;       // Histogram position in file.
};
#pragma pack()

479
// Thread safety: None. The user of this type is responsible for that.
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
class OffsetHeaderV2Access : public OffsetHeaderAccess
{
public:
  // Set by calculate().
  std::int64_t _derived_infoff;        // InfoHeader position in file.
  std::int64_t _derived_stroff;        // String table position, N/A in V1 and pulled from InfoHeader in V2.
  std::int64_t _derived_alphalupoff;   // Alpha tile lookup table position in file.
  std::int64_t _derived_bricklupoff;   // Brick data lookup table position in file.
  std::int64_t _derived_histoff;       // Histogram position in file.
  std::int64_t _derived_infsize;
  std::int64_t _derived_strsize;
  std::int64_t _derived_histsize;
  std::int64_t _derived_alphalupsize;
  std::int64_t _derived_bricklupsize;
public:
  // There is no _pod, and read() and byteswap() do nothing
  // Because V2 and V3 have no offset header on the file.
  // The data members declared here are computed from other headers.
  // For historical reasons the header still occupies one byte on file.
  virtual podbytes_t podbytes() const override { return podbytes_t(1); }
  virtual void read(const std::shared_ptr<FileADT>& file, std::int64_t offset) override;
  virtual void byteswap() override;
  virtual void calculate(const std::shared_ptr<IInfoHeaderAccess>& ih) override;
  virtual std::int64_t      infoff()      const override { return sizeof(FileHeaderPOD)+1; }
  virtual std::int64_t      stroff()      const override { return align(_derived_stroff); }
  virtual std::int64_t      alphalupoff() const override { return align(_derived_alphalupoff); }
  virtual std::int64_t      bricklupoff() const override { return align(_derived_bricklupoff); }
  virtual std::int64_t      histoff()     const override { return align(_derived_histoff); }
  virtual std::int64_t      infsize()      const override { return align(_derived_infsize); }
  virtual std::int64_t      histsize()     const override { return align(_derived_histsize); }
  virtual std::int64_t      alphalupsize() const override { return align(_derived_alphalupsize); }
  virtual std::int64_t      bricklupsize() const override { return align(_derived_bricklupsize); }
};

void
OffsetHeaderV2Access::read(const std::shared_ptr<FileADT>&, std::int64_t)
{
  // Not stored on file in V1 and V2.
}

void
OffsetHeaderV2Access::byteswap()
{
  // Not stored on file in V1 and V2.
}

std::shared_ptr<IOffsetHeaderAccess>
HeaderAccessFactory::createOffsetHeader(std::uint32_t version)
{
  switch (version) {
  case 1: return std::shared_ptr<IOffsetHeaderAccess>(new OffsetHeaderV1Access());
531
532
  case 2:
  case 3:
533
  case 4: return std::shared_ptr<IOffsetHeaderAccess>(new OffsetHeaderV2Access());
534
535
536
537
538
539
540
541
  default: throw OpenZGY::Errors::ZgyFormatError("Unsupported ZGY version");
  }
}

/////////////////////////////////////////////////////////////////////////////
//    InfoHeader   //////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

542
// Thread safety: None. The user of this type is responsible for that.
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
class InfoHeaderAccess : public IInfoHeaderAccess
{
  virtual void dump(std::ostream& out, const std::string& prefix = "") override;
  // Derived information computed from other virtual members, which means
  // they can be implemented in the base class. Note that all of these
  // could be cached if desired. They cannot change after a file is created.
  virtual std::int64_t bytesperalpha() const override;
  virtual std::int64_t bytesperbrick() const override;
  virtual std::int64_t bytespersample() const override;
  virtual std::array<double,2> storagetofloat() const override;
  virtual double storagetofloat_slope() const override;
  virtual double storagetofloat_intercept() const override;
  virtual double defaultstorage() const override;
  virtual double defaultvalue() const override;
};

void
InfoHeaderAccess::dump(std::ostream& out, const std::string& prefix)
{
  out
    << prefix << "bricksize():        " << array_to_string(bricksize()) << "\n"
    << prefix << "datatype():         " << (int)datatype() << "\n"
565
    << prefix << "safe_codingrange(): " << array_to_string(safe_codingrange()) << "\n"
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
    << prefix << "dataid():           " << array_to_hex(dataid()) << "\n"
    << prefix << "verid():            " << array_to_hex(verid()) << "\n"
    << prefix << "previd():           " << array_to_hex(previd()) << "\n"
    << prefix << "srcname():          " << srcname() << "\n"
    << prefix << "srcdesc():          " << srcdesc() << "\n"
    //<< prefix << "srctype():          " << srctype() << "\n"
    << prefix << "orig():             " << array_to_string(orig()) << "\n"
    << prefix << "inc():              " << array_to_string(inc()) << "\n"
    << prefix << "size():             " << array_to_string(size()) << "\n"
    //<< prefix << "curorig():          " << array_to_string(curorig()) << "\n"
    //<< prefix << "cursize():          " << array_to_string(cursize()) << "\n"
    << prefix << "scnt():             " << scnt() << "\n"
    << prefix << "ssum():             " << ssum() << "\n"
    << prefix << "sssq():             " << sssq() << "\n"
    << prefix << "smin():             " << smin() << "\n"
    << prefix << "smax():             " << smax() << "\n"
    //<< prefix << "srvorig():          " << array_to_string(srvorig()) << "\n"
    //<< prefix << "srvsize():          " << array_to_string(srvsize()) << "\n"
    //<< prefix << "gdef():             " << gdef() << "\n"
    //<< prefix << "gazim():            " << array_to_string(gazim()) << "\n"
    //<< prefix << "gbinsz():           " << array_to_string(gbinsz()) << "\n"
    << prefix << "gpiline():          " << array_to_string(gpiline()) << "\n"
    << prefix << "gpxline():          " << array_to_string(gpxline()) << "\n"
    << prefix << "gpx():              " << array_to_string(gpx()) << "\n"
    << prefix << "gpy():              " << array_to_string(gpy()) << "\n"
    << prefix << "hprjsys():          " << hprjsys() << "\n"
    << prefix << "hdim():             " << (int)hdim() << "\n"
    << prefix << "hunitfactor():      " << hunitfactor() << "\n"
    << prefix << "hunitname():        " << hunitname() << "\n"
    << prefix << "vdim():             " << (int)vdim() << "\n"
    << prefix << "vunitfactor():      " << vunitfactor() << "\n"
    << prefix << "vunitname():        " << vunitname() << "\n"
    << prefix << "slbufsize():        " << slbufsize() << "\n"
    << prefix << "nlods():            " << nlods() << "\n"
    << prefix << "bytesperalpha():    " << bytesperalpha() << "\n"
    << prefix << "bytesperbrick():    " << bytesperbrick() << "\n"
    << prefix << "bytespersample():   " << bytespersample() << "\n"
    << prefix << "storagetofloat():   " << array_to_string(storagetofloat()) << "\n"
    << prefix << "defaultstorage():   " << defaultstorage() << "\n"
    << prefix << "defaultvalue():     " << defaultvalue() << "\n"
    ;
  //out << prefix << "ocp_world():        " << "(derived)\n";
  out << prefix << "lodsizes():       ";
  for (const auto& it : lodsizes())
    out << "  " << array_to_string(it);
  out << "\n";
  out << prefix << "alphaoffsets():   " << std::hex;
  for (const auto& it : alphaoffsets())
    out << "  " << std::setw(4) << it;
  out << std::dec << "\n";
  out << prefix << "brickoffsets():   " << std::hex;
  for (const auto& it : brickoffsets())
    out << "  " << std::setw(4) << it;
  out << std::dec << "\n";
}

std::int64_t
InfoHeaderAccess::bytesperalpha() const
{
  std::array<std::int64_t,3> bs = this->bricksize();
  return bs[0] * bs[1] * sizeof(std::uint8_t);
}

std::int64_t
InfoHeaderAccess::bytesperbrick() const
{
  std::array<std::int64_t,3> bs = this->bricksize();
  return bs[0] * bs[1] * bs[2] * this->bytespersample();
}

std::int64_t
InfoHeaderAccess::bytespersample() const
{
  return (std::int64_t)RawDataTypeDetails(this->datatype()).size;
}

/**
 * Get the linear transform y = a*x + b for converting from
 * storage values to actual floating point values.
 * The smallest value in storage (e.g. -128) should map to codingrange[0]
 * and the largest value (e.g. +127) should map to codingrange[1].
 * If file_dtype is a floating point type there is never any conversion.
Paal Kvamme's avatar
Paal Kvamme committed
648
649
650
651
652
653
654
655
656
657
658
659
 *
 * Note that the codingrange stored in file is two float32 numbers i.e.
 * 24 bits mantissa. This is ok for our purpose because it is only used
 * for converting int8 and int16 data so the user doesn't expect anything
 * more than 16 bits of precision. But to avoid numerical issues with
 * the transform the codingrange should immediately be upcast to float64
 * befor being used. Case in point: (double)(hi-lo) is less accurate than
 * ((double)hi - (double)lo) when hi,lo are float32. Admittedly only by
 * 1 ULPS but the error might be amplified later. Enough to raise errors
 * when testing even if the signal still has 16 bits of precision intact.
 *
 * See also ZgyInternalBulk._scaleToFloat in OpenZGY/Python.
660
661
662
663
664
665
666
 */
std::array<double,2>
InfoHeaderAccess::storagetofloat() const
{
  double slope = 1.0, intercept = 0.0;
  const RawDataTypeDetails info(this->datatype());
  if (info.is_integer) {
667
    const std::array<float,2> range = this->safe_codingrange();
Paal Kvamme's avatar
Paal Kvamme committed
668
    slope = ((double)range[1]-(double)range[0]) / (info.highest - info.lowest);
669
    intercept = range[0] - slope * info.lowest;
Paal Kvamme's avatar
Paal Kvamme committed
670
671
672
673
674
675
676
677
678
679
680
681
682
683
#if 0 // Debug issues with numerical inaccuracy.
    static double debug_old_slope = 0, debug_old_intercept = 0;
    if (debug_old_slope != slope || debug_old_intercept != intercept) {
      std::cerr << "@@ storagetofloat " << std::setprecision(14)
                << " slope " << slope
                << " intercept " << intercept
                << " range " << range[0] << " " << range[1]
                << " test " << slope*-32768+intercept
                << " " << slope*32767+intercept
                << std::endl;
      debug_old_slope = slope;
      debug_old_intercept = intercept;
    }
#endif
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
  }
  return std::array<double,2>{slope,intercept};
}

double
InfoHeaderAccess::storagetofloat_slope() const
{
  return storagetofloat()[0];
}

double
InfoHeaderAccess::storagetofloat_intercept() const
{
  return storagetofloat()[1];
}

/**
 * Return the storage value to be used for data that has never been written.
 * This is supposed to be the storage value that, when converted to float,
 * is as close to zero as possible. I.e. it is simply zero converted
 * from float to storage values.
 *
 * Implementation note: Should have called RoundAndClip<T>() here,
 * but that method is templated which makes it awkward to use here.
 * Try duplicating the logic in RoundAndClip as close as possible.
 */
double InfoHeaderAccess::defaultstorage() const
{
  const RawDataTypeDetails info(this->datatype());
  double value = (0 - storagetofloat_intercept()) / storagetofloat_slope();
  if (!info.is_integer)
    return 0;
  else if (value <= info.lowest)
    return info.lowest;
  else if (value >= info.highest)
    return info.highest;
  else if (value < 0)
    return static_cast<double>(static_cast<std::int64_t>(value - 0.5));
  else
    return static_cast<double>(static_cast<std::int64_t>(value + 0.5));
}

/**
 * Return the float value to be used for data that has never been written.
 * This is supposed to be zero afyer a float/storage/float round trip.
 */
double InfoHeaderAccess::defaultvalue() const
{
  return defaultstorage() * storagetofloat_slope() + storagetofloat_intercept();
}

// Python layout is: <3i 3i 3i 3f 4i 4i 4d 4d B B
// Size in storage: 146 bytes
#pragma pack(1)
738
// Thread safety: None. The user of this type is responsible for that.
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
/** \brief Physical layout of Info Header version 1. */
class InfoHeaderV1POD
{
public:
  std::int32_t      _size[3];       // Integer size in inline, crossline, vertical directions.
  std::int32_t      _orig[3];       // First inline, crossline, time/depth. Only integral values allowed.
  std::int32_t      _inc[3];        // Integer increment in inline, crossline, vertical directions.
  float             _incfactor[3];  // Unused. Write as (1,1,1), ignore on read.
  std::int32_t      _gpiline[4];    // Inline component of 4 control points.
  std::int32_t      _gpxline[4];    // Crossline component of 4 control points.
  double            _gpx[4];        // X coordinate of 4 control points.
  double            _gpy[4];        // Y coordinate of 4 control points.
  std::uint8_t      _datatype;      // Type of samples in each brick: int8 = 0, int16 = 2, float32 = 6.
  std::uint8_t      _coordtype;     // Coordinate type: unknown = 0, meters = 1, feet = 2, degrees*3600 = 3, degrees = 4, DMS = 5.
  // Note, _datatype and _coordtype are V1 specific and need special handling in accessor.
};
#pragma pack()

757
// Thread safety: None. The user of this type is responsible for that.
758
759
760
761
class InfoHeaderV1Access : public InfoHeaderAccess
{
public:
  InfoHeaderV1POD _pod;
762
763
764
765
  float _cached_file_sample_min;
  float _cached_file_sample_max;
  float _cached_safe_codingrange_min;
  float _cached_safe_codingrange_max;
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
  std::int32_t _cached_nlods;
  std::vector<std::array<std::int64_t,3>> _cached_lodsizes;
  std::vector<std::int64_t> _cached_alphaoffsets;
  std::vector<std::int64_t> _cached_brickoffsets;
  std::array<std::array<double,2>,4> _cached_index;
  std::array<std::array<double,2>,4> _cached_annot;
  std::array<std::array<double,2>,4> _cached_world;
  InfoHeaderV1Access() { memset(&_pod, 0, sizeof(_pod)); }
  virtual podbytes_t podbytes() const override { return pod2bytes(_pod); }
  virtual void       read(const std::shared_ptr<FileADT>& file, std::int64_t offset, std::int64_t size) override;
  virtual void       byteswap() override;
  virtual void       calculate_cache() override;
  virtual void       calculate_read(const podbytes_t& slbuf, const std::shared_ptr<IHistHeaderAccess>& hh) override;
  virtual podbytes_t calculate_write() override;
public:
  virtual std::array<std::int64_t,3> size() const override { return array_cast<std::int64_t,std::int32_t,3>(ptr_to_array<std::int32_t,3>(_pod._size)); }
  virtual std::array<float,3>        orig() const override { return array_cast<float,std::int32_t,3>(ptr_to_array<std::int32_t,3>(_pod._orig)); }
  virtual std::array<float,3>        inc()  const override { return array_cast<float,std::int32_t,3>(ptr_to_array<std::int32_t,3>(_pod._inc)); }
  //unused: virtual std::array<float,3>        incfactor() const override { return ptr_to_array<float,3>(_pod._incfactor); }
  virtual std::array<float,4>        gpiline() const override { return array_cast<float,std::int32_t,4>(ptr_to_array<std::int32_t,4>(_pod._gpiline)); }
  virtual std::array<float,4>        gpxline() const override { return array_cast<float,std::int32_t,4>(ptr_to_array<std::int32_t,4>(_pod._gpxline)); }
  virtual std::array<double,4>       gpx() const override { return ptr_to_array<double,4>(_pod._gpx); }
  virtual std::array<double,4>       gpy() const override { return ptr_to_array<double,4>(_pod._gpy); }
  virtual RawDataType                datatype() const override { return DecodeDataType(_pod._datatype); }
  //special: virtual std::uint8_t               coordtype() const override { return _pod._coordtype; }
  virtual std::array<std::int64_t,3> bricksize() const override { return std::array<std::int64_t,3>{64, 64, 64}; }
792
793
  virtual std::array<float,2>        safe_codingrange() const override { return std::array<float,2>{_cached_safe_codingrange_min, _cached_safe_codingrange_max}; }
  virtual std::array<float,2>        raw_codingrange() const override { return std::array<float,2>{_cached_file_sample_min, _cached_file_sample_max}; }
794
795
796
797
798
799
800
801
802
803
804
  virtual std::array<std::uint8_t,16>dataid() const override { return std::array<std::uint8_t,16>{0}; }
  virtual std::array<std::uint8_t,16>verid() const override { return std::array<std::uint8_t,16>{0}; }
  virtual std::array<std::uint8_t,16>previd() const override { return std::array<std::uint8_t,16>{0}; }
  virtual std::string                srcname() const override { return std::string(); }
  virtual std::string                srcdesc() const override { return std::string(); }
  virtual RawDataType                srctype() const override { return datatype(); }
  //unused: virtual std::array<std::int32_t,3> curorig() const override { throw OpenZGY::Errors::ZgyInternalError("Not implemented"); }
  //unused: virtual std::array<std::int32_t,3> cursize() const override { throw OpenZGY::Errors::ZgyInternalError("Not implemented"); }
  virtual std::int64_t               scnt() const override { return 0; }
  virtual double                     ssum() const override { return 0; }
  virtual double                     sssq() const override { return 0; }
805
806
  virtual float                      smin() const override { return align(_cached_file_sample_min); }
  virtual float                      smax() const override { return align(_cached_file_sample_max); }
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
  //unused: virtual std::array<float,3>        srvorig() const override { throw OpenZGY::Errors::ZgyInternalError("Not implemented"); }
  //unused: virtual std::array<float,3>        srvsize() const override { throw OpenZGY::Errors::ZgyInternalError("Not implemented"); }
  //unused: virtual std::uint8_t               gdef() const override { throw OpenZGY::Errors::ZgyInternalError("Not implemented"); }
  //unused: virtual std::array<double,2>       gazim() const override { throw OpenZGY::Errors::ZgyInternalError("Not implemented"); }
  //unused: virtual std::array<double,2>       gbinsz() const override { throw OpenZGY::Errors::ZgyInternalError("Not implemented"); }
  virtual std::string                hprjsys() const override { return std::string(); }
  virtual RawHorizontalDimension     hdim() const override {
    switch (DecodeCoordType(_pod._coordtype)) {
    default:
    case RawCoordType::Unknown:      return RawHorizontalDimension::Unknown;
    case RawCoordType::Meters:       return RawHorizontalDimension::Length;
    case RawCoordType::Feet:         return RawHorizontalDimension::Length;
    case RawCoordType::ArcSec:       return RawHorizontalDimension::ArcAngle;
    case RawCoordType::ArcDeg:       return RawHorizontalDimension::ArcAngle;
    //case RawCoordType::ArcDegMinSec: return RawHorizontalDimension::ArcAngle;
    // ArcDegMinSec is unsupported. Does not work in the old code either.
    }
  }
  virtual double                     hunitfactor() const override {
    switch (DecodeCoordType(_pod._coordtype)) {
    default:
    case RawCoordType::Unknown:      return 1.0;
    case RawCoordType::Meters:       return 1.0;
    case RawCoordType::Feet:         return 0.3048;
    case RawCoordType::ArcSec:       return 3600.0;
    case RawCoordType::ArcDeg:       return 1.0;
    case RawCoordType::ArcDegMinSec: return 1.0;
    }
  }
  virtual std::string                hunitname() const override {
    switch (DecodeCoordType(_pod._coordtype)) {
    default:
    case RawCoordType::Unknown:      return "";
    case RawCoordType::Meters:       return "m";
    case RawCoordType::Feet:         return "ft";
    case RawCoordType::ArcSec:       return "arcsec";
    case RawCoordType::ArcDeg:       return "deg";
    case RawCoordType::ArcDegMinSec: return "DMS";
    }
  }
  // V1 did not store the vertical unit at all.
  virtual RawVerticalDimension       vdim() const override { return RawVerticalDimension::Unknown; }
  virtual double                     vunitfactor() const override { return 1.0; }
  virtual std::string                vunitname() const override { return std::string(); }
  virtual std::uint32_t              slbufsize() const override { return 0; }
  virtual const std::array<std::array<double,2>,4>& ocp_index() const override { return _cached_index; }
  virtual const std::array<std::array<double,2>,4>& ocp_annot() const override { return _cached_annot; }
  virtual const std::array<std::array<double,2>,4>& ocp_world() const override { return _cached_world; }
  virtual const std::vector<std::array<std::int64_t,3>>& lodsizes() const override { return _cached_lodsizes; }
  virtual std::int32_t nlods() const override { return _cached_nlods; }
  virtual const std::vector<std::int64_t>& alphaoffsets() const override { return _cached_alphaoffsets; }
  virtual const std::vector<std::int64_t>& brickoffsets() const override { return _cached_brickoffsets; }
  // Write support.
  virtual void setstats(std::int64_t scnt, double ssum, double sssq,
                        float smin, float smax)
  {
    throw OpenZGY::Errors::ZgyInternalError("Writing InfoHeader is only supported for the latest version.");
  }
};

void
InfoHeaderV1Access::read(const std::shared_ptr<FileADT>& file, std::int64_t offset, std::int64_t size)
{
  // In V1, V2, V3 all the headers are fixed size, so we might as well check.
  if (size != 146 || size != sizeof(this->_pod))
    throw OpenZGY::Errors::ZgyFormatError("Wrong InfoHeaderV1 size, expected 146 bytes");
  memset(&this->_pod, 0, sizeof(this->_pod));
  file->xx_read(&this->_pod, offset, std::min(sizeof(this->_pod), (size_t)size));
  byteswap();
}

void
InfoHeaderV1Access::byteswap()
{
  byteswapT(&_pod._size[0], 3);
  byteswapT(&_pod._orig[0], 3);
  byteswapT(&_pod._inc[0], 3);
  byteswapT(&_pod._incfactor[0], 3);
  byteswapT(&_pod._gpiline[0], 4);
  byteswapT(&_pod._gpxline[0], 4);
  byteswapT(&_pod._gpx[0], 4);
  byteswapT(&_pod._gpy[0], 4);
}

// Python layout is: <3i B 2f 16s 16s 16s   B 3f 3f 3i 3i 3i q d d f f 3f 3f B 2d 2d 4f 4f 4d 4d  B d  B d  I
// Size in storage: 337 bytes
#pragma pack(1)
894
// Thread safety: None. The user of this type is responsible for that.
895
/** \brief Physical layout of Info Header version 2 and 3 and 4. */
896
897
898
899
900
class InfoHeaderV2POD
{
public:
  std::int32_t      _bricksize[3];  // Brick size. Values other than (64,64,64) will likely not work.
  std::uint8_t      _datatype;      // Type of samples in each brick: int8 = 0, int16 = 2, float32 = 6.
901
  float             _file_codingrange[2]; // If datatype is integral, this is the value range samples will be scaled to when read as float. In this case it must be specified on file creation. If datatype is float then this is the value range of the data and should be set automatically when writing the file.
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
  std::uint8_t      _dataid[16];    // GUID set on file creation.
  std::uint8_t      _verid[16];     // GUID set each time the file is changed.
  std::uint8_t      _previd[16];    // GUID before last change.
  //char*             _srcname;       // Optional name of this data set. Rarely used.
  //char*             _srcdesc;       // Optional description of this data set. Rarely used.
  std::uint8_t      _srctype;       // Optional datatype the samples had before being stored in this file.
  float             _orig[3];       // First inline, crossline, time/depth. Unlike v1 these are now floating point.
  float             _inc[3];        // Increment in inline, crossline, vertical directions.
  std::int32_t      _size[3];       // Size in inline, crossline, vertical directions.
  std::int32_t      _curorig[3];    // Unused. Set to (0,0,0) on write and ignore on read.
  std::int32_t      _cursize[3];    // Unused. Set to size on write and ignore on read.
  std::int64_t      _scnt;          // Count of values used to compute statistics.
  double            _ssum;          // Sum of all "scnt" values.
  double            _sssq;          // Sum of squared "scnt" values.
  float             _smin;          // Statistical (computed) minimum value.
  float             _smax;          // Statistical (computed) maximum value.
  float             _srvorig[3];    // Unused. Set equal to orig on write. Ignore on read.
  float             _srvsize[3];    // Unused. Set to inc*size on write. Ignore on read.
  std::uint8_t      _gdef;          // Grid definition type. Set to 3 (enum: "FourPoint") on write. Ignored on read. See notes for a longer explanation.
  double            _gazim[2];      // Unused.
  double            _gbinsz[2];     // Unused.
  float             _gpiline[4];    // Inline component of 4 control points.
  float             _gpxline[4];    // Crossline component of 4 control points.
  double            _gpx[4];        // X coordinate of 4 control points.
  double            _gpy[4];        // Y coordinate of 4 control points.
  //char*             _hprjsys;       // Free form description of the projection coordinate system. Usually not parseable into a well known CRS.
  std::uint8_t      _hdim;          // Horizontal dimension. Unknown = 0, Length = 1, ArcAngle = 2. Few applications support ArcAngle.
  double            _hunitfactor;   // Multiply by this factor to convert from storage units to SI units. Applies to gpx, gpy.
  //char*             _hunitname;     // For annotation only. Use hunitfactor, not the name, to convert to or from SI.
  std::uint8_t      _vdim;          // Vertical dimension. Unknown = 0, Depth = 1, SeismicTWT = 1, SeismicOWT = 3.
  double            _vunitfactor;   // Multiply by this factor to convert from storage units to SI units. Applies to orig[2], inc[2].
  //char*             _vunitname;     // For annotation only. Use vunitfactor, not the name, to convert to or from SI.
  std::uint32_t     _slbufsize;     // Size of the StringList section.
  //enum              _ocp_world;     // Ordered corner points: ((i0,j0),(iN,j0),(i0,jM),(iN,jM)
  //std::int32_t      _lodsizes[lod]; // Size of the survey at reduced level of detail.
  //std::int32_t      _nlods;         // How many levels of details. 1 means only full resolution. Currently nlods will always be just enough to make the highest LOD (i.e. lowest resolution) fit in a single brick.
  //std::int64_t      _brickoffsets[lod]; // How many entries in the lookup table to skip when dealing with level N.
  //std::int64_t      _alphaoffsets[lod]; // How many entries in the lookup table to skip when dealing with level N.
};
#pragma pack()

943
// Thread safety: None. The user of this type is responsible for that.
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
class InfoHeaderV2Access : public InfoHeaderAccess
{
public:
  InfoHeaderV2POD _pod;
  std::string _srcname;
  std::string _srcdesc;
  std::string _hprjsys;
  std::string _hunitname;
  std::string _vunitname;
  std::int32_t _cached_nlods;
  std::vector<std::array<std::int64_t,3>> _cached_lodsizes;
  std::vector<std::int64_t> _cached_alphaoffsets;
  std::vector<std::int64_t> _cached_brickoffsets;
  std::array<std::array<double,2>,4> _cached_index;
  std::array<std::array<double,2>,4> _cached_annot;
  std::array<std::array<double,2>,4> _cached_world;
960
  float _cached_safe_codingrange[2];
961
962
963
964
965
966
967
968
969
970
971

  InfoHeaderV2Access() { memset(&_pod, 0, sizeof(_pod)); }
  virtual podbytes_t podbytes() const override { return pod2bytes(_pod); }
  virtual void       read(const std::shared_ptr<FileADT>& file, std::int64_t offset, std::int64_t size) override;
  virtual void       byteswap() override;
  virtual void       calculate_read(const podbytes_t& slbuf, const std::shared_ptr<IHistHeaderAccess>& hh) override;
  virtual void       calculate_cache() override;
  virtual podbytes_t calculate_write() override;
public:
  virtual std::array<std::int64_t,3> bricksize() const override { return array_cast<std::int64_t,std::int32_t,3>(ptr_to_array<std::int32_t,3>(_pod._bricksize)); }
  virtual RawDataType                datatype() const override { return DecodeDataType(_pod._datatype); }
972
973
  virtual std::array<float,2>        safe_codingrange() const override { return ptr_to_array<float,2>(_cached_safe_codingrange); }
  virtual std::array<float,2>        raw_codingrange() const override { return ptr_to_array<float,2>(_pod._file_codingrange); }
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
  virtual std::array<std::uint8_t,16>dataid() const override { return ptr_to_array<std::uint8_t,16>(_pod._dataid); }
  virtual std::array<std::uint8_t,16>verid() const override { return ptr_to_array<std::uint8_t,16>(_pod._verid); }
  virtual std::array<std::uint8_t,16>previd() const override { return ptr_to_array<std::uint8_t,16>(_pod._previd); }
  virtual std::string                srcname() const override { return _srcname; }
  virtual std::string                srcdesc() const override { return _srcdesc; }
  virtual RawDataType                srctype() const override { return DecodeDataType(_pod._srctype); }
  virtual std::array<float,3>        orig() const override { return ptr_to_array<float,3>(_pod._orig); }
  virtual std::array<float,3>        inc()  const override { return ptr_to_array<float,3>(_pod._inc); }
  virtual std::array<std::int64_t,3> size() const override { return array_cast<std::int64_t,std::int32_t,3>(ptr_to_array<std::int32_t,3>(_pod._size)); }
  //unused: virtual std::array<std::int32_t,3> curorig() const override { return ptr_to_array<std::int32_t,3>(_pod._curorig); }
  //unused: virtual std::array<std::int32_t,3> cursize() const override { return ptr_to_array<std::int32_t,3>(_pod._cursize); }
  virtual std::int64_t               scnt() const override { return align(_pod._scnt); }
  virtual double                     ssum() const override { return align(_pod._ssum); }
  virtual double                     sssq() const override { return align(_pod._sssq); }
  virtual float                      smin() const override { return align(_pod._smin); }
  virtual float                      smax() const override { return align(_pod._smax); }
  //unused: virtual std::array<float,3> srvorig() const override { return ptr_to_array<float,3>(_pod._srvorig); }
  //unused: virtual std::array<float,3> srvsize() const override { return ptr_to_array<float,3>(_pod._srvsize); }
  //unused: virtual std::uint8_t gdef() const override { return _pod._gdef; }
  //unused: virtual std::array<double,2> gazim() const override { return ptr_to_array<double,2>(_pod._gazim); }
  //unused: virtual std::array<double,2> gbinsz() const override { return ptr_to_array<double,2>(_pod._gbinsz); }
  virtual std::array<float,4>        gpiline() const override { return ptr_to_array<float,4>(_pod._gpiline); }
  virtual std::array<float,4>        gpxline() const override { return ptr_to_array<float,4>(_pod._gpxline); }
  virtual std::array<double,4>       gpx() const override { return ptr_to_array<double,4>(_pod._gpx); }
  virtual std::array<double,4>       gpy() const override { return ptr_to_array<double,4>(_pod._gpy); }
  virtual std::string                hprjsys() const override { return _hprjsys; }
  virtual RawHorizontalDimension     hdim() const override { return DecodeHorizontalDimension(_pod._hdim); }
For faster browsing, not all history is shown. View entire blame