#5829 Add reader for ROFF files to extract category names and values from file

This commit is contained in:
Kristian Bendiksen 2020-04-28 14:04:31 +02:00 committed by Magne Sjaastad
parent 423b6c2887
commit 3f4778509d
10 changed files with 434 additions and 1 deletions

View File

@ -51,6 +51,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RifActiveCellsReader.h
${CMAKE_CURRENT_LIST_DIR}/RifCsvDataTableFormatter.h
${CMAKE_CURRENT_LIST_DIR}/RifEclipseInputPropertyLoader.h
${CMAKE_CURRENT_LIST_DIR}/RifSurfaceReader.h
${CMAKE_CURRENT_LIST_DIR}/RifRoffReader.h
# HDF5 file reader is directly included in ResInsight main CmakeList.txt
#${CMAKE_CURRENT_LIST_DIR}/RifHdf5Reader.h
@ -106,7 +107,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RifCsvDataTableFormatter.cpp
${CMAKE_CURRENT_LIST_DIR}/RifReaderEnsembleStatisticsRft.cpp
${CMAKE_CURRENT_LIST_DIR}/RifEclipseInputPropertyLoader.cpp
${CMAKE_CURRENT_LIST_DIR}/RifSurfaceReader.cpp
${CMAKE_CURRENT_LIST_DIR}/RifRoffReader.cpp
# HDF5 file reader is directly included in ResInsight main CmakeList.txt
#${CMAKE_CURRENT_LIST_DIR}/RifHdf5Reader.cpp

View File

@ -0,0 +1,141 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RifRoffReader.h"
#include <QFile>
#include <QTextStream>
const QString codeValuesString = QString( "array int codeValues" );
const QString codeNamesString = QString( "array char codeNames" );
const QString headerString = QString( "roff-asc" );
bool RifRoffReader::isCodeValuesDefinition( const QString& line )
{
return line.startsWith( codeValuesString );
}
bool RifRoffReader::isCodeNamesDefinition( const QString& line )
{
return line.startsWith( codeNamesString );
}
bool RifRoffReader::isCorrectHeader( const QString& line )
{
return line.startsWith( headerString );
}
int RifRoffReader::extractNumberAfterString( const QString& line, const QString& prefix )
{
QString copiedLine( line );
bool ok;
int num = copiedLine.remove( prefix ).toInt( &ok );
if ( !ok )
{
throw RifRoffReaderException( "Unexpected value: not an integer." );
}
return num;
}
int RifRoffReader::extractCodeValuesCount( const QString& line )
{
return extractNumberAfterString( line, codeValuesString );
}
int RifRoffReader::extractCodeNamesCount( const QString& line )
{
return extractNumberAfterString( line, codeNamesString );
}
void RifRoffReader::readCodeNames( const QString& filename, std::map<int, QString>& codeNames )
{
QFile file( filename );
if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) )
{
throw RifRoffReaderException( "Unable to open roff file." );
}
bool isFirstLine = true;
int numCodeValues = -1;
int numCodeNames = -1;
std::vector<int> readCodeValues;
std::vector<QString> readCodeNames;
QTextStream in( &file );
while ( !in.atEnd() )
{
QString line = in.readLine();
// Check that the first line has the roff-asc header
if ( isFirstLine )
{
if ( !isCorrectHeader( line ) )
{
throw RifRoffReaderException( "Unexpected file type: roff-asc header missing." );
}
isFirstLine = false;
}
else if ( isCodeValuesDefinition( line ) )
{
// Expected line:
// array int codeValues 99
numCodeValues = extractCodeValuesCount( line );
for ( int i = 0; i < numCodeValues; i++ )
{
// The code values comes next, can be multiple per line.
int codeValue;
in >> codeValue;
readCodeValues.push_back( codeValue );
}
}
else if ( isCodeNamesDefinition( line ) )
{
// Expected line:
// array char codeNames 99
numCodeNames = extractCodeNamesCount( line );
for ( int i = 0; i < numCodeNames; i++ )
{
// Read code names. Assumes one name per line.
QString codeName = in.readLine();
readCodeNames.push_back( codeName.trimmed().remove( "\"" ) );
}
}
}
if ( numCodeValues == -1 )
{
throw RifRoffReaderException( "Code values not found." );
}
if ( numCodeNames == -1 )
{
throw RifRoffReaderException( "Code names not found." );
}
if ( numCodeNames != numCodeValues )
{
throw RifRoffReaderException( "Inconsistent code names and values: must be equal length." );
}
for ( int i = 0; i < numCodeNames; i++ )
{
codeNames[readCodeValues[i]] = readCodeNames[i];
}
}

