QGIS/external/mdal/frmts/mdal_selafin.hpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

342 lines
14 KiB
C++
Raw Normal View History

2019-05-03 10:33:13 +02:00
/*
MDAL - Mesh Data Abstraction Library (MIT License)
Copyright (C) 2019 Peter Petrik (zilolv at gmail dot com)
*/
#ifndef MDAL_SELAFIN_HPP
#define MDAL_SELAFIN_HPP
#include <string>
#include <memory>
#include <map>
#include <iostream>
#include <fstream>
#include "mdal_data_model.hpp"
#include "mdal_memory_data_model.hpp"
#include "mdal.h"
#include "mdal_driver.hpp"
namespace MDAL
{
/**
* This class is used to read the selafin file format.
* The file is opened with initialize() and stay opened until this object is destroyed
*
* \note SelafinFile object is shared between different datasets, with the mesh and its iterators.
* As SelafinFile is not thread safe, it has to be shared in the same thread.
*
* This class can be used to create a mesh with all the dataset contained in a file with the static method createMessh()
2022-10-10 11:11:40 -04:00
* It is also possible to add all the dataset of a file in a separate existing mesh with the static method populateDataset()
*
* All the method to access with lazy loading to the mesh data or datasets are encapsulted and only accessible by the friend classes :
* MeshSelafin
* MeshSelafinVertexIterator
* MeshSelafinFaceIterator
* DatasetSelafin
*
* This ecapsulation protects these lazy loading access methods because they can't be used before the instance of SelafinFile has been initialized and parsed.
*
*/
class SelafinFile
2019-05-03 10:33:13 +02:00
{
public:
//! Constructor
SelafinFile( const std::string &fileName );
2019-05-03 10:33:13 +02:00
//! Returns a mesh created with the file
static std::unique_ptr<Mesh> createMesh( const std::string &fileName );
//! Populates the mesh with dataset from the file
static void populateDataset( Mesh *mesh, const std::string &fileName );
2019-05-03 10:33:13 +02:00
2021-12-15 09:31:15 -04:00
//! Extracts data related to the mesh frame for the file
void parseMeshFrame();
//! Add the dataset group to the file (persist), replace dataset in the new group by Selafindataset with lazy loading
bool addDatasetGroup( DatasetGroup *datasetGroup );
2019-05-03 10:33:13 +02:00
private:
//! Initializes and open the file file with the \a fileName
void initialize();
2021-12-15 09:31:15 -04:00
//! Reads the header of the file and return the project name
std::string readHeader();
//! Extracts data from the file
void parseFile();
//! Returns the vertices count in the mesh stored in the file
size_t verticesCount();
//! Returns the faces count in the mesh stored in the file
size_t facesCount();
//! Returns the vertices count per face for the mesh stored in the file
size_t verticesPerFace();
//! Returns \a count values at \a timeStepIndex and \a variableIndex, and an \a offset from the start
std::vector<double> datasetValues( size_t timeStepIndex, size_t variableIndex, size_t offset, size_t count );
//! Returns \a count vertex indexex in face with an \a offset from the start
std::vector<int> connectivityIndex( size_t offset, size_t count );
//! Returns \a count vertices with an \a offset from the start
std::vector<double> vertices( size_t offset, size_t count );
//! Reads a string record with a size \a len from current position in the stream, throws an exception if the size in not compaitble
std::string readString( size_t len );
/**
* Reads a double array record with a size \a len from current position in the stream,
* throws an exception if the size in not compatible
*/
std::vector<double> readDoubleArr( size_t len );
/**
* Reads a int array record with a size \a len from current position in the stream,
* throws an exception if the size in not compatible
*/
std::vector<int> readIntArr( size_t len );
/**
* Reads some values in a double array record. The values count is \a len,
* the reading begin at the stream \a position with the \a offset
*/
std::vector<double> readDoubleArr( const std::streampos &position, size_t offset, size_t len );
/**
* Reads some values in a int array record. The values count is \a len,
* the reading begin at the stream \a position with the \a offset
*/
std::vector<int> readIntArr( const std::streampos &position, size_t offset, size_t len );
//! Returns whether there is a int array with size \a len at the current position in the stream
bool checkIntArraySize( size_t len );
//! Returns whether there is a double array with size \a len at the current position in the stream
bool checkDoubleArraySize( size_t len );
//! Returns the remaining bytes in the stream from current position until the end
size_t remainingBytes();
/**
* Set the position in the stream just after the int array with \a size, returns position of the beginning of the array
* The presence of int array can be check with checkIntArraySize()
*/
std::streampos passThroughIntArray( size_t size );
/**
* Set the position in the stream just after the double array with \a size, returns position of the beginning of the array
* The presence of double array can be check with checkDoubleArraySize()
*/
std::streampos passThroughDoubleArray( size_t size );
double readDouble( );
int readInt( );
2020-07-23 10:09:20 +02:00
size_t readSizeT( );
void ignoreArrayLength( );
std::string readStringWithoutLength( size_t len );
2019-05-03 10:33:13 +02:00
void ignore( int len );
static void populateDataset( Mesh *mesh, std::shared_ptr<SelafinFile> reader );
// ///////
// attribute updated by parseFile() method
// //////
std::vector<int> mParameters;
// Dataset
DateTime mReferenceTime;
std::vector<std::vector<std::streampos>> mVariableStreamPosition; //! [variableIndex][timeStep]
std::vector<RelativeTimestamp> mTimeSteps;
std::vector<std::string> mVariableNames;
// Mesh
size_t mVerticesCount;
size_t mFacesCount;
size_t mVerticesPerFace;
std::streampos mXStreamPosition;
std::streampos mYStreamPosition;
std::streampos mConnectivityStreamPosition;
std::streampos mIPOBOStreamPosition;
double mXOrigin;
double mYOrigin;
2019-05-03 10:33:13 +02:00
std::string mFileName;
bool mStreamInFloatPrecision = true;
2020-07-23 10:09:20 +02:00
bool mChangeEndianness = true;
2019-05-03 10:33:13 +02:00
long long mFileSize = -1;
2019-05-03 10:33:13 +02:00
std::ifstream mIn;
bool mParsed = false;
friend class MeshSelafin;
friend class MeshSelafinVertexIterator;
friend class MeshSelafinFaceIterator;
friend class DatasetSelafin;
};
class DatasetSelafin : public Dataset2D
{
public:
/**
* Contructs a dataset with a SelafinFile object and the index of the time step
*
* \note SelafinFile object is shared between different dataset, with the mesh and its iterators.
* As SerafinStreamReader is not thread safe, it has to be shared in the same thread.
*
* Position of array(s) in the stream has to be set after construction (default = begin of the stream),
* see setXStreamPosition() and setYStreamPosition() (X for scalar dataset, X and Y for vector dataset)
*/
DatasetSelafin( DatasetGroup *parent,
std::shared_ptr<SelafinFile> reader,
size_t timeStepIndex );
size_t scalarData( size_t indexStart, size_t count, double *buffer ) override;
size_t vectorData( size_t indexStart, size_t count, double *buffer ) override;
//! Sets the position of the X array in the stream
void setXVariableIndex( size_t index );
//! Sets the position of the Y array in the stream
void setYVariableIndex( size_t index );
private:
std::shared_ptr<SelafinFile> mReader;
size_t mXVariableIndex = 0;
size_t mYVariableIndex = 0;
size_t mTimeStepIndex = 0;
};
class MeshSelafinVertexIterator: public MeshVertexIterator
{
public:
/**
* Contructs a vertex iterator with a SerafinFile instance
*
* \note SerafinFile instance is shared between different dataset, with the mesh and its iterators.
* As SerafinStreamReader is not thread safe, it has to be shared in the same thread.
*/
MeshSelafinVertexIterator( std::shared_ptr<SelafinFile> reader );
size_t next( size_t vertexCount, double *coordinates ) override;
private:
std::shared_ptr<SelafinFile> mReader;
size_t mPosition = 0;
};
class MeshSelafinFaceIterator: public MeshFaceIterator
{
public:
/**
* Contructs a face iterator with a SerafinFile instance
*
* \note SerafinFile instance is shared between different dataset, with the mesh and its iterators.
* As SerafinFile is not thread safe, it has to be shared in the same thread.
*/
MeshSelafinFaceIterator( std::shared_ptr<SelafinFile> reader );
size_t next( size_t faceOffsetsBufferLen, int *faceOffsetsBuffer, size_t vertexIndicesBufferLen, int *vertexIndicesBuffer ) override;
private:
std::shared_ptr<SelafinFile> mReader;
size_t mPosition = 0;
};
class MeshSelafin: public Mesh
{
public:
/**
* Contructs a dataset with a SerafinFile instance \a reader
*
* \note SerafinFile instance is shared between different dataset, with the mesh and its iterators.
* As SerafinFile is not thread safe, it has to be shared in the same thread.
*/
MeshSelafin( const std::string &uri,
std::shared_ptr<SelafinFile> reader );
std::unique_ptr<MeshVertexIterator> readVertices() override;
//! Selafin format doesn't support edges in MDAL, returns a void unique_ptr
std::unique_ptr<MeshEdgeIterator> readEdges() override;
std::unique_ptr<MeshFaceIterator> readFaces() override;
size_t verticesCount() const override {return mReader->verticesCount();}
size_t edgesCount() const override {return 0;}
size_t facesCount() const override {return mReader->facesCount();}
BBox extent() const override;
2020-08-25 08:59:11 +02:00
void closeSource() override;
private:
mutable bool mIsExtentUpToDate = false;
mutable BBox mExtent;
std::shared_ptr<SelafinFile> mReader;
void calculateExtent() const;
2019-05-03 10:33:13 +02:00
};
/**
* Serafin format (also called Selafin)
*
* Binary format for triangular mesh with datasets defined on vertices
* Source of this doc come from :
* http://www.opentelemac.org/downloads/MANUALS/TELEMAC-2D/telemac-2d_user_manual_en_v7p0.pdf Appendix 3
2019-05-03 10:33:13 +02:00
* https://www.gdal.org/drv_selafin.html
*
* The Selafin file records are listed below:
* - 1 record containing the title of the study (72 characters) and a 8 characters string indicating the type
* of format (SERAFIN or SERAFIND)
* - record containing the two integers NBV(1)and NBV(2)(number of linear and quadratic variables, NBV(2)with the value of 0 for Telemac,
* cas quadratic values are not saved so far),
* - NBV(1)records containing the names and units of each variable (over 32 characters),
* - 1 record containing the integers table IPARAM(10 integers, of which only the 6are currently being used),
* - if IPARAM (3)!=0: the value corresponds to the x-coordinate of the origin of the mesh,
* - if IPARAM (4)!=0: the value corresponds to the y-coordinate of the origin of the mesh,
* - if IPARAM (7): the value corresponds to the number of planes on the vertical (3D computation),
* - if IPARAM (8)!=0: the value corresponds to the number of boundary points (in parallel),
* - if IPARAM (9)!=0: the value corresponds to the number of interface points (in parallel),
* - if IPARAM(8 ) or IPARAM(9) !=0: the array IPOBO below is replaced by the array KNOLG(total initial number of points).
* All the other numbers are local to the sub-domain, including IKLE.
*
* - if IPARAM(10)= 1: a record containing the computation starting date,
* - 1 record containing the integers NELEM,NPOIN,NDP,1(number of elements, number of points, number of points per element and the value 1),
* - 1 record containing table IKLE(integer array of dimension (NDP,NELEM) which is the connectivity table.
* N.B.: in TELEMAC-2D, the dimensions of this array are (NELEM,NDP)),
* - 1 record containing table IPOBO(integer array of dimension NPOIN);
* the value of one element is 0 for an internal point, and gives the numbering of boundary points for the others,
* - 1 record containing table X(real array of dimension NPOINcontaining the abscissae of the points),
* - 1 record containing table Y(real array of dimension NPOINcontaining the ordinates of the points),
*
* Next, for each time step, the following are found:
* - 1 record containing time T(real),
* - NBV(1)+NBV(2)records containing the results tables for each variable at time T.
2019-05-03 10:33:13 +02:00
*/
class DriverSelafin: public Driver
{
public:
DriverSelafin();
~DriverSelafin() override;
DriverSelafin *create() override;
bool canReadMesh( const std::string &uri ) override;
bool canReadDatasets( const std::string &uri ) override;
std::unique_ptr< Mesh > load( const std::string &meshFile, const std::string &meshName = "" ) override;
void load( const std::string &datFile, Mesh *mesh ) override;
bool persist( DatasetGroup *group ) override;
int faceVerticesMaximumCount() const override {return 3;}
2021-08-18 10:49:05 +02:00
void save( const std::string &fileName, const std::string &meshName, Mesh *mesh ) override;
std::string writeDatasetOnFileSuffix() const override;
2021-06-30 13:45:22 +02:00
std::string saveMeshOnFileSuffix() const override;
2019-05-03 10:33:13 +02:00
private:
bool saveDatasetGroupOnFile( DatasetGroup *datasetGroup );
2019-05-03 10:33:13 +02:00
};
} // namespace MDAL
#endif //MDAL_SELAFIN_HPP