#5539 Initial AnalysisPlot objects and barchart builder

This commit is contained in:
Jacob Støren 2020-02-12 16:48:25 +01:00
parent 86fa10fd13
commit a1410ca118
18 changed files with 1533 additions and 0 deletions

View File

@ -70,10 +70,12 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/ModelVisualization/Intersections
${CMAKE_CURRENT_SOURCE_DIR}/ModelVisualization/Surfaces
${CMAKE_CURRENT_SOURCE_DIR}/UserInterface
${CMAKE_CURRENT_SOURCE_DIR}/UserInterface/AnalysisPlots
${CMAKE_CURRENT_SOURCE_DIR}/CommandFileInterface
${CMAKE_CURRENT_SOURCE_DIR}/CommandFileInterface/Core
${CMAKE_CURRENT_SOURCE_DIR}/ProjectDataModel
${CMAKE_CURRENT_SOURCE_DIR}/ProjectDataModel/AnalysisPlots
${CMAKE_CURRENT_SOURCE_DIR}/ProjectDataModel/Annotations
${CMAKE_CURRENT_SOURCE_DIR}/ProjectDataModel/Completions
${CMAKE_CURRENT_SOURCE_DIR}/ProjectDataModel/Flow
@ -139,6 +141,7 @@ list( APPEND REFERENCED_CMAKE_FILES
FileInterface/CMakeLists_files.cmake
ProjectDataModel/CMakeLists_files.cmake
ProjectDataModel/AnalysisPlots/CMakeLists_files.cmake
ProjectDataModel/GridCrossPlots/CMakeLists_files.cmake
ProjectDataModel/GridCrossPlots/CellFilters/CMakeLists_files.cmake
ProjectDataModel/Summary/CMakeLists_files.cmake
@ -158,8 +161,10 @@ list( APPEND REFERENCED_CMAKE_FILES
ModelVisualization/WindowEdgeAxesOverlayItem/CMakeLists_files.cmake
UserInterface/CMakeLists_files.cmake
UserInterface/AnalysisPlots/CMakeLists_files.cmake
Commands/CMakeLists_files.cmake
Commands/AnalysisPlotCommands/CMakeLists_files.cmake
Commands/ApplicationCommands/CMakeLists_files.cmake
Commands/AnnotationCommands/CMakeLists_files.cmake
Commands/CompletionCommands/CMakeLists_files.cmake

View File

@ -0,0 +1,18 @@
set (SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RicNewAnalysisPlotFeature.h
)
set (SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RicNewAnalysisPlotFeature.cpp
)
list(APPEND CODE_HEADER_FILES
${SOURCE_GROUP_HEADER_FILES}
)
list(APPEND CODE_SOURCE_FILES
${SOURCE_GROUP_SOURCE_FILES}
)
source_group( "CommandFeature\\AnalysisPlotCommands" FILES ${SOURCE_GROUP_HEADER_FILES} ${SOURCE_GROUP_SOURCE_FILES} ${CMAKE_CURRENT_LIST_DIR}/CMakeLists_files.cmake )

View File

@ -0,0 +1,81 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RicNewAnalysisPlotFeature.h"
#include "RimAnalysisPlot.h"
#include "RimAnalysisPlotCollection.h"
#include "RiuPlotMainWindowTools.h"
#include "cafSelectionManager.h"
#include <QAction>
CAF_CMD_SOURCE_INIT( RicNewAnalysisPlotFeature, "RicNewAnalysisPlotFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicNewAnalysisPlotFeature::isCommandEnabled()
{
RimAnalysisPlotCollection* analysisPlotColl = nullptr;
caf::PdmObject* selObj = dynamic_cast<caf::PdmObject*>( caf::SelectionManager::instance()->selectedItem() );
if ( selObj )
{
selObj->firstAncestorOrThisOfType( analysisPlotColl );
}
if ( analysisPlotColl ) return true;
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicNewAnalysisPlotFeature::onActionTriggered( bool isChecked )
{
RimAnalysisPlotCollection* analysisPlotColl = nullptr;
caf::PdmObject* selObj = dynamic_cast<caf::PdmObject*>( caf::SelectionManager::instance()->selectedItem() );
if ( selObj )
{
selObj->firstAncestorOrThisOfType( analysisPlotColl );
}
if ( !analysisPlotColl ) return;
auto newPlot = analysisPlotColl->createAnalysisPlot();
newPlot->loadDataAndUpdate();
analysisPlotColl->updateConnectedEditors();
RiuPlotMainWindowTools::setExpanded( newPlot );
RiuPlotMainWindowTools::selectAsCurrentItem( newPlot );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicNewAnalysisPlotFeature::setupActionLook( QAction* actionToSetup )
{
actionToSetup->setText( "New Analysis Plot" );
actionToSetup->setIcon( QIcon( ":/Histogram16x16.png" ) );
}

View File

@ -0,0 +1,35 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "cafCmdFeature.h"
//==================================================================================================
///
//==================================================================================================
class RicNewAnalysisPlotFeature : public caf::CmdFeature
{
CAF_CMD_HEADER_INIT;
protected:
// Overrides
bool isCommandEnabled() override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};

View File

@ -0,0 +1,25 @@
set (SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimAnalysisPlot.h
${CMAKE_CURRENT_LIST_DIR}/RimAnalysisPlotDataEntry.h
${CMAKE_CURRENT_LIST_DIR}/RimAnalysisPlotCollection.h
)
set (SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimAnalysisPlot.cpp
${CMAKE_CURRENT_LIST_DIR}/RimAnalysisPlotDataEntry.cpp
${CMAKE_CURRENT_LIST_DIR}/RimAnalysisPlotCollection.cpp
)
list(APPEND CODE_HEADER_FILES
${SOURCE_GROUP_HEADER_FILES}
)
list(APPEND CODE_SOURCE_FILES
${SOURCE_GROUP_SOURCE_FILES}
)
list(APPEND QT_MOC_HEADERS
)
source_group( "ProjectDataModel\\AnalysisPlots" FILES ${SOURCE_GROUP_HEADER_FILES} ${SOURCE_GROUP_SOURCE_FILES} ${CMAKE_CURRENT_LIST_DIR}/CMakeLists_files.cmake )

View File

@ -0,0 +1,385 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RimAnalysisPlot.h"
#include "RiaColorTables.h"
#include "RimAnalysisPlotDataEntry.h"
#include "RiuGroupedBarChartBuilder.h"
#include "RiuSummaryQwtPlot.h"
#include "qwt_column_symbol.h"
#include "qwt_legend.h"
#include "qwt_painter.h"
#include "qwt_plot_barchart.h"
#include "qwt_scale_draw.h"
#include <limits>
#include <map>
namespace caf
{
template <>
void caf::AppEnum<RimAnalysisPlot::SortGroupType>::setUp()
{
addItem( RimAnalysisPlot::SUMMARY_ITEM, "SUMMARY_ITEM", "Summary Item" );
addItem( RimAnalysisPlot::CASE, "CASE", "Case" );
addItem( RimAnalysisPlot::ENSEMBLE, "ENSEMBLE", "Ensemble" );
addItem( RimAnalysisPlot::VALUE, "VALUE", "Value" );
addItem( RimAnalysisPlot::ABS_VALUE, "ABS_VALUE", "abs(Value)" );
addItem( RimAnalysisPlot::OTHER_VALUE, "OTHER_VALUE", "Other Value" );
addItem( RimAnalysisPlot::ABS_OTHER_VALUE, "ABS_OTHER_VALUE", "abs(Other Value)" );
addItem( RimAnalysisPlot::TIME_STEP, "TIME_STEP", "Time Step" );
}
} // namespace caf
CAF_PDM_SOURCE_INIT( RimAnalysisPlot, "AnalysisPlot" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimAnalysisPlot::RimAnalysisPlot()
: RimPlot()
{
CAF_PDM_InitObject( "Analysis Plot", ":/Histogram16x16.png", "", "" );
CAF_PDM_InitField( &m_showPlotTitle, "ShowPlotTitle", true, "Plot Title", "", "", "" );
m_showPlotTitle.xmlCapability()->setIOWritable( false );
CAF_PDM_InitField( &m_useAutoPlotTitle, "IsUsingAutoName", true, "Auto Title", "", "", "" );
CAF_PDM_InitField( &m_description, "PlotDescription", QString( "Summary Plot" ), "Name", "", "", "" );
CAF_PDM_InitFieldNoDefault( &m_data, "AnalysisPlotData", "", "", "", "" );
CAF_PDM_InitFieldNoDefault( &m_selectedTimeSteps, "TimeSteps", "", "", "", "" );
CAF_PDM_InitFieldNoDefault( &m_sortGroupSortingOrder, "sortingOrder", "Sort Order", "", "", "" );
CAF_PDM_InitFieldNoDefault( &m_sortGroupsToGroup, "grouping", "Grouping", "", "", "" );
CAF_PDM_InitFieldNoDefault( &m_sortGroupForLegend, "groupForLegend", "Legend Using", "", "", "" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimAnalysisPlot::~RimAnalysisPlot()
{
removeMdiWindowFromMdiArea();
cleanupBeforeClose();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::cleanupBeforeClose()
{
detachAllCurves();
if ( m_plotWidget )
{
m_plotWidget->setParent( nullptr );
delete m_plotWidget;
m_plotWidget = nullptr;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimAnalysisPlot::showPlotTitle() const
{
return m_showPlotTitle;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::setShowPlotTitle( bool showTitle )
{
m_showPlotTitle = showTitle;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::detachAllCurves() {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::reattachAllCurves() {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::updateAxes() {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QWidget* RimAnalysisPlot::viewWidget()
{
return m_plotWidget;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuQwtPlotWidget* RimAnalysisPlot::viewer()
{
return m_plotWidget;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimAnalysisPlot::asciiDataForPlotExport() const
{
return "";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::updateLegend() {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimAnalysisPlot::hasCustomFontSizes( RiaDefines::FontSettingType fontSettingType, int defaultFontSize ) const
{
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimAnalysisPlot::applyFontSize( RiaDefines::FontSettingType fontSettingType,
int oldFontSize,
int fontSize,
bool forceChange /*= false */ )
{
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::setAutoScaleXEnabled( bool enabled ) {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::setAutoScaleYEnabled( bool enabled ) {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::zoomAll() {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::updateZoomInQwt() {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::updateZoomFromQwt() {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmObject* RimAnalysisPlot::findPdmObjectFromQwtCurve( const QwtPlotCurve* curve ) const
{
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::onAxisSelected( int axis, bool toggle ) {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::deleteViewWidget()
{
cleanupBeforeClose();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimAnalysisPlot::description() const
{
return m_description();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::updateCaseNameHasChanged()
{
// Todo
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuQwtPlotWidget* RimAnalysisPlot::doCreatePlotViewWidget( QWidget* mainWindowParent /*= nullptr */ )
{
if ( !m_plotWidget )
{
m_plotWidget = new RiuQwtPlotWidget( this, mainWindowParent );
this->connect( m_plotWidget, SIGNAL( plotZoomed() ), SLOT( onPlotZoomed() ) );
// updatePlotTitle();
}
return m_plotWidget;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::doUpdateLayout() {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::doRemoveFromCollection() {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QImage RimAnalysisPlot::snapshotWindowContent()
{
QImage image;
if ( m_plotWidget )
{
QPixmap pix = m_plotWidget->grab();
image = pix.toImage();
}
return image;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmFieldHandle* RimAnalysisPlot::userDescriptionField()
{
return &m_description;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
const QVariant& oldValue,
const QVariant& newValue )
{
this->loadDataAndUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= "" */ ) {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlot::onLoadDataAndUpdate()
{
updateMdiWindowVisibility();
if ( m_plotWidget )
{
RiuGroupedBarChartBuilder chartBuilder( Qt::Horizontal );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W1", "", std::numeric_limits<double>::infinity(), "R1", 0.5 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W1", "", std::numeric_limits<double>::infinity(), "R2", 0.55 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W3", "", std::numeric_limits<double>::infinity(), "R1", 0.7 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W3", "", std::numeric_limits<double>::infinity(), "R2", 0.75 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W2", "", std::numeric_limits<double>::infinity(), "R1", 1.05 );
chartBuilder.addBarEntry( "T1_The_red_Fox", "W2", "", std::numeric_limits<double>::infinity(), "R2", 1.0 );
chartBuilder.addBarEntry( "T2", "W1", "", std::numeric_limits<double>::infinity(), "R1", 1.5 );
chartBuilder.addBarEntry( "T2", "W1", "", std::numeric_limits<double>::infinity(), "R2", 1.5 );
chartBuilder.addBarEntry( "T2", "W2", "", std::numeric_limits<double>::infinity(), "R1", 2.0 );
chartBuilder.addBarEntry( "T2", "W2", "", std::numeric_limits<double>::infinity(), "R2", 2.0 );
chartBuilder.addBarEntry( "T3", "W1", "1", std::numeric_limits<double>::infinity(), "R1", 1.5 );
chartBuilder.addBarEntry( "T3", "W1", "2", std::numeric_limits<double>::infinity(), "R2", 1.5 );
chartBuilder.addBarEntry( "T3", "W2", "3", std::numeric_limits<double>::infinity(), "R1", 2.0 );
chartBuilder.addBarEntry( "T3", "W2", "4", std::numeric_limits<double>::infinity(), "R1", 2.0 );
chartBuilder.addBarEntry( "T3", "W2", "5", std::numeric_limits<double>::infinity(), "R1", 2.0 );
chartBuilder.addBarEntry( "T4", "W1", "1", std::numeric_limits<double>::infinity(), "R1", 1.5 );
chartBuilder.addBarEntry( "T4", "W1", "2", std::numeric_limits<double>::infinity(), "R2", 1.5 );
chartBuilder.addBarEntry( "T4", "W2", "3", std::numeric_limits<double>::infinity(), "R1", 2.0 );
chartBuilder.addBarEntry( "T4", "W2", "4", std::numeric_limits<double>::infinity(), "R2", 2.0 );
chartBuilder.addBarEntry( "T4", "W1", "1", std::numeric_limits<double>::infinity(), "R1", 1.6 );
chartBuilder.addBarEntry( "T4", "W1", "2", std::numeric_limits<double>::infinity(), "R2", 1.6 );
chartBuilder.addBarEntry( "T4", "W2", "3", std::numeric_limits<double>::infinity(), "R1", 2.6 );
chartBuilder.addBarEntry( "T4", "W2", "4", std::numeric_limits<double>::infinity(), "R2", -0.3 );
chartBuilder.addBarEntry( "T5", "", "G1", 1.5, "R3", 1.5 );
chartBuilder.addBarEntry( "T5", "", "G2", 1.5, "R3", 1.5 );
chartBuilder.addBarEntry( "T5", "", "G3", 2.0, "R3", 2.0 );
chartBuilder.addBarEntry( "T5", "", "G4", 2.0, "R3", 2.0 );
chartBuilder.addBarEntry( "T5", "", "G5", 1.6, "R3", 1.6 );
chartBuilder.addBarEntry( "T5", "", "G6", 1.6, "R3", 1.6 );
chartBuilder.addBarEntry( "T5", "", "G7", 2.6, "R3", 2.6 );
chartBuilder.addBarEntry( "T5", "", "G8", -0.3, "R3", -0.3 );
chartBuilder.addBarEntry( "", "", "", 1.2, "", 1.2 );
chartBuilder.addBarEntry( "", "", "", 1.5, "", 1.5 );
chartBuilder.addBarEntry( "", "", "", 2.3, "", 2.3 );
chartBuilder.addBarEntry( "", "", "", 2.0, "", 2.0 );
chartBuilder.addBarEntry( "", "", "", 1.6, "", 1.6 );
chartBuilder.addBarEntry( "", "", "", 2.4, "", -2.4 );
chartBuilder.addBarChartToPlot( m_plotWidget );
if ( m_showPlotLegends && m_plotWidget->legend() == nullptr )
{
QwtLegend* legend = new QwtLegend( m_plotWidget );
m_plotWidget->insertLegend( legend, QwtPlot::RightLegend );
}
else
{
m_plotWidget->insertLegend( nullptr );
}
// m_plotWidget->setLegendFontSize( m_legendFontSize() );
m_plotWidget->updateLegend();
}
this->updateAxes();
}

View File

@ -0,0 +1,124 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RimPlot.h"
#include <QDateTime>
class RiuSummaryQwtPlot;
class RimAnalysisPlotDataEntry;
//==================================================================================================
///
///
//==================================================================================================
class RimAnalysisPlot : public RimPlot
{
CAF_PDM_HEADER_INIT;
public:
RimAnalysisPlot();
~RimAnalysisPlot() override;
bool showPlotTitle() const;
void setShowPlotTitle( bool showTitle );
void detachAllCurves() override;
void reattachAllCurves() override;
void updateAxes() override;
QWidget* viewWidget() override;
RiuQwtPlotWidget* viewer() override;
QString asciiDataForPlotExport() const override;
void updateLegend() override;
bool hasCustomFontSizes( RiaDefines::FontSettingType fontSettingType, int defaultFontSize ) const override;
bool applyFontSize( RiaDefines::FontSettingType fontSettingType,
int oldFontSize,
int fontSize,
bool forceChange = false ) override;
void setAutoScaleXEnabled( bool enabled ) override;
void setAutoScaleYEnabled( bool enabled ) override;
void zoomAll() override;
void updateZoomInQwt() override;
void updateZoomFromQwt() override;
caf::PdmObject* findPdmObjectFromQwtCurve( const QwtPlotCurve* curve ) const override;
void onAxisSelected( int axis, bool toggle ) override;
// RimViewWindow overrides
void deleteViewWidget() override;
enum SortGroupType
{
SUMMARY_ITEM,
CASE,
ENSEMBLE,
VALUE,
ABS_VALUE,
OTHER_VALUE,
ABS_OTHER_VALUE,
TIME_STEP,
};
QString description() const override;
void updateCaseNameHasChanged();
private:
RiuQwtPlotWidget* doCreatePlotViewWidget( QWidget* mainWindowParent = nullptr ) override;
void cleanupBeforeClose();
void doUpdateLayout() override;
void doRemoveFromCollection() override;
QImage snapshotWindowContent() override;
// Overridden PDM methods
caf::PdmFieldHandle* userDescriptionField() override;
void fieldChangedByUi( const caf::PdmFieldHandle* changedField,
const QVariant& oldValue,
const QVariant& newValue ) override;
void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override;
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void onLoadDataAndUpdate() override;
private:
QPointer<RiuQwtPlotWidget> m_plotWidget;
caf::PdmField<bool> m_showPlotTitle;
caf::PdmField<bool> m_useAutoPlotTitle;
caf::PdmField<QString> m_description;
caf::PdmChildArrayField<RimAnalysisPlotDataEntry*> m_data;
caf::PdmField<std::vector<QDateTime>> m_selectedTimeSteps;
caf::PdmField<std::vector<caf::AppEnum<SortGroupType>>> m_sortGroupSortingOrder;
caf::PdmField<std::vector<caf::AppEnum<SortGroupType>>> m_sortGroupsToGroup;
caf::PdmField<caf::AppEnum<SortGroupType>> m_sortGroupForLegend;
};

View File

@ -0,0 +1,80 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RimAnalysisPlotCollection.h"
#include "RimAnalysisPlot.h"
CAF_PDM_SOURCE_INIT( RimAnalysisPlotCollection, "AnalysisPlotCollection" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimAnalysisPlotCollection::RimAnalysisPlotCollection()
{
CAF_PDM_InitObject( "Analysis Plots", ":/Histograms16x16.png", "", "" );
CAF_PDM_InitFieldNoDefault( &m_analysisPlots, "AnalysisPlots", "Analysis Plots", "", "", "" );
m_analysisPlots.uiCapability()->setUiHidden( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimAnalysisPlotCollection::~RimAnalysisPlotCollection() {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimAnalysisPlot* RimAnalysisPlotCollection::createAnalysisPlot()
{
RimAnalysisPlot* plot = new RimAnalysisPlot();
plot->setAsPlotMdiWindow();
// plot->enableAutoPlotTitle( true );
m_analysisPlots.push_back( plot );
return plot;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlotCollection::updateSummaryNameHasChanged()
{
for ( RimAnalysisPlot* plot : m_analysisPlots )
{
plot->updateCaseNameHasChanged();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlotCollection::removeSummaryPlot( RimAnalysisPlot* analysisPlot )
{
m_analysisPlots.removeChildObject( analysisPlot );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlotCollection::deleteAllChildObjects()
{
m_analysisPlots.deleteAllChildObjects();
}

View File

@ -0,0 +1,46 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "cafPdmChildArrayField.h"
#include "cafPdmObject.h"
class RimAnalysisPlot;
//==================================================================================================
///
///
//==================================================================================================
class RimAnalysisPlotCollection : public caf::PdmObject
{
CAF_PDM_HEADER_INIT;
public:
RimAnalysisPlotCollection();
~RimAnalysisPlotCollection() override;
RimAnalysisPlot* createAnalysisPlot();
void updateSummaryNameHasChanged();
void removeSummaryPlot( RimAnalysisPlot* analysisPlot );
void deleteAllChildObjects();
private:
caf::PdmChildArrayField<RimAnalysisPlot*> m_analysisPlots;
};

View File

@ -0,0 +1,78 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RimAnalysisPlotDataEntry.h"
#include "RifEclipseSummaryAddress.h"
#include "RimSummaryAddress.h"
#include "RimSummaryCase.h"
CAF_PDM_SOURCE_INIT( RimAnalysisPlotDataEntry, "AnalysisPlotDataEntry" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimAnalysisPlotDataEntry::RimAnalysisPlotDataEntry()
{
CAF_PDM_InitObject( "Data Entry", "", "", "" );
CAF_PDM_InitFieldNoDefault( &m_summaryCase, "SummaryCase", "Case", "", "", "" );
m_summaryCase.uiCapability()->setUiTreeChildrenHidden( true );
m_summaryCase.uiCapability()->setAutoAddingOptionFromValue( false );
CAF_PDM_InitFieldNoDefault( &m_summaryAddress, "SummaryAddress", "Summary Address", "", "", "" );
m_summaryAddress.uiCapability()->setUiHidden( true );
m_summaryAddress.uiCapability()->setUiTreeChildrenHidden( true );
m_summaryAddress = new RimSummaryAddress;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimAnalysisPlotDataEntry::~RimAnalysisPlotDataEntry() {}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlotDataEntry::setSummaryCase( RimSummaryCase* sumCase )
{
m_summaryCase = sumCase;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSummaryCase* RimAnalysisPlotDataEntry::summaryCase() const
{
return m_summaryCase;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimAnalysisPlotDataEntry::setSummaryAddress( const RifEclipseSummaryAddress& address )
{
m_summaryAddress->setAddress( address );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifEclipseSummaryAddress RimAnalysisPlotDataEntry::summaryAddress() const
{
return m_summaryAddress->address();
}

View File

@ -0,0 +1,46 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RifEclipseSummaryAddress.h"
#include "cafPdmChildField.h"
#include "cafPdmObject.h"
#include "cafPdmPtrField.h"
class RimSummaryCase;
class RimSummaryAddress;
class RimAnalysisPlotDataEntry : public caf::PdmObject
{
CAF_PDM_HEADER_INIT;
public:
RimAnalysisPlotDataEntry();
~RimAnalysisPlotDataEntry() override;
void setSummaryCase( RimSummaryCase* sumCase );
RimSummaryCase* summaryCase() const;
void setSummaryAddress( const RifEclipseSummaryAddress& address );
RifEclipseSummaryAddress summaryAddress() const;
private:
caf::PdmPtrField<RimSummaryCase*> m_summaryCase;
caf::PdmChildField<RimSummaryAddress*> m_summaryAddress;
};

View File

@ -148,6 +148,8 @@
#include <QString>
#include <QStringList>
#include "RimAnalysisPlot.h"
#include "RimAnalysisPlotCollection.h"
#include <vector>
//--------------------------------------------------------------------------------------------------
@ -511,6 +513,14 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection()
menuBuilder << "Separator";
menuBuilder << "RicShowSummaryCurveCalculatorFeature";
}
else if ( dynamic_cast<RimAnalysisPlotCollection*>( firstUiItem ) )
{
menuBuilder << "RicNewAnalysisPlotFeature";
}
else if ( dynamic_cast<RimAnalysisPlot*>( firstUiItem ) )
{
menuBuilder << "RicNewAnalysisPlotFeature";
}
else if ( dynamic_cast<RimSummaryCrossPlotCollection*>( firstUiItem ) )
{
menuBuilder << "RicPasteSummaryCrossPlotFeature";

View File

@ -40,6 +40,7 @@
#include "RiuMainWindow.h"
#include "RiuProjectPropertyView.h"
#include "RimAnalysisPlotCollection.h"
CAF_PDM_SOURCE_INIT( RimMainPlotCollection, "MainPlotCollection" );
@ -65,6 +66,9 @@ RimMainPlotCollection::RimMainPlotCollection()
CAF_PDM_InitFieldNoDefault( &m_summaryPlotCollection, "SummaryPlotCollection", "Summary Plots", "", "", "" );
m_summaryPlotCollection.uiCapability()->setUiHidden( true );
CAF_PDM_InitFieldNoDefault( &m_analysisPlotCollection, "AnalysisPlotCollection", "Analysis Plots", "", "", "" );
m_analysisPlotCollection.uiCapability()->setUiHidden( true );
CAF_PDM_InitFieldNoDefault( &m_summaryCrossPlotCollection, "SummaryCrossPlotCollection", "Summary Cross Plots", "", "", "" );
m_summaryCrossPlotCollection.uiCapability()->setUiHidden( true );
@ -94,6 +98,7 @@ RimMainPlotCollection::RimMainPlotCollection()
m_gridCrossPlotCollection = new RimGridCrossPlotCollection;
m_saturationPressurePlotCollection = new RimSaturationPressurePlotCollection;
m_multiPlotCollection = new RimMultiPlotCollection;
m_analysisPlotCollection = new RimAnalysisPlotCollection;
}
//--------------------------------------------------------------------------------------------------
@ -192,6 +197,14 @@ RimMultiPlotCollection* RimMainPlotCollection::multiPlotCollection()
return m_multiPlotCollection();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimAnalysisPlotCollection* RimMainPlotCollection::analysisPlotCollection()
{
return m_analysisPlotCollection();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -206,6 +219,7 @@ void RimMainPlotCollection::deleteAllContainedObjects()
m_flowPlotCollection()->closeDefaultPlotWindowAndDeletePlots();
m_saturationPressurePlotCollection()->deleteAllChildObjects();
m_multiPlotCollection()->deleteAllChildObjects();
m_analysisPlotCollection()->deleteAllChildObjects();
}
//--------------------------------------------------------------------------------------------------

View File

@ -26,6 +26,7 @@
#include <QDockWidget>
#include <QPointer>
class RimAnalysisPlotCollection;
class RimWellLogPlotCollection;
class RimRftPlotCollection;
class RimPltPlotCollection;
@ -56,6 +57,7 @@ public:
RimPltPlotCollection* pltPlotCollection();
RimSummaryPlotCollection* summaryPlotCollection();
RimSummaryCrossPlotCollection* summaryCrossPlotCollection();
RimAnalysisPlotCollection* analysisPlotCollection();
RimFlowPlotCollection* flowPlotCollection();
RimGridCrossPlotCollection* gridCrossPlotCollection();
RimSaturationPressurePlotCollection* saturationPressurePlotCollection();
@ -80,6 +82,7 @@ private:
caf::PdmChildField<RimPltPlotCollection*> m_pltPlotCollection;
caf::PdmChildField<RimSummaryPlotCollection*> m_summaryPlotCollection;
caf::PdmChildField<RimSummaryCrossPlotCollection*> m_summaryCrossPlotCollection;
caf::PdmChildField<RimAnalysisPlotCollection*> m_analysisPlotCollection;
caf::PdmChildField<RimFlowPlotCollection*> m_flowPlotCollection;
caf::PdmChildField<RimGridCrossPlotCollection*> m_gridCrossPlotCollection;
caf::PdmChildField<RimSaturationPressurePlotCollection*> m_saturationPressurePlotCollection;

View File

@ -33,6 +33,7 @@
#include "PlotTemplates/RimPlotTemplateFolderItem.h"
#include "RimAdvancedSnapshotExportDefinition.h"
#include "RimAnalysisPlotCollection.h"
#include "RimAnnotationCollection.h"
#include "RimAnnotationInViewCollection.h"
#include "RimCalcScript.h"
@ -1320,6 +1321,11 @@ void RimProject::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, Q
itemCollection->add( mainPlotCollection->summaryPlotCollection() );
}
if ( mainPlotCollection->analysisPlotCollection() )
{
itemCollection->add( mainPlotCollection->analysisPlotCollection() );
}
if ( mainPlotCollection->summaryCrossPlotCollection() )
{
itemCollection->add( mainPlotCollection->summaryCrossPlotCollection() );

View File

@ -0,0 +1,26 @@
set (SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RiuGroupedBarChartBuilder.h
)
set (SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RiuGroupedBarChartBuilder.cpp
)
list(APPEND CODE_HEADER_FILES
${SOURCE_GROUP_HEADER_FILES}
)
list(APPEND CODE_SOURCE_FILES
${SOURCE_GROUP_SOURCE_FILES}
)
list(APPEND QT_MOC_HEADERS
)
list(APPEND QT_UI_FILES
)
source_group( "UserInterface/AnalysisPlots" FILES ${SOURCE_GROUP_HEADER_FILES} ${SOURCE_GROUP_SOURCE_FILES} ${CMAKE_CURRENT_LIST_DIR}/CMakeLists_files.cmake )

View File

@ -0,0 +1,470 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RiuGroupedBarChartBuilder.h"
#include "RiaColorTables.h"
#include "qwt_column_symbol.h"
#include "qwt_legend.h"
#include "qwt_painter.h"
#include "qwt_plot.h"
#include "qwt_plot_barchart.h"
#include "qwt_scale_draw.h"
#include <limits>
#include <map>
//--------------------------------------------------------------------------------------------------
/// Overriding to avoid one-pixel overlap of bars.
//--------------------------------------------------------------------------------------------------
class RiuAvoidPixelOverlapColumnSymbol : public QwtColumnSymbol
{
public:
RiuAvoidPixelOverlapColumnSymbol( Style style )
: QwtColumnSymbol( style )
{
}
void draw( QPainter* painter, const QwtColumnRect& rect ) const override
{
painter->save();
switch ( this->style() )
{
case QwtColumnSymbol::Box:
{
switch ( this->frameStyle() )
{
case QwtColumnSymbol::NoFrame:
{
QRectF r = rect.toRect();
if ( QwtPainter::roundingAlignment( painter ) )
{
r.setLeft( qRound( r.left() ) );
r.setRight( qRound( r.right() ) );
r.setTop( qRound( r.top() ) );
r.setBottom( qRound( r.bottom() ) );
}
painter->fillRect( r,
this->palette()
.window() ); // This line here is the difference. Qwt adds a 1 to width and height.
}
break;
default:
QwtColumnSymbol::drawBox( painter, rect );
}
break;
}
default:;
}
painter->restore();
}
};
//--------------------------------------------------------------------------------------------------
/// Overridden ScaleDraw to add labels for med and min ticks, and to add newlines to get the
/// tick texts on different height according to tick level
//--------------------------------------------------------------------------------------------------
class RiuBarChartScaleDraw : public QwtScaleDraw
{
public:
RiuBarChartScaleDraw( const std::map<double, std::pair<QwtScaleDiv::TickType, QString>>& posTickTypeAndTexts )
: m_posTickTypeAndTexts( posTickTypeAndTexts )
{
}
/// Override to add new lines to the labels according to the tick level
QwtText label( double v ) const override
{
auto posTypeTextPairIt = m_posTickTypeAndTexts.find( v );
if ( posTypeTextPairIt != m_posTickTypeAndTexts.end() )
{
if ( this->alignment() == BottomScale )
{
if ( posTypeTextPairIt->second.first == QwtScaleDiv::MediumTick )
{
return "\n" + posTypeTextPairIt->second.second;
}
else if ( posTypeTextPairIt->second.first == QwtScaleDiv::MajorTick )
{
return "\n\n" + posTypeTextPairIt->second.second;
}
else
{
return posTypeTextPairIt->second.second;
}
}
else if ( this->alignment() == LeftScale )
{
if ( posTypeTextPairIt->second.first == QwtScaleDiv::MediumTick )
{
return posTypeTextPairIt->second.second + " ";
}
else if ( posTypeTextPairIt->second.first == QwtScaleDiv::MajorTick )
{
return posTypeTextPairIt->second.second + " ";
}
else
{
return posTypeTextPairIt->second.second;
}
}
else
{
return posTypeTextPairIt->second.second;
}
}
else
{
return "X"; // Just for debugging
}
}
// Override to draw text labels at medium and minor ticks also
void draw( QPainter* painter, const QPalette& palette ) const override
{
QwtScaleDraw::draw( painter, palette );
if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
{
painter->save();
painter->setPen( palette.color( QPalette::Text ) );
const QList<double>& mediumTicks = scaleDiv().ticks( QwtScaleDiv::MediumTick );
for ( int i = 0; i < mediumTicks.count(); i++ )
{
const double v = mediumTicks[i];
if ( scaleDiv().contains( v ) ) drawLabel( painter, mediumTicks[i] );
}
const QList<double>& minorTicks = scaleDiv().ticks( QwtScaleDiv::MinorTick );
for ( int i = 0; i < minorTicks.count(); i++ )
{
const double v = minorTicks[i];
if ( scaleDiv().contains( v ) ) drawLabel( painter, minorTicks[i] );
}
painter->restore();
}
}
private:
std::map<double, std::pair<QwtScaleDiv::TickType, QString>> m_posTickTypeAndTexts;
bool m_hasMinorTickText;
bool m_hasMediumTickText;
protected:
virtual void drawBackbone( QPainter* ) const override {}
};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuGroupedBarChartBuilder::RiuGroupedBarChartBuilder( Qt::Orientation orientation )
: m_orientation( orientation )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuGroupedBarChartBuilder::addBarEntry( const QString& majorTickText,
const QString& midTickText,
const QString& minTickText,
const double sortValue,
const QString& legendText,
const double value )
{
m_sortedBarEntries.insert( BarEntry( majorTickText, midTickText, minTickText, sortValue, legendText, value ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuGroupedBarChartBuilder::addBarChartToPlot( QwtPlot* plot )
{
const double majGroupSpacing = 1.6;
const double midGroupSpacing = 0.5;
const double minGroupSpacing = 0.2;
std::set<QString> majTickTexts;
std::set<QString> midTickTexts;
std::set<QString> minTickTexts;
double currentBarPosition = 1.0;
double currentMajGroupStartPos = 1.0;
double currentMidGroupStartPos = 1.0;
double currentMinGroupStartPos = 1.0;
QString previousMajText;
QString previousMidText;
QString previousMinText;
std::map<QString, QVector<QPointF>> legendToBarPointsMap;
std::map<double, std::pair<QwtScaleDiv::TickType, QString>> positionedAxisTexts;
QList<double> majTickPoss;
QList<double> midTickPoss;
QList<double> minTickPoss;
// clang-format off
auto addGroupTickText = [&]( double groupStartPos, QString tickText, QList<double>& groupTickPosList )
{
if( tickText.isEmpty() ) return;
double tickPos = midPoint( groupStartPos, currentBarPosition );
QwtScaleDiv::TickType ttyp = &groupTickPosList == &majTickPoss
? QwtScaleDiv::MajorTick
: &groupTickPosList == &midTickPoss ? QwtScaleDiv::MediumTick
: QwtScaleDiv::MinorTick;
// Make sure we do not get ticks of different level exactly at the same spot,
// so that the drawing is able to distinguish
if( ttyp == QwtScaleDiv::MinorTick ) tickPos += 2e-4;
if( ttyp == QwtScaleDiv::MediumTick ) tickPos += 1e-4;
positionedAxisTexts[tickPos] = { ttyp, tickText };
groupTickPosList.push_back( tickPos );
};
// clang-format on
for ( const BarEntry& barDef : m_sortedBarEntries )
{
auto majInsertResult = majTickTexts.insert( barDef.m_majTickText );
bool isStartingNewMajGroup = majInsertResult.second;
bool isFinishingMajGroup = isStartingNewMajGroup && !previousMajText.isEmpty();
if ( isFinishingMajGroup )
{
addGroupTickText( currentMajGroupStartPos, previousMajText, majTickPoss );
addGroupTickText( currentMidGroupStartPos, previousMidText, midTickPoss );
addGroupTickText( currentMinGroupStartPos, previousMinText, minTickPoss );
currentBarPosition += majGroupSpacing;
}
if ( isStartingNewMajGroup )
{
previousMajText = barDef.m_majTickText;
previousMidText = "";
previousMinText = "";
midTickTexts.clear();
minTickTexts.clear();
currentMajGroupStartPos = currentBarPosition;
currentMidGroupStartPos = currentBarPosition;
currentMinGroupStartPos = currentBarPosition;
}
auto midInsertResult = midTickTexts.insert( barDef.m_midTickText );
bool isStartingNewMidGroup = midInsertResult.second;
bool isFinishingMidGroup = isStartingNewMidGroup && !previousMidText.isEmpty();
if ( isFinishingMidGroup )
{
addGroupTickText( currentMidGroupStartPos, previousMidText, midTickPoss );
addGroupTickText( currentMinGroupStartPos, previousMinText, minTickPoss );
currentBarPosition += midGroupSpacing;
}
if ( isStartingNewMidGroup )
{
previousMidText = barDef.m_midTickText;
previousMinText = "";
minTickTexts.clear();
currentMidGroupStartPos = currentBarPosition;
currentMinGroupStartPos = currentBarPosition;
}
auto minInsertResult = minTickTexts.insert( barDef.m_minTickText );
bool isStartingNewMinGroup = minInsertResult.second;
bool isFinishingMinGroup = minInsertResult.second && !previousMinText.isEmpty();
if ( isFinishingMinGroup )
{
addGroupTickText( currentMinGroupStartPos, previousMinText, minTickPoss );
currentBarPosition += minGroupSpacing;
}
if ( isStartingNewMinGroup )
{
previousMinText = barDef.m_minTickText;
currentMinGroupStartPos = currentBarPosition;
}
// Insert bar value in correct set of colored bars
auto legendToBarPointsPair = legendToBarPointsMap.find( barDef.m_legendText );
QVector<QPointF>* barPoints = nullptr;
if ( legendToBarPointsPair == legendToBarPointsMap.end() )
{
barPoints = &( legendToBarPointsMap[barDef.m_legendText] );
}
else
{
barPoints = &( legendToBarPointsPair->second );
}
barPoints->push_back( {currentBarPosition, barDef.m_value} );
// Increment the bar position for the next bar
currentBarPosition += 1.0;
}
// Add group tick texts for the last groups
if ( !previousMajText.isEmpty() )
{
addGroupTickText( currentMajGroupStartPos, previousMajText, majTickPoss );
}
if ( !previousMidText.isEmpty() )
{
addGroupTickText( currentMidGroupStartPos, previousMidText, midTickPoss );
}
if ( !previousMinText.isEmpty() )
{
addGroupTickText( currentMinGroupStartPos, previousMinText, minTickPoss );
}
int idx = 0;
for ( const auto& legendToBarPointsPair : legendToBarPointsMap )
{
addQwtBarChart( plot,
legendToBarPointsPair.second,
legendToBarPointsPair.first,
RiaColorTables::summaryCurveDefaultPaletteColors().cycledQColor( idx ) );
idx++;
}
QwtPlot::Axis axis = QwtPlot::xBottom;
RiuBarChartScaleDraw* scaleDrawer = new RiuBarChartScaleDraw( positionedAxisTexts );
if ( m_orientation == Qt::Horizontal )
{
axis = QwtPlot::yLeft;
}
plot->setAxisScaleDraw( axis, scaleDrawer );
QwtScaleDiv scaleDiv( 0, currentBarPosition );
if ( majTickPoss.size() ) scaleDiv.setTicks( QwtScaleDiv::MajorTick, majTickPoss );
if ( midTickPoss.size() ) scaleDiv.setTicks( QwtScaleDiv::MediumTick, midTickPoss );
if ( minTickPoss.size() ) scaleDiv.setTicks( QwtScaleDiv::MinorTick, minTickPoss );
if ( m_orientation == Qt::Horizontal )
{
scaleDiv.invert();
}
plot->setAxisScaleDiv( axis, scaleDiv );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuGroupedBarChartBuilder::addQwtBarChart( QwtPlot* plot,
const QVector<QPointF>& posAndValue,
const QString& legendText,
const QColor& barColor )
{
QPalette palette;
palette.setColor( QPalette::Window, barColor );
palette.setColor( QPalette::Dark, barColor );
RiuAvoidPixelOverlapColumnSymbol* barStyle = new RiuAvoidPixelOverlapColumnSymbol( QwtColumnSymbol::Box );
barStyle->setPalette( palette );
barStyle->setFrameStyle( QwtColumnSymbol::NoFrame );
barStyle->setLineWidth( 0 );
QwtPlotBarChart* barChart = new QwtPlotBarChart( legendText );
barChart->setSamples( posAndValue );
barChart->setLegendMode( QwtPlotBarChart::LegendChartTitle );
barChart->setLayoutPolicy( QwtPlotAbstractBarChart::ScaleSamplesToAxes );
barChart->setLayoutHint( 1.0 );
barChart->setSymbol( barStyle );
barChart->setOrientation( m_orientation );
barChart->attach( plot );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuGroupedBarChartBuilder::BarEntry::BarEntry()
: m_sortValue( std::numeric_limits<double>::infinity() )
, m_value( std::numeric_limits<double>::infinity() )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuGroupedBarChartBuilder::BarEntry::BarEntry( QString majorTickText,
QString midTickText,
QString minTickText,
double sortValue,
QString legendText,
double value )
: m_majTickText( majorTickText )
, m_midTickText( midTickText )
, m_minTickText( minTickText )
, m_sortValue( sortValue )
, m_legendText( legendText )
, m_value( value )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuGroupedBarChartBuilder::BarEntry::operator<( const BarEntry& other ) const
{
if ( m_majTickText != other.m_majTickText ) return m_majTickText < other.m_majTickText;
if ( m_midTickText != other.m_midTickText ) return m_midTickText < other.m_midTickText;
if ( m_minTickText != other.m_minTickText ) return m_minTickText < other.m_minTickText;
if ( m_sortValue != other.m_sortValue )
{
return m_sortValue > other.m_sortValue;
}
if ( m_legendText != other.m_legendText ) return m_legendText < other.m_legendText;
return false;
}

View File

@ -0,0 +1,81 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 <QString>
#include <QVector>
#include <set>
class QwtPlot;
class QColor;
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
class RiuGroupedBarChartBuilder
{
public:
RiuGroupedBarChartBuilder( Qt::Orientation orientation );
void addBarEntry( const QString& majorTickText,
const QString& midTickText,
const QString& minTickText,
const double sortValue,
const QString& legendText,
const double value );
void addBarChartToPlot( QwtPlot* plot );
private:
double midPoint( double v1, double v2 )
{
return v1 + 0.5 * ( v2 - 1.0 - v1 );
}
void addQwtBarChart( QwtPlot* plot,
const QVector<QPointF>& posAndValue,
const QString& legendText,
const QColor& barColor );
struct BarEntry
{
BarEntry();
BarEntry( QString majorTickText,
QString midTickText,
QString minTickText,
double sortValue,
QString legendText,
double value );
QString m_majTickText;
QString m_midTickText;
QString m_minTickText;
double m_sortValue;
QString m_legendText;
double m_value;
bool operator<( const BarEntry& other ) const;
};
std::multiset<BarEntry> m_sortedBarEntries;
Qt::Orientation m_orientation;
};