You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationDl...

1069 lines
37 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmWxPropertyInterpolationDlg.h"
#include <QAbstractItemView>
#include <QApplication>
#include <QCheckBox>
#include <QComboBox>
#include <QCoreApplication>
#include <QDoubleSpinBox>
#include <QEvent>
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QIcon>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QMouseEvent>
#include <QPainter>
#include <QPolygonF>
#include <QPushButton>
#include <QSize>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QTransform>
#include <QVBoxLayout>
#include <QtCore/qmath.h>
#include "nmCalculationUtils.h"
#include "nmDataOutline.h"
#include "nmGuiPlot.h"
#include "nmObjPoint.h"
#include "nmWxPropertyInterpolationPreviewDlg.h"
#include "tCurvePlotView.h"
#include "ZxObjText.h"
#include "ZxPlot.h"
namespace
{
// 使用独立翻译上下文,便于统一维护对话框文本.
QString interpolationText(const char* sourceText)
{
return QCoreApplication::translate("nmWxPropertyInterpolationDlg", sourceText);
}
// 创建统一格式的非负数值输入框.
QDoubleSpinBox* createNumberSpinBox(QWidget* parent, double value)
{
QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
spinBox->setRange(0.0, 1.0e12);
spinBox->setDecimals(6);
spinBox->setValue(value);
spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons);
return spinBox;
}
// 插值测点使用独立的小方块标记,避免与井图元混淆.
class PropertyInterpolationMarker : public nmObjPoint
{
public:
PropertyInterpolationMarker(const QString& name,
ZxSubAxisX* axisX,
ZxSubAxisY* axisY)
: nmObjPoint(name, axisX, axisY)
{
setDotRadius(1.0);
}
virtual bool drawWellPos(QPainter* painter, QPointF point) override
{
if(painter == NULL) {
return false;
}
const QTransform transform = painter->combinedTransform();
qreal scaleX = qAbs(transform.m11());
qreal scaleY = qAbs(transform.m22());
if(scaleX < 0.0001) {
scaleX = 1.0;
}
if(scaleY < 0.0001) {
scaleY = 1.0;
}
const qreal halfPixelSize = 3.0;
QRectF markerRect(point.x() - halfPixelSize / scaleX,
point.y() - halfPixelSize / scaleY,
halfPixelSize * 2.0 / scaleX,
halfPixelSize * 2.0 / scaleY);
painter->save();
QPen pen(QColor(0, 25, 100));
pen.setWidthF(1.0);
pen.setCosmetic(true);
painter->setPen(pen);
painter->setBrush(QColor(0, 70, 200));
painter->drawRect(markerRect);
painter->restore();
return true;
}
};
void setMarkerLabel(nmObjPoint* marker, const QString& text)
{
if(marker == NULL || marker->getChildren().isEmpty()) {
return;
}
ZxObjText* label = dynamic_cast<ZxObjText*>(marker->at(0));
if(label != NULL) {
label->setText(text);
label->setTextColor(QColor(30, 30, 30));
}
}
// 将储层边界转换成预览裁剪使用的多边形,并计算规则采样范围.
bool createPreviewOutline(nmDataOutline* outline,
QVector<QPointF>& outlinePoints,
QRectF& bounds)
{
outlinePoints.clear();
bounds = QRectF();
if(outline == NULL) {
return false;
}
if(outline->getOutlineType() == NM_Round_Outline_Type) {
// 圆形边界离散成多边形,便于预览窗口统一裁剪.
const QPointF center = outline->getCenter();
const double radius = outline->getRadius();
if(radius <= 0.0) {
return false;
}
const int segmentCount = 72;
const double fullCircle = 6.28318530717958647692;
for(int i = 0; i < segmentCount; ++i) {
const double angle = fullCircle * i / segmentCount;
outlinePoints.append(QPointF(center.x() + radius * qCos(angle),
center.y() + radius * qSin(angle)));
}
} else {
outlinePoints = outline->getOutlinePoints();
if(outlinePoints.size() > 3 &&
outlinePoints.first() == outlinePoints.last()) {
outlinePoints.remove(outlinePoints.size() - 1);
}
}
if(outlinePoints.size() < 3) {
return false;
}
bounds = QPolygonF(outlinePoints).boundingRect().normalized();
return bounds.width() > 0.0 && bounds.height() > 0.0;
}
}
nmWxPropertyInterpolationDlg::nmWxPropertyInterpolationDlg(
nmGuiPlot* plot,
nmDataAnalyzeManager* dataManager,
QWidget* parent)
: iDlgBase(parent),
m_pPlot(plot),
m_pDataManager(dataManager),
m_pDataSetCombo(NULL),
m_pAddDataSetButton(NULL),
m_pDeleteDataSetButton(NULL),
m_nCurrentDataSetIndex(-1),
m_nNextPointId(0),
m_bUpdatingUi(true),
m_pPropertyCombo(NULL),
m_pNameEdit(NULL),
m_pNewValueSpin(NULL),
m_pMapPickButton(NULL),
m_pPointTable(NULL),
m_pDeletePointButton(NULL),
m_pClearPointsButton(NULL),
m_pShowPointsCheck(NULL),
m_pShowLabelsCheck(NULL),
m_pNuggetSpin(NULL),
m_pSillSpin(NULL),
m_pRangeSpin(NULL),
m_pModelCombo(NULL),
m_pPreviewButton(NULL),
m_pApplyButton(NULL)
{
setWindowTitle(interpolationText("Property Interpolation"));
setWindowModality(Qt::NonModal);
setModal(false);
setMinimumSize(520, 620);
resize(560, 680);
initInterpolationUI();
initializeDataSets();
if(qApp != NULL) {
qApp->installEventFilter(this);
}
}
nmWxPropertyInterpolationDlg::~nmWxPropertyInterpolationDlg()
{
if(qApp != NULL) {
qApp->removeEventFilter(this);
}
onMapPickToggled(false);
saveCurrentDataSet();
syncDataSetsToManager(false);
removeAllMarkers();
}
void nmWxPropertyInterpolationDlg::initInterpolationUI()
{
QVBoxLayout* mainLayout = new QVBoxLayout(this);
// 数据组创建、切换及删除.
QHBoxLayout* dataSetSelectLayout = new QHBoxLayout;
dataSetSelectLayout->addWidget(new QLabel(interpolationText("Dataset:"), this));
m_pDataSetCombo = new QComboBox(this);
dataSetSelectLayout->addWidget(m_pDataSetCombo, 1);
QString iconDir = QCoreApplication::applicationDirPath();
iconDir = iconDir.section('/', 0, -2) + "/Res/Icon/";
m_pAddDataSetButton = new QPushButton(this);
m_pAddDataSetButton->setIcon(QIcon(iconDir + "Add.png"));
m_pAddDataSetButton->setIconSize(QSize(18, 18));
m_pAddDataSetButton->setFixedSize(28, 28);
m_pAddDataSetButton->setToolTip(interpolationText("Add dataset"));
dataSetSelectLayout->addWidget(m_pAddDataSetButton);
m_pDeleteDataSetButton = new QPushButton(this);
m_pDeleteDataSetButton->setIcon(QIcon(iconDir + "NmDelete.png"));
m_pDeleteDataSetButton->setIconSize(QSize(18, 18));
m_pDeleteDataSetButton->setFixedSize(28, 28);
m_pDeleteDataSetButton->setToolTip(interpolationText("Delete dataset"));
dataSetSelectLayout->addWidget(m_pDeleteDataSetButton);
mainLayout->addLayout(dataSetSelectLayout);
// 当前数据组的名称和属性.
QFormLayout* dataSetLayout = new QFormLayout;
m_pNameEdit = new QLineEdit(this);
dataSetLayout->addRow(interpolationText("Name:"), m_pNameEdit);
m_pPropertyCombo = new QComboBox(this);
m_pPropertyCombo->addItem(interpolationText("Permeability k"), QString("k"));
m_pPropertyCombo->addItem(interpolationText("Porosity phi"), QString("phi"));
m_pPropertyCombo->addItem(interpolationText("Reservoir thickness h"), QString("h"));
dataSetLayout->addRow(interpolationText("Property:"), m_pPropertyCombo);
mainLayout->addLayout(dataSetLayout);
// 测点数据录入区域.
QGroupBox* pointGroup = new QGroupBox(interpolationText("Measurement Points"), this);
QVBoxLayout* pointLayout = new QVBoxLayout(pointGroup);
QHBoxLayout* newPointLayout = new QHBoxLayout;
newPointLayout->addWidget(new QLabel(interpolationText("New value:"), pointGroup));
m_pNewValueSpin = createNumberSpinBox(pointGroup, 0.0);
newPointLayout->addWidget(m_pNewValueSpin, 1);
m_pMapPickButton = new QPushButton(interpolationText("Select on Map"), pointGroup);
m_pMapPickButton->setCheckable(true);
m_pMapPickButton->setEnabled(!m_pPlot.isNull() &&
m_pPlot->m_pPlotView != NULL &&
m_pPlot->m_pPlot != NULL);
newPointLayout->addWidget(m_pMapPickButton);
pointLayout->addLayout(newPointLayout);
m_pPointTable = new QTableWidget(0, 3, pointGroup);
QStringList headers;
headers << interpolationText("X") << interpolationText("Y") << interpolationText("Value");
m_pPointTable->setHorizontalHeaderLabels(headers);
m_pPointTable->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
m_pPointTable->setSelectionBehavior(QAbstractItemView::SelectRows);
m_pPointTable->setSelectionMode(QAbstractItemView::SingleSelection);
m_pPointTable->setAlternatingRowColors(true);
pointLayout->addWidget(m_pPointTable, 1);
QHBoxLayout* pointButtonLayout = new QHBoxLayout;
pointButtonLayout->addStretch();
m_pDeletePointButton = new QPushButton(interpolationText("Delete Selected"), pointGroup);
m_pDeletePointButton->setEnabled(false);
pointButtonLayout->addWidget(m_pDeletePointButton);
m_pClearPointsButton = new QPushButton(interpolationText("Clear"), pointGroup);
m_pClearPointsButton->setEnabled(false);
pointButtonLayout->addWidget(m_pClearPointsButton);
pointLayout->addLayout(pointButtonLayout);
QHBoxLayout* displayLayout = new QHBoxLayout;
m_pShowPointsCheck = new QCheckBox(interpolationText("Show measurement points"), pointGroup);
m_pShowPointsCheck->setChecked(true);
displayLayout->addWidget(m_pShowPointsCheck);
m_pShowLabelsCheck = new QCheckBox(interpolationText("Show labels"), pointGroup);
m_pShowLabelsCheck->setChecked(true);
displayLayout->addWidget(m_pShowLabelsCheck);
displayLayout->addStretch();
pointLayout->addLayout(displayLayout);
mainLayout->addWidget(pointGroup, 1);
// Kriging插值参数设置.
QGroupBox* parameterGroup = new QGroupBox(interpolationText("Kriging Parameters"), this);
QFormLayout* parameterLayout = new QFormLayout(parameterGroup);
m_pNuggetSpin = createNumberSpinBox(parameterGroup, 0.01);
parameterLayout->addRow(interpolationText("Nugget:"), m_pNuggetSpin);
m_pSillSpin = createNumberSpinBox(parameterGroup, 100.0);
parameterLayout->addRow(interpolationText("Sill:"), m_pSillSpin);
m_pRangeSpin = createNumberSpinBox(parameterGroup, 1000.0);
parameterLayout->addRow(interpolationText("Range:"), m_pRangeSpin);
m_pModelCombo = new QComboBox(parameterGroup);
m_pModelCombo->addItem(interpolationText("Spherical (0)"), 0);
m_pModelCombo->addItem(interpolationText("Exponential (1)"), 1);
m_pModelCombo->addItem(interpolationText("Gaussian (2)"), 2);
parameterLayout->addRow(interpolationText("Model:"), m_pModelCombo);
mainLayout->addWidget(parameterGroup);
// 底部操作按钮.
QHBoxLayout* buttonLayout = new QHBoxLayout;
m_pPreviewButton = new QPushButton(interpolationText("Preview"), this);
m_pPreviewButton->setEnabled(false);
buttonLayout->addWidget(m_pPreviewButton);
buttonLayout->addStretch();
m_pApplyButton = new QPushButton(interpolationText("Apply"), this);
m_pApplyButton->setEnabled(false);
buttonLayout->addWidget(m_pApplyButton);
QPushButton* closeButton = new QPushButton(interpolationText("Close"), this);
buttonLayout->addWidget(closeButton);
mainLayout->addLayout(buttonLayout);
connect(m_pDataSetCombo, SIGNAL(currentIndexChanged(int)),
this, SLOT(onDataSetChanged(int)));
connect(m_pAddDataSetButton, SIGNAL(clicked()), this, SLOT(onAddDataSet()));
connect(m_pDeleteDataSetButton, SIGNAL(clicked()), this, SLOT(onDeleteDataSet()));
connect(m_pNameEdit, SIGNAL(textChanged(QString)),
this, SLOT(onDataSetNameChanged(QString)));
connect(m_pPropertyCombo, SIGNAL(currentIndexChanged(int)),
this, SLOT(onCurrentDataSetEdited()));
connect(m_pNuggetSpin, SIGNAL(valueChanged(double)),
this, SLOT(onCurrentDataSetEdited()));
connect(m_pSillSpin, SIGNAL(valueChanged(double)),
this, SLOT(onCurrentDataSetEdited()));
connect(m_pRangeSpin, SIGNAL(valueChanged(double)),
this, SLOT(onCurrentDataSetEdited()));
connect(m_pModelCombo, SIGNAL(currentIndexChanged(int)),
this, SLOT(onCurrentDataSetEdited()));
connect(m_pMapPickButton, SIGNAL(toggled(bool)), this, SLOT(onMapPickToggled(bool)));
connect(m_pPointTable, SIGNAL(itemSelectionChanged()),
this, SLOT(onPointSelectionChanged()));
connect(m_pPointTable, SIGNAL(itemChanged(QTableWidgetItem*)),
this, SLOT(onPointItemChanged(QTableWidgetItem*)));
connect(m_pDeletePointButton, SIGNAL(clicked()), this, SLOT(onDeleteSelectedPoint()));
connect(m_pClearPointsButton, SIGNAL(clicked()), this, SLOT(onClearPoints()));
connect(m_pShowPointsCheck, SIGNAL(toggled(bool)), this, SLOT(onShowPointsToggled(bool)));
connect(m_pShowLabelsCheck, SIGNAL(toggled(bool)), this, SLOT(onShowLabelsToggled(bool)));
connect(m_pPreviewButton, SIGNAL(clicked()), this, SLOT(onPreview()));
connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
}
void nmWxPropertyInterpolationDlg::initializeDataSets()
{
m_bUpdatingUi = true;
if(!m_pDataManager.isNull()) {
m_vecDataSets = m_pDataManager->getPropertyInterpolationDataSets();
}
bool dataChanged = false;
for(int i = 0; i < m_vecDataSets.size(); ++i) {
if(m_vecDataSets[i].name.isEmpty()) {
m_vecDataSets[i].name = nextDataSetName();
dataChanged = true;
}
}
m_vecMarkerNames.resize(m_vecDataSets.size());
// 测点标记不参与持久化,打开对话框时根据已保存测点重新创建.
createMarkersForAllDataSets();
bool oldBlock = m_pDataSetCombo->blockSignals(true);
m_pDataSetCombo->clear();
for(int i = 0; i < m_vecDataSets.size(); ++i) {
m_pDataSetCombo->addItem(m_vecDataSets[i].name);
}
const int initialIndex = m_vecDataSets.isEmpty() ? -1 : 0;
m_pDataSetCombo->setCurrentIndex(initialIndex);
m_pDataSetCombo->blockSignals(oldBlock);
m_nCurrentDataSetIndex = initialIndex;
loadDataSet(initialIndex);
m_bUpdatingUi = false;
updateDataSetButtons();
if(dataChanged) {
syncDataSetsToManager(false);
}
}
nmPropertyInterpolationDataSet nmWxPropertyInterpolationDlg::createDefaultDataSet()
{
nmPropertyInterpolationDataSet dataSet;
dataSet.name = nextDataSetName();
dataSet.property = "k";
dataSet.showPoints = true;
dataSet.showLabels = true;
dataSet.nugget = 0.01;
dataSet.sill = 100.0;
dataSet.range = 1000.0;
dataSet.model = 0;
return dataSet;
}
QString nmWxPropertyInterpolationDlg::nextDataSetName()
{
for(int number = 1; ; ++number) {
const QString name = interpolationText("Dataset #%1").arg(number);
bool exists = false;
for(int i = 0; i < m_vecDataSets.size(); ++i) {
if(m_vecDataSets[i].name == name) {
exists = true;
break;
}
}
if(!exists) {
return name;
}
}
}
void nmWxPropertyInterpolationDlg::loadDataSet(int index)
{
if(index < 0 || index >= m_vecDataSets.size()) {
clearCurrentDataSet();
return;
}
// 切换数据组时批量刷新控件,避免控件信号被误判为用户修改.
bool wasUpdating = m_bUpdatingUi;
m_bUpdatingUi = true;
const nmPropertyInterpolationDataSet& dataSet = m_vecDataSets[index];
m_pNameEdit->setText(dataSet.name);
int propertyIndex = m_pPropertyCombo->findData(dataSet.property);
m_pPropertyCombo->setCurrentIndex(propertyIndex < 0 ? 0 : propertyIndex);
m_pNuggetSpin->setValue(dataSet.nugget);
m_pSillSpin->setValue(dataSet.sill);
m_pRangeSpin->setValue(dataSet.range);
int modelIndex = m_pModelCombo->findData(dataSet.model);
m_pModelCombo->setCurrentIndex(modelIndex < 0 ? 0 : modelIndex);
m_pShowPointsCheck->setChecked(dataSet.showPoints);
m_pShowLabelsCheck->setChecked(dataSet.showLabels);
bool oldTableBlock = m_pPointTable->blockSignals(true);
m_pPointTable->setRowCount(0);
const QStringList markerNames = m_vecMarkerNames.value(index);
for(int i = 0; i < dataSet.points.size(); ++i) {
appendPointRow(dataSet.points[i], markerNames.value(i));
}
m_pPointTable->blockSignals(oldTableBlock);
clearTableSelection();
updatePointButtons();
updateCurrentMarkerVisibility();
m_bUpdatingUi = wasUpdating;
updateDataSetButtons();
}
void nmWxPropertyInterpolationDlg::clearCurrentDataSet()
{
bool wasUpdating = m_bUpdatingUi;
m_bUpdatingUi = true;
if(m_pMapPickButton->isChecked()) {
m_pMapPickButton->setChecked(false);
}
m_pNameEdit->clear();
m_pPropertyCombo->setCurrentIndex(-1);
m_pNewValueSpin->setValue(0.0);
m_pPointTable->setRowCount(0);
m_pShowPointsCheck->setChecked(false);
m_pShowLabelsCheck->setChecked(false);
m_pNuggetSpin->setValue(0.01);
m_pSillSpin->setValue(100.0);
m_pRangeSpin->setValue(1000.0);
m_pModelCombo->setCurrentIndex(-1);
clearTableSelection();
m_bUpdatingUi = wasUpdating;
updateDataSetButtons();
}
void nmWxPropertyInterpolationDlg::saveCurrentDataSet()
{
if(m_nCurrentDataSetIndex < 0 ||
m_nCurrentDataSetIndex >= m_vecDataSets.size()) {
return;
}
nmPropertyInterpolationDataSet& dataSet = m_vecDataSets[m_nCurrentDataSetIndex];
dataSet.name = m_pNameEdit->text();
dataSet.property = m_pPropertyCombo->itemData(m_pPropertyCombo->currentIndex()).toString();
dataSet.nugget = m_pNuggetSpin->value();
dataSet.sill = m_pSillSpin->value();
dataSet.range = m_pRangeSpin->value();
dataSet.model = m_pModelCombo->itemData(m_pModelCombo->currentIndex()).toInt();
dataSet.showPoints = m_pShowPointsCheck->isChecked();
dataSet.showLabels = m_pShowLabelsCheck->isChecked();
}
void nmWxPropertyInterpolationDlg::syncDataSetsToManager(bool markModified)
{
if(!m_pDataManager.isNull()) {
m_pDataManager->setPropertyInterpolationDataSets(m_vecDataSets);
}
if(markModified && !m_pPlot.isNull()) {
m_pPlot->setModified(true);
}
}
void nmWxPropertyInterpolationDlg::updateDataSetButtons()
{
const bool hasCurrent = m_nCurrentDataSetIndex >= 0 &&
m_nCurrentDataSetIndex < m_vecDataSets.size();
const bool canPickOnMap = hasCurrent && !m_pPlot.isNull() &&
m_pPlot->m_pPlotView != NULL && m_pPlot->m_pPlot != NULL;
m_pDataSetCombo->setEnabled(hasCurrent);
m_pDeleteDataSetButton->setEnabled(hasCurrent);
m_pNameEdit->setEnabled(hasCurrent);
m_pPropertyCombo->setEnabled(hasCurrent);
m_pNewValueSpin->setEnabled(hasCurrent);
m_pMapPickButton->setEnabled(canPickOnMap);
m_pPointTable->setEnabled(hasCurrent);
m_pShowPointsCheck->setEnabled(hasCurrent);
m_pShowLabelsCheck->setEnabled(hasCurrent);
m_pNuggetSpin->setEnabled(hasCurrent);
m_pSillSpin->setEnabled(hasCurrent);
m_pRangeSpin->setEnabled(hasCurrent);
m_pModelCombo->setEnabled(hasCurrent);
updatePointButtons();
}
void nmWxPropertyInterpolationDlg::onDataSetChanged(int index)
{
if(m_bUpdatingUi || index < 0 || index >= m_vecDataSets.size() ||
index == m_nCurrentDataSetIndex) {
return;
}
saveCurrentDataSet();
syncDataSetsToManager(false);
m_nCurrentDataSetIndex = index;
loadDataSet(index);
}
void nmWxPropertyInterpolationDlg::onAddDataSet()
{
saveCurrentDataSet();
m_vecDataSets.append(createDefaultDataSet());
m_vecMarkerNames.append(QStringList());
int newIndex = m_vecDataSets.size() - 1;
bool oldBlock = m_pDataSetCombo->blockSignals(true);
m_pDataSetCombo->addItem(m_vecDataSets[newIndex].name);
m_pDataSetCombo->setCurrentIndex(newIndex);
m_pDataSetCombo->blockSignals(oldBlock);
m_nCurrentDataSetIndex = newIndex;
loadDataSet(newIndex);
updateDataSetButtons();
syncDataSetsToManager(true);
}
void nmWxPropertyInterpolationDlg::onDeleteDataSet()
{
if(m_nCurrentDataSetIndex < 0 ||
m_nCurrentDataSetIndex >= m_vecDataSets.size()) {
return;
}
int removedIndex = m_nCurrentDataSetIndex;
removeDataSetMarkers(removedIndex);
m_vecDataSets.remove(removedIndex);
m_vecMarkerNames.remove(removedIndex);
const int newIndex = m_vecDataSets.isEmpty() ? -1 :
qMin(removedIndex, m_vecDataSets.size() - 1);
bool oldBlock = m_pDataSetCombo->blockSignals(true);
m_pDataSetCombo->removeItem(removedIndex);
m_pDataSetCombo->setCurrentIndex(newIndex);
m_pDataSetCombo->blockSignals(oldBlock);
m_nCurrentDataSetIndex = newIndex;
loadDataSet(newIndex);
updateDataSetButtons();
syncDataSetsToManager(true);
}
void nmWxPropertyInterpolationDlg::onDataSetNameChanged(const QString& name)
{
if(m_bUpdatingUi || m_nCurrentDataSetIndex < 0 ||
m_nCurrentDataSetIndex >= m_vecDataSets.size()) {
return;
}
m_vecDataSets[m_nCurrentDataSetIndex].name = name;
m_pDataSetCombo->setItemText(m_nCurrentDataSetIndex, name);
syncDataSetsToManager(true);
}
void nmWxPropertyInterpolationDlg::onCurrentDataSetEdited()
{
if(m_bUpdatingUi) {
return;
}
saveCurrentDataSet();
syncDataSetsToManager(true);
}
bool nmWxPropertyInterpolationDlg::eventFilter(QObject* watched, QEvent* event)
{
// 点击表格空白处或其他控件时取消测点行的选中状态.
if(event != NULL && event->type() == QEvent::MouseButtonPress && m_pPointTable != NULL) {
QWidget* clickedWidget = qobject_cast<QWidget*>(watched);
if(clickedWidget != NULL) {
bool isTableViewport = clickedWidget == m_pPointTable->viewport() ||
m_pPointTable->viewport()->isAncestorOf(clickedWidget);
if(clickedWidget == m_pPointTable->viewport()) {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
if(m_pPointTable->itemAt(mouseEvent->pos()) == NULL) {
clearTableSelection();
}
} else if(!isTableViewport && clickedWidget != m_pDeletePointButton) {
clearTableSelection();
}
}
}
return iDlgBase::eventFilter(watched, event);
}
void nmWxPropertyInterpolationDlg::onMapPickToggled(bool checked)
{
if(m_pPlot.isNull() || m_pPlot->m_pPlotView == NULL) {
return;
}
// 每次切换前先断开旧连接,防止一次地图点击被重复处理.
disconnect(m_pPlot->m_pPlotView, SIGNAL(sigLeftClick(QPointF)),
this, SLOT(onMapClicked(QPointF)));
if(checked) {
m_pPlot->cancelActiveTools();
connect(m_pPlot->m_pPlotView, SIGNAL(sigLeftClick(QPointF)),
this, SLOT(onMapClicked(QPointF)));
m_pPlot->m_pPlotView->setCursor(Qt::CrossCursor);
} else {
m_pPlot->m_pPlotView->restoreCursor();
}
}
void nmWxPropertyInterpolationDlg::onMapClicked(const QPointF& position)
{
if(!m_pMapPickButton->isChecked() || m_pPlot.isNull() ||
m_pPlot->m_pPlot == NULL || m_nCurrentDataSetIndex < 0) {
return;
}
if(!m_pPlot->m_pPlot->getInnerRectF().contains(position)) {
return;
}
// 点击位置为绘图区坐标,需要转换成实际数据坐标后保存.
QPointF plotPosition = position;
QPointF valuePoint = m_pPlot->m_pPlot->getValueForPos(plotPosition);
appendPoint(valuePoint);
}
void nmWxPropertyInterpolationDlg::appendPoint(const QPointF& valuePoint)
{
if(m_nCurrentDataSetIndex < 0 ||
m_nCurrentDataSetIndex >= m_vecDataSets.size()) {
return;
}
nmPropertyInterpolationDataSet& dataSet = m_vecDataSets[m_nCurrentDataSetIndex];
double pointValue = m_pNewValueSpin->value();
nmPropertyInterpolationPointData point(valuePoint.x(), valuePoint.y(), pointValue);
QString markerName = createMarker(valuePoint, pointValue,
dataSet.showPoints, dataSet.showLabels);
// 测点数据、地图标记和表格行按相同顺序同步追加.
dataSet.points.append(point);
m_vecMarkerNames[m_nCurrentDataSetIndex].append(markerName);
bool oldBlock = m_pPointTable->blockSignals(true);
appendPointRow(point, markerName);
m_pPointTable->blockSignals(oldBlock);
m_pPointTable->scrollToBottom();
updatePointButtons();
syncDataSetsToManager(true);
}
void nmWxPropertyInterpolationDlg::appendPointRow(
const nmPropertyInterpolationPointData& point,
const QString& markerName)
{
int row = m_pPointTable->rowCount();
m_pPointTable->insertRow(row);
QTableWidgetItem* xItem = new QTableWidgetItem(QString::number(point.x, 'g', 15));
xItem->setFlags(xItem->flags() & ~Qt::ItemIsEditable);
xItem->setData(Qt::UserRole, markerName);
m_pPointTable->setItem(row, 0, xItem);
QTableWidgetItem* yItem = new QTableWidgetItem(QString::number(point.y, 'g', 15));
yItem->setFlags(yItem->flags() & ~Qt::ItemIsEditable);
m_pPointTable->setItem(row, 1, yItem);
m_pPointTable->setItem(row, 2,
new QTableWidgetItem(QString::number(point.value, 'f', 6)));
}
QString nmWxPropertyInterpolationDlg::createMarker(const QPointF& valuePosition,
double value,
bool showPoints,
bool showLabels)
{
if(m_pPlot.isNull() || m_pPlot->m_pPlot == NULL) {
return QString();
}
QString markerName;
do {
markerName = QString("__PropertyInterpolationPoint_%1").arg(++m_nNextPointId);
} while(m_pPlot->m_pPlot->getObjByName(markerName) != NULL);
// 地图标记仅用于临时显示,不应单独改变工程修改状态.
bool wasModified = m_pPlot->isModified();
PropertyInterpolationMarker* marker = new PropertyInterpolationMarker(
markerName,
m_pPlot->m_pPlot->getMainAxisX(),
m_pPlot->m_pPlot->getMainAxisY());
m_pPlot->bindObjSignals(marker);
if(!m_pPlot->m_pPlot->addOneObj(marker)) {
delete marker;
m_pPlot->setModified(wasModified);
return QString();
}
QVector<QPointF> values;
values.append(valuePosition);
marker->setAllValues(values);
m_pPlot->slotObjCompleted(marker);
setMarkerLabel(marker, QString::number(value, 'g', 10));
marker->setReadOnly(true);
marker->setLockPos(true);
marker->showSubObjs(showPoints && showLabels);
marker->setVisible(showPoints);
marker->deselect(true);
marker->update();
m_pPlot->setModified(wasModified);
return markerName;
}
void nmWxPropertyInterpolationDlg::createMarkersForAllDataSets()
{
if(m_pPlot.isNull() || m_pPlot->m_pPlot == NULL) {
return;
}
for(int dataSetIndex = 0; dataSetIndex < m_vecDataSets.size(); ++dataSetIndex) {
const nmPropertyInterpolationDataSet& dataSet = m_vecDataSets[dataSetIndex];
QStringList markerNames;
for(int pointIndex = 0; pointIndex < dataSet.points.size(); ++pointIndex) {
const nmPropertyInterpolationPointData& point = dataSet.points[pointIndex];
QPointF valuePosition(point.x, point.y);
markerNames.append(createMarker(valuePosition, point.value,
dataSet.showPoints, dataSet.showLabels));
}
m_vecMarkerNames[dataSetIndex] = markerNames;
}
}
void nmWxPropertyInterpolationDlg::removeDataSetMarkers(int dataSetIndex)
{
if(dataSetIndex < 0 || dataSetIndex >= m_vecMarkerNames.size()) {
return;
}
const QStringList markerNames = m_vecMarkerNames[dataSetIndex];
for(int i = 0; i < markerNames.size(); ++i) {
removeMarkerByName(markerNames[i]);
}
m_vecMarkerNames[dataSetIndex].clear();
}
void nmWxPropertyInterpolationDlg::removeAllMarkers()
{
for(int i = 0; i < m_vecMarkerNames.size(); ++i) {
removeDataSetMarkers(i);
}
m_vecMarkerNames.clear();
}
QString nmWxPropertyInterpolationDlg::markerNameAt(int row) const
{
if(m_pPointTable == NULL || row < 0 || row >= m_pPointTable->rowCount()) {
return QString();
}
QTableWidgetItem* item = m_pPointTable->item(row, 0);
return item == NULL ? QString() : item->data(Qt::UserRole).toString();
}
nmObjPoint* nmWxPropertyInterpolationDlg::markerByName(const QString& markerName) const
{
if(m_pPlot.isNull() || m_pPlot->m_pPlot == NULL || markerName.isEmpty()) {
return NULL;
}
return dynamic_cast<nmObjPoint*>(m_pPlot->m_pPlot->getObjByName(markerName));
}
nmObjPoint* nmWxPropertyInterpolationDlg::markerAt(int row) const
{
return markerByName(markerNameAt(row));
}
void nmWxPropertyInterpolationDlg::removeMarkerByName(const QString& markerName)
{
if(m_pPlot.isNull() || m_pPlot->m_pPlot == NULL || markerName.isEmpty() ||
m_pPlot->m_pPlot->getObjByName(markerName) == NULL) {
return;
}
bool wasModified = m_pPlot->isModified();
m_pPlot->m_pPlot->removeObjByName(markerName);
m_pPlot->setModified(wasModified);
}
void nmWxPropertyInterpolationDlg::onPointSelectionChanged()
{
updatePointButtons();
}
void nmWxPropertyInterpolationDlg::onPointItemChanged(QTableWidgetItem* item)
{
if(m_bUpdatingUi || item == NULL || item->column() != 2 ||
m_nCurrentDataSetIndex < 0 ||
m_nCurrentDataSetIndex >= m_vecDataSets.size() ||
item->row() >= m_vecDataSets[m_nCurrentDataSetIndex].points.size()) {
return;
}
// 数值修改后同步更新数据组和地图标签,坐标列保持只读.
m_vecDataSets[m_nCurrentDataSetIndex].points[item->row()].value = item->text().toDouble();
nmObjPoint* marker = markerAt(item->row());
if(marker != NULL) {
setMarkerLabel(marker, item->text());
marker->update();
}
syncDataSetsToManager(true);
}
void nmWxPropertyInterpolationDlg::onDeleteSelectedPoint()
{
int row = m_pPointTable->currentRow();
if(row < 0 || m_nCurrentDataSetIndex < 0 ||
m_nCurrentDataSetIndex >= m_vecDataSets.size()) {
return;
}
removeMarkerByName(markerNameAt(row));
if(row < m_vecDataSets[m_nCurrentDataSetIndex].points.size()) {
m_vecDataSets[m_nCurrentDataSetIndex].points.remove(row);
}
if(row < m_vecMarkerNames[m_nCurrentDataSetIndex].size()) {
m_vecMarkerNames[m_nCurrentDataSetIndex].removeAt(row);
}
m_pPointTable->removeRow(row);
clearTableSelection();
updatePointButtons();
syncDataSetsToManager(true);
}
void nmWxPropertyInterpolationDlg::onClearPoints()
{
if(m_nCurrentDataSetIndex < 0 ||
m_nCurrentDataSetIndex >= m_vecDataSets.size()) {
return;
}
removeDataSetMarkers(m_nCurrentDataSetIndex);
m_vecDataSets[m_nCurrentDataSetIndex].points.clear();
m_pPointTable->setRowCount(0);
clearTableSelection();
updatePointButtons();
syncDataSetsToManager(true);
}
void nmWxPropertyInterpolationDlg::onShowPointsToggled(bool checked)
{
if(m_bUpdatingUi || m_nCurrentDataSetIndex < 0 ||
m_nCurrentDataSetIndex >= m_vecDataSets.size()) {
return;
}
m_vecDataSets[m_nCurrentDataSetIndex].showPoints = checked;
updateCurrentMarkerVisibility();
syncDataSetsToManager(true);
}
void nmWxPropertyInterpolationDlg::onShowLabelsToggled(bool checked)
{
if(m_bUpdatingUi || m_nCurrentDataSetIndex < 0 ||
m_nCurrentDataSetIndex >= m_vecDataSets.size()) {
return;
}
m_vecDataSets[m_nCurrentDataSetIndex].showLabels = checked;
updateCurrentMarkerVisibility();
syncDataSetsToManager(true);
}
void nmWxPropertyInterpolationDlg::clearTableSelection()
{
m_pPointTable->clearSelection();
m_pPointTable->setCurrentCell(-1, -1);
}
void nmWxPropertyInterpolationDlg::updatePointButtons()
{
const bool hasCurrent = m_nCurrentDataSetIndex >= 0 &&
m_nCurrentDataSetIndex < m_vecDataSets.size();
m_pDeletePointButton->setEnabled(hasCurrent && m_pPointTable->currentRow() >= 0);
m_pClearPointsButton->setEnabled(hasCurrent && m_pPointTable->rowCount() > 0);
m_pPreviewButton->setEnabled(hasCurrent &&
m_vecDataSets[m_nCurrentDataSetIndex].points.size() >= 2);
}
void nmWxPropertyInterpolationDlg::onPreview()
{
if(m_nCurrentDataSetIndex < 0 ||
m_nCurrentDataSetIndex >= m_vecDataSets.size() ||
m_pDataManager.isNull()) {
return;
}
saveCurrentDataSet();
const nmPropertyInterpolationDataSet& dataSet =
m_vecDataSets[m_nCurrentDataSetIndex];
// 预览范围直接使用储层边界,与地图中的数据坐标保持一致.
QVector<QPointF> outlinePoints;
QRectF previewBounds;
if(!createPreviewOutline(m_pDataManager->getOutlineData(),
outlinePoints, previewBounds)) {
QMessageBox::warning(this,
interpolationText("Preview Failed"),
interpolationText("Please create a valid reservoir boundary before previewing."));
return;
}
QVector<QPointF> measurementPoints;
QVector<double> measurementValues;
measurementPoints.reserve(dataSet.points.size());
measurementValues.reserve(dataSet.points.size());
for(int i = 0; i < dataSet.points.size(); ++i) {
measurementPoints.append(QPointF(dataSet.points[i].x,
dataSet.points[i].y));
measurementValues.append(dataSet.points[i].value);
}
// 按行生成规则待插值点,输出下标与预览网格下标保持一致;
// 较疏采样可使分级色带边界更清晰.
const int columnCount = 31;
const int rowCount = 31;
QVector<QPointF> targetPoints;
targetPoints.reserve(columnCount * rowCount);
for(int row = 0; row < rowCount; ++row) {
const double y = previewBounds.top() +
previewBounds.height() * row / (rowCount - 1);
for(int column = 0; column < columnCount; ++column) {
const double x = previewBounds.left() +
previewBounds.width() * column / (columnCount - 1);
targetPoints.append(QPointF(x, y));
}
}
QVector<double> interpolationValues;
QString errorMessage;
QApplication::setOverrideCursor(Qt::WaitCursor);
// 通过统一封装调用Kriging接口返回值顺序与待插值点一致.
const bool calculationSucceeded = nmCalculationUtils::calculateKriging(
targetPoints,
measurementPoints,
measurementValues,
dataSet.nugget,
dataSet.sill,
dataSet.range,
dataSet.model,
m_pDataManager->getLicensePath(),
interpolationValues,
&errorMessage);
QApplication::restoreOverrideCursor();
if(!calculationSucceeded) {
QMessageBox::warning(this,
interpolationText("Preview Failed"),
errorMessage);
return;
}
// 预览窗口只负责裁剪和渲染,不修改数据组及正式求解参数.
nmWxPropertyInterpolationPreviewDlg previewDialog(
dataSet.name,
m_pPropertyCombo->currentText(),
previewBounds,
columnCount,
rowCount,
interpolationValues,
measurementPoints,
measurementValues,
outlinePoints,
dataSet.showPoints,
dataSet.showLabels,
this);
previewDialog.exec();
}
void nmWxPropertyInterpolationDlg::updateCurrentMarkerVisibility()
{
// 地图上只显示当前数据组的测点,其他数据组统一隐藏.
for(int dataSetIndex = 0;
dataSetIndex < m_vecMarkerNames.size(); ++dataSetIndex) {
const bool isCurrentDataSet = dataSetIndex == m_nCurrentDataSetIndex &&
dataSetIndex < m_vecDataSets.size();
const bool showPoints = isCurrentDataSet &&
m_vecDataSets[dataSetIndex].showPoints;
const bool showLabels = showPoints &&
m_vecDataSets[dataSetIndex].showLabels;
const QStringList markerNames = m_vecMarkerNames[dataSetIndex];
for(int markerIndex = 0; markerIndex < markerNames.size(); ++markerIndex) {
nmObjPoint* marker = markerByName(markerNames[markerIndex]);
if(marker != NULL) {
marker->setVisible(showPoints);
marker->showSubObjs(showLabels);
marker->update();
}
}
}
if(!m_pPlot.isNull() && m_pPlot->m_pPlotView != NULL) {
m_pPlot->m_pPlotView->updateViewport();
}
}