diff --git a/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriFeature.cpp b/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriFeature.cpp index 32d9c8f8fc..0a9c012b02 100644 --- a/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriFeature.cpp +++ b/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriFeature.cpp @@ -40,9 +40,31 @@ #include #include +#include CAF_CMD_SOURCE_INIT( RicExportSurfaceToGriFeature, "RicExportSurfaceToGriFeature" ); +namespace +{ +//-------------------------------------------------------------------------------------------------- +/// Returns the surface if it is a regular surface with a grid matching gridParams exactly, nullptr otherwise +//-------------------------------------------------------------------------------------------------- +RimRegularSurface* regularSurfaceWithMatchingGrid( RimSurface* surf, const RigRegularSurfaceData& gridParams ) +{ + auto* reg = dynamic_cast( surf ); + if ( !reg ) return nullptr; + + if ( reg->nx() == gridParams.nx && reg->ny() == gridParams.ny && reg->originX() == gridParams.originX && + reg->originY() == gridParams.originY && reg->incrementX() == gridParams.incrementX && reg->incrementY() == gridParams.incrementY && + reg->rotation() == gridParams.rotation ) + { + return reg; + } + + return nullptr; +} +} // namespace + //-------------------------------------------------------------------------------------------------- /// For a RimRegularSurface whose stored grid matches gridParams exactly, depth values are returned /// directly. Otherwise the surface is resampled onto the grid via RigSurfaceResampler. @@ -50,14 +72,9 @@ CAF_CMD_SOURCE_INIT( RicExportSurfaceToGriFeature, "RicExportSurfaceToGriFeature std::vector RicExportSurfaceToGriFeature::resampleToGrid( RimSurface* surf, const RigRegularSurfaceData& gridParams ) { // Regular surface with matching grid: use stored depth values directly (lossless) - if ( auto* reg = dynamic_cast( surf ) ) + if ( auto* reg = regularSurfaceWithMatchingGrid( surf, gridParams ) ) { - if ( reg->nx() == gridParams.nx && reg->ny() == gridParams.ny && reg->originX() == gridParams.originX && - reg->originY() == gridParams.originY && reg->incrementX() == gridParams.incrementX && - reg->incrementY() == gridParams.incrementY && reg->rotation() == gridParams.rotation ) - { - return reg->depthValues(); - } + return reg->depthValues(); } // Resample the surface onto the requested grid @@ -80,6 +97,23 @@ std::vector RicExportSurfaceToGriFeature::resampleToGrid( RimSurface* sur return depthValues; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector + RicExportSurfaceToGriFeature::propertyValuesOnGrid( RimSurface* surf, const QString& propertyName, const RigRegularSurfaceData& gridParams ) +{ + auto* reg = dynamic_cast( surf ); + if ( !reg ) return {}; + + // Property values are stored per node on the surface grid and are exported as they are. The export grid must + // therefore have the same number of nodes as the surface. + auto values = reg->getProperty( propertyName ); + if ( values.size() != static_cast( gridParams.nx * gridParams.ny ) ) return {}; + + return values; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -96,6 +130,18 @@ void RicExportSurfaceToGriFeature::exportSurfaces( const std::vector( surfaces[0] ) : nullptr; + // Collect the properties available for export from all selected surfaces + std::set availableProperties; + for ( RimSurface* surf : surfaces ) + { + if ( RigSurface* rig = surf->surfaceData() ) + { + for ( const auto& name : rig->propertyNames() ) + availableProperties.insert( name ); + } + } + ui.setAvailableProperties( { availableProperties.begin(), availableProperties.end() } ); + if ( regularSurface ) { ui.setGridDefaults( regularSurface->nx(), @@ -103,7 +149,8 @@ void RicExportSurfaceToGriFeature::exportSurfaces( const std::vectororiginX(), regularSurface->originY(), regularSurface->incrementX(), - regularSurface->incrementY() ); + regularSurface->incrementY(), + regularSurface->rotation() ); } else { @@ -124,11 +171,12 @@ void RicExportSurfaceToGriFeature::exportSurfaces( const std::vector 0.0 ) ? std::sqrt( areaApprox / static_cast( totalVertexCount ) ) : 1.0; const int nx = std::max( 2, static_cast( std::ceil( bb.extent().x() / spacing ) ) + 1 ); const int ny = std::max( 2, static_cast( std::ceil( bb.extent().y() / spacing ) ) + 1 ); - ui.setGridDefaults( nx, ny, bb.min().x(), bb.min().y(), spacing, spacing ); + // The estimated grid is a starting point for the user, so round the values to something readable + ui.setGridDefaults( nx, ny, std::round( bb.min().x() ), std::round( bb.min().y() ), std::round( spacing ), std::round( spacing ), 0.0 ); } caf::PdmUiPropertyViewDialog dialog( nullptr, &ui, "Export Surface to IRAP/GRI", "" ); - dialog.resize( QSize( 400, 300 ) ); + dialog.resize( QSize( 400, 450 ) ); if ( dialog.exec() != QDialog::Accepted ) return; const QString exportDir = ui.exportFolder(); @@ -140,21 +188,54 @@ void RicExportSurfaceToGriFeature::exportSurfaces( const std::vector 1 ) || + ( selectedProperties[0] != RicExportSurfaceToGriUi::depthEntryName() ); + for ( RimSurface* surf : surfaces ) { - const QString fileName = - caf::Utils::constructFullFileName( exportDir, caf::Utils::makeValidFileBasename( surf->fullName() ), extension ); + for ( const QString& valueName : selectedProperties ) + { + const bool isDepth = ( valueName == RicExportSurfaceToGriUi::depthEntryName() ); + const auto values = isDepth ? resampleToGrid( surf, gridParams ) : propertyValuesOnGrid( surf, valueName, gridParams ); - const auto depthValues = resampleToGrid( surf, gridParams ); - if ( depthValues.empty() ) continue; + if ( values.empty() ) + { + if ( isDepth ) + { + RiaLogging::warning( std::format( "No depth values found for surface '{}', skipping export.", surf->fullName() ) ); + } + else + { + RiaLogging::warning( std::format( "Skipping export of '{}' for surface '{}'. Properties can only be exported for a " + "regular surface, and Nx and Ny must match the surface.", + valueName, + surf->fullName() ) ); + } + continue; + } - bool ok = binary ? RifSurfio::exportToGri( fileName.toStdString(), gridParams, depthValues ) - : RifSurfio::exportToIrap( fileName.toStdString(), gridParams, depthValues ); + QString baseName = caf::Utils::makeValidFileBasename( surf->fullName() ); + if ( useValueNameSuffix ) baseName += "--" + caf::Utils::makeValidFileBasename( valueName ); - if ( ok ) - RiaLogging::info( std::format( "Exported surface to: {}", fileName ) ); - else - RiaLogging::error( std::format( "Failed to export surface to: {}", fileName ) ); + const QString fileName = caf::Utils::constructFullFileName( exportDir, baseName, extension ); + + bool ok = binary ? RifSurfio::exportToGri( fileName.toStdString(), gridParams, values ) + : RifSurfio::exportToIrap( fileName.toStdString(), gridParams, values ); + + if ( ok ) + RiaLogging::info( std::format( "Exported surface to: {}", fileName ) ); + else + RiaLogging::error( std::format( "Failed to export surface to: {}", fileName ) ); + } } } diff --git a/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriFeature.h b/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriFeature.h index af00868867..1c231c87fa 100644 --- a/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriFeature.h +++ b/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriFeature.h @@ -41,6 +41,11 @@ public: // in row-major order (index = j * nx + i). Returns an empty vector on failure. static std::vector resampleToGrid( RimSurface* surf, const RigRegularSurfaceData& gridParams ); + // Returns the values of the named property in row-major order (index = j * nx + i). Property values are exported + // without resampling, so an empty vector is returned unless the surface is a regular surface with the same number + // of nodes as gridParams. + static std::vector propertyValuesOnGrid( RimSurface* surf, const QString& propertyName, const RigRegularSurfaceData& gridParams ); + // Shows the export dialog, resamples and writes each surface. Format is chosen in the dialog. static void exportSurfaces( const std::vector& surfaces ); diff --git a/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriUi.cpp b/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriUi.cpp index a68f2035ae..1efbfe3a7c 100644 --- a/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriUi.cpp +++ b/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriUi.cpp @@ -19,8 +19,7 @@ #include "RicExportSurfaceToGriUi.h" #include "cafPdmUiFilePathEditor.h" - -#include +#include "cafPdmUiTreeSelectionEditor.h" namespace caf { @@ -47,12 +46,25 @@ RicExportSurfaceToGriUi::RicExportSurfaceToGriUi() CAF_PDM_InitFieldNoDefault( &m_exportFolder, "ExportFolder", "Export Folder" ); m_exportFolder.uiCapability()->setUiEditorTypeName( caf::PdmUiFilePathEditor::uiEditorTypeName() ); + CAF_PDM_InitFieldNoDefault( &m_selectedProperties, "SelectedProperties", "Export Values" ); + m_selectedProperties.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() ); + m_selectedProperties = std::vector{ depthEntryName() }; + CAF_PDM_InitField( &m_nx, "Nx", 10, "Nx (columns)" ); CAF_PDM_InitField( &m_ny, "Ny", 10, "Ny (rows)" ); CAF_PDM_InitField( &m_originX, "OriginX", 0.0, "Origin X" ); CAF_PDM_InitField( &m_originY, "OriginY", 0.0, "Origin Y" ); CAF_PDM_InitField( &m_incrementX, "IncrementX", 1.0, "Increment X" ); CAF_PDM_InitField( &m_incrementY, "IncrementY", 1.0, "Increment Y" ); + CAF_PDM_InitField( &m_rotation, "Rotation", 0.0, "Rotation" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RicExportSurfaceToGriUi::depthEntryName() +{ + return "Depth"; } //-------------------------------------------------------------------------------------------------- @@ -66,14 +78,23 @@ void RicExportSurfaceToGriUi::setExportFolder( const QString& exportFolder ) //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RicExportSurfaceToGriUi::setGridDefaults( int nx, int ny, double originX, double originY, double incrementX, double incrementY ) +void RicExportSurfaceToGriUi::setGridDefaults( int nx, int ny, double originX, double originY, double incrementX, double incrementY, double rotation ) { m_nx = nx; m_ny = ny; - m_originX = std::round( originX ); - m_originY = std::round( originY ); - m_incrementX = std::round( incrementX ); - m_incrementY = std::round( incrementY ); + m_originX = originX; + m_originY = originY; + m_incrementX = incrementX; + m_incrementY = incrementY; + m_rotation = rotation; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicExportSurfaceToGriUi::setAvailableProperties( const std::vector& propertyNames ) +{ + m_availableProperties = propertyNames; } //-------------------------------------------------------------------------------------------------- @@ -88,10 +109,18 @@ RigRegularSurfaceData RicExportSurfaceToGriUi::gridParams() const p.originY = m_originY; p.incrementX = m_incrementX; p.incrementY = m_incrementY; - p.rotation = 0.0; + p.rotation = m_rotation; return p; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RicExportSurfaceToGriUi::selectedProperties() const +{ + return m_selectedProperties(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -121,3 +150,26 @@ void RicExportSurfaceToGriUi::defineEditorAttribute( const caf::PdmFieldHandle* } } } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList RicExportSurfaceToGriUi::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) +{ + QList options; + + if ( fieldNeedingOptions == &m_selectedProperties ) + { + options.push_back( caf::PdmOptionItemInfo( depthEntryName(), depthEntryName() ) ); + + for ( const auto& name : m_availableProperties ) + { + // A property with the same name as the depth entry is already represented in the list + if ( name == depthEntryName() ) continue; + + options.push_back( caf::PdmOptionItemInfo( name, name ) ); + } + } + + return options; +} diff --git a/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriUi.h b/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriUi.h index 0964f6c10f..a4fd15408b 100644 --- a/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriUi.h +++ b/ApplicationLibCode/Commands/SurfaceCommands/RicExportSurfaceToGriUi.h @@ -24,6 +24,8 @@ #include "cafPdmField.h" #include "cafPdmObject.h" +#include + //================================================================================================== /// PdmObject holding the parameters for resampling a surface onto a regular grid before export. //================================================================================================== @@ -40,23 +42,33 @@ public: RicExportSurfaceToGriUi(); + // Name of the entry representing the surface depth values in the list of exported values + static QString depthEntryName(); + void setExportFolder( const QString& exportFolder ); - void setGridDefaults( int nx, int ny, double originX, double originY, double incrementX, double incrementY ); + void setGridDefaults( int nx, int ny, double originX, double originY, double incrementX, double incrementY, double rotation ); + void setAvailableProperties( const std::vector& propertyNames ); RigRegularSurfaceData gridParams() const; QString exportFolder() const; ExportFormat exportFormat() const; + std::vector selectedProperties() const; protected: void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override; + QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override; private: caf::PdmField> m_exportFormat; caf::PdmField m_exportFolder; + caf::PdmField> m_selectedProperties; caf::PdmField m_nx; caf::PdmField m_ny; caf::PdmField m_originX; caf::PdmField m_originY; caf::PdmField m_incrementX; caf::PdmField m_incrementY; + caf::PdmField m_rotation; + + std::vector m_availableProperties; };