diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm index abc015a..32bc151 100644 Binary files a/Bin/Config/Lang/cn/nmNum_cn.qm and b/Bin/Config/Lang/cn/nmNum_cn.qm differ diff --git a/Bin/Config/Lang/cn/nmNum_cn.ts b/Bin/Config/Lang/cn/nmNum_cn.ts index 0eb1ffe..3dc3397 100644 --- a/Bin/Config/Lang/cn/nmNum_cn.ts +++ b/Bin/Config/Lang/cn/nmNum_cn.ts @@ -6642,21 +6642,25 @@ Average pressure in contour: %2 MPa Show labels 显示标签 + + Clear measurement points before changing the property. + 清空测点后可修改属性。 + Kriging Parameters - Kriging参数 + 克里金参数 Nugget: - Nugget: + 块金值: Sill: - Sill: + 基台值: Range: - Range: + 变程: Model: @@ -6682,10 +6686,6 @@ Average pressure in contour: %2 MPa Preview 预览 - - Apply - 应用 - Close 关闭 diff --git a/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.h b/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.h index 0053cf2..c264ade 100644 --- a/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.h +++ b/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.h @@ -74,6 +74,7 @@ private: void updateUnitControls(nmPropertyInterpolationDataSet& dataSet); void updatePointDisplayValues(); void clearPointRows(); + void updatePointRowNumbers(); void syncDataSetsToManager(bool markModified); void updateDataSetButtons(); // 管理正式求解时k、phi和h各自使用的插值数据组. @@ -157,9 +158,8 @@ private: QCheckBox* m_pUseHCheck; QComboBox* m_pHCalculationDataSetCombo; - // 结果预览及应用. + // 插值结果预览. QPushButton* m_pPreviewButton; - QPushButton* m_pApplyButton; QPointer m_pPreviewDialog; QTimer* m_pPreviewRefreshTimer; }; diff --git a/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationPreviewDlg.h b/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationPreviewDlg.h index 3ff345d..cf1afa1 100644 --- a/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationPreviewDlg.h +++ b/Include/nmNum/nmSubWxs/nmWxPropertyInterpolationPreviewDlg.h @@ -7,6 +7,7 @@ #include #include #include +#include #include class QVTKWidget; @@ -25,6 +26,8 @@ public: const QVector& measurementPoints, const QVector& measurementValues, const QVector& outlinePoints, + const QVector& wellPoints, + const QStringList& wellNames, bool showMeasurementPoints, bool showMeasurementLabels, QWidget* parent = 0); @@ -38,6 +41,8 @@ public: const QVector& measurementPoints, const QVector& measurementValues, const QVector& outlinePoints, + const QVector& wellPoints, + const QStringList& wellNames, bool showMeasurementPoints, bool showMeasurementLabels); void showMessage(const QString& dataSetName, const QString& message); @@ -51,6 +56,8 @@ private: const QVector& measurementPoints, const QVector& measurementValues, const QVector& outlinePoints, + const QVector& wellPoints, + const QStringList& wellNames, bool showMeasurementPoints, bool showMeasurementLabels); diff --git a/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.cpp b/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.cpp index 44d4571..b5b8f5f 100644 --- a/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.cpp +++ b/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationDlg.cpp @@ -33,10 +33,12 @@ #include "nmDataOutline.h" #include "nmGuiPlot.h" #include "nmObjPoint.h" +#include "nmObjPointWell.h" #include "nmWxPropertyInterpolationPreviewDlg.h" #include "iUnitGroup.h" #include "iUnitHelper.h" #include "tCurvePlotView.h" +#include "ZxDataWell.h" #include "ZxObjText.h" #include "ZxPlot.h" @@ -336,15 +338,14 @@ nmWxPropertyInterpolationDlg::nmWxPropertyInterpolationDlg( m_pUseHCheck(NULL), m_pHCalculationDataSetCombo(NULL), m_pPreviewButton(NULL), - m_pApplyButton(NULL), m_pPreviewDialog(NULL), m_pPreviewRefreshTimer(NULL) { setWindowTitle(interpolationText("Property Interpolation")); setWindowModality(Qt::NonModal); setModal(false); - setMinimumSize(520, 680); - resize(560, 740); + setMinimumSize(600, 620); + resize(640, 680); // 合并连续编辑触发的刷新,避免每次数值变化都立即重新插值. m_pPreviewRefreshTimer = new QTimer(this); @@ -375,38 +376,52 @@ nmWxPropertyInterpolationDlg::~nmWxPropertyInterpolationDlg() void nmWxPropertyInterpolationDlg::initInterpolationUI() { + const int topControlWidth = 350; + const int compactComboWidth = 200; + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(12, 10, 12, 10); + mainLayout->setSpacing(8); - // 数据组创建、切换及删除. - QHBoxLayout* dataSetSelectLayout = new QHBoxLayout; - dataSetSelectLayout->addWidget(new QLabel(interpolationText("Dataset:"), this)); - m_pDataSetCombo = new QComboBox(this); + // 数据组、名称和属性共用一个表单,保证三行标签及输入控件对齐. + QFormLayout* dataSetLayout = new QFormLayout; + dataSetLayout->setContentsMargins(0, 0, 0, 0); + dataSetLayout->setHorizontalSpacing(8); + dataSetLayout->setVerticalSpacing(6); + dataSetLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + + QWidget* dataSetSelectWidget = new QWidget(this); + dataSetSelectWidget->setMaximumWidth(topControlWidth); + QHBoxLayout* dataSetSelectLayout = new QHBoxLayout(dataSetSelectWidget); + dataSetSelectLayout->setContentsMargins(0, 0, 0, 0); + dataSetSelectLayout->setSpacing(6); + m_pDataSetCombo = new QComboBox(dataSetSelectWidget); dataSetSelectLayout->addWidget(m_pDataSetCombo, 1); QString iconDir = QCoreApplication::applicationDirPath(); iconDir = iconDir.section('/', 0, -2) + "/Res/Icon/"; - m_pAddDataSetButton = new QPushButton(this); + m_pAddDataSetButton = new QPushButton(dataSetSelectWidget); 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 = new QPushButton(dataSetSelectWidget); 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); + dataSetLayout->addRow(interpolationText("Dataset:"), dataSetSelectWidget); - // 当前数据组的名称和属性. - QFormLayout* dataSetLayout = new QFormLayout; m_pNameEdit = new QLineEdit(this); + m_pNameEdit->setMaximumWidth(topControlWidth); dataSetLayout->addRow(interpolationText("Name:"), m_pNameEdit); m_pPropertyCombo = new QComboBox(this); + m_pPropertyCombo->setMaximumWidth(topControlWidth); 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")); @@ -416,17 +431,23 @@ void nmWxPropertyInterpolationDlg::initInterpolationUI() // 测点数据录入区域. QGroupBox* pointGroup = new QGroupBox(interpolationText("Measurement Points"), this); QVBoxLayout* pointLayout = new QVBoxLayout(pointGroup); + pointLayout->setContentsMargins(10, 8, 10, 10); + pointLayout->setSpacing(6); QHBoxLayout* newPointLayout = new QHBoxLayout; + newPointLayout->setSpacing(6); newPointLayout->addWidget(new QLabel(interpolationText("New value:"), pointGroup)); m_pNewValueSpin = createNumberSpinBox(pointGroup, 0.0); newPointLayout->addWidget(m_pNewValueSpin, 1); m_pNewValueUnitCombo = new QComboBox(pointGroup); - m_pNewValueUnitCombo->setMinimumWidth(80); + m_pNewValueUnitCombo->setFixedWidth(80); populateUnitCombo(m_pNewValueUnitCombo, PERMEABILITY_BASE_UNIT, PERMEABILITY_DISPLAY_UNIT); newPointLayout->addWidget(m_pNewValueUnitCombo); m_pMapPickButton = new QPushButton(interpolationText("Select on Map"), pointGroup); + m_pMapPickButton->setIcon(QIcon(iconDir + "AddPoint.png")); + m_pMapPickButton->setIconSize(QSize(16, 16)); + m_pMapPickButton->setMinimumWidth(100); m_pMapPickButton->setCheckable(true); m_pMapPickButton->setEnabled(!m_pPlot.isNull() && m_pPlot->m_pPlotView != NULL && @@ -438,11 +459,17 @@ void nmWxPropertyInterpolationDlg::initInterpolationUI() QStringList headers; headers << interpolationText("X") << interpolationText("Y") << interpolationText("Value"); m_pPointTable->setHorizontalHeaderLabels(headers); - m_pPointTable->verticalHeader()->setVisible(false); + m_pPointTable->verticalHeader()->setVisible(true); + m_pPointTable->verticalHeader()->setFixedWidth(36); + m_pPointTable->verticalHeader()->setDefaultAlignment(Qt::AlignCenter); + // 第0行为单位选择,不占用测点序号. + m_pPointTable->setVerticalHeaderItem( + POINT_UNIT_ROW, new QTableWidgetItem(QString())); m_pPointTable->horizontalHeader()->setResizeMode(QHeaderView::Stretch); m_pPointTable->setSelectionBehavior(QAbstractItemView::SelectRows); m_pPointTable->setSelectionMode(QAbstractItemView::SingleSelection); m_pPointTable->setAlternatingRowColors(true); + m_pPointTable->setMinimumHeight(170); m_pPointTable->setRowHeight(POINT_UNIT_ROW, 24); // 第0行固定显示各列单位,数据行从第1行开始. @@ -461,83 +488,99 @@ void nmWxPropertyInterpolationDlg::initInterpolationUI() m_pPointTable->setCellWidget(POINT_UNIT_ROW, 2, m_pValueUnitCombo); pointLayout->addWidget(m_pPointTable, 1); + // 显示选项放在左侧,当前测点操作放在右侧,减少分散的按钮行. QHBoxLayout* pointButtonLayout = new QHBoxLayout; + pointButtonLayout->setSpacing(8); + m_pShowPointsCheck = new QCheckBox(interpolationText("Show measurement points"), pointGroup); + m_pShowPointsCheck->setChecked(true); + pointButtonLayout->addWidget(m_pShowPointsCheck); + m_pShowLabelsCheck = new QCheckBox(interpolationText("Show labels"), pointGroup); + m_pShowLabelsCheck->setChecked(true); + pointButtonLayout->addWidget(m_pShowLabelsCheck); pointButtonLayout->addStretch(); m_pDeletePointButton = new QPushButton(interpolationText("Delete Selected"), pointGroup); + m_pDeletePointButton->setMinimumWidth(84); m_pDeletePointButton->setEnabled(false); pointButtonLayout->addWidget(m_pDeletePointButton); m_pClearPointsButton = new QPushButton(interpolationText("Clear"), pointGroup); + m_pClearPointsButton->setMinimumWidth(72); 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插值参数设置. + // Kriging参数采用两行双列布局,保持信息完整并压缩纵向空间. QGroupBox* parameterGroup = new QGroupBox(interpolationText("Kriging Parameters"), this); - QFormLayout* parameterLayout = new QFormLayout(parameterGroup); + QGridLayout* parameterLayout = new QGridLayout(parameterGroup); + parameterLayout->setContentsMargins(10, 8, 10, 10); + parameterLayout->setHorizontalSpacing(8); + parameterLayout->setVerticalSpacing(6); m_pNuggetSpin = createNumberSpinBox(parameterGroup, 0.01); - parameterLayout->addRow(interpolationText("Nugget:"), m_pNuggetSpin); + parameterLayout->addWidget(new QLabel(interpolationText("Nugget:"), parameterGroup), 0, 0); + parameterLayout->addWidget(m_pNuggetSpin, 0, 1); m_pSillSpin = createNumberSpinBox(parameterGroup, 100.0); - parameterLayout->addRow(interpolationText("Sill:"), m_pSillSpin); + parameterLayout->addWidget(new QLabel(interpolationText("Sill:"), parameterGroup), 0, 2); + parameterLayout->addWidget(m_pSillSpin, 0, 3); QWidget* rangeWidget = new QWidget(parameterGroup); QHBoxLayout* rangeLayout = new QHBoxLayout(rangeWidget); rangeLayout->setContentsMargins(0, 0, 0, 0); rangeLayout->setSpacing(6); - m_pRangeSpin = createNumberSpinBox(parameterGroup, 1000.0); + m_pRangeSpin = createNumberSpinBox(rangeWidget, 1000.0); rangeLayout->addWidget(m_pRangeSpin, 1); - m_pRangeUnitCombo = new QComboBox(parameterGroup); - m_pRangeUnitCombo->setMinimumWidth(80); + m_pRangeUnitCombo = new QComboBox(rangeWidget); + m_pRangeUnitCombo->setFixedWidth(72); populateUnitCombo(m_pRangeUnitCombo, LENGTH_BASE_UNIT, LENGTH_BASE_UNIT); rangeLayout->addWidget(m_pRangeUnitCombo); - parameterLayout->addRow(interpolationText("Range:"), rangeWidget); + parameterLayout->addWidget(new QLabel(interpolationText("Range:"), parameterGroup), 1, 0); + parameterLayout->addWidget(rangeWidget, 1, 1); m_pModelCombo = new QComboBox(parameterGroup); + m_pModelCombo->setFixedWidth(compactComboWidth); 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); + parameterLayout->addWidget(new QLabel(interpolationText("Model:"), parameterGroup), 1, 2); + parameterLayout->addWidget(m_pModelCombo, 1, 3); + parameterLayout->setColumnStretch(1, 1); + parameterLayout->setColumnStretch(3, 1); mainLayout->addWidget(parameterGroup); // 分别指定正式求解时需要使用插值结果的属性和数据组. QGroupBox* calculationGroup = new QGroupBox( interpolationText("Use Interpolation in Solver"), this); QGridLayout* calculationLayout = new QGridLayout(calculationGroup); + calculationLayout->setContentsMargins(10, 8, 10, 10); + calculationLayout->setHorizontalSpacing(8); + calculationLayout->setVerticalSpacing(6); m_pUseKCheck = new QCheckBox(interpolationText("Permeability k"), calculationGroup); m_pKCalculationDataSetCombo = new QComboBox(calculationGroup); + m_pKCalculationDataSetCombo->setFixedWidth(compactComboWidth); calculationLayout->addWidget(m_pUseKCheck, 0, 0); calculationLayout->addWidget(m_pKCalculationDataSetCombo, 0, 1); m_pUsePhiCheck = new QCheckBox(interpolationText("Porosity phi"), calculationGroup); m_pPhiCalculationDataSetCombo = new QComboBox(calculationGroup); + m_pPhiCalculationDataSetCombo->setFixedWidth(compactComboWidth); calculationLayout->addWidget(m_pUsePhiCheck, 1, 0); calculationLayout->addWidget(m_pPhiCalculationDataSetCombo, 1, 1); m_pUseHCheck = new QCheckBox(interpolationText("Reservoir thickness h"), calculationGroup); m_pHCalculationDataSetCombo = new QComboBox(calculationGroup); + m_pHCalculationDataSetCombo->setFixedWidth(compactComboWidth); calculationLayout->addWidget(m_pUseHCheck, 2, 0); calculationLayout->addWidget(m_pHCalculationDataSetCombo, 2, 1); - calculationLayout->setColumnStretch(1, 1); + calculationLayout->setColumnMinimumWidth(1, compactComboWidth); + calculationLayout->setColumnStretch(2, 1); mainLayout->addWidget(calculationGroup); // 底部操作按钮. QHBoxLayout* buttonLayout = new QHBoxLayout; m_pPreviewButton = new QPushButton(interpolationText("Preview"), this); + m_pPreviewButton->setMinimumWidth(84); 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); + closeButton->setMinimumWidth(84); buttonLayout->addWidget(closeButton); mainLayout->addLayout(buttonLayout); @@ -848,6 +891,28 @@ void nmWxPropertyInterpolationDlg::clearPointRows() if(m_pPointTable != NULL && m_pPointTable->rowCount() > FIRST_POINT_ROW) { m_pPointTable->setRowCount(FIRST_POINT_ROW); } + updatePointRowNumbers(); +} + +void nmWxPropertyInterpolationDlg::updatePointRowNumbers() +{ + if(m_pPointTable == NULL) { + return; + } + + // 单位行留空,测点数据行始终从1开始连续编号. + for(int row = POINT_UNIT_ROW; row < m_pPointTable->rowCount(); ++row) { + const QString number = row < FIRST_POINT_ROW ? + QString() : QString::number(row - FIRST_POINT_ROW + 1); + QTableWidgetItem* headerItem = m_pPointTable->verticalHeaderItem(row); + if(headerItem == NULL) { + m_pPointTable->setVerticalHeaderItem(row, + new QTableWidgetItem(number)); + } + else { + headerItem->setText(number); + } + } } void nmWxPropertyInterpolationDlg::syncDataSetsToManager(bool markModified) @@ -1277,6 +1342,9 @@ void nmWxPropertyInterpolationDlg::appendPointRow( // rowCount已包含单位行,新插入行天然与points下标保持FIRST_POINT_ROW偏移. int row = m_pPointTable->rowCount(); m_pPointTable->insertRow(row); + m_pPointTable->setVerticalHeaderItem( + row, new QTableWidgetItem(QString::number( + row - FIRST_POINT_ROW + 1))); QString xUnit = LENGTH_BASE_UNIT; QString yUnit = LENGTH_BASE_UNIT; @@ -1294,14 +1362,12 @@ void nmWxPropertyInterpolationDlg::appendPointRow( QTableWidgetItem* xItem = new QTableWidgetItem(displayValueText( convertedUnitValue(point.x, LENGTH_BASE_UNIT, xUnit))); - xItem->setFlags(xItem->flags() & ~Qt::ItemIsEditable); // 将地图标记名称绑定在X列,后续可由表格行直接定位对应标记. xItem->setData(Qt::UserRole, markerName); m_pPointTable->setItem(row, 0, xItem); QTableWidgetItem* yItem = new QTableWidgetItem(displayValueText( convertedUnitValue(point.y, LENGTH_BASE_UNIT, yUnit))); - yItem->setFlags(yItem->flags() & ~Qt::ItemIsEditable); m_pPointTable->setItem(row, 1, yItem); m_pPointTable->setItem(row, 2, new QTableWidgetItem(displayValueText( @@ -1449,7 +1515,8 @@ void nmWxPropertyInterpolationDlg::onPointItemChanged(QTableWidgetItem* item) // 表格行减去单位行偏移后,才是数据组中的测点下标. const int pointIndex = item == NULL ? -1 : item->row() - FIRST_POINT_ROW; - if(m_bUpdatingUi || item == NULL || item->column() != 2 || + if(m_bUpdatingUi || item == NULL || item->column() < 0 || + item->column() > 2 || pointIndex < 0 || m_nCurrentDataSetIndex < 0 || m_nCurrentDataSetIndex >= m_vecDataSets.size() || @@ -1457,15 +1524,72 @@ void nmWxPropertyInterpolationDlg::onPointItemChanged(QTableWidgetItem* item) return; } - // 数值修改后同步更新数据组和地图标签,坐标列保持只读. nmPropertyInterpolationDataSet& dataSet = m_vecDataSets[m_nCurrentDataSetIndex]; - dataSet.points[pointIndex].value = convertedUnitValue( - item->text().toDouble(), dataSet.valueDisplayUnit, - propertyBaseUnit(dataSet.property)); + nmPropertyInterpolationPointData& point = dataSet.points[pointIndex]; + + // X、Y及属性值分别按当前显示单位换算回数据中心使用的基准单位. + QString baseUnit; + QString displayUnit; + double oldBaseValue = 0.0; + if(item->column() == 0) { + baseUnit = LENGTH_BASE_UNIT; + displayUnit = dataSet.xDisplayUnit; + oldBaseValue = point.x; + } + else if(item->column() == 1) { + baseUnit = LENGTH_BASE_UNIT; + displayUnit = dataSet.yDisplayUnit; + oldBaseValue = point.y; + } + else { + baseUnit = propertyBaseUnit(dataSet.property); + displayUnit = dataSet.valueDisplayUnit; + oldBaseValue = point.value; + } + + bool valueOk = false; + const double displayValue = item->text().toDouble(&valueOk); + if(!valueOk || (item->column() == 2 && displayValue < 0.0)) { + // 坐标允许为负数;属性值仍要求非负,非法输入恢复为修改前的值. + const double oldDisplayValue = convertedUnitValue( + oldBaseValue, baseUnit, displayUnit); + const bool oldBlock = m_pPointTable->blockSignals(true); + item->setText(displayValueText(oldDisplayValue)); + m_pPointTable->blockSignals(oldBlock); + return; + } + + const double baseValue = convertedUnitValue( + displayValue, displayUnit, baseUnit); + if(item->column() == 0) { + point.x = baseValue; + } + else if(item->column() == 1) { + point.y = baseValue; + } + else { + point.value = baseValue; + } + + const QString displayText = displayValueText(displayValue); + if(item->text() != displayText) { + const bool oldBlock = m_pPointTable->blockSignals(true); + item->setText(displayText); + m_pPointTable->blockSignals(oldBlock); + } + + // 坐标修改后移动Map标记,属性值修改后刷新标记标签. nmObjPoint* marker = markerAt(item->row()); if(marker != NULL) { - setMarkerLabel(marker, item->text()); + if(item->column() < 2) { + QVector positions; + positions.append(QPointF(point.x, point.y)); + marker->setAllValues(positions); + } + else { + setMarkerLabel(marker, displayText); + } marker->update(); } syncDataSetsToManager(true); @@ -1490,6 +1614,7 @@ void nmWxPropertyInterpolationDlg::onDeleteSelectedPoint() m_vecMarkerNames[m_nCurrentDataSetIndex].removeAt(pointIndex); } m_pPointTable->removeRow(row); + updatePointRowNumbers(); clearTableSelection(); updatePointButtons(); syncDataSetsToManager(true); @@ -1548,6 +1673,14 @@ void nmWxPropertyInterpolationDlg::updatePointButtons() { const bool hasCurrent = m_nCurrentDataSetIndex >= 0 && m_nCurrentDataSetIndex < m_vecDataSets.size(); + const bool hasPoints = hasCurrent && + !m_vecDataSets[m_nCurrentDataSetIndex].points.isEmpty(); + + // 已有测点的数值含义已经确定,需清空测点后才能切换数据组属性. + m_pPropertyCombo->setEnabled(hasCurrent && !hasPoints); + m_pPropertyCombo->setToolTip(hasPoints ? + interpolationText("Clear measurement points before changing the property.") : + QString()); m_pDeletePointButton->setEnabled( hasCurrent && m_pPointTable->currentRow() >= FIRST_POINT_ROW); m_pClearPointsButton->setEnabled( @@ -1646,8 +1779,8 @@ void nmWxPropertyInterpolationDlg::updatePreviewWindow(bool activateWindow) // 按行生成规则待插值点,输出下标与预览网格下标保持一致; // 较疏采样可使分级色带边界更清晰. - const int columnCount = 31; - const int rowCount = 31; + const int columnCount = 19; + const int rowCount = 19; QVector targetPoints; targetPoints.reserve(columnCount * rowCount); for(int row = 0; row < rowCount; ++row) { @@ -1705,6 +1838,31 @@ void nmWxPropertyInterpolationDlg::updatePreviewWindow(bool activateWindow) scalarTitle += QString(" (%1)").arg(dataSet.valueDisplayUnit); } + // 直接读取Map中实际显示的井,避免“参与计算井”尚未设置时预览中没有井位. + QVector wellPoints; + QStringList wellNames; + const QVector wellPlots = m_pPlot.isNull() ? + QVector() : m_pPlot->getWellPlots(); + for(int i = 0; i < wellPlots.size(); ++i) { + nmObjPointWell* wellPlot = wellPlots[i]; + if(wellPlot == NULL || wellPlot->getWellData() == NULL) { + continue; + } + + const QVector wellInformation = wellPlot->getWellInformation(); + const QString wellName = wellPlot->getWellData()->getName(); + if(wellInformation.size() < 2 || wellName.isEmpty() || + wellNames.contains(wellName)) { + continue; + } + + const QPointF wellPoint(wellInformation[0], wellInformation[1]); + if(previewBounds.contains(wellPoint)) { + wellPoints.append(wellPoint); + wellNames.append(wellName); + } + } + // 预览窗口只负责裁剪和渲染,不修改数据组及正式求解参数. if(m_pPreviewDialog.isNull()) { m_pPreviewDialog = new nmWxPropertyInterpolationPreviewDlg( @@ -1717,6 +1875,8 @@ void nmWxPropertyInterpolationDlg::updatePreviewWindow(bool activateWindow) measurementPoints, displayMeasurementValues, outlinePoints, + wellPoints, + wellNames, dataSet.showPoints, dataSet.showLabels, this); @@ -1733,6 +1893,8 @@ void nmWxPropertyInterpolationDlg::updatePreviewWindow(bool activateWindow) measurementPoints, displayMeasurementValues, outlinePoints, + wellPoints, + wellNames, dataSet.showPoints, dataSet.showLabels); } diff --git a/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationPreviewDlg.cpp b/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationPreviewDlg.cpp index bd2ebfa..916c22d 100644 --- a/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationPreviewDlg.cpp +++ b/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationPreviewDlg.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -85,6 +86,8 @@ nmWxPropertyInterpolationPreviewDlg::nmWxPropertyInterpolationPreviewDlg( const QVector& measurementPoints, const QVector& measurementValues, const QVector& outlinePoints, + const QVector& wellPoints, + const QStringList& wellNames, bool showMeasurementPoints, bool showMeasurementLabels, QWidget* parent) @@ -113,7 +116,7 @@ nmWxPropertyInterpolationPreviewDlg::nmWxPropertyInterpolationPreviewDlg( updatePreview(dataSetName, scalarTitle, bounds, columnCount, rowCount, interpolationValues, measurementPoints, - measurementValues, outlinePoints, + measurementValues, outlinePoints, wellPoints, wellNames, showMeasurementPoints, showMeasurementLabels); } @@ -127,14 +130,16 @@ void nmWxPropertyInterpolationPreviewDlg::updatePreview( const QVector& measurementPoints, const QVector& measurementValues, const QVector& outlinePoints, + const QVector& wellPoints, + const QStringList& wellNames, bool showMeasurementPoints, bool showMeasurementLabels) { setWindowTitle(previewText("Interpolation Preview") + " - " + dataSetName); initializeRenderer(scalarTitle, bounds, columnCount, rowCount, - interpolationValues, measurementPoints, - measurementValues, outlinePoints, - showMeasurementPoints, showMeasurementLabels); + interpolationValues, measurementPoints, + measurementValues, outlinePoints, wellPoints, wellNames, + showMeasurementPoints, showMeasurementLabels); } void nmWxPropertyInterpolationPreviewDlg::showMessage( @@ -171,6 +176,8 @@ void nmWxPropertyInterpolationPreviewDlg::initializeRenderer( const QVector& measurementPoints, const QVector& measurementValues, const QVector& outlinePoints, + const QVector& wellPoints, + const QStringList& wellNames, bool showMeasurementPoints, bool showMeasurementLabels) { @@ -325,8 +332,8 @@ void nmWxPropertyInterpolationPreviewDlg::initializeRenderer( vtkSmartPointer lookupTable = createPreviewLookupTable(displayMinimum, displayMaximum); - // 将连续点标量划分为10级色带,Mapper按CellData进行分级着色. - const int colorBandCount = 10; + // 将连续点标量划分为8级色带,Mapper按CellData进行分级着色. + const int colorBandCount = 8; vtkSmartPointer bandFilter = vtkSmartPointer::New(); bandFilter->SetInputData(visibleSurface); @@ -439,9 +446,26 @@ void nmWxPropertyInterpolationPreviewDlg::initializeRenderer( } if(showMeasurementLabels) { + // 标签使用独立坐标上移,测点方框仍保持在原始测量位置. + const double labelOffset = + qMax(bounds.width(), bounds.height()) * 0.018; + vtkSmartPointer measurementLabelPoints = + vtkSmartPointer::New(); + for(int i = 0; i < measurementPoints.size(); ++i) { + measurementLabelPoints->InsertNextPoint( + measurementPoints[i].x(), + measurementPoints[i].y() + labelOffset, 0.03); + } + + vtkSmartPointer measurementLabelData = + vtkSmartPointer::New(); + measurementLabelData->SetPoints(measurementLabelPoints); + measurementLabelData->GetPointData()->SetScalars( + measurementScalars); + vtkSmartPointer labelMapper = vtkSmartPointer::New(); - labelMapper->SetInputData(measurementData); + labelMapper->SetInputData(measurementLabelData); labelMapper->SetLabelModeToLabelScalars(); labelMapper->SetLabelFormat("%.6g"); labelMapper->GetLabelTextProperty()->SetColor(0.12, 0.12, 0.12); @@ -449,15 +473,96 @@ void nmWxPropertyInterpolationPreviewDlg::initializeRenderer( labelMapper->GetLabelTextProperty()->BoldOff(); labelMapper->GetLabelTextProperty()->ItalicOff(); labelMapper->GetLabelTextProperty()->ShadowOff(); + labelMapper->GetLabelTextProperty()->SetJustificationToCentered(); + labelMapper->GetLabelTextProperty()->SetVerticalJustificationToBottom(); vtkSmartPointer labelActor = vtkSmartPointer::New(); labelActor->SetMapper(labelMapper); - labelActor->SetPosition(0.0, 10.0); renderer->AddActor2D(labelActor); } } + // Map中显示的井使用独立红色标记和名称叠加,不参与插值表面计算. + if(!wellPoints.isEmpty() && wellPoints.size() == wellNames.size()) { + vtkSmartPointer wellVtkPoints = + vtkSmartPointer::New(); + vtkSmartPointer wellNameArray = + vtkSmartPointer::New(); + wellNameArray->SetName("WellName"); + for(int i = 0; i < wellPoints.size(); ++i) { + wellVtkPoints->InsertNextPoint(wellPoints[i].x(), + wellPoints[i].y(), 0.04); + wellNameArray->InsertNextValue(wellNames[i].toUtf8().constData()); + } + + vtkSmartPointer wellData = + vtkSmartPointer::New(); + wellData->SetPoints(wellVtkPoints); + wellData->GetPointData()->AddArray(wellNameArray); + + vtkSmartPointer wellGlyphFilter = + vtkSmartPointer::New(); + wellGlyphFilter->SetInputData(wellData); + + vtkSmartPointer wellBorderMapper = + vtkSmartPointer::New(); + wellBorderMapper->SetInputConnection(wellGlyphFilter->GetOutputPort()); + wellBorderMapper->ScalarVisibilityOff(); + vtkSmartPointer wellBorderActor = + vtkSmartPointer::New(); + wellBorderActor->SetMapper(wellBorderMapper); + wellBorderActor->GetProperty()->SetColor(0.12, 0.12, 0.12); + wellBorderActor->GetProperty()->SetPointSize(9.0); + renderer->AddActor(wellBorderActor); + + vtkSmartPointer wellMapper = + vtkSmartPointer::New(); + wellMapper->SetInputConnection(wellGlyphFilter->GetOutputPort()); + wellMapper->ScalarVisibilityOff(); + vtkSmartPointer wellActor = vtkSmartPointer::New(); + wellActor->SetMapper(wellMapper); + wellActor->GetProperty()->SetColor(0.90, 0.12, 0.10); + wellActor->GetProperty()->SetPointSize(5.0); + wellActor->SetPosition(0.0, 0.0, 0.001); + renderer->AddActor(wellActor); + + // 井名同样使用独立坐标,避免文字覆盖井位标记. + const double wellLabelOffset = + qMax(bounds.width(), bounds.height()) * 0.022; + vtkSmartPointer wellLabelPoints = + vtkSmartPointer::New(); + for(int i = 0; i < wellPoints.size(); ++i) { + wellLabelPoints->InsertNextPoint( + wellPoints[i].x(), + wellPoints[i].y() + wellLabelOffset, 0.05); + } + + vtkSmartPointer wellLabelData = + vtkSmartPointer::New(); + wellLabelData->SetPoints(wellLabelPoints); + wellLabelData->GetPointData()->AddArray(wellNameArray); + + vtkSmartPointer wellLabelMapper = + vtkSmartPointer::New(); + wellLabelMapper->SetInputData(wellLabelData); + wellLabelMapper->SetLabelModeToLabelFieldData(); + wellLabelMapper->SetFieldDataName("WellName"); + wellLabelMapper->SetLabelFormat("%s"); + wellLabelMapper->GetLabelTextProperty()->SetColor(0.10, 0.10, 0.10); + wellLabelMapper->GetLabelTextProperty()->SetFontSize(13); + wellLabelMapper->GetLabelTextProperty()->BoldOff(); + wellLabelMapper->GetLabelTextProperty()->ItalicOff(); + wellLabelMapper->GetLabelTextProperty()->ShadowOff(); + wellLabelMapper->GetLabelTextProperty()->SetJustificationToCentered(); + wellLabelMapper->GetLabelTextProperty()->SetVerticalJustificationToBottom(); + + vtkSmartPointer wellLabelActor = + vtkSmartPointer::New(); + wellLabelActor->SetMapper(wellLabelMapper); + renderer->AddActor2D(wellLabelActor); + } + vtkSmartPointer scalarBar = vtkSmartPointer::New(); scalarBar->SetLookupTable(lookupTable);