• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lutraconsulting / MDAL / 4292411804

pending completion
4292411804

Pull #446

github

GitHub
Merge 2934e06c8 into 06c59ac6b
Pull Request #446: Some backports

100 of 100 new or added lines in 3 files covered. (100.0%)

8925 of 9966 relevant lines covered (89.55%)

75594.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.98
/mdal/frmts/mdal_binary_dat.cpp
1
/*
2
 MDAL - Mesh Data Abstraction Library (MIT License)
3
 Copyright (C) 2018 Lutra Consulting Ltd.
4
*/
5

6
#include <stddef.h>
7
#include <iosfwd>
8
#include <iostream>
9
#include <fstream>
10
#include <sstream>
11
#include <string>
12
#include <vector>
13
#include <map>
14
#include <cassert>
15
#include <memory>
16

17
#include "mdal_binary_dat.hpp"
18
#include "mdal.h"
19
#include "mdal_utils.hpp"
20
#include "mdal_logger.hpp"
21

22
#include <math.h>
23

24
static const int CT_VERSION   = 3000;
25
static const int CT_OBJTYPE   = 100;
26
static const int CT_SFLT      = 110;
27
static const int CT_SFLG      = 120;
28
static const int CT_BEGSCL    = 130;
29
static const int CT_BEGVEC    = 140;
30
static const int CT_VECTYPE   = 150;
31
static const int CT_OBJID     = 160;
32
static const int CT_NUMDATA   = 170;
33
static const int CT_NUMCELLS  = 180;
34
static const int CT_NAME      = 190;
35
static const int CT_TS        = 200;
36
static const int CT_ENDDS     = 210;
37
static const int CT_RT_JULIAN = 240;
38
static const int CT_TIMEUNITS = 250;
39
static const int CT_2D_MESHES = 3;
40
static const int CT_FLOAT_SIZE = 4;
41
static const int CF_FLAG_SIZE = 1;
42
static const int CF_FLAG_INT_SIZE = 4;
43

44

45
static void exit_with_error( MDAL_Status error, const std::string msg )
×
46
{
47
  MDAL::Log::error( error, "BINARY_DAT", msg );
×
48
}
×
49

50
static bool read( std::ifstream &in, char *s, int n )
856,212✔
51
{
52
  in.read( s, n );
856,212✔
53
  if ( !in )
856,212✔
54
    return true; //error
1✔
55
  else
56
    return false; //OK
856,211✔
57
}
58

59
static bool readIStat( std::ifstream &in, int sflg, char *flag )
232,687✔
60
{
61
  if ( sflg == CF_FLAG_SIZE )
232,687✔
62
  {
63
    in.read( flag, sflg );
232,639✔
64
    if ( !in )
232,639✔
65
      return true; // error
×
66
  }
67
  else
68
  {
69
    int istat;
70
    in.read( reinterpret_cast< char * >( &istat ), sflg );
48✔
71
    if ( !in )
48✔
72
      return true; // error
×
73
    else
74
      *flag = ( istat == 1 );
48✔
75
  }
76
  return false;
232,687✔
77
}
78

79
MDAL::DriverBinaryDat::DriverBinaryDat():
33✔
80
  Driver( "BINARY_DAT",
81
          "Binary DAT",
82
          "*.dat",
83
          Capability::ReadDatasets | Capability::WriteDatasetsOnVertices
84
        )
33✔
85
{
86
}
33✔
87

88
MDAL::DriverBinaryDat *MDAL::DriverBinaryDat::create()
6✔
89
{
90
  return new DriverBinaryDat();
6✔
91
}
92

93
MDAL::DriverBinaryDat::~DriverBinaryDat() = default;
39✔
94

95
bool MDAL::DriverBinaryDat::canReadDatasets( const std::string &uri )
23✔
96
{
97
  std::ifstream in = MDAL::openInputFile( uri, std::ifstream::in | std::ifstream::binary );
23✔
98
  int version;
99

100
  if ( read( in, reinterpret_cast< char * >( &version ), 4 ) )
23✔
101
    return false;
×
102

103
  if ( version != CT_VERSION ) // Version should be 3000
23✔
104
    return false;
17✔
105

106
  return true;
6✔
107
}
23✔
108

