fix(Nm): 统一模型位置显示界面并修复参考点刷新

- 统一只读坐标输入框和底部按钮样式,标签去除冒号
- 接入框架单位换算,鼠标移动后保留所选显示单位
- 输入框与单位框按 8:5 横向伸缩,取消窗口固定尺寸
- 绑定所属分析,动态读取参考点,避免数据串用及失效指针访问
- 完善无效坐标处理和度分秒显示,补充中文注释及翻译
feature/Dialog-Unification-20261106
lh 3 days ago
parent 0bcf0b1219
commit aabbf0bc72

Binary file not shown.

@ -7000,36 +7000,36 @@ Please check your input coordinates.</source>
<translation>本地网格</translation>
</message>
<message>
<source>X:</source>
<translation></translation>
<source>X</source>
<translation>X</translation>
</message>
<message>
<source>Y:</source>
<translation></translation>
<source>Y</source>
<translation>Y</translation>
</message>
<message>
<source>Coordinates</source>
<translation>坐标</translation>
</message>
<message>
<source>Longitude:</source>
<translation>经度:</translation>
<source>Longitude</source>
<translation>经度</translation>
</message>
<message>
<source>Latitude:</source>
<translation>纬度:</translation>
<source>Latitude</source>
<translation>纬度</translation>
</message>
<message>
<source>UTM zone:</source>
<translation>UTM 带:</translation>
<source>UTM zone</source>
<translation>UTM 带</translation>
</message>
<message>
<source>Easting:</source>
<translation>东向坐标:</translation>
<source>Easting</source>
<translation>东向坐标</translation>
</message>
<message>
<source>Northing:</source>
<translation>北向坐标:</translation>
<source>Northing</source>
<translation>北向坐标</translation>
</message>
<message>
<source>Close</source>

@ -4,45 +4,72 @@
#include "iDlgBase.h"
#include "nmSubWxs_global.h"
#include "nmDataGeoRef.h"
#include <QPointer>
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<nmDataAnalyzeManager> 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

@ -1756,7 +1756,8 @@ QWidget* nmSubWndMain::showPointerPosDialog()
{
nmGuiPlot* plot = dynamic_cast<nmGuiPlot*>(m_pWxPlot);
if(plot == nullptr) {
// 位置窗口只服务于已绑定分析的画布,避免回退到全局当前分析。
if(plot == nullptr || plot->boundDataManager() == nullptr) {
return nullptr;
}

@ -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 <QPushButton>
#include <QComboBox>
#include <QLineEdit>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <qnumeric.h>
#include <cmath>
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<nmGuiPlot*>(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<QLineEdit*>();
if(leX) {
leX->setMinimumWidth(100);
}
QLineEdit* leY = m_pY->findChild<QLineEdit*>();
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<QLabel*> listLabels = findChildren<QLabel*>();
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<int>(absCoordinate);
// 计算分数
double minutesFloat = (absCoordinate - degrees) * 60.0;
int minutes = static_cast<int>(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<int>(seconds), 2, 10, QChar('0')); // 秒数
result += QString(tr("sec")); // 秒符号
result += direction; // 方向
void nmWxPointerPosDlg::setPointerPos(double x, double y)
{
// 若所属分析已释放或画布已改绑,不把新画布的坐标套用到旧分析的参考点。
nmGuiPlot* pOwnerPlot = qobject_cast<nmGuiPlot*>(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;
}
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);
}

Loading…
Cancel
Save