View File

@ -0,0 +1,58 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include <exception>
#include <map>
#include <string>
#include <QString>
//==================================================================================================
///
//==================================================================================================
class RifRoffReaderException : public std::exception
{
public:
RifRoffReaderException( const std::string& message )
: message( message )
{
}
std::string message;
};
//==================================================================================================
///
//==================================================================================================
class RifRoffReader
{
public:
// Throws RifRoffReaderException on error
static void readCodeNames( const QString& filename, std::map<int, QString>& codeNames );
private:
static bool isCorrectHeader( const QString& line );
static bool isCodeNamesDefinition( const QString& line );
static bool isCodeValuesDefinition( const QString& line );
static int extractNumberAfterString( const QString& line, const QString& prefix );
static int extractCodeNamesCount( const QString& line );
static int extractCodeValuesCount( const QString& line );
};

View File

@ -66,6 +66,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RifWellMeasurementReader-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaDateStringParser-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigHexGradientTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifSurfaceReader-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifRoffReader-Test.cpp
)
if (RESINSIGHT_ENABLE_GRPC)

View File

@ -0,0 +1,87 @@
#include "gtest/gtest.h"
#include "RifRoffReader.h"
#include "RiaTestDataDirectory.h"
#include <QDir>
#include <QString>
TEST( RifRoffReader, ReadValidFile )
{
QDir baseFolder( TEST_DATA_DIR );
QString filename( "RifRoffReader/facies_info.roff" );
QString filePath = baseFolder.absoluteFilePath( filename );
EXPECT_TRUE( QFile::exists( filePath ) );
std::map<int, QString> codeNames;
RifRoffReader::readCodeNames( filePath, codeNames );
ASSERT_EQ( 6u, codeNames.size() );
for ( int i = 0; i < 6; i++ )
{
ASSERT_TRUE( codeNames.find( i ) != codeNames.end() );
ASSERT_EQ( codeNames.find( i )->second.toStdString(), QString( "code name %1" ).arg( i + 1 ).toStdString() );
}
}
std::string readIncorrectFile( const QString filename )
{
QDir baseFolder( TEST_DATA_DIR );
QString filePath = baseFolder.absoluteFilePath( filename );
std::map<int, QString> codeNames;
try
{
RifRoffReader::readCodeNames( filePath, codeNames );
return "";
}
catch ( RifRoffReaderException& ex )
{
return ex.message;
}
}
TEST( RifRoffReader, ReadWrongFileType )
{
// Read a surface file: no expected to work
QString filename( "RifSurfaceReader/test.ptl" );
ASSERT_EQ( readIncorrectFile( filename ), std::string( "Unexpected file type: roff-asc header missing." ) );
}
TEST( RifRoffReader, ReadNonExistingFileType )
{
// Read a non-existing file
QString filename( "RifRoffReader/this_file_does_not_exist.roff" );
ASSERT_EQ( readIncorrectFile( filename ), std::string( "Unable to open roff file." ) );
}
TEST( RifRoffReader, ReadFileWithIncorrectInteger )
{
// Read a file with incorrect integer for code values
QString filename( "RifRoffReader/code_values_integer_wrong.roff" );
ASSERT_EQ( readIncorrectFile( filename ), std::string( "Unexpected value: not an integer." ) );
}
TEST( RifRoffReader, ReadFileCodeNamesMissing )
{
// Read a file without code names
QString filename( "RifRoffReader/code_names_missing.roff" );
ASSERT_EQ( readIncorrectFile( filename ), std::string( "Code names not found." ) );
}
TEST( RifRoffReader, ReadFileCodeValuesMissing )
{
// Read a file without code values
QString filename( "RifRoffReader/code_values_missing.roff" );
ASSERT_EQ( readIncorrectFile( filename ), std::string( "Code values not found." ) );
}
TEST( RifRoffReader, ReadFileCodeNamesAndValuesMismatch )
{
// Read a file without code values
QString filename( "RifRoffReader/code_names_and_values_mismatch.roff" );
ASSERT_EQ( readIncorrectFile( filename ), std::string( "Inconsistent code names and values: must be equal length." ) );
}

