feat(numerical): 按井位置初始化边界并适配 Map 坐标轴

- 根据全部有效数值井的包围盒创建默认矩形边界
- 默认边界在井位置四周各保留 1500 米范围
- Map 首次打开时根据可见内容和绘图区比例适配坐标轴
- 首次适配后保留用户调整的坐标轴范围,不再自动刷新
- 修正 Map 初始化和分析切换时的数据管理器绑定
- 完整重建 Map 图元,避免不同数值分析之间残留绘图对象
develop
lh 7 days ago
parent 25cb5343ed
commit 2d5b56fe21

@ -18,6 +18,7 @@
#include <QMap>
#include <QVector>
#include <QPointF>
#include <QRectF>
#include <QPair>
#include <QString>
#include <QStringList>
@ -348,6 +349,10 @@ public:
// 初始化边界数据
nmDataOutline* createOutline();
nmDataOutline* getOutlineData();
/** @brief 按当前数值井包围盒及四周 1500m 留量创建一次性默认矩形边界。 */
bool createDefaultOutlineFromWells();
/** @brief 计算当前数值分析全部有效井位置的包围盒。 */
bool calculateNumericalWellBounds(QRectF& oBounds) const;
// 移除边界数据
bool removeOutlineData();
// 返回边界副本数据

@ -17,6 +17,11 @@ public:
// 实现 nmDataBase 的抽象方法:从 RapidJSON Value 反序列化数据到当前 C++ 对象
virtual void FromJsonValue(const rapidjson::Value& jsonValue) override;
/** @brief 返回 Map 是否已经完成首次等比例坐标轴初始化。 */
bool isInitialViewFitted() const;
/** @brief 记录 Map 的首次等比例坐标轴初始化是否完成。 */
void setInitialViewFitted(bool bInitialViewFitted);
void setXMax(const nmDataAttribute& attr);
nmDataAttribute& getXMax();
void setXMin(const nmDataAttribute& attr);
@ -37,6 +42,7 @@ public:
private:
bool m_bInitialViewFitted; ///< 完成后不再因数据或窗口变化自动修改坐标轴。
nmDataAttribute m_xMax;
nmDataAttribute m_xMin;
nmDataAttribute m_yMax;

