mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 07:00:17 -05:00
* #14411 Enforce name uniqueness for surfaces, polygons and folders Names must be unique among siblings sharing the same parent folder. Items in different folders may keep identical names, and comparison is case sensitive. Siblings are the objects held by the same caf::PdmChildArrayField. Items and folders live in two distinct child arrays in caf::PdmNestedCollection, so a folder and an item may share a name under the same parent. The Python API gets an OnNameConflict flag on AddFolder, CreatePolygon, ImportSurface and NewRegularSurface, supporting FAIL (default), AUTO_RENAME and OVERWRITE. NewSurface is left out, as grid case surfaces derive their tree label from the case and K index and carry no name of their own.
553 lines
18 KiB
C++
553 lines
18 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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 "RimSurfaceCollection.h"
|
|
|
|
#include "RiaColorTables.h"
|
|
#include "RiaLogging.h"
|
|
#include "RiaNameUniquenessTools.h"
|
|
|
|
#include "Rim2dIntersectionView.h"
|
|
#include "Rim2dIntersectionViewCollection.h"
|
|
#include "RimCase.h"
|
|
#include "RimEnsembleSurface.h"
|
|
#include "RimFileSurface.h"
|
|
#include "RimFractureSurface.h"
|
|
#include "RimGridCaseSurface.h"
|
|
#include "RimGridView.h"
|
|
#include "RimProject.h"
|
|
#include "RimRegularFileSurface.h"
|
|
#include "RimRegularLegendConfig.h"
|
|
#include "RimSeismicSectionCollection.h"
|
|
#include "RimSurface.h"
|
|
#include "RimSurfaceInView.h"
|
|
#include "RimSurfaceResultDefinition.h"
|
|
|
|
#include "cafCmdFeatureMenuBuilder.h"
|
|
#include "cafPdmFieldReorderCapability.h"
|
|
#include "cafPdmFieldScriptingCapability.h"
|
|
#include "cafPdmObjectScriptingCapability.h"
|
|
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
|
|
CAF_PDM_SOURCE_INIT( RimSurfaceCollection, "SurfaceCollection" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimSurfaceCollection::RimSurfaceCollection()
|
|
{
|
|
CAF_PDM_InitScriptableObject( "Surfaces", ":/Folder.png" );
|
|
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_collectionName, "SurfaceUserDescription", "Name" );
|
|
m_collectionName.registerKeywordAlias( "SurfaceUserDecription" );
|
|
|
|
m_collectionName = "Surfaces";
|
|
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_subCollections, "SubCollections", "Surfaces" );
|
|
auto reorderability = caf::PdmFieldReorderCapability::addToField( &m_subCollections );
|
|
reorderability->orderChanged.connect( this, &RimSurfaceCollection::orderChanged );
|
|
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_items, "SurfacesField", "Surfaces" );
|
|
|
|
setDeletable( true );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimSurfaceCollection::~RimSurfaceCollection()
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimSurfaceCollection* RimSurfaceCollection::createTopmost()
|
|
{
|
|
auto* coll = new RimSurfaceCollection();
|
|
coll->setAsTopmostFolder();
|
|
coll->uiCapability()->setUiIconFromResourceString( ":/ReservoirSurfaces16x16.png" );
|
|
return coll;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::addSurface( RimSurface* surface )
|
|
{
|
|
m_items.push_back( surface );
|
|
ensureUniqueSurfaceName( surface );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::ensureUniqueSurfaceName( RimSurface* surface )
|
|
{
|
|
if ( !surface ) return;
|
|
|
|
RiaNameUniquenessTools::ensureUniqueAmongSiblings( surface );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
|
|
{
|
|
if ( changedField == &m_collectionName )
|
|
{
|
|
// Keep the name unique among the folders in the same parent folder
|
|
m_collectionName = RiaNameUniquenessTools::resolveRenameConflict( this, newValue.toString() );
|
|
}
|
|
|
|
caf::PdmNestedCollection<RimSurfaceCollection, RimSurface>::fieldChangedByUi( changedField, oldValue, newValue );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::addEnsembleSurface( RimEnsembleSurface* ensembleSurface )
|
|
{
|
|
addSubCollection( ensembleSurface );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimEnsembleSurface*> RimSurfaceCollection::ensembleSurfaces() const
|
|
{
|
|
return descendantsIncludingThisOfType<RimEnsembleSurface>();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimSurface* RimSurfaceCollection::createSurfaceFromFile( const QString& fileName )
|
|
{
|
|
RimSurface* newSurface = nullptr;
|
|
|
|
auto extension = QFileInfo( fileName ).suffix().toLower();
|
|
|
|
if ( extension == "pvd" )
|
|
{
|
|
RimFractureSurface* fractureSurface = new RimFractureSurface;
|
|
fractureSurface->setSurfaceFilePath( fileName );
|
|
newSurface = fractureSurface;
|
|
}
|
|
else if ( extension == "irap" || extension == "gri" )
|
|
{
|
|
auto regularFileSurface = new RimRegularFileSurface;
|
|
regularFileSurface->setFilePath( fileName );
|
|
regularFileSurface->setPropertyAsDepth( RimRegularFileSurface::propertyName() );
|
|
newSurface = regularFileSurface;
|
|
}
|
|
else
|
|
{
|
|
RimFileSurface* fileSurface = new RimFileSurface;
|
|
fileSurface->setFilePath( fileName );
|
|
newSurface = fileSurface;
|
|
}
|
|
|
|
return newSurface;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimSurface* RimSurfaceCollection::importSurfacesFromFiles( const QStringList& fileNames, bool showLegend /* = true */ )
|
|
{
|
|
size_t newSurfCount = 0;
|
|
size_t existingSurfCount = m_items().size();
|
|
QString errorMessages;
|
|
|
|
std::vector<RimSurface*> surfacesToLoad;
|
|
|
|
for ( const QString& newFileName : fileNames )
|
|
{
|
|
RimSurface* newSurface = createSurfaceFromFile( newFileName );
|
|
|
|
auto newColor = RiaColorTables::categoryPaletteColors().cycledColor3f( existingSurfCount + newSurfCount );
|
|
newSurface->setColor( newColor );
|
|
newSurface->setUserDescription( QFileInfo( newFileName ).fileName() );
|
|
|
|
if ( !newSurface->onLoadData() )
|
|
{
|
|
delete newSurface;
|
|
errorMessages += newFileName + "\n";
|
|
}
|
|
else
|
|
{
|
|
addSurface( newSurface );
|
|
surfacesToLoad.push_back( newSurface );
|
|
++newSurfCount;
|
|
}
|
|
}
|
|
|
|
if ( !errorMessages.isEmpty() )
|
|
{
|
|
RiaLogging::warning( "Import Surfaces : Could not import the following files:\n" + errorMessages.toStdString() );
|
|
}
|
|
|
|
updateConnectedEditors();
|
|
|
|
updateViews( surfacesToLoad, showLegend );
|
|
|
|
if ( newSurfCount > 0 && !m_items.empty() )
|
|
{
|
|
return m_items[m_items.size() - 1];
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::reloadSurfaces( std::vector<RimSurface*> surfaces, bool showLegend /*=true*/ )
|
|
{
|
|
// ask the surfaces given to reload its data
|
|
for ( RimSurface* surface : surfaces )
|
|
{
|
|
surface->reloadData();
|
|
}
|
|
|
|
updateConnectedEditors();
|
|
|
|
updateViews( surfaces, showLegend );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimSurface* RimSurfaceCollection::copySurfaces( std::vector<RimSurface*> surfaces )
|
|
{
|
|
std::vector<RimSurface*> newsurfaces;
|
|
|
|
// create a copy of each surface given
|
|
for ( RimSurface* surface : surfaces )
|
|
{
|
|
RimSurface* copy = surface->createCopy();
|
|
if ( copy )
|
|
{
|
|
newsurfaces.push_back( copy );
|
|
}
|
|
else
|
|
{
|
|
RiaLogging::warning( "Create Surface Copy: Could not create a copy of the surface " + surface->fullName().toStdString() );
|
|
}
|
|
}
|
|
|
|
RimSurface* retsurf = nullptr;
|
|
for ( RimSurface* surface : newsurfaces )
|
|
{
|
|
m_items.push_back( surface );
|
|
ensureUniqueSurfaceName( surface );
|
|
retsurf = surface;
|
|
}
|
|
|
|
updateConnectedEditors();
|
|
|
|
return retsurf;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimSurface* RimSurfaceCollection::addGridCaseSurface( RimCase* sourceCase, int oneBasedSliceIndex )
|
|
{
|
|
auto s = new RimGridCaseSurface;
|
|
s->setCase( sourceCase );
|
|
|
|
s->setOneBasedIndex( oneBasedSliceIndex );
|
|
|
|
if ( !s->onLoadData() )
|
|
{
|
|
RiaLogging::warning( "Add Grid Case Surface : Could not create the grid case surface." );
|
|
return nullptr;
|
|
}
|
|
|
|
m_items.push_back( s );
|
|
ensureUniqueSurfaceName( s );
|
|
|
|
updateConnectedEditors();
|
|
|
|
std::vector<RimSurface*> surfacesToRefresh;
|
|
surfacesToRefresh.push_back( s );
|
|
updateViews( surfacesToRefresh );
|
|
|
|
return s;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimSurface*> RimSurfaceCollection::surfaces() const
|
|
{
|
|
return m_items.childrenByType();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
|
|
{
|
|
menuBuilder << "RicImportSurfacesFeature";
|
|
menuBuilder << "RicNewGridSurfaceFeature";
|
|
menuBuilder << "RicNewDepthSurfaceFeature";
|
|
menuBuilder << "RicNewRegularSurfaceFeature";
|
|
menuBuilder << "RicImportEnsembleSurfaceFeature";
|
|
menuBuilder << "RicCreateEnsembleSurfaceFeature";
|
|
menuBuilder.addSeparator();
|
|
menuBuilder << "RicNewNestedCollectionFeature";
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::loadData()
|
|
{
|
|
for ( auto surf : m_items )
|
|
{
|
|
surf->loadDataIfRequired();
|
|
}
|
|
|
|
for ( auto subColl : m_subCollections )
|
|
{
|
|
subColl->loadData();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::updateViews( const std::vector<RimSurface*>& surfsToReload, bool showLegend /* = true */ )
|
|
{
|
|
RimProject* proj = RimProject::current();
|
|
|
|
// Make sure the tree items are synchronized
|
|
for ( auto view : proj->allViews() )
|
|
{
|
|
view->updateViewTreeItems( RiaDefines::ItemIn3dView::SURFACE );
|
|
|
|
if ( auto gridView = dynamic_cast<RimGridView*>( view ) )
|
|
{
|
|
auto seismicCollection = gridView->seismicSectionCollection();
|
|
seismicCollection->setSurfacesVisible( surfsToReload );
|
|
}
|
|
}
|
|
|
|
std::set<Rim3dView*> viewsNeedingUpdate;
|
|
|
|
for ( auto surf : surfsToReload )
|
|
{
|
|
std::vector<RimSurfaceInView*> surfsInView = surf->objectsWithReferringPtrFieldsOfType<RimSurfaceInView>();
|
|
for ( auto surfInView : surfsInView )
|
|
{
|
|
surfInView->clearGeometry();
|
|
surfInView->surfaceResultDefinition()->legendConfig()->setShowLegend( showLegend );
|
|
|
|
auto gridView = surfInView->firstAncestorOrThisOfType<Rim3dView>();
|
|
if ( gridView ) viewsNeedingUpdate.insert( gridView );
|
|
}
|
|
}
|
|
|
|
// Update the views:
|
|
for ( auto view : viewsNeedingUpdate )
|
|
{
|
|
view->scheduleCreateDisplayModelAndRedraw();
|
|
|
|
if ( view->ownerCase() )
|
|
{
|
|
auto views = view->ownerCase()->intersectionViewCollection()->views();
|
|
for ( Rim2dIntersectionView* view : views )
|
|
{
|
|
view->scheduleCreateDisplayModelAndRedraw();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::updateViews()
|
|
{
|
|
RimProject* proj = RimProject::current();
|
|
std::vector<Rim3dView*> views = proj->allViews();
|
|
|
|
// Make sure the tree items are synchronized
|
|
|
|
for ( auto view : views )
|
|
{
|
|
view->updateViewTreeItems( RiaDefines::ItemIn3dView::SURFACE );
|
|
}
|
|
|
|
for ( auto view : views )
|
|
{
|
|
view->scheduleCreateDisplayModelAndRedraw();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::onChildDeleted( caf::PdmChildArrayFieldHandle* childArray, std::vector<caf::PdmObjectHandle*>& referringObjects )
|
|
{
|
|
updateViews();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::orderChanged( const caf::SignalEmitter* emitter )
|
|
{
|
|
updateViews();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::removeSurface( RimSurface* surface )
|
|
{
|
|
m_items.removeChild( surface );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSurfaceCollection::removeMissingFileSurfaces()
|
|
{
|
|
// get the existing surfaces
|
|
std::list<RimFileSurface*> missingSurfaces;
|
|
|
|
// check if a filesurface references a file no longer present
|
|
for ( auto& surface : surfaces() )
|
|
{
|
|
RimFileSurface* fileSurface = dynamic_cast<RimFileSurface*>( surface );
|
|
if ( fileSurface == nullptr ) continue;
|
|
|
|
QString filename = fileSurface->filePath();
|
|
if ( !QFile::exists( filename ) )
|
|
{
|
|
missingSurfaces.push_back( fileSurface );
|
|
}
|
|
}
|
|
|
|
// remove all surfaces with a missing input file
|
|
for ( auto& surface : missingSurfaces )
|
|
{
|
|
removeSurface( surface );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimSurface* RimSurfaceCollection::addSurfacesAtIndex( int position, std::vector<RimSurface*> surfaces )
|
|
{
|
|
// adjust index for number of folders we have
|
|
position = position - static_cast<int>( m_subCollections.size() );
|
|
|
|
RimSurface* returnSurface = nullptr;
|
|
if ( !surfaces.empty() ) returnSurface = surfaces[0];
|
|
|
|
// insert position at end?
|
|
if ( ( position >= static_cast<int>( m_items.size() ) ) || ( position < 0 ) )
|
|
{
|
|
for ( auto surf : surfaces )
|
|
{
|
|
m_items.push_back( surf );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// build the new surface order
|
|
std::vector<RimSurface*> orderedSurfs;
|
|
|
|
int i = 0;
|
|
|
|
while ( i < position )
|
|
{
|
|
orderedSurfs.push_back( m_items[i++] );
|
|
}
|
|
|
|
for ( auto surf : surfaces )
|
|
{
|
|
orderedSurfs.push_back( surf );
|
|
}
|
|
|
|
int surfcount = static_cast<int>( m_items.size() );
|
|
while ( i < surfcount )
|
|
{
|
|
orderedSurfs.push_back( m_items[i++] );
|
|
}
|
|
|
|
// reset the surface collection and use the new order
|
|
m_items.clearWithoutDelete();
|
|
for ( auto surf : orderedSurfs )
|
|
{
|
|
m_items.push_back( surf );
|
|
}
|
|
}
|
|
|
|
// Surfaces moved in from another folder may collide with the names already present here
|
|
for ( auto surf : surfaces )
|
|
{
|
|
ensureUniqueSurfaceName( surf );
|
|
}
|
|
|
|
// make sure the views are in sync with the collection order
|
|
updateViews();
|
|
|
|
return returnSurface;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimSurfaceCollection::containsSurface()
|
|
{
|
|
bool containsSurface = ( !surfaces().empty() );
|
|
|
|
for ( auto coll : m_subCollections )
|
|
{
|
|
containsSurface |= coll->containsSurface();
|
|
}
|
|
|
|
return containsSurface;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimSurfaceCollection::containsFileSurface( QString filename )
|
|
{
|
|
for ( auto& surface : surfaces() )
|
|
{
|
|
RimFileSurface* fileSurface = dynamic_cast<RimFileSurface*>( surface );
|
|
if ( fileSurface == nullptr ) continue;
|
|
if ( fileSurface->filePath() == filename ) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|