diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm
index 140921bd..37718f1e 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 a9bfb2f5..5d7f2533 100644
--- a/Bin/Config/Lang/cn/nmNum_cn.ts
+++ b/Bin/Config/Lang/cn/nmNum_cn.ts
@@ -7000,36 +7000,36 @@ Please check your input coordinates.
本地网格
- X:
-
+ X
+ X
- Y:
-
+ Y
+ Y
Coordinates
坐标
- Longitude:
- 经度:
+ Longitude
+ 经度
- Latitude:
- 纬度:
+ Latitude
+ 纬度
- UTM zone:
- UTM 带:
+ UTM zone
+ UTM 带
- Easting:
- 东向坐标:
+ Easting
+ 东向坐标
- Northing:
- 北向坐标:
+ Northing
+ 北向坐标
Close
diff --git a/Include/nmNum/nmSubWxs/nmWxPointerPosDlg.h b/Include/nmNum/nmSubWxs/nmWxPointerPosDlg.h
index d8366f49..8756d787 100644
--- a/Include/nmNum/nmSubWxs/nmWxPointerPosDlg.h
+++ b/Include/nmNum/nmSubWxs/nmWxPointerPosDlg.h
@@ -4,45 +4,72 @@
#include "iDlgBase.h"
#include "nmSubWxs_global.h"
-#include "nmDataGeoRef.h"
+#include
-class QLineEdit;
class QComboBox;
-class QPushButton;
-class nmGUIComponentLineEdit;
-class nmDataAttribute;
+class QGridLayout;
+class QLineEdit;
+class nmDataAnalyzeManager;
+/**
+ * @brief 模型位置显示窗口,跟随所属画布的鼠标位置更新坐标,不修改分析数据。
+ * @note 本地 X/Y 和东向、北向坐标以米接收,由框架单位绑定器负责显示换算。
+ * 坐标均可选中复制;UTM 带只展示当前参考点的设置,不提供编辑入口。
+ * 输入列与单位列按 8:5 横向伸展,纵向采用字体和主题的推荐高度。
+ */
class NM_SUB_WXS_EXPORT nmWxPointerPosDlg : public iDlgBase
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit nmWxPointerPosDlg(QWidget* parent = 0);
-
- public slots:
- void setPointerPos(double x, double y);
+ /** @brief 绑定父级 nmGuiPlot 的分析管理器,由 Qt 父子关系管理窗口生命周期。 */
+ explicit nmWxPointerPosDlg(QWidget* parent = nullptr);
+public slots:
+ /** @brief 接收画布的米制坐标,刷新显示并保留各字段已选择的单位。 */
+ void setPointerPos(double x, double y);
private:
- void initUI();
- void loadGeoRefDataToUI();
- QString formatCoordinate(double coordinate, bool isLatitude); // ¾γ¶Èת»»
+ /** @brief 四个长度字段分别保存单位选择,不混用本地坐标与地理坐标。 */
+ enum LengthField
+ {
+ LocalX,
+ LocalY,
+ Easting,
+ Northing,
+ LengthFieldCount
+ };
+
+ /** @brief 创建本地网格、地理坐标分组及标准关闭按钮。 */
+ void initUI() override;
+ /** @brief 添加只读长度字段,并通过初始单位 m 识别框架长度单位组。 */
+ void createLengthField(QGridLayout* pGrid, int nRow, LengthField field,
+ const QString& sLabel, const QString& sObjectName);
- nmGUIComponentLineEdit* m_pX;
- nmGUIComponentLineEdit* m_pY;
+ /** @brief 回填米制值;无有效数据时清空显示并暂停单位切换,避免恢复旧值。 */
+ void setLengthValue(LengthField field, double dValue, bool bValid);
- nmDataAttribute* m_pAttrX;
- nmDataAttribute* m_pAttrY;
+ /** @brief 未设置参考点或参考数据失效时,清空地理坐标,保留本地坐标显示。 */
+ void clearGeographicValues();
+
+ /** @brief 将有限且在范围内的经纬度转换为度分秒,按百分之一秒四舍五入并进位。 */
+ QString formatCoordinate(double dCoordinate, bool bLatitude) const;
+
+private:
+ // 借用所属分析的管理器,删除时自动置空,不使用全局当前分析。
+ // 参考点可能在窗口打开后新增、清空或重建,每次刷新重新读取,不缓存其裸指针。
+ QPointer m_pDataManager;
- QLineEdit* m_pLonEdit;
- QLineEdit* m_pLatEdit;
- QComboBox* m_pUtmZoneCombo;
- QLineEdit* m_pEastingEdit;
- QLineEdit* m_pNorthingEdit;
- QPushButton* m_pCloseButton;
+ // 长度控件均由 Qt 父子关系释放;绑定状态独立记录,禁止以错误单位展示原始值。
+ QLineEdit* m_pLengthEdits[LengthFieldCount];
+ QComboBox* m_pLengthUnits[LengthFieldCount];
+ bool m_bUnitsBound[LengthFieldCount];
- nmDataGeoRef* m_pGeoRefData;
+ // 非长度字段使用普通只读文本框,经纬度文本本身包含度、分、秒符号。
+ QLineEdit* m_pLonEdit;
+ QLineEdit* m_pLatEdit;
+ QLineEdit* m_pUtmZoneEdit;
};
#endif // NMWXPOINTERPOSDLG_H
diff --git a/Src/nmNum/nmSubWnd/nmSubWndMain.cpp b/Src/nmNum/nmSubWnd/nmSubWndMain.cpp
index ee94e63a..75ee1667 100644
--- a/Src/nmNum/nmSubWnd/nmSubWndMain.cpp
+++ b/Src/nmNum/nmSubWnd/nmSubWndMain.cpp
@@ -1756,7 +1756,8 @@ QWidget* nmSubWndMain::showPointerPosDialog()
{
nmGuiPlot* plot = dynamic_cast(m_pWxPlot);
- if(plot == nullptr) {
+ // 位置窗口只服务于已绑定分析的画布,避免回退到全局当前分析。
+ if(plot == nullptr || plot->boundDataManager() == nullptr) {
return nullptr;
}
diff --git a/Src/nmNum/nmSubWxs/nmWxPointerPosDlg.cpp b/Src/nmNum/nmSubWxs/nmWxPointerPosDlg.cpp
index 27f1b423..e69d522e 100644
--- a/Src/nmNum/nmSubWxs/nmWxPointerPosDlg.cpp
+++ b/Src/nmNum/nmSubWxs/nmWxPointerPosDlg.cpp
@@ -1,277 +1,287 @@
#include "nmWxPointerPosDlg.h"
-#include "nmGUIComponentLineEdit.h"
+
+#include "iUnitWxBinder.h"
#include "nmDataAnalyzeManager.h"
-#include "nmDataAttribute.h"
+#include "nmDataGeoRef.h"
+#include "nmGuiPlot.h"
+#include "ZxBaseUtil.h"
-#include
#include
-#include
+#include
+#include
#include
-#include
#include
-#include
#include
+#include
+#include
+#include
+#include
#include
-nmWxPointerPosDlg::nmWxPointerPosDlg(QWidget* parent)
- : iDlgBase(parent)
+namespace
{
- setWindowTitle(tr("Pointer position"));
-
- m_pAttrX = new nmDataAttribute();
- m_pAttrX->setName("X");
- m_pAttrX->setUnit("m");
- m_pAttrX->setListUnitSelections(QStringList() << "m" << "ft" << "cm");
- m_pAttrX->setValue(0.0);
-
- m_pAttrY = new nmDataAttribute();
- m_pAttrY->setName("Y");
- m_pAttrY->setUnit("m");
- m_pAttrY->setListUnitSelections(QStringList() << "m" << "ft" << "cm");
- m_pAttrY->setValue(0.0);
+ // 与比例尺、测量工具保持相同的三列布局,不固定窗口或控件的像素尺寸。
+ QGridLayout* createCoordinateGrid(QGroupBox* pGroup)
+ {
+ pGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ QGridLayout* pGrid = new QGridLayout(pGroup);
+ pGrid->setContentsMargins(8, 15, 8, 10);
+ pGrid->setHorizontalSpacing(6);
+ pGrid->setVerticalSpacing(6);
+ pGrid->setColumnStretch(0, 0);
+ pGrid->setColumnStretch(1, 8);
+ pGrid->setColumnStretch(2, 5);
+ return pGrid;
+ }
+
+ // 只读而非禁用,保留鼠标选择、键盘导航和复制能力;纯文本字段可跨两列显示。
+ QLineEdit* createReadOnlyField(QGridLayout* pGrid, int nRow,
+ const QString& sLabel, const QString& sObjectName,
+ int nColumnSpan)
+ {
+ QWidget* pParent = pGrid->parentWidget();
+ QLabel* pLabel = new QLabel(sLabel, pParent);
+ pLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ pGrid->addWidget(pLabel, nRow, 0);
+
+ QLineEdit* pEdit = new QLineEdit(pParent);
+ pEdit->setObjectName(sObjectName);
+ pEdit->setReadOnly(true);
+ pEdit->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+ pEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ pGrid->addWidget(pEdit, nRow, 1, 1, nColumnSpan);
+ return pEdit;
+ }
+}
- m_pGeoRefData = nmDataAnalyzeManager::getCurrentInstance()->getGeoRefData();
+nmWxPointerPosDlg::nmWxPointerPosDlg(QWidget* parent)
+ : iDlgBase(parent),
+ m_pLonEdit(nullptr),
+ m_pLatEdit(nullptr),
+ m_pUtmZoneEdit(nullptr)
+{
+ for (int nField = 0; nField < LengthFieldCount; ++nField)
+ {
+ m_pLengthEdits[nField] = nullptr;
+ m_pLengthUnits[nField] = nullptr;
+ m_bUnitsBound[nField] = false;
+ }
+
+ // 浮动窗口的坐标与参考点必须来自同一画布,不能随全局当前分析切换而串用。
+ nmGuiPlot* pOwnerPlot = qobject_cast(parent);
+ if (pOwnerPlot != nullptr)
+ {
+ m_pDataManager = pOwnerPlot->boundDataManager();
+ }
initUI();
- loadGeoRefDataToUI();
- this->resize(320, 400);
- this->setFixedSize(this->size());
+ // 沿用原窗口的初始状态:尚未收到鼠标位置时,展示本地原点及对应参考点。
+ setPointerPos(0.0, 0.0);
+ setWindowTitle(tr("Pointer position"));
+ setWindowFlags((windowFlags() | Qt::WindowStaysOnTopHint) &
+ ~Qt::WindowContextHelpButtonHint);
+ // 初始大小由布局、字体和主题决定,允许用户随后调整窗口。
+ adjustSize();
}
void nmWxPointerPosDlg::initUI()
{
- // Local grid group
- QGroupBox* localGroup = new QGroupBox(tr("Local grid"));
-
- QFormLayout* localForm = new QFormLayout;
- localForm->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
- localForm->setVerticalSpacing(10);
- localForm->setHorizontalSpacing(15);
- localForm->setContentsMargins(8, 8, 8, 8);
-
- m_pX = new nmGUIComponentLineEdit(m_pAttrX, false);
- m_pY = new nmGUIComponentLineEdit(m_pAttrY, false);
- QLineEdit* leX = m_pX->findChild();
-
- if(leX) {
- leX->setMinimumWidth(100);
- }
-
- QLineEdit* leY = m_pY->findChild();
-
- if(leY) {
- leY->setMinimumWidth(100);
- }
-
- QWidget* xWidget = new QWidget;
- QHBoxLayout* xLay = new QHBoxLayout(xWidget);
- xLay->setContentsMargins(0, 0, 0, 0);
- xLay->addWidget(m_pX);
-
- QWidget* yWidget = new QWidget;
- QHBoxLayout* yLay = new QHBoxLayout(yWidget);
- yLay->setContentsMargins(0, 0, 0, 0);
- yLay->addWidget(m_pY);
-
- localForm->addRow(tr("X:"), xWidget);
- localForm->addRow(tr("Y:"), yWidget);
-
- QVBoxLayout* localWrapper = new QVBoxLayout;
- localWrapper->addStretch(1);
- localWrapper->addLayout(localForm);
- localWrapper->addStretch(1);
- localWrapper->setContentsMargins(0, 0, 0, 0);
- localGroup->setLayout(localWrapper);
-
- // Coordinates group
- QGroupBox* coordGroup = new QGroupBox(tr("Coordinates"));
- coordGroup->setEnabled(true);
-
- QFormLayout* coordForm = new QFormLayout;
- coordForm->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
- coordForm->setVerticalSpacing(10);
- coordForm->setHorizontalSpacing(15);
- coordForm->setContentsMargins(8, 8, 8, 8);
-
- m_pLonEdit = new QLineEdit;
- m_pLatEdit = new QLineEdit;
- m_pUtmZoneCombo = new QComboBox;
- m_pEastingEdit = new QLineEdit;
- m_pNorthingEdit = new QLineEdit;
-
- // 初始化UTM Zone下拉框选项
- QStringList bands;
- bands << "C" << "D" << "E" << "F" << "G" << "H" << "J" << "K" << "L" << "M"
- << "N" << "P" << "Q" << "R" << "S" << "T" << "U" << "V" << "W" << "X";
-
- // 外层循环:Band;内层循环:Zone
- for(int i = 0; i < bands.size(); ++i) {
- QString band = bands.at(i);
- for(int zone = 1; zone <= 60; ++zone) {
- QString zoneStr = QString("%1%2").arg(zone, 2, 10, QLatin1Char('0')).arg(band);
- m_pUtmZoneCombo->addItem(zoneStr);
- }
- }
-
- m_pLonEdit->setEnabled(false);
- m_pLatEdit->setEnabled(false);
- m_pUtmZoneCombo->setEnabled(false); // ComboBox禁用编辑
- m_pEastingEdit->setEnabled(false);
- m_pNorthingEdit->setEnabled(false);
-
- coordForm->addRow(tr("Longitude:"), m_pLonEdit);
- coordForm->addRow(tr("Latitude:"), m_pLatEdit);
- coordForm->addItem(new QSpacerItem(0, 20, QSizePolicy::Minimum, QSizePolicy::Fixed));
- coordForm->addRow(tr("UTM zone:"), m_pUtmZoneCombo);
- coordForm->addRow(tr("Easting:"), m_pEastingEdit);
- coordForm->addRow(tr("Northing:"), m_pNorthingEdit);
-
- QVBoxLayout* coordWrapper = new QVBoxLayout;
- coordWrapper->addStretch(1);
- coordWrapper->addLayout(coordForm);
- coordWrapper->addStretch(1);
- coordWrapper->setContentsMargins(0, 0, 0, 0);
- coordGroup->setLayout(coordWrapper);
-
- // Bottom button
- m_pCloseButton = new QPushButton(tr("Close"));
- connect(m_pCloseButton, SIGNAL(clicked()), this, SLOT(reject()));
- QHBoxLayout* btnLay = new QHBoxLayout;
- btnLay->addStretch();
- btnLay->addWidget(m_pCloseButton);
-
- // 整体布局
- QVBoxLayout* mainLay = new QVBoxLayout;
- mainLay->setContentsMargins(12, 12, 12, 12);
- mainLay->setSpacing(15);
- mainLay->addWidget(localGroup);
- mainLay->addWidget(coordGroup);
- mainLay->addLayout(btnLay);
- setLayout(mainLay);
+ QVBoxLayout* pMainLayout = new QVBoxLayout(this);
+ pMainLayout->setContentsMargins(12, 10, 12, 0);
+ pMainLayout->setSpacing(10);
+
+ QGroupBox* pLocalGroup = new QGroupBox(tr("Local grid"), this);
+ QGridLayout* pLocalGrid = createCoordinateGrid(pLocalGroup);
+ createLengthField(pLocalGrid, 0, LocalX, tr("X"), "pointerX");
+ createLengthField(pLocalGrid, 1, LocalY, tr("Y"), "pointerY");
+ pMainLayout->addWidget(pLocalGroup);
+
+ QGroupBox* pGeoGroup = new QGroupBox(tr("Coordinates"), this);
+ QGridLayout* pGeoGrid = createCoordinateGrid(pGeoGroup);
+ m_pLonEdit = createReadOnlyField(pGeoGrid, 0, tr("Longitude"), "pointerLongitude", 2);
+ m_pLatEdit = createReadOnlyField(pGeoGrid, 1, tr("Latitude"), "pointerLatitude", 2);
+ // UTM 带是参考点的只读属性,无需创建包含全部分带选项的禁用下拉框。
+ m_pUtmZoneEdit = createReadOnlyField(pGeoGrid, 2, tr("UTM zone"), "pointerUtmZone", 2);
+ createLengthField(pGeoGrid, 3, Easting, tr("Easting"), "pointerEasting");
+ createLengthField(pGeoGrid, 4, Northing, tr("Northing"), "pointerNorthing");
+ pMainLayout->addWidget(pGeoGroup);
+
+ // 两个分组按当前标签的推荐宽度对齐,不写死中文字宽或控件宽度。
+ int nLabelWidth = 0;
+ const QList listLabels = findChildren();
+ for (int nIndex = 0; nIndex < listLabels.size(); ++nIndex)
+ {
+ nLabelWidth = qMax(nLabelWidth, listLabels[nIndex]->sizeHint().width());
+ }
+ pLocalGrid->setColumnMinimumWidth(0, nLabelWidth);
+ pGeoGrid->setColumnMinimumWidth(0, nLabelWidth);
+ // 纵向余量留在内容与按钮之间,输入框和分组不随窗口拉高而变高。
+ pMainLayout->addStretch(1);
+
+ QWidget* pFooter = new QWidget(this);
+ pFooter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ QHBoxLayout* pFooterLayout = new QHBoxLayout(pFooter);
+ pFooterLayout->setContentsMargins(4, 5, 4, 5);
+ pFooterLayout->setSpacing(8);
+ pFooterLayout->addStretch();
+ QDialogButtonBox* pButtons = new QDialogButtonBox(
+ QDialogButtonBox::Close, Qt::Horizontal, pFooter);
+ QPushButton* pClose = pButtons->button(QDialogButtonBox::Close);
+ pClose->setText(tr("Close"));
+ pClose->setIcon(zxLoadIcon("Cancel"));
+ pClose->setAutoDefault(false);
+ pFooterLayout->addWidget(pButtons);
+ pMainLayout->addWidget(pFooter);
+ connect(pButtons, SIGNAL(rejected()), this, SLOT(reject()));
}
-void nmWxPointerPosDlg::setPointerPos(double x, double y)
+void nmWxPointerPosDlg::createLengthField(QGridLayout* pGrid, int nRow,
+ LengthField field, const QString& sLabel,
+ const QString& sObjectName)
{
- // 更新X、Y坐标显示
- m_pX->setDisplayValue(x);
- m_pY->setDisplayValue(y);
-
- // 检查是否有地理参考数据
- if (!m_pGeoRefData) {
- return; // 提前返回,避免后续无效计算
- }
-
- // 验证地理参考数据有效性
- QString utmZone = m_pGeoRefData->getUtmZone().getValue().toString();
- if (utmZone.isEmpty()) {
- return; // 提前返回,避免后续无效计算
- }
-
- // 获取基础地理参考坐标
- double baseEasting = m_pGeoRefData->getEasting().getValue().toDouble();
- double baseNorthing = m_pGeoRefData->getNorthing().getValue().toDouble();
- double baseLongitude = m_pGeoRefData->getLongitude().getValue().toDouble();
- double baseLatitude = m_pGeoRefData->getLatitude().getValue().toDouble();
-
- // 计算并更新UTM坐标
- double newEasting = baseEasting + x;
- double newNorthing = baseNorthing + y;
- m_pEastingEdit->setText(QString::number(newEasting, 'f', 2));
- m_pNorthingEdit->setText(QString::number(newNorthing, 'f', 2));
-
- // 计算并更新经纬度坐标
- const double METERS_PER_DEGREE_LAT = 111111.0;
- const double PI = 3.14159265358979323846;
-
- double latitudeRadians = baseLatitude * PI / 180.0;
- double metersPerDegreeLon = METERS_PER_DEGREE_LAT * cos(latitudeRadians);
-
- double newLatitude = baseLatitude + (y / METERS_PER_DEGREE_LAT);
- double newLongitude = baseLongitude + (x / metersPerDegreeLon);
-
- // 转换为度分秒格式并更新UI
- QString latStr = formatCoordinate(newLatitude, true);
- QString lonStr = formatCoordinate(newLongitude, false);
- m_pLatEdit->setText(latStr);
- m_pLonEdit->setText(lonStr);
+ QLineEdit* pEdit = createReadOnlyField(pGrid, nRow, sLabel, sObjectName, 1);
+ QComboBox* pUnit = new QComboBox(pGrid->parentWidget());
+ pUnit->setObjectName(sObjectName + "Unit");
+ pUnit->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ pUnit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ pGrid->addWidget(pUnit, nRow, 2);
+
+ // 空参数标识配合 m 识别长度单位组,避免借用井参数而附带不适用的数值范围。
+ pUnit->addItem("m");
+ m_pLengthEdits[field] = pEdit;
+ m_pLengthUnits[field] = pUnit;
+ m_bUnitsBound[field] = bindUnitPairWxs(pEdit, pUnit, QString(), false);
+ m_bUnitsBound[field] = m_bUnitsBound[field] && m_pUnitBinder != nullptr;
+ // 初次有效值到达前保持空白;绑定失败时也不显示未经换算的坐标。
+ setLengthValue(field, 0.0, false);
}
-void nmWxPointerPosDlg::loadGeoRefDataToUI()
+void nmWxPointerPosDlg::setLengthValue(LengthField field, double dValue, bool bValid)
{
- if (!m_pGeoRefData) {
- m_pUtmZoneCombo->setCurrentIndex(-1); // 设置为空选项
- return;
- }
-
- // 检查是否有有效的地理参考数据
- QString utmZone = m_pGeoRefData->getUtmZone().getValue().toString();
- bool hasValidData = !utmZone.isEmpty();
-
- if (hasValidData) {
- // 加载经纬度
- double longitude = m_pGeoRefData->getLongitude().getValue().toDouble();
- double latitude = m_pGeoRefData->getLatitude().getValue().toDouble();
-
- // 分别转换经度和纬度,并设置到UI
- QString lonStr = formatCoordinate(longitude, false); // false表示经度
- QString latStr = formatCoordinate(latitude, true); // true表示纬度
-
- m_pLonEdit->setText(lonStr); // 显示转换后的经度
- m_pLatEdit->setText(latStr); // 显示转换后的纬度
-
- // 加载UTM Zone
- int index = m_pUtmZoneCombo->findText(utmZone);
- if (index >= 0) {
- m_pUtmZoneCombo->setCurrentIndex(index);
- }
-
- // 加载UTM坐标
- double easting = m_pGeoRefData->getEasting().getValue().toDouble();
- double northing = m_pGeoRefData->getNorthing().getValue().toDouble();
-
- m_pEastingEdit->setText(QString::number(easting, 'f', 2));
- m_pNorthingEdit->setText(QString::number(northing, 'f', 2));
- }
+ const bool bCanDisplay = bValid && qIsFinite(dValue) && m_bUnitsBound[field];
+ m_pLengthUnits[field]->setEnabled(bCanDisplay);
+ if (m_bUnitsBound[field])
+ {
+ // 只更新米制基准值,不重建单位列表,鼠标移动后仍保留用户的显示单位。
+ // 无数据时同步清除绑定器的旧值,避免后续单位切换使过期坐标重新出现。
+ m_pUnitBinder->setBaseValue(m_pLengthEdits[field], bCanDisplay ? dValue : 0.0);
+ }
+ if (!bCanDisplay)
+ {
+ m_pLengthEdits[field]->clear();
+ }
}
-QString nmWxPointerPosDlg::formatCoordinate(double coordinate, bool isLatitude)
+void nmWxPointerPosDlg::clearGeographicValues()
{
- // 判断正负,确定方向
- bool isPositive = coordinate >= 0;
- double absCoordinate = qAbs(coordinate);
-
- // 提取度数
- int degrees = static_cast(absCoordinate);
-
- // 计算分数
- double minutesFloat = (absCoordinate - degrees) * 60.0;
- int minutes = static_cast(minutesFloat);
-
- // 计算秒数
- double seconds = (minutesFloat - minutes) * 60.0;
-
- // 防止浮点精度问题导致秒数≥60
- if (seconds >= 60.0) {
- seconds = 59.999;
- }
-
- // 确定方向字符
- QString direction;
- if (isLatitude) {
- direction = isPositive ? "N" : "S"; // 纬度
- } else {
- direction = isPositive ? "E" : "W"; // 经度
- }
+ m_pLonEdit->clear();
+ m_pLatEdit->clear();
+ m_pUtmZoneEdit->clear();
+ setLengthValue(Easting, 0.0, false);
+ setLengthValue(Northing, 0.0, false);
+}
- // 分步构建字符串
- QString result;
- result += QString("%1").arg(degrees, 2, 10, QChar('0')); // 度数
- result += QString(tr("deg")); // 度符号
- result += QString("%1").arg(minutes, 2, 10, QChar('0')); // 分数
- result += QString(tr("min")); // 分符号
- result += QString("%1").arg(static_cast(seconds), 2, 10, QChar('0')); // 秒数
- result += QString(tr("sec")); // 秒符号
- result += direction; // 方向
+void nmWxPointerPosDlg::setPointerPos(double x, double y)
+{
+ // 若所属分析已释放或画布已改绑,不把新画布的坐标套用到旧分析的参考点。
+ nmGuiPlot* pOwnerPlot = qobject_cast(parentWidget());
+ const bool bValidPoint = !m_pDataManager.isNull() && pOwnerPlot != nullptr &&
+ pOwnerPlot->boundDataManager() == m_pDataManager.data() &&
+ qIsFinite(x) && qIsFinite(y);
+ setLengthValue(LocalX, x, bValidPoint);
+ setLengthValue(LocalY, y, bValidPoint);
+ if (!bValidPoint)
+ {
+ clearGeographicValues();
+ return;
+ }
+
+ // 每次从所属分析取最新对象,覆盖打开窗口后才设置参考点、参考点重建等情况。
+ nmDataGeoRef* pGeoRef = m_pDataManager->getGeoRefData();
+ if (pGeoRef == nullptr)
+ {
+ clearGeographicValues();
+ return;
+ }
+
+ const QString sZone = pGeoRef->getUtmZone().getValue().toString().trimmed();
+ bool bZoneOK = false;
+ const int nZone = sZone.left(2).toInt(&bZoneOK);
+ const QString sBands("CDEFGHJKLMNPQRSTUVWX");
+ if (!bZoneOK || nZone < 1 || nZone > 60 || sZone.size() != 3 ||
+ !sBands.contains(sZone.at(2)))
+ {
+ clearGeographicValues();
+ return;
+ }
+
+ // 空字段不能按 0 参与换算;无效数据只清空显示,不弹校验提示。
+ bool bEastingOK = false;
+ bool bNorthingOK = false;
+ bool bLongitudeOK = false;
+ bool bLatitudeOK = false;
+ const double dBaseEasting = pGeoRef->getEasting().getValue().toDouble(&bEastingOK);
+ const double dBaseNorthing = pGeoRef->getNorthing().getValue().toDouble(&bNorthingOK);
+ const double dBaseLongitude = pGeoRef->getLongitude().getValue().toDouble(&bLongitudeOK);
+ const double dBaseLatitude = pGeoRef->getLatitude().getValue().toDouble(&bLatitudeOK);
+ if (!bEastingOK || !bNorthingOK || !bLongitudeOK || !bLatitudeOK ||
+ !qIsFinite(dBaseEasting) || !qIsFinite(dBaseNorthing) ||
+ !qIsFinite(dBaseLongitude) || !qIsFinite(dBaseLatitude) ||
+ qAbs(dBaseLongitude) > 180.0 || qAbs(dBaseLatitude) > 90.0)
+ {
+ clearGeographicValues();
+ return;
+ }
+
+ // 模型坐标为相对参考点的米制偏移,显示单位不参与坐标计算。
+ const double dEasting = dBaseEasting + x;
+ const double dNorthing = dBaseNorthing + y;
+ if (!qIsFinite(dEasting) || !qIsFinite(dNorthing))
+ {
+ clearGeographicValues();
+ return;
+ }
+ m_pUtmZoneEdit->setText(sZone);
+ setLengthValue(Easting, dEasting, true);
+ setLengthValue(Northing, dNorthing, true);
+
+ // 沿用现有的小范围经纬度近似换算,并非严格的 UTM 反投影。
+ // 极点附近经度换算分母趋近于零,此时保留平面坐标,清空无法可靠计算的经纬度。
+ const double dMetersPerDegreeLat = 111111.0;
+ const double dPi = 3.14159265358979323846;
+ const double dMetersPerDegreeLon = dMetersPerDegreeLat *
+ std::cos(dBaseLatitude * dPi / 180.0);
+ if (qAbs(dMetersPerDegreeLon) < 1.0e-6)
+ {
+ m_pLonEdit->clear();
+ m_pLatEdit->clear();
+ return;
+ }
+
+ const double dLatitude = dBaseLatitude + y / dMetersPerDegreeLat;
+ const double dLongitude = dBaseLongitude + x / dMetersPerDegreeLon;
+ m_pLonEdit->setText(formatCoordinate(dLongitude, false));
+ m_pLatEdit->setText(formatCoordinate(dLatitude, true));
+}
- return result;
-}
\ No newline at end of file
+QString nmWxPointerPosDlg::formatCoordinate(double dCoordinate, bool bLatitude) const
+{
+ if (!qIsFinite(dCoordinate) || qAbs(dCoordinate) > (bLatitude ? 90.0 : 180.0))
+ {
+ return QString();
+ }
+
+ // 先按百分之一秒整体取整再分解,自动处理 59.995 秒进位,避免出现 60 秒。
+ // 经纬度范围已限定,最大值为 180 * 3600 * 100,不会超出 int 范围。
+ const int nTotalHundredths = qRound(qAbs(dCoordinate) * 360000.0);
+ const int nDegrees = nTotalHundredths / 360000;
+ const int nMinutes = (nTotalHundredths / 6000) % 60;
+ const double dSeconds = (nTotalHundredths % 6000) / 100.0;
+ const QString sDirection = bLatitude ?
+ (dCoordinate >= 0.0 ? "N" : "S") : (dCoordinate >= 0.0 ? "E" : "W");
+ return QString("%1%2%3%4%5%6%7")
+ .arg(nDegrees, 2, 10, QLatin1Char('0')).arg(tr("deg"))
+ .arg(nMinutes, 2, 10, QLatin1Char('0')).arg(tr("min"))
+ .arg(dSeconds, 5, 'f', 2, QLatin1Char('0')).arg(tr("sec")).arg(sDirection);
+}