@ -4,6 +4,7 @@
#include "nmDefines.h"
#include "nmPlot_global.h"
#include <QPointF>
#include <QRectF>
class nmObjBase;
class nmObjPointWell;
@ -14,6 +15,7 @@ class nmDataAnalyzeManager;
class nmDataWellBase;
class nmPlotGraphicBinder;
class QWidget;
class QResizeEvent;
// 数值试井绘图载体的样例
class NM_PLOT_EXPORT nmGuiPlot : public iGuiPlot {
@ -97,6 +99,8 @@ class NM_PLOT_EXPORT nmGuiPlot : public iGuiPlot {
#endif
public slots:
/** @brief Map 首次显示稳定后,按内容和绘图区比例初始化一次坐标轴。 */
void initializeAxisView();
// Obj选择状态改变
void slotObjSelChanged(bool);
@ -219,9 +223,27 @@ class NM_PLOT_EXPORT nmGuiPlot : public iGuiPlot {
// 绘制Painter函数重载
virtual void paintEvent(QPaintEvent*) override;
/** @brief 首次视图尚未完成时,等待有效绘图区后再次尝试初始化。 */
virtual void resizeEvent(QResizeEvent* pEvent) override;
private:
/** @brief 计算边界及当前可见数值对象的数据坐标包围盒。 */
bool calculateVisibleContentBounds(QRectF& oBounds) const;
/** @brief 以程序方式同时应用两个坐标轴范围,并写回当前 Manager。 */
void applyAxisRange(double dXMin, double dXMax,
double dYMin, double dYMax);
/** @brief 按当前内容完成一次等比例视图计算,成功时返回 true。 */
bool fitInitialAxisToContent();
/** @brief 将首次视图初始化延迟到本轮布局完成后执行。 */
void scheduleInitialAxisView();
/** @brief 返回当前 Map 绑定的数据管理器,避免多窗口时误用全局当前对象。 */
nmDataAnalyzeManager* dataManager() const;
int m_iRegionMarkCount; //记录创建了多少个标记区域对象
nmPlotGraphicBinder* m_pGraphicBinder; // 图元-数据绑定协调器
nmDataAnalyzeManager* m_pDataManager; // 当前 Map 借用的数据管理器,不负责销毁
bool m_bApplyingAxisRange; // 正在由程序设置坐标轴,防止提前完成首次初始化
bool m_bAxisInitializationPending; // 是否已排队一次首次坐标轴初始化
};

@ -136,6 +136,9 @@ static bool isSupportedNumericalWell(const nmDataWellBase* pWellData)
eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well;
}
// 默认边界只在创建数值分析时计算一次,后续完全由用户编辑。
static const double DEFAULT_OUTLINE_MARGIN_METERS = 1500.0;
// 将工程井井别转换为数值模块的稳定枚举。枚举值同时就是流量大字段索引。
static NM_WELL_CATEGORY frameworkWellCategory(const ZxDataWell* pWellData)
{
@ -2679,6 +2682,81 @@ nmDataOutline *nmDataAnalyzeManager::createOutline()
return m_outlineData;
}
bool nmDataAnalyzeManager::calculateNumericalWellBounds(QRectF& oBounds) const
{
bool bHasWell = false;
double dMinX = 0.0;
double dMaxX = 0.0;
double dMinY = 0.0;
double dMaxY = 0.0;
foreach(nmDataWellBase* pWellData, m_vWellData) {
if(pWellData == nullptr) {
continue;
}
const double dX = pWellData->getX().getValue().toDouble();
const double dY = pWellData->getY().getValue().toDouble();
if(!qIsFinite(dX) || !qIsFinite(dY)) {
continue;
}
if(!bHasWell) {
dMinX = dMaxX = dX;
dMinY = dMaxY = dY;
bHasWell = true;
} else {
dMinX = qMin(dMinX, dX);
dMaxX = qMax(dMaxX, dX);
dMinY = qMin(dMinY, dY);
dMaxY = qMax(dMaxY, dY);
}
}
if(!bHasWell) {
oBounds = QRectF();
return false;
}
oBounds = QRectF(QPointF(dMinX, dMinY),
QPointF(dMaxX, dMaxY)).normalized();
return true;
}
bool nmDataAnalyzeManager::createDefaultOutlineFromWells()
{
if(m_outlineData != nullptr) {
return false;
}
QRectF oWellBounds;
if(!calculateNumericalWellBounds(oWellBounds)) {
return false;
}
nmDataOutline* pOutlineData = createOutline();
if(pOutlineData == nullptr) {
return false;
}
const double dLeft = oWellBounds.left() - DEFAULT_OUTLINE_MARGIN_METERS;
const double dRight = oWellBounds.right() + DEFAULT_OUTLINE_MARGIN_METERS;
const double dTop = oWellBounds.bottom() + DEFAULT_OUTLINE_MARGIN_METERS;
const double dBottom = oWellBounds.top() - DEFAULT_OUTLINE_MARGIN_METERS;
QVector<QPointF> vecPoints;
vecPoints << QPointF(dLeft, dTop)
<< QPointF(dRight, dTop)
<< QPointF(dRight, dBottom)
<< QPointF(dLeft, dBottom);
pOutlineData->setName("");
pOutlineData->setOutlineType(NM_Rect_Outline_Type);
pOutlineData->setOutlinePoints(vecPoints);
pOutlineData->setFlowTypeList(QVector<int>(vecPoints.size(), 0));
notifyGeometryListChanged();
return true;
}
nmDataOutline* nmDataAnalyzeManager::getOutlineData()
{
return m_outlineData;

@ -1,6 +1,7 @@
#include "nmDataAxis.h"
nmDataAxis::nmDataAxis()
: m_bInitialViewFitted(false)
{
m_xMax = nmDataAttribute("AxisXmax", 2800.0, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
m_xMin = nmDataAttribute("AxisXmin", -1100.0, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "ft" << "m" << "cm" << "mm" << "in" << "0.1 in" << "mile" << "km");
@ -28,6 +29,7 @@ rapidjson::Value nmDataAxis::ToJsonValue(rapidjson::Document::AllocatorType& all
axisObject.AddMember("AxisXmin", m_xMin.ToJsonValue(allocator), allocator);
axisObject.AddMember("AxisYmax", m_yMax.ToJsonValue(allocator), allocator);
axisObject.AddMember("AxisYmin", m_yMin.ToJsonValue(allocator), allocator);
axisObject.AddMember("InitialViewFitted", m_bInitialViewFitted, allocator);
//axisObject.AddMember("MainScaleX", m_xMainScale.ToJsonValue(allocator), allocator);
//axisObject.AddMember("SubScaleX", m_xSubScale.ToJsonValue(allocator), allocator);
@ -40,6 +42,13 @@ rapidjson::Value nmDataAxis::ToJsonValue(rapidjson::Document::AllocatorType& all
// 从 RapidJSON Value 反序列化数据到 nmDataAxis
void nmDataAxis::FromJsonValue(const rapidjson::Value& jsonValue)
{
// 缺少该字段的已有成果已经保存了轴范围,加载时不得擅自覆盖。
m_bInitialViewFitted = true;
if (jsonValue.HasMember("InitialViewFitted") &&
jsonValue["InitialViewFitted"].IsBool()) {
m_bInitialViewFitted = jsonValue["InitialViewFitted"].GetBool();
}
// 反序列化 nmDataAttribute 类型的成员
// 调用 nmDataAttribute 自身的 FromJsonValue 方法进行递归反序列化
if (jsonValue.HasMember("AxisXmax") && jsonValue["AxisXmax"].IsObject()) {
@ -69,6 +78,16 @@ void nmDataAxis::FromJsonValue(const rapidjson::Value& jsonValue)
//}
}
bool nmDataAxis::isInitialViewFitted() const
{
return m_bInitialViewFitted;
}
void nmDataAxis::setInitialViewFitted(bool bInitialViewFitted)
{
m_bInitialViewFitted = bInitialViewFitted;
}
nmDataAttribute& nmDataAxis::getXMax()
{
return m_xMax;

@ -64,10 +64,57 @@
#include <QMessageBox>
#include <QResizeEvent>
#include <QTimer>
#include <QWidget>
namespace {
void includeMapPoint(const QPointF& oPoint,
bool& bHasBounds,
double& dMinX,
double& dMaxX,
double& dMinY,
double& dMaxY)
{
if(!qIsFinite(oPoint.x()) || !qIsFinite(oPoint.y())) {
return;
}
if(!bHasBounds) {
dMinX = dMaxX = oPoint.x();
dMinY = dMaxY = oPoint.y();
bHasBounds = true;
return;
}
dMinX = qMin(dMinX, oPoint.x());
dMaxX = qMax(dMaxX, oPoint.x());
dMinY = qMin(dMinY, oPoint.y());
dMaxY = qMax(dMaxY, oPoint.y());
}
void includeMapPoints(const QVector<QPointF>& vecPoints,
bool& bHasBounds,
double& dMinX,
double& dMaxX,
double& dMinY,
double& dMaxY)
{
foreach(const QPointF& oPoint, vecPoints) {
includeMapPoint(oPoint, bHasBounds,
dMinX, dMaxX, dMinY, dMaxY);
}
}
}
nmGuiPlot::nmGuiPlot(bool bUseBtns, QWidget *parent) :
iGuiPlot(bUseBtns, parent)
iGuiPlot(bUseBtns, parent),
m_pGraphicBinder(NULL),
m_pDataManager(nmDataAnalyzeManager::getCurrentInstance()),
m_bApplyingAxisRange(false),
m_bAxisInitializationPending(false)
{
m_bUseBtns = bUseBtns;
@ -86,8 +133,7 @@ nmGuiPlot::nmGuiPlot(bool bUseBtns, QWidget *parent) :
m_iRegionMarkCount = 0;
m_pGraphicBinder = new nmPlotGraphicBinder(
nmDataAnalyzeManager::getCurrentInstance(), this);
m_pGraphicBinder = new nmPlotGraphicBinder(m_pDataManager, this);
}
@ -131,7 +177,6 @@ void nmGuiPlot::initChartView(QString sTitle, QSize szDefault)
m_pPlot = m_pPlotScene->m_pPlot;
m_pPlotView->setMargins(0, 0, 0, 0);
m_pPlotView->setZoomMode(ZxRenderView::eZoomFitWindow, true);
connectSignals();
initDefultGeoObj();
@ -179,68 +224,21 @@ void nmGuiPlot::cancelActiveTools()
void nmGuiPlot::initDefultGeoObj()
{
// 获取边界数据,来自数据中心
nmDataOutline* pOutlineData = nmDataAnalyzeManager::getCurrentInstance()->getOutlineData();
nmDataAnalyzeManager* pDataManager = dataManager();
if(pDataManager == nullptr) {
return;
}
nmDataAxis* pAxisData = nmDataAnalyzeManager::getCurrentInstance()->getAxisData();
nmDataAxis* pAxisData = pDataManager->getAxisData();
if(pAxisData == nullptr) {
pAxisData = new nmDataAxis;
// 将 pAxisData 存入数据中心
nmDataAnalyzeManager::getCurrentInstance()->setAxisData(pAxisData);
}
// 获取 X 最大值
nmDataAttribute xMaxAttr = pAxisData->getXMax();
double xMaxValue = xMaxAttr.getValue().toDouble();
m_pPlot->getMainAxisX()->setRangeMax(xMaxValue);
// 获取 Y 最大值
nmDataAttribute yMaxAttr = pAxisData->getYMax();
double yMaxValue = yMaxAttr.getValue().toDouble();
m_pPlot->getMainAxisY()->setRangeMax(yMaxValue);
// 获取 X 最小值
nmDataAttribute xMinAttr = pAxisData->getXMin();
double xMinValue = xMinAttr.getValue().toDouble();
m_pPlot->getMainAxisX()->setRangeMin(xMinValue);
// 获取 Y 最小值
nmDataAttribute yMinAttr = pAxisData->getYMin();
double yMinValue = yMinAttr.getValue().toDouble();
m_pPlot->getMainAxisY()->setRangeMin(yMinValue);
connect(m_pPlot->getMainAxisX(), SIGNAL(sigRangeChanged(double, double, bool, bool)),
this, SLOT(onRangeXChanged(double, double, bool, bool)));
connect(m_pPlot->getMainAxisY(), SIGNAL(sigRangeChanged(double, double, bool, bool)),
this, SLOT(onRangeYChanged(double, double, bool, bool)));
// 数据中心无边界数据,创建默认边界数据
if(pOutlineData == nullptr) {
pOutlineData = nmDataAnalyzeManager::getCurrentInstance()->createOutline();
// 设置默认边界名称
pOutlineData->setName("");
// 设置边界点
QVector<QPointF> vecPoints;
vecPoints << QPointF(-1000.00, 1000.00)
<< QPointF(1000.00, 1000.00)
<< QPointF(1000.00, -1000.00)
<< QPointF(-1000.00, -1000.00);
pOutlineData->setOutlinePoints(vecPoints);
// 设置边界类型
pOutlineData->setOutlineType(NM_Rect_Outline_Type);
// 设置流型列表假设全部为0
QVector<int> vecFlowTypes(vecPoints.size(), 0);
pOutlineData->setFlowTypeList(vecFlowTypes);
pDataManager->setAxisData(pAxisData);
}
// 获取井数据,来自数据中心
QVector<nmDataWellBase*> vecWellDatas = nmDataAnalyzeManager::getCurrentInstance()->getWellDataList();
QVector<nmDataWellBase*> vecWellDatas = pDataManager->getWellDataList();
// 数据中心没有一口井数据,添加默认井数据
if(vecWellDatas.isEmpty()) {
@ -248,7 +246,7 @@ void nmGuiPlot::initDefultGeoObj()
ZxDataWell* pWellData = zxCurWell;
if(pWellData == nullptr) {
this->setPlotsByDataManger(nmDataAnalyzeManager::getCurrentInstance());
this->setPlotsByDataManger(pDataManager);
return;
}
@ -258,8 +256,7 @@ void nmGuiPlot::initDefultGeoObj()
static_cast<NM_WELL_CATEGORY>(
pWellData->getWellTypeIndex());
if(!nmIsValidWellCategory(eWellCategory)) {
this->setPlotsByDataManger(
nmDataAnalyzeManager::getCurrentInstance());
this->setPlotsByDataManger(pDataManager);
return;
}
@ -327,11 +324,11 @@ void nmGuiPlot::initDefultGeoObj()
if(ZxBaseUtil::isSameStr(wellClass, "VerticalWell")) {
// 初始化直井默认参数
nmDataWellBase* pWell = nmDataAnalyzeManager::getCurrentInstance()->createWell(NM_WELL_MODEL::Vertical_Well);
nmDataWellBase* pWell = pDataManager->createWell(NM_WELL_MODEL::Vertical_Well);
nmDataVerticalWell* m_VerticalWell = dynamic_cast<nmDataVerticalWell*>(pWell);
if(m_VerticalWell == nullptr) {
this->setPlotsByDataManger(nmDataAnalyzeManager::getCurrentInstance());
this->setPlotsByDataManger(pDataManager);
return;
}
@ -353,7 +350,7 @@ void nmGuiPlot::initDefultGeoObj()
m_VerticalWell->setFlowPoints(vecPtsF);
// TODO: 计算井的历史双对数/半对数数据
nmDataAnalyzeManager::getCurrentInstance()->calculationLogData(m_VerticalWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
pDataManager->calculationLogData(m_VerticalWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
// 存储历史数据到井对象
m_VerticalWell->setHistoryPressure(vvecHistoryPressureData);
@ -361,15 +358,15 @@ void nmGuiPlot::initDefultGeoObj()
m_VerticalWell->setHistorySemiLog(vvecHistorySemiLogData);
// 设置为当前查看的井
nmDataAnalyzeManager::getCurrentInstance()->setCurWellData(m_VerticalWell);
pDataManager->setCurWellData(m_VerticalWell);
} else if(ZxBaseUtil::isSameStr(wellClass, "VerticalFracturedWell")) {
// 初始化垂直裂缝井默认参数
nmDataWellBase* pWell = nmDataAnalyzeManager::getCurrentInstance()->createWell(NM_WELL_MODEL::Vertical_Fractured_Well);
nmDataWellBase* pWell = pDataManager->createWell(NM_WELL_MODEL::Vertical_Fractured_Well);
nmDataVerticalFracturedWell* m_VFracturedWell = dynamic_cast<nmDataVerticalFracturedWell*>(pWell);
if(m_VFracturedWell == nullptr) {
this->setPlotsByDataManger(nmDataAnalyzeManager::getCurrentInstance());
this->setPlotsByDataManger(pDataManager);
return;
}
@ -391,7 +388,7 @@ void nmGuiPlot::initDefultGeoObj()
m_VFracturedWell->setFlowPoints(vecPtsF);
// TODO: 计算井的历史双对数/半对数数据
nmDataAnalyzeManager::getCurrentInstance()->calculationLogData(m_VFracturedWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
pDataManager->calculationLogData(m_VFracturedWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
// 存储历史数据到井对象
m_VFracturedWell->setHistoryPressure(vvecHistoryPressureData);
@ -402,15 +399,15 @@ void nmGuiPlot::initDefultGeoObj()
m_VFracturedWell->setFracs();
// 设置为当前查看的井
nmDataAnalyzeManager::getCurrentInstance()->setCurWellData(m_VFracturedWell);
pDataManager->setCurWellData(m_VFracturedWell);
} else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalMultiFracturedWell")) {
// 初始化多段压裂水平井默认参数
nmDataWellBase* pWell = nmDataAnalyzeManager::getCurrentInstance()->createWell(NM_WELL_MODEL::Horizontal_Fractured_Well);
nmDataWellBase* pWell = pDataManager->createWell(NM_WELL_MODEL::Horizontal_Fractured_Well);
nmDataHorizontalFracturedWell* m_HFracturedWell = dynamic_cast<nmDataHorizontalFracturedWell*>(pWell);
if(m_HFracturedWell == nullptr) {
this->setPlotsByDataManger(nmDataAnalyzeManager::getCurrentInstance());
this->setPlotsByDataManger(pDataManager);
return;
}
@ -432,7 +429,7 @@ void nmGuiPlot::initDefultGeoObj()
m_HFracturedWell->setFlowPoints(vecPtsF);
// TODO: 计算井的历史双对数/半对数数据
nmDataAnalyzeManager::getCurrentInstance()->calculationLogData(m_HFracturedWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
pDataManager->calculationLogData(m_HFracturedWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
// 存储历史数据到井对象
m_HFracturedWell->setHistoryPressure(vvecHistoryPressureData);
@ -443,17 +440,21 @@ void nmGuiPlot::initDefultGeoObj()
m_HFracturedWell->setFracs();
// 设置为当前查看的井
nmDataAnalyzeManager::getCurrentInstance()->setCurWellData(m_HFracturedWell);
pDataManager->setCurWellData(m_HFracturedWell);
}
// 重新获取添加后的井数据
nmDataAnalyzeManager::getCurrentInstance()->appendNmWellData(
nmDataAnalyzeManager::getCurrentInstance()->getCurWellData());
vecWellDatas = nmDataAnalyzeManager::getCurrentInstance()->getWellDataList();
pDataManager->appendNmWellData(pDataManager->getCurWellData());
vecWellDatas = pDataManager->getWellDataList();
}
// 极端兼容入口没有预先建立边界时,也按当前井位置创建一次性默认边界。
if(pDataManager->getOutlineData() == nullptr) {
pDataManager->createDefaultOutlineFromWells();
}
// 根据数据中心的数据渲染所有的图元,上面只是为了保证打开时有一口井和边界
this->setPlotsByDataManger(nmDataAnalyzeManager::getCurrentInstance());
this->setPlotsByDataManger(pDataManager);
}
void nmGuiPlot::initAxisFromData(nmDataAnalyzeManager* pDataManager)
@ -461,33 +462,39 @@ void nmGuiPlot::initAxisFromData(nmDataAnalyzeManager* pDataManager)
Q_ASSERT(nullptr != pDataManager);
// 0.坐标轴
// 0.1 获取坐标轴数据,来自数据中心
nmDataAxis* pAxisData = nmDataAnalyzeManager::getCurrentInstance()->getAxisData();
nmDataAxis* pAxisData = pDataManager->getAxisData();
Q_ASSERT(nullptr != pAxisData);
// 0.2 获取 X 最大值
nmDataAttribute xMaxAttr = pAxisData->getXMax();
double xMaxValue = xMaxAttr.getValue().toDouble();
m_pPlot->getMainAxisX()->setRangeMax(xMaxValue);
// 0.3 获取 Y 最大值
nmDataAttribute yMaxAttr = pAxisData->getYMax();
double yMaxValue = yMaxAttr.getValue().toDouble();
m_pPlot->getMainAxisY()->setRangeMax(yMaxValue);
// 0.4 获取 X 最小值
nmDataAttribute xMinAttr = pAxisData->getXMin();
double xMinValue = xMinAttr.getValue().toDouble();
m_pPlot->getMainAxisX()->setRangeMin(xMinValue);
// 0.5 获取 Y 最小值
nmDataAttribute yMinAttr = pAxisData->getYMin();
double yMinValue = yMinAttr.getValue().toDouble();
m_pPlot->getMainAxisY()->setRangeMin(yMinValue);
if(qIsFinite(xMinValue) && qIsFinite(xMaxValue) &&
qIsFinite(yMinValue) && qIsFinite(yMaxValue) &&
xMaxValue > xMinValue && yMaxValue > yMinValue) {
applyAxisRange(xMinValue, xMaxValue, yMinValue, yMaxValue);
} else {
pAxisData->setInitialViewFitted(false);
}
connect(m_pPlot->getMainAxisX(), SIGNAL(sigRangeChanged(double, double, bool, bool)),
this, SLOT(onRangeXChanged(double, double, bool, bool)));
this, SLOT(onRangeXChanged(double, double, bool, bool)),
Qt::UniqueConnection);
connect(m_pPlot->getMainAxisY(), SIGNAL(sigRangeChanged(double, double, bool, bool)),
this, SLOT(onRangeYChanged(double, double, bool, bool)));
this, SLOT(onRangeYChanged(double, double, bool, bool)),
Qt::UniqueConnection);
}
void nmGuiPlot::initBoundaryObjFromData(nmDataAnalyzeManager* pDataManager)
@ -785,13 +792,17 @@ void nmGuiPlot::initRegionMarkObjsFromData(nmDataAnalyzeManager* pDataManager)
void nmGuiPlot::deleteAllPlotObjs()
{
QVector<nmObjBase*> vecObjs = this->getAllPlots();
if(m_pPlot == nullptr) {
return;
}
foreach(nmObjBase* pObj, vecObjs) {
// 1、移除图元
m_pPlot->removeObjByName(pObj->getName());
// 2、清理数据
//pObj->removeData();
// 这里只清理显示对象不能调用removeData()删除DataManager中的业务数据。
// 先解除全部绑定再倒序删除图元避免重名对象残留或Binder保存悬空指针。
if(m_pGraphicBinder != nullptr) {
m_pGraphicBinder->updateDataMgr(dataManager());
}
for(int nIndex = m_pPlot->getObjCount() - 1; nIndex >= 0; --nIndex) {
m_pPlot->removeObjByIndex(nIndex);
}
}
@ -1493,9 +1504,222 @@ void nmGuiPlot::deleteAllDFNPlots()
}
}
nmDataAnalyzeManager* nmGuiPlot::dataManager() const
{
return m_pDataManager != nullptr
? m_pDataManager
: nmDataAnalyzeManager::getCurrentInstance();
}
bool nmGuiPlot::calculateVisibleContentBounds(QRectF& oBounds) const
{
nmDataAnalyzeManager* pDataManager = dataManager();
if(pDataManager == nullptr) {
return false;
}
bool bHasBounds = false;
double dMinX = 0.0;
double dMaxX = 0.0;
double dMinY = 0.0;
double dMaxY = 0.0;
nmDataOutline* pOutlineData = pDataManager->getOutlineData();
if(pOutlineData != nullptr && pOutlineData->getPlotVisible()) {
if(pOutlineData->getOutlineType() == NM_Round_Outline_Type) {
const QPointF oCenter = pOutlineData->getCenter();
const double dRadius = qAbs(pOutlineData->getRadius());
includeMapPoint(QPointF(oCenter.x() - dRadius,
oCenter.y() - dRadius),
bHasBounds, dMinX, dMaxX, dMinY, dMaxY);
includeMapPoint(QPointF(oCenter.x() + dRadius,
oCenter.y() + dRadius),
bHasBounds, dMinX, dMaxX, dMinY, dMaxY);
} else {
includeMapPoints(pOutlineData->getOutlinePoints(),
bHasBounds, dMinX, dMaxX, dMinY, dMaxY);
}
}
const QVector<nmDataWellBase*> vecWells = pDataManager->getWellDataList();
foreach(nmDataWellBase* pWellData, vecWells) {
if(pWellData != nullptr && pWellData->getPlotVisible()) {
includeMapPoint(QPointF(
pWellData->getX().getValue().toDouble(),
pWellData->getY().getValue().toDouble()),
bHasBounds, dMinX, dMaxX, dMinY, dMaxY);
}
}
const QVector<nmDataFault*> vecFaults = pDataManager->getFaultDataList();
foreach(nmDataFault* pFaultData, vecFaults) {
if(pFaultData != nullptr && pFaultData->getPlotVisible()) {
includeMapPoints(pFaultData->getFaultPoints(),
bHasBounds, dMinX, dMaxX, dMinY, dMaxY);
}
}
const QVector<nmDataFracture*> vecFractures =
pDataManager->getFractureDataList();
foreach(nmDataFracture* pFractureData, vecFractures) {
if(pFractureData != nullptr && pFractureData->getPlotVisible()) {
includeMapPoints(pFractureData->getFracturePoints(),
bHasBounds, dMinX, dMaxX, dMinY, dMaxY);
}
}
const QVector<nmDataRegion*> vecRegions = pDataManager->getRegionDataList();
foreach(nmDataRegion* pRegionData, vecRegions) {
if(pRegionData != nullptr && pRegionData->getPlotVisible()) {
includeMapPoints(pRegionData->getVecPts(),
bHasBounds, dMinX, dMaxX, dMinY, dMaxY);
}
}
const QVector<nmDataRegionMark*> vecRegionMarks =
pDataManager->getRegionMarkDataList();
foreach(nmDataRegionMark* pRegionMarkData, vecRegionMarks) {
if(pRegionMarkData != nullptr && pRegionMarkData->getPlotVisible()) {
includeMapPoint(pRegionMarkData->getPtPos(),
bHasBounds, dMinX, dMaxX, dMinY, dMaxY);
}
}
if(!bHasBounds) {
return false;
}
oBounds = QRectF(QPointF(dMinX, dMinY),
QPointF(dMaxX, dMaxY)).normalized();
return true;
}
void nmGuiPlot::applyAxisRange(double dXMin, double dXMax,
double dYMin, double dYMax)
{
if(m_pPlot == nullptr ||
!qIsFinite(dXMin) || !qIsFinite(dXMax) ||
!qIsFinite(dYMin) || !qIsFinite(dYMax) ||
dXMax <= dXMin || dYMax <= dYMin) {
return;
}
const bool bOldApplying = m_bApplyingAxisRange;
m_bApplyingAxisRange = true;
m_pPlot->getMainAxisX()->setRangeMinMax(dXMin, dXMax, true, false);
m_pPlot->getMainAxisY()->setRangeMinMax(dYMin, dYMax, true, false);
nmDataAnalyzeManager* pDataManager = dataManager();
nmDataAxis* pAxisData = pDataManager != nullptr
? pDataManager->getAxisData() : nullptr;
if(pAxisData != nullptr) {
pAxisData->getXMin().setValue(dXMin);
pAxisData->getXMax().setValue(dXMax);
pAxisData->getYMin().setValue(dYMin);
pAxisData->getYMax().setValue(dYMax);
}
m_bApplyingAxisRange = bOldApplying;
}
bool nmGuiPlot::fitInitialAxisToContent()
{
if(m_pPlot == nullptr) {
return false;
}
QRectF oBounds;
if(!calculateVisibleContentBounds(oBounds)) {
return false;
}
QRectF oPlotRect = m_pPlot->getInnerRectF();
if(oPlotRect.width() <= 0.0 || oPlotRect.height() <= 0.0) {
return false;
}
double dCenterX = (oBounds.left() + oBounds.right()) * 0.5;
double dCenterY = (oBounds.top() + oBounds.bottom()) * 0.5;
double dRangeX = oBounds.width();
double dRangeY = oBounds.height();
// 退化为点或直线时补足基础跨度,避免产生零范围坐标轴。
if(dRangeX <= 0.0) {
dRangeX = 1500.0;
}
if(dRangeY <= 0.0) {
dRangeY = 1500.0;
}
// 内容四周各增加最大边长的 5%,边界本身不因此发生变化。
const double dPadding = qMax(dRangeX, dRangeY) * 0.05;
dRangeX += dPadding * 2.0;
dRangeY += dPadding * 2.0;
const double dViewRatio = oPlotRect.width() / oPlotRect.height();
const double dContentRatio = dRangeX / dRangeY;
if(dContentRatio < dViewRatio) {
dRangeX = dRangeY * dViewRatio;
} else {
dRangeY = dRangeX / dViewRatio;
}
applyAxisRange(dCenterX - dRangeX * 0.5,
dCenterX + dRangeX * 0.5,
dCenterY - dRangeY * 0.5,
dCenterY + dRangeY * 0.5);
return true;
}
void nmGuiPlot::scheduleInitialAxisView()
{
if(m_bAxisInitializationPending) {
return;
}
m_bAxisInitializationPending = true;
QTimer::singleShot(0, this, SLOT(initializeAxisView()));
}
void nmGuiPlot::initializeAxisView()
{
m_bAxisInitializationPending = false;
nmDataAnalyzeManager* pDataManager = dataManager();
nmDataAxis* pAxisData = pDataManager != nullptr
? pDataManager->getAxisData() : nullptr;
if(pAxisData == nullptr || pAxisData->isInitialViewFitted()) {
return;
}
// 只有真实绘图区和图元均准备好后才封闭初始化,失败时等待下一次显示尺寸变化。
if(fitInitialAxisToContent()) {
pAxisData->setInitialViewFitted(true);
}
}
void nmGuiPlot::resizeEvent(QResizeEvent* pEvent)
{
nmDataAnalyzeManager* pDataManager = dataManager();
nmDataAxis* pAxisData = pDataManager != nullptr
? pDataManager->getAxisData() : nullptr;
const bool bWaitingForInitialView =
pAxisData != nullptr && !pAxisData->isInitialViewFitted();
const bool bOldApplying = m_bApplyingAxisRange;
if(bWaitingForInitialView) {
// 布局阶段可能触发轴信号,不能把它误认为用户已经设置了范围。
m_bApplyingAxisRange = true;
}
iGuiPlot::resizeEvent(pEvent);
m_bApplyingAxisRange = bOldApplying;
if(bWaitingForInitialView) {
scheduleInitialAxisView();
}
}
void nmGuiPlot::onRangeXChanged(double dMin, double dMax, bool bRecalTicks, bool bAdjustRectZoomRatio)
{
nmDataAxis* pAxisData = nmDataAnalyzeManager::getCurrentInstance()->getAxisData();
nmDataAnalyzeManager* pDataManager = dataManager();
nmDataAxis* pAxisData = pDataManager != nullptr
? pDataManager->getAxisData() : nullptr;
if(pAxisData == nullptr) {
return;
@ -1506,11 +1730,18 @@ void nmGuiPlot::onRangeXChanged(double dMin, double dMax, bool bRecalTicks, bool
// 获取 X 最小值
pAxisData->getXMin().setValue(dMin);
if(!m_bApplyingAxisRange) {
// 任一用户轴操作都终止首次初始化,后续范围完全由用户控制。
pAxisData->setInitialViewFitted(true);
}
}
void nmGuiPlot::onRangeYChanged(double dMin, double dMax, bool bRecalTicks, bool bAdjustRectZoomRatio)
{
nmDataAxis* pAxisData = nmDataAnalyzeManager::getCurrentInstance()->getAxisData();
nmDataAnalyzeManager* pDataManager = dataManager();
nmDataAxis* pAxisData = pDataManager != nullptr
? pDataManager->getAxisData() : nullptr;
if(pAxisData == nullptr) {
return;
@ -1521,6 +1752,10 @@ void nmGuiPlot::onRangeYChanged(double dMin, double dMax, bool bRecalTicks, bool
// 获取 Y 最小值
pAxisData->getYMin().setValue(dMin);
if(!m_bApplyingAxisRange) {
pAxisData->setInitialViewFitted(true);
}
}
// ============================== 可见性设置方法实现 ==============================
@ -1762,6 +1997,25 @@ void nmGuiPlot::finishMeasure()
bool nmGuiPlot::setPlotsByDataManger(nmDataAnalyzeManager* pDataManager)
{
if(pDataManager == nullptr || m_pPlot == nullptr) {
return false;
}
// 本函数按DataManager完整重建Map重复调用也只能保留一套图元。
// 清理过程不删除业务数据,因此同一分析刷新和跨分析切换可使用同一路径。
deleteAllPlotObjs();
if(m_pGraphicBinder != nullptr) {
m_pGraphicBinder->updateDataMgr(pDataManager);
}
m_pDataManager = pDataManager;
nmDataAxis* pAxisData = pDataManager->getAxisData();
const bool bWaitingForInitialView =
pAxisData != nullptr && !pAxisData->isInitialViewFitted();
const bool bOldApplying = m_bApplyingAxisRange;
if(bWaitingForInitialView) {
// 图元创建过程可能发出轴范围信号,首次适配完成前统一按内部更新处理。
m_bApplyingAxisRange = true;
}
// 0.坐标轴
this->initAxisFromData(pDataManager);
// 1.边界
@ -1776,6 +2030,12 @@ bool nmGuiPlot::setPlotsByDataManger(nmDataAnalyzeManager* pDataManager)
this->initRegionObjsFromData(pDataManager);
// 6.区域标记
this->initRegionMarkObjsFromData(pDataManager);
m_bApplyingAxisRange = bOldApplying;
if(pAxisData != nullptr && !pAxisData->isInitialViewFitted()) {
// 等本轮布局和坐标轴内边距稳定后只初始化一次。
scheduleInitialAxisView();
}
return true;
}

@ -2893,14 +2893,6 @@ void nmSubWndMain::updateMapByDataManager(nmDataAnalyzeManager* pDataManager)
// 更新对应的Plot成员变量
pDataManager->setPlot(m_pWxPlot);
m_pWxPlot->deleteAllPlotObjs();
// 切换分析时更新 Binder 绑定的 DataManager
// 确保用户新画断层时添加到正确的 DataManager
if(m_pWxPlot->graphicBinder()) {
m_pWxPlot->graphicBinder()->updateDataMgr(pDataManager);
}
// 根据数据中心内容绘制新图元
// 完整清理、Binder切换和快照刷新均由Map重建入口统一完成。
m_pWxPlot->setPlotsByDataManger(pDataManager);
}

@ -518,7 +518,7 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
if(pAxisData == nullptr) {
pAxisData = new nmDataAxis;
// 将 pAxisData 存入数据中心
nmDataAnalyzeManager::getCurrentInstance()->setAxisData(pAxisData);
pDataManager->setAxisData(pAxisData);
}
// 读取解析解的参数
@ -659,28 +659,11 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
pOutlineData->setOutlinePoints(vecPoints);
}
} else {
// 如果解析解这里没有设置边界,数值解设置默认边界
// 创建默认矩形边界数据对象
nmDataOutline* pDefaultOutlineData = pDataManager->createOutline();
if(pDefaultOutlineData == nullptr) {
// 解析解未提供边界时,按当前数值井位置创建一次性默认边界。
if(!pDataManager->createDefaultOutlineFromWells()) {
return showNmDockInitializationError(
tr("The default numerical boundary could not be created."));
}
Q_ASSERT(pDefaultOutlineData);
// 默认名称留空,由界面根据边界类型进行翻译显示。
pDefaultOutlineData->setName("");
// 设置边界点
QVector<QPointF> vecPoints;
vecPoints << QPointF(-1000.00, 1000.00)
<< QPointF(1000.00, 1000.00)
<< QPointF(1000.00, -1000.00)
<< QPointF(-1000.00, -1000.00);
pDefaultOutlineData->setOutlinePoints(vecPoints);
// 设置边界类型
pDefaultOutlineData->setOutlineType(NM_Rect_Outline_Type);
// 设置流型列表假设全部为0
QVector<int> vecFlowTypes(vecPoints.size(), 0);
pDefaultOutlineData->setFlowTypeList(vecFlowTypes);
}
// 1.6 创建时间步设置相关数据

Loading…
Cancel
Save