diff --git a/ApplicationLibCode/Commands/SeismicCommands/RicImportSeismicFeature.cpp b/ApplicationLibCode/Commands/SeismicCommands/RicImportSeismicFeature.cpp index 2c4b408f1f..54ed72b717 100644 --- a/ApplicationLibCode/Commands/SeismicCommands/RicImportSeismicFeature.cpp +++ b/ApplicationLibCode/Commands/SeismicCommands/RicImportSeismicFeature.cpp @@ -21,18 +21,22 @@ #include "RiaApplication.h" #include "RimOilField.h" +#include "RimProcess.h" #include "RimProject.h" +#include "RimSEGYConvertOptions.h" #include "RimSeismicData.h" #include "RimSeismicDataCollection.h" #include "Riu3DMainWindowTools.h" #include "RiuFileDialogTools.h" +#include "cafPdmUiPropertyViewDialog.h" #include "cafSelectionManagerTools.h" #include "cafUtils.h" #include #include +#include CAF_CMD_SOURCE_INIT( RicImportSeismicFeature, "RicImportSeismicFeature" ); @@ -49,11 +53,18 @@ bool RicImportSeismicFeature::isCommandEnabled() //-------------------------------------------------------------------------------------------------- void RicImportSeismicFeature::onActionTriggered( bool isChecked ) { - QString filter = "Seismic files (*.zgy *.vds);;All Files (*.*)"; + QString filter = "Seismic volumes (*.zgy *.vds);;SEG-Y files (*.sgy *.segy);;All Files (*.*)"; RiaApplication* app = RiaApplication::instance(); QString defaultDir = app->lastUsedDialogDirectory( "SEISMIC_GRID" ); QString fileName = RiuFileDialogTools::getOpenFileName( Riu3DMainWindowTools::mainWindowWidget(), "Import Seismic", defaultDir, filter ); + QFileInfo fi( fileName ); + QString ext = fi.suffix().toLower(); + if ( ( ext == "segy" ) || ( ext == "sgy" ) ) + { + fileName = convertSEGYtoVDS( fileName ); + } + if ( fileName.isEmpty() ) return; // Remember the path to next time @@ -83,3 +94,107 @@ void RicImportSeismicFeature::setupActionLook( QAction* actionToSetup ) actionToSetup->setIcon( QIcon( ":/Seismic16x16.png" ) ); actionToSetup->setText( "Import Seismic" ); } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RicImportSeismicFeature::convertSEGYtoVDS( QString filename ) +{ + RimSEGYConvertOptions segyoptions; + + segyoptions.setInputFilename( filename ); + + QFileInfo fi( filename ); + QString outputFilename = fi.path() + "/" + fi.completeBaseName() + ".vds"; + segyoptions.setOutputFilename( outputFilename ); + + caf::PdmUiPropertyViewDialog propDlg( nullptr, + &segyoptions, + "Convert SEG-Y to VDS file format", + "", + QDialogButtonBox::Ok | QDialogButtonBox::Cancel ); + + propDlg.resize( QSize( 520, 420 ) ); + + while ( true ) + { + if ( propDlg.exec() != QDialog::Accepted ) + { + outputFilename = ""; + break; + } + + outputFilename = segyoptions.outputFilename(); + + if ( QFile::exists( outputFilename ) ) + { + QString question = QString( "File \"%1\" already exists. \n\nDo you want to overwrite it?" ).arg( outputFilename ); + auto reply = QMessageBox::question( nullptr, "Replace existing file?", question, QMessageBox::Yes, QMessageBox::No ); + if ( reply != QMessageBox::Yes ) continue; + } + + if ( !segyoptions.headerFormatFilename().isEmpty() && !QFile::exists( segyoptions.headerFormatFilename() ) ) + { + QString warning = QString( "Header Format Definition File \"%1\" could not be found." ).arg( segyoptions.headerFormatFilename() ); + QMessageBox::warning( nullptr, "File Not Found.", warning ); + continue; + } + + auto [overrideSampleStart, sampleStartOffset] = segyoptions.sampleStartOverride(); + if ( overrideSampleStart && ( sampleStartOffset < 0.0 ) ) + { + QString warning = "Depth (Z) Offset Override must be 0 or larger."; + QMessageBox::warning( nullptr, "Invalid input.", warning ); + continue; + } + + break; + } + + if ( !outputFilename.isEmpty() ) + { + if ( !runSEGYConversion( &segyoptions ) ) outputFilename = ""; + } + + return outputFilename; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RicImportSeismicFeature::runSEGYConversion( RimSEGYConvertOptions* options ) +{ + QString command = options->convertCommand(); + QStringList parameters = options->convertCommandParameters(); + + RimProcess process; + +#ifdef WIN32 + command += ".exe"; +#else + process.addEnvironmentVariable( "LD_LIBRARY_PATH", options->programDirectory() ); +#endif + + if ( !QFile::exists( command ) ) + { + QString warning = QString( "The SEG-Y import utility \"%1\" could not be found!" ).arg( command ); + QMessageBox::critical( nullptr, "Missing Converter.", warning ); + return false; + } + + process.setCommand( command ); + process.setParameters( parameters ); + + bool showStdOut = false; + bool showStdErr = true; + + if ( !process.execute( showStdOut, showStdErr ) ) + { + QMessageBox::critical( nullptr, + "SEG-Y conversion failed.", + "Failed to convert the input SEG-Y file. Check log window for additional information." ); + return false; + } + + return true; +} diff --git a/ApplicationLibCode/Commands/SeismicCommands/RicImportSeismicFeature.h b/ApplicationLibCode/Commands/SeismicCommands/RicImportSeismicFeature.h index 90159fb3fb..a4c1f1a7a8 100644 --- a/ApplicationLibCode/Commands/SeismicCommands/RicImportSeismicFeature.h +++ b/ApplicationLibCode/Commands/SeismicCommands/RicImportSeismicFeature.h @@ -20,6 +20,10 @@ #include "cafCmdFeature.h" +#include + +class RimSEGYConvertOptions; + //================================================================================================== /// //================================================================================================== @@ -27,8 +31,14 @@ class RicImportSeismicFeature : public caf::CmdFeature { CAF_CMD_HEADER_INIT; +public: + static QString convertSEGYtoVDS( QString filename ); + protected: bool isCommandEnabled() override; void onActionTriggered( bool isChecked ) override; void setupActionLook( QAction* actionToSetup ) override; + +private: + static bool runSEGYConversion( RimSEGYConvertOptions* options ); }; diff --git a/ApplicationLibCode/FileInterface/RifOpenVDSReader.h b/ApplicationLibCode/FileInterface/RifOpenVDSReader.h index d354aa356f..a470294167 100644 --- a/ApplicationLibCode/FileInterface/RifOpenVDSReader.h +++ b/ApplicationLibCode/FileInterface/RifOpenVDSReader.h @@ -36,7 +36,7 @@ class RifOpenVDSReader : public RifSeismicReader { public: RifOpenVDSReader(); - ~RifOpenVDSReader(); + ~RifOpenVDSReader() override; bool open( QString filename ) override; void close() override; diff --git a/ApplicationLibCode/ProjectDataModel/ProcessControl/RimProcess.cpp b/ApplicationLibCode/ProjectDataModel/ProcessControl/RimProcess.cpp index ac92f75a57..5fe493acef 100644 --- a/ApplicationLibCode/ProjectDataModel/ProcessControl/RimProcess.cpp +++ b/ApplicationLibCode/ProjectDataModel/ProcessControl/RimProcess.cpp @@ -24,6 +24,7 @@ #include "cafPdmFieldCapability.h" #include +#include // Disable deprecation warning for QProcess::start() #ifdef _MSC_VER @@ -133,7 +134,7 @@ int RimProcess::ID() const //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -bool RimProcess::execute() +bool RimProcess::execute( bool enableStdOut, bool enableStdErr ) { QProcess* proc = new QProcess(); QString cmd = commandLine(); @@ -141,11 +142,19 @@ bool RimProcess::execute() RiaLogging::info( QString( "Start process %1: %2" ).arg( m_id ).arg( cmd ) ); QObject::connect( proc, SIGNAL( finished( int, QProcess::ExitStatus ) ), m_monitor, SLOT( finished( int, QProcess::ExitStatus ) ) ); - QObject::connect( proc, SIGNAL( readyReadStandardOutput() ), m_monitor, SLOT( readyReadStandardOutput() ) ); - QObject::connect( proc, SIGNAL( readyReadStandardError() ), m_monitor, SLOT( readyReadStandardError() ) ); + if ( enableStdOut ) QObject::connect( proc, SIGNAL( readyReadStandardOutput() ), m_monitor, SLOT( readyReadStandardOutput() ) ); + if ( enableStdErr ) QObject::connect( proc, SIGNAL( readyReadStandardError() ), m_monitor, SLOT( readyReadStandardError() ) ); QObject::connect( proc, SIGNAL( started() ), m_monitor, SLOT( started() ) ); bool retval = false; + + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + for ( auto& [key, val] : m_environmentVariables ) + { + env.insert( key, val ); + } + proc->setProcessEnvironment( env ); + proc->start( cmd ); if ( proc->waitForStarted( -1 ) ) { @@ -218,3 +227,11 @@ QString RimProcess::handleSpaces( QString arg ) const } return arg; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimProcess::addEnvironmentVariable( QString name, QString value ) +{ + m_environmentVariables.push_back( std::make_pair( name, value ) ); +} diff --git a/ApplicationLibCode/ProjectDataModel/ProcessControl/RimProcess.h b/ApplicationLibCode/ProjectDataModel/ProcessControl/RimProcess.h index 7257d8480c..67ef910e8d 100644 --- a/ApplicationLibCode/ProjectDataModel/ProcessControl/RimProcess.h +++ b/ApplicationLibCode/ProjectDataModel/ProcessControl/RimProcess.h @@ -24,6 +24,9 @@ #include #include +#include +#include + class RimProcessMonitor; class RimProcess : public caf::PdmObject @@ -39,13 +42,15 @@ public: void addParameter( QString paramStr ); void setParameters( QStringList parameterList ); + void addEnvironmentVariable( QString name, QString value ); + QString commandLine() const; QString command() const; QStringList parameters() const; int ID() const; - bool execute(); + bool execute( bool enableStdOut = true, bool enableStdErr = true ); protected: caf::PdmFieldHandle* userDescriptionField() override; @@ -59,6 +64,8 @@ private: caf::PdmField m_description; caf::PdmField m_id; + std::vector> m_environmentVariables; + static int m_nextProcessId; RimProcessMonitor* m_monitor; }; diff --git a/ApplicationLibCode/ProjectDataModel/Seismic/CMakeLists_files.cmake b/ApplicationLibCode/ProjectDataModel/Seismic/CMakeLists_files.cmake index 813c017603..cc34a84dfe 100644 --- a/ApplicationLibCode/ProjectDataModel/Seismic/CMakeLists_files.cmake +++ b/ApplicationLibCode/ProjectDataModel/Seismic/CMakeLists_files.cmake @@ -4,6 +4,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RimSeismicSectionCollection.h ${CMAKE_CURRENT_LIST_DIR}/RimSeismicSection.h ${CMAKE_CURRENT_LIST_DIR}/RimSeismicAlphaMapper.h + ${CMAKE_CURRENT_LIST_DIR}/RimSEGYConvertOptions.h ) set(SOURCE_GROUP_SOURCE_FILES @@ -12,6 +13,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RimSeismicSectionCollection.cpp ${CMAKE_CURRENT_LIST_DIR}/RimSeismicSection.cpp ${CMAKE_CURRENT_LIST_DIR}/RimSeismicAlphaMapper.cpp + ${CMAKE_CURRENT_LIST_DIR}/RimSEGYConvertOptions.cpp ) list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES}) diff --git a/ApplicationLibCode/ProjectDataModel/Seismic/RimSEGYConvertOptions.cpp b/ApplicationLibCode/ProjectDataModel/Seismic/RimSEGYConvertOptions.cpp new file mode 100644 index 0000000000..1cd0c6418b --- /dev/null +++ b/ApplicationLibCode/ProjectDataModel/Seismic/RimSEGYConvertOptions.cpp @@ -0,0 +1,188 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RimSEGYConvertOptions.h" + +#include "cafPdmUiFilePathEditor.h" +#include "cafPdmUiLineEditor.h" + +#include + +CAF_PDM_SOURCE_INIT( RimSEGYConvertOptions, "RimSEGYConvertOptions" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSEGYConvertOptions::RimSEGYConvertOptions() +{ + CAF_PDM_InitObject( "SEG-Y convert options", ":/Seismic16x16.png" ); + + CAF_PDM_InitFieldNoDefault( &m_inputFilename, "InputFilename", "Input SEG-Y File" ); + CAF_PDM_InitFieldNoDefault( &m_outputFilename, "OutputFilename", "Output VDS File" ); + + CAF_PDM_InitField( &m_sampleStartOverride, "SampleStartOverride", std::make_pair( false, 0.0 ), "Depth (Z) Offset Override" ); + CAF_PDM_InitField( &m_sampleUnit, "SampleUnit", QString( "m" ), "Depth (Z) Unit" ); + + CAF_PDM_InitFieldNoDefault( &m_headerFormatFilename, "HeaderFilename", "Header Definition File (optional)" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimSEGYConvertOptions::~RimSEGYConvertOptions() +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSEGYConvertOptions::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) +{ +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSEGYConvertOptions::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) +{ + auto fileGrp = uiOrdering.addNewGroup( "Input/Output Files" ); + fileGrp->add( &m_inputFilename ); + fileGrp->add( &m_outputFilename ); + + auto convGrp = uiOrdering.addNewGroup( "Convert Options" ); + convGrp->add( &m_sampleUnit ); + convGrp->add( &m_sampleStartOverride ); + convGrp->add( &m_headerFormatFilename ); + + uiOrdering.skipRemainingFields(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSEGYConvertOptions::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) +{ + if ( field == &m_outputFilename ) + { + caf::PdmUiFilePathEditorAttribute* myAttr = dynamic_cast( attribute ); + if ( myAttr ) + { + myAttr->m_selectSaveFileName = true; + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimSEGYConvertOptions::inputFilename() const +{ + return m_inputFilename().path(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSEGYConvertOptions::setInputFilename( QString filename ) +{ + m_inputFilename = filename; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimSEGYConvertOptions::outputFilename() const +{ + return m_outputFilename().path(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSEGYConvertOptions::setOutputFilename( QString filename ) +{ + m_outputFilename = filename; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimSEGYConvertOptions::headerFormatFilename() const +{ + return m_headerFormatFilename().path(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::pair RimSEGYConvertOptions::sampleStartOverride() const +{ + return m_sampleStartOverride(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimSEGYConvertOptions::programDirectory() const +{ + return QCoreApplication::applicationDirPath(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimSEGYConvertOptions::convertCommand() const +{ + return QString( "%1/%2" ).arg( programDirectory() ).arg( "SEGYImport" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QStringList RimSEGYConvertOptions::convertCommandParameters() const +{ + QStringList retVal; + + retVal.append( m_inputFilename().path() ); + + auto& [overrideSampleStart, overrideValue] = m_sampleStartOverride(); + if ( overrideSampleStart ) + { + retVal.append( "--sample-start" ); + retVal.append( QString::number( overrideValue ) ); + } + + if ( !m_headerFormatFilename().path().isEmpty() ) + { + retVal.append( "--header-format" ); + retVal.append( m_headerFormatFilename().path() ); + } + + if ( !m_sampleUnit().isEmpty() ) + { + retVal.append( "--sample-unit" ); + retVal.append( m_sampleUnit() ); + } + + retVal.append( "--vdsfile" ); + retVal.append( m_outputFilename().path() ); + + retVal.append( "--quiet" ); + + return retVal; +} diff --git a/ApplicationLibCode/ProjectDataModel/Seismic/RimSEGYConvertOptions.h b/ApplicationLibCode/ProjectDataModel/Seismic/RimSEGYConvertOptions.h new file mode 100644 index 0000000000..5f06120650 --- /dev/null +++ b/ApplicationLibCode/ProjectDataModel/Seismic/RimSEGYConvertOptions.h @@ -0,0 +1,62 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023 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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// +#pragma once + +#include "cafFilePath.h" +#include "cafPdmField.h" +#include "cafPdmObject.h" + +#include +#include + +#include + +class RimSEGYConvertOptions : public caf::PdmObject +{ + CAF_PDM_HEADER_INIT; + +public: + RimSEGYConvertOptions(); + ~RimSEGYConvertOptions() override; + + QString inputFilename() const; + void setInputFilename( QString filename ); + + QString outputFilename() const; + void setOutputFilename( QString filename ); + + QString headerFormatFilename() const; + + std::pair sampleStartOverride() const; + + QString programDirectory() const; + QString convertCommand() const; + QStringList convertCommandParameters() const; + +protected: + void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override; + void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; + void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override; + +private: + caf::PdmField m_inputFilename; + caf::PdmField m_outputFilename; + caf::PdmField> m_sampleStartOverride; + caf::PdmField m_headerFormatFilename; + caf::PdmField m_sampleUnit; +};