View File

@ -0,0 +1,31 @@
roff-asc
#ROFF file#
#Creator: RMS - Reservoir Modeling System, version 11.1.0#
tag filedata
int byteswaptest 1
char filetype "parameter"
char creationDate "06/03/2020 14:25:36"
endtag
tag version
int major 2
int minor 0
endtag
tag dimensions
int nX 46
int nY 112
int nZ 242
endtag
tag parameter
char name "composite"
array char codeNames 4
"only"
"four"
"code"
"names"
array int codeValues 6
0 1 2 3 4 5
array int data 4
-999 -999 -999 -999
endtag
tag eof
endtag

View File

@ -0,0 +1,26 @@
roff-asc
#ROFF file#
#Creator: RMS - Reservoir Modeling System, version 11.1.0#
tag filedata
int byteswaptest 1
char filetype "parameter"
char creationDate "06/03/2020 14:25:36"
endtag
tag version
int major 2
int minor 0
endtag
tag dimensions
int nX 46
int nY 112
int nZ 242
endtag
tag parameter
char name "composite"
array int codeValues 6
0 1 2 3 4 5
array int data 4
-999 -999 -999 -999
endtag
tag eof
endtag

View File

@ -0,0 +1,29 @@
roff-asc
#ROFF file#
#Creator: RMS - Reservoir Modeling System, version 11.1.0#
tag filedata
int byteswaptest 1
char filetype "parameter"
char creationDate "06/03/2020 14:25:36"
endtag
tag version
int major 2
int minor 0
endtag
tag dimensions
int nX 46
int nY 112
int nZ 242
endtag
tag parameter
char name "composite"
array char codeNames 2
"cn1"
"cn2"
array int codeValues this_is_not_a_number
0 1 2 3 4 5
array int data 4
-999 -999 -999 -999
endtag
tag eof
endtag

View File

@ -0,0 +1,26 @@
roff-asc
#ROFF file#
#Creator: RMS - Reservoir Modeling System, version 11.1.0#
tag filedata
int byteswaptest 1
char filetype "parameter"
char creationDate "06/03/2020 14:25:36"
endtag
tag version
int major 2
int minor 0
endtag
tag dimensions
int nX 46
int nY 112
int nZ 242
endtag
tag parameter
char name "composite"
array char codeNames 1
"a name"
array int data 4
-999 -999 -999 -999
endtag
tag eof
endtag

View File

@ -0,0 +1,33 @@
roff-asc
#ROFF file#
#Creator: RMS - Reservoir Modeling System, version 11.1.0#
tag filedata
int byteswaptest 1
char filetype "parameter"
char creationDate "06/03/2020 14:25:36"
endtag
tag version
int major 2
int minor 0
endtag
tag dimensions
int nX 46
int nY 112
int nZ 242
endtag
tag parameter
char name "composite"
array char codeNames 6
"code name 1"
"code name 2"
"code name 3"
"code name 4"
"code name 5"
"code name 6"
array int codeValues 6
0 1 2 3 4 5
array int data 4
-999 -999 -999 -999
endtag
tag eof
endtag