109
/**
110
 * The DAT format contains "datasets" and each dataset has N-outputs. One output
111
 * represents data for all vertices/faces for one timestep
112
 *
113
 * in TUFLOW results there could be also a special timestep (99999) with maximums
114
 * we will put it into a separate dataset with name suffixed with "/Maximums"
115
 *
116
 * In MDAL we convert one output to one MDAL dataset;
117
 *
118
 */
119
void MDAL::DriverBinaryDat::load( const std::string &datFile, MDAL::Mesh *mesh )
6✔
120
{
121
  mDatFile = datFile;
6✔
122
  MDAL::Log::resetLastStatus();
6✔
123

124
  if ( !MDAL::fileExists( mDatFile ) )
6✔
125
  {
126
    MDAL::Log::error( MDAL_Status::Err_FileNotFound, name(), "File could not be found " + mDatFile );
×
127
    return;
×
128
  }
129

130
  std::ifstream in = MDAL::openInputFile( mDatFile, std::ifstream::in | std::ifstream::binary );
6✔
131

132
  // implementation based on information from:
133
  // http://www.xmswiki.com/wiki/SMS:Binary_Dataset_Files_*.dat
134
  if ( !in ) return exit_with_error( MDAL_Status::Err_FileNotFound, "Couldn't open the file" );
6✔
135

136
  size_t vertexCount = mesh->verticesCount();
6✔
137
  size_t elemCount = mesh->facesCount();
6✔
138

139
  int card = 0;
6✔
140
  int version;
141
  int objecttype;
142
  int sflt;
143
  int sflg = 0;
6✔
144
  int vectype;
145
  int objid;
146
  int numdata;
147
  int numcells;
148
  char groupName[40];
149
  double referenceTime;
150
  int timeUnit = 0;
6✔
151
  std::string timeUnitStr;
6✔
152
  char istat;
153
  float time;
154

155
  if ( read( in, reinterpret_cast< char * >( &version ), 4 ) ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "Unable to read version" );
6✔
156

157
  if ( version != CT_VERSION ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "Invalid version " );
6✔
158

159
  std::shared_ptr<DatasetGroup> group = std::make_shared< DatasetGroup >(
160
                                          name(),
×
161
                                          mesh,
162
                                          mDatFile
6✔
163
                                        ); // DAT datasets
6✔
164
  group->setDataLocation( MDAL_DataLocation::DataOnVertices );
6✔
165

166
  // in TUFLOW results there could be also a special timestep (99999) with maximums
167
  // we will put it into a separate dataset
168
  std::shared_ptr<DatasetGroup> groupMax = std::make_shared< DatasetGroup >(
169
        name(),
×
170
        mesh,
171
        mDatFile
6✔
172
      );
6✔
173
  groupMax->setDataLocation( MDAL_DataLocation::DataOnVertices );
6✔
174

175
  while ( card != CT_ENDDS )
237✔
176
  {
177
    if ( read( in, reinterpret_cast< char * >( &card ), 4 ) )
232✔
178
    {
179
      // We've reached the end of the file and there was no ends card
180
      break;
1✔
181
    }
182

183
    switch ( card )
231✔
184
    {
185
      case CT_OBJTYPE:
6✔
186
        // Object type
187
        if ( read( in, reinterpret_cast< char * >( &objecttype ), 4 ) || objecttype != CT_2D_MESHES ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "Invalid object type" );
6✔
188
        break;
226✔
189

190
      case CT_SFLT:
6✔
191
        // Float size
192
        if ( read( in, reinterpret_cast< char * >( &sflt ), 4 ) || sflt != CT_FLOAT_SIZE ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read float size" );
6✔
193
        break;
6✔
194

195
      case CT_SFLG:
6✔
196
        // Flag size
197
        if ( read( in, reinterpret_cast< char * >( &sflg ), 4 ) )
6✔
198
          if ( sflg != CF_FLAG_SIZE && sflg != CF_FLAG_INT_SIZE )
×
199
            return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read flag size" );
×
200
        break;
6✔
201

202
      case CT_BEGSCL:
4✔
203
        group->setIsScalar( true );
4✔
204
        groupMax->setIsScalar( true );
4✔
205
        break;
4✔
206

207
      case CT_BEGVEC:
2✔
208
        group->setIsScalar( false );
2✔
209
        groupMax->setIsScalar( false );
2✔
210
        break;
2✔
211

212
      case CT_VECTYPE:
1✔
213
        // Vector type
214
        if ( read( in, reinterpret_cast< char * >( &vectype ), 4 ) || vectype != 0 ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read vector type" );
1✔
215
        break;
1✔
216

217
      case CT_OBJID:
3✔
218
        // Object id
219
        if ( read( in, reinterpret_cast< char * >( &objid ), 4 ) ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read object id" );
3✔
220
        break;
3✔
221

222
      case CT_NUMDATA:
6✔
223
        // Num data
224
        if ( read( in, reinterpret_cast< char * >( &numdata ), 4 ) ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read num data" );
6✔
225
        if ( numdata != static_cast< int >( vertexCount ) ) return exit_with_error( MDAL_Status::Err_IncompatibleMesh, "invalid num data" );
6✔
226
        break;
6✔
227

228
      case CT_NUMCELLS:
6✔
229
        // Num data
230
        if ( read( in, reinterpret_cast< char * >( &numcells ), 4 ) ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read num cells" );
6✔
231
        if ( numcells != static_cast< int >( elemCount ) ) return exit_with_error( MDAL_Status::Err_IncompatibleMesh, "invalid num cells" );
6✔
232
        break;
6✔
233

234
      case CT_NAME:
6✔
235
        // Name
236
        if ( read( in, reinterpret_cast< char * >( &groupName ), 40 ) ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read name" );
6✔
237
        if ( groupName[39] != 0 )
6✔
238
          groupName[39] = 0;
4✔
239
        group->setName( trim( std::string( groupName ) ) );
6✔
240
        groupMax->setName( group->name() + "/Maximums" );
6✔
241
        break;
6✔
242

243
      case CT_RT_JULIAN:
×
244
        // Reference time
245
        if ( readIStat( in, sflg, &istat ) )
×
246
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read reference time" );
×
247

248
        if ( read( in, reinterpret_cast< char * >( &time ), 8 ) )
×
249
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read reference time" );
×
250

251
        referenceTime = static_cast<double>( time );
×
252
        group->setReferenceTime( DateTime( referenceTime, DateTime::JulianDay ) );
×
253
        break;
×
254

255
      case CT_TIMEUNITS:
3✔
256
        // Time unit
257
        if ( read( in, reinterpret_cast< char * >( &timeUnit ), 4 ) )
3✔
258
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "Unable to read time units" );
×
259

260
        switch ( timeUnit )
3✔
261
        {
262
          case 0:
2✔
263
            timeUnitStr = "hours";
2✔
264
            break;
2✔
265
          case 1:
×
266
            timeUnitStr = "minutes";
×
267
            break;
×
268
          case 2:
1✔
269
            timeUnitStr = "seconds";
1✔
270
            break;
1✔
271
          case 4:
×
272
            timeUnitStr = "days";
×
273
            break;
×
274
          default:
×
275
            timeUnitStr = "unknown";
×
276
            break;
×
277
        }
278
        group->setMetadata( "TIMEUNITS", timeUnitStr );
3✔
279
        break;
3✔
280

281
      case CT_TS:
177✔
282
        // Time step!
283
        if ( readIStat( in, sflg, &istat ) )
177✔
284
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "Invalid time step" );
×
285

286
        if ( read( in, reinterpret_cast< char * >( &time ), 4 ) )
177✔
287
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "Invalid time step" );
×
288

289
        double rawTime = static_cast<double>( time );
177✔
290
        MDAL::RelativeTimestamp t( rawTime, MDAL::parseDurationTimeUnit( timeUnitStr ) );
177✔
291

292
        if ( readVertexTimestep( mesh, group, groupMax, t, istat, sflg, in ) )
177✔
293
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "Unable to read vertex timestep" );
×
294

295
        break;
177✔
296
    }
297
  }
298

299
  if ( !group || group->datasets.size() == 0 )
6✔
300
    return exit_with_error( MDAL_Status::Err_UnknownFormat, "No datasets" );
×
301

302
  group->setStatistics( MDAL::calculateStatistics( group ) );
6✔
303
  mesh->datasetGroups.push_back( group );
6✔
304

305
  if ( groupMax && groupMax->datasets.size() > 0 )
6✔
306
  {
307
    groupMax->setStatistics( MDAL::calculateStatistics( groupMax ) );
2✔
308
    mesh->datasetGroups.push_back( groupMax );
2✔
309
  }
310
}
6✔
311

312
bool MDAL::DriverBinaryDat::readVertexTimestep(
177✔
313
  const MDAL::Mesh *mesh,
314
  std::shared_ptr<DatasetGroup> group,
315
  std::shared_ptr<DatasetGroup> groupMax,
316
  MDAL::RelativeTimestamp time,
317
  bool hasStatus,
318
  int sflg,
319
  std::ifstream &in )
320
{
321
  assert( group && groupMax && ( group->isScalar() == groupMax->isScalar() ) );
177✔
322
  bool isScalar = group->isScalar();
177✔
323

324
  size_t vertexCount = mesh->verticesCount();
177✔
325
  size_t faceCount = mesh->facesCount();
177✔
326

327
  std::shared_ptr<MDAL::MemoryDataset2D> dataset = std::make_shared< MDAL::MemoryDataset2D >( group.get(), hasStatus );
177✔
328

329
  bool active = true;
177✔
330
  for ( size_t i = 0; i < faceCount; ++i )
1,191,055✔
331
  {
332
    if ( hasStatus )
1,190,878✔
333
    {
334
      if ( readIStat( in, sflg, reinterpret_cast< char * >( &active ) ) )
232,510✔
335
        return true; //error
×
336
      dataset->setActive( i, active );
232,510✔
337
    }
338
  }
339

340
  for ( size_t i = 0; i < vertexCount; ++i )
733,386✔
341
  {
342
    if ( !isScalar )
733,209✔
343
    {
344
      float x, y;
345

346
      if ( read( in, reinterpret_cast< char * >( &x ), 4 ) )
122,522✔
347
        return true; //error
×
348
      if ( read( in, reinterpret_cast< char * >( &y ), 4 ) )
122,522✔
349
        return true; //error
×
350

351
      dataset->setVectorValue( i, static_cast< double >( x ), static_cast< double >( y ) );
122,522✔
352
    }
353
    else
354
    {
355
      float scalar;
356

357
      if ( read( in, reinterpret_cast< char * >( &scalar ), 4 ) )
610,687✔
358
        return true; //error
×
359

360
      dataset->setScalarValue( i, static_cast< double >( scalar ) );
610,687✔
361
    }
362
  }
363

364
  if ( MDAL::equals( time.value( MDAL::RelativeTimestamp::hours ), 99999.0 ) ) // Special TUFLOW dataset with maximus
177✔
365
  {
366
    dataset->setTime( time );
2✔
367
    dataset->setStatistics( MDAL::calculateStatistics( dataset ) );
2✔
368
    groupMax->datasets.push_back( dataset );
2✔
369
  }
370
  else
371
  {
372
    dataset->setTime( time );
175✔
373
    dataset->setStatistics( MDAL::calculateStatistics( dataset ) );
175✔
374
    group->datasets.push_back( dataset );
175✔
375
  }
376
  return false; //OK
177✔
377
}
177✔
378

379
// ////////////////////////////////////////////
380
// WRITE
381
// ////////////////////////////////////////////
382

383
static bool writeRawData( std::ofstream &out, const char *s, int n )
84✔
384
{
385
  out.write( s, n );
84✔
386
  if ( !out )
84✔
387
    return true; //error
×
388
  else
389
    return false; //OK
84✔
390
}
391

392
bool MDAL::DriverBinaryDat::persist( MDAL::DatasetGroup *group )
2✔
393
{
394
  assert( group->dataLocation() == MDAL_DataLocation::DataOnVertices );
2✔
395

396
  std::ofstream out = MDAL::openOutputFile( group->uri(), std::ofstream::out | std::ofstream::binary );
2✔
397

398
  // implementation based on information from:
399
  // http://www.xmswiki.com/wiki/SMS:Binary_Dataset_Files_*.dat
400
  if ( !out )
2✔
401
    return true; // Couldn't open the file
×
402

403
  const Mesh *mesh = group->mesh();
2✔
404
  size_t nodeCount = mesh->verticesCount();
2✔
405
  size_t elemCount = mesh->facesCount();
2✔
406

407
  // version card
408
  writeRawData( out, reinterpret_cast< const char * >( &CT_VERSION ), 4 );
2✔
409

410
  // objecttype
411
  writeRawData( out, reinterpret_cast< const char * >( &CT_OBJTYPE ), 4 );
2✔
412
  writeRawData( out, reinterpret_cast< const char * >( &CT_2D_MESHES ), 4 );
2✔
413

414
  // float size
415
  writeRawData( out, reinterpret_cast< const char * >( &CT_SFLT ), 4 );
2✔
416
  writeRawData( out, reinterpret_cast< const char * >( &CT_FLOAT_SIZE ), 4 );
2✔
417

418
  // Flag size
419
  writeRawData( out, reinterpret_cast< const char * >( &CT_SFLG ), 4 );
2✔
420
  writeRawData( out, reinterpret_cast< const char * >( &CF_FLAG_SIZE ), 4 );
2✔
421

422
  // Dataset Group Type
423
  if ( group->isScalar() )
2✔
424
  {
425
    writeRawData( out, reinterpret_cast< const char * >( &CT_BEGSCL ), 4 );
1✔
426
  }
427
  else
428
  {
429
    writeRawData( out, reinterpret_cast< const char * >( &CT_BEGVEC ), 4 );
1✔
430
  }
431

432
  // Object id (ignored)
433
  int ignored_val = 1;
2✔
434
  writeRawData( out, reinterpret_cast< const char * >( &CT_OBJID ), 4 );
2✔
435
  writeRawData( out, reinterpret_cast< const char * >( &ignored_val ), 4 );
2✔
436

437
  // Num nodes
438
  writeRawData( out, reinterpret_cast< const char * >( &CT_NUMDATA ), 4 );
2✔
439
  writeRawData( out, reinterpret_cast< const char * >( &nodeCount ), 4 );
2✔
440

441
  // Num cells
442
  writeRawData( out, reinterpret_cast< const char * >( &CT_NUMCELLS ), 4 );
2✔
443
  writeRawData( out, reinterpret_cast< const char * >( &elemCount ), 4 );
2✔
444

445
  // Name
446
  writeRawData( out, reinterpret_cast< const char * >( &CT_NAME ), 4 );
2✔
447
  writeRawData( out, MDAL::leftJustified( group->name(), 39 ).c_str(), 40 );
2✔
448

449
  // Time steps
450
  int istat = 1; // include if elements are active
2✔
451

452
  for ( size_t time_index = 0; time_index < group->datasets.size(); ++ time_index )
6✔
453
  {
454
    const std::shared_ptr<MDAL::MemoryDataset2D> dataset = std::dynamic_pointer_cast<MDAL::MemoryDataset2D>( group->datasets[time_index] );
4✔
455

456
    writeRawData( out, reinterpret_cast< const char * >( &CT_TS ), 4 );
4✔
457
    writeRawData( out, reinterpret_cast< const char * >( &istat ), 1 );
4✔
458
    float ftime = static_cast<float>( dataset->time( RelativeTimestamp::hours ) );
4✔
459
    writeRawData( out, reinterpret_cast< const char * >( &ftime ), 4 );
4✔
460

461
    if ( istat )
4✔
462
    {
463
      // Write status flags
464
      for ( size_t i = 0; i < elemCount; i++ )
12✔
465
      {
466
        bool active = static_cast<bool>( dataset->active( i ) );
8✔
467
        writeRawData( out, reinterpret_cast< const char * >( &active ), 1 );
8✔
468
      }
469
    }
470

471
    for ( size_t i = 0; i < nodeCount; i++ )
24✔
472
    {
473
      // Read values flags
474
      if ( !group->isScalar() )
20✔
475
      {
476
        float x = static_cast<float>( dataset->valueX( i ) );
10✔
477
        float y = static_cast<float>( dataset->valueY( i ) );
10✔
478
        writeRawData( out, reinterpret_cast< const char * >( &x ), 4 );
10✔
479
        writeRawData( out, reinterpret_cast< const char * >( &y ), 4 );
10✔
480
      }
481
      else
482
      {
483
        float val = static_cast<float>( dataset->scalarValue( i ) );
10✔
484
        writeRawData( out, reinterpret_cast< const char * >( &val ), 4 );
10✔
485
      }
486
    }
487
  }
4✔
488

489
  if ( writeRawData( out, reinterpret_cast< const char * >( &CT_ENDDS ), 4 ) ) return true;
2✔
490

491
  return false;
2✔
492
}
2✔
493

494
std::string MDAL::DriverBinaryDat::writeDatasetOnFileSuffix() const
1✔
495
{
496
  return "dat";
1✔
497
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc