|
|
#define _USE_MATH_DEFINES
|
|
|
|
|
|
#include "nmWxGeoRefDlg.h"
|
|
|
#include <QGroupBox>
|
|
|
#include <QMessageBox>
|
|
|
#include <QFormLayout>
|
|
|
#include <QVBoxLayout>
|
|
|
#include <QHBoxLayout>
|
|
|
#include <cmath>
|
|
|
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
|
#include "nmDataAxis.h"
|
|
|
|
|
|
nmWxGeoRefDlg::nmWxGeoRefDlg(QWidget* parent)
|
|
|
: iDlgBase(parent), m_bUpdating(false)
|
|
|
{
|
|
|
this->setWindowTitle(tr("Geo referencing"));
|
|
|
initUI();
|
|
|
this->resize(370, 320);
|
|
|
this->setFixedSize(this->size());
|
|
|
|
|
|
m_geoRefData = nmDataAnalyzeManager::getCurrentInstance()->getGeoRefDataCopy();
|
|
|
|
|
|
loadDataToUI();
|
|
|
}
|
|
|
|
|
|
void nmWxGeoRefDlg::initUI()
|
|
|
{
|
|
|
// Location group
|
|
|
QGroupBox* locationGroup = new QGroupBox(tr("Location"));
|
|
|
|
|
|
// form 布局
|
|
|
QFormLayout* locForm = new QFormLayout;
|
|
|
locForm->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
|
locForm->setVerticalSpacing(12);
|
|
|
locForm->setHorizontalSpacing(20);
|
|
|
locForm->setContentsMargins(8, 8, 8, 8);
|
|
|
|
|
|
m_pLongitudeEdit = new QLineEdit; m_pLongitudeEdit->setFixedWidth(180);
|
|
|
m_pLatitudeEdit = new QLineEdit; m_pLatitudeEdit ->setFixedWidth(180);
|
|
|
locForm->addRow(tr("Longitude:"), m_pLongitudeEdit);
|
|
|
locForm->addRow(tr("Latitude:"), m_pLatitudeEdit);
|
|
|
|
|
|
// 包一层 vbox,用 stretch 让 form 居中
|
|
|
QVBoxLayout* locWrapper = new QVBoxLayout;
|
|
|
locWrapper->addStretch(1);
|
|
|
locWrapper->addLayout(locForm);
|
|
|
locWrapper->addStretch(1);
|
|
|
locWrapper->setContentsMargins(0, 0, 0, 0); // 根据需要调整 groupbox 内边距
|
|
|
locationGroup->setLayout(locWrapper);
|
|
|
|
|
|
// Zone group
|
|
|
QGroupBox* zoneGroup = new QGroupBox(tr("Zone"));
|
|
|
|
|
|
QFormLayout* zoneForm = new QFormLayout;
|
|
|
zoneForm->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
|
zoneForm->setVerticalSpacing(12);
|
|
|
zoneForm->setHorizontalSpacing(20);
|
|
|
zoneForm->setContentsMargins(8, 8, 8, 8);
|
|
|
|
|
|
m_pUtmZoneCombo = new QComboBox; m_pUtmZoneCombo->setFixedWidth(180);
|
|
|
m_pEastingEdit = new QLineEdit; m_pEastingEdit ->setFixedWidth(180);
|
|
|
m_pNorthingEdit = new QLineEdit; m_pNorthingEdit->setFixedWidth(180);
|
|
|
zoneForm->addRow(tr("UTM zone:"), m_pUtmZoneCombo);
|
|
|
zoneForm->addRow(tr("Easting:"), m_pEastingEdit);
|
|
|
zoneForm->addRow(tr("Northing:"), m_pNorthingEdit);
|
|
|
|
|
|
// 初始化 UTM Zone 下拉框(格式:01C~60X)
|
|
|
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) {
|
|
|
// 格式化为两位数 Zone + 纬度带(例如 "01C", "02C")
|
|
|
QString zoneStr = QString("%1%2").arg(zone, 2, 10, QLatin1Char('0')).arg(band);
|
|
|
m_pUtmZoneCombo->addItem(zoneStr);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
m_pUtmZoneCombo->setCurrentIndex(-1);
|
|
|
|
|
|
QVBoxLayout* zoneWrapper = new QVBoxLayout;
|
|
|
zoneWrapper->addStretch(1);
|
|
|
zoneWrapper->addLayout(zoneForm);
|
|
|
zoneWrapper->addStretch(1);
|
|
|
zoneWrapper->setContentsMargins(0, 0, 0, 0);
|
|
|
zoneGroup->setLayout(zoneWrapper);
|
|
|
|
|
|
// Buttons
|
|
|
m_pOkButton = new QPushButton(tr("OK"));
|
|
|
m_pCancelButton = new QPushButton(tr("Cancel"));
|
|
|
m_pOkButton->setDefault(true);
|
|
|
connect(m_pOkButton, SIGNAL(clicked()), this, SLOT(okAccept()));
|
|
|
connect(m_pCancelButton, SIGNAL(clicked()), this, SLOT(cancelReject()));
|
|
|
|
|
|
QHBoxLayout* btnLay = new QHBoxLayout;
|
|
|
btnLay->addStretch();
|
|
|
btnLay->addWidget(m_pOkButton);
|
|
|
btnLay->addWidget(m_pCancelButton);
|
|
|
|
|
|
// 主布局
|
|
|
QVBoxLayout* mainLay = new QVBoxLayout;
|
|
|
mainLay->setContentsMargins(10, 10, 10, 10);
|
|
|
mainLay->setSpacing(15);
|
|
|
mainLay->addWidget(locationGroup);
|
|
|
mainLay->addWidget(zoneGroup);
|
|
|
mainLay->addLayout(btnLay);
|
|
|
setLayout(mainLay);
|
|
|
|
|
|
// 初始都设成不可编辑
|
|
|
m_pLongitudeEdit->setEnabled(false);
|
|
|
m_pLatitudeEdit ->setEnabled(false);
|
|
|
m_pUtmZoneCombo ->setEnabled(true);
|
|
|
m_pEastingEdit ->setEnabled(false);
|
|
|
m_pNorthingEdit ->setEnabled(false);
|
|
|
|
|
|
connect(m_pUtmZoneCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onUtmZoneChanged(int)));
|
|
|
connect(m_pLongitudeEdit, SIGNAL(editingFinished()), this, SLOT(onLongitudeChanged()));
|
|
|
connect(m_pLatitudeEdit, SIGNAL(editingFinished()), this, SLOT(onLatitudeChanged()));
|
|
|
connect(m_pEastingEdit, SIGNAL(editingFinished()), this, SLOT(onEastingChanged()));
|
|
|
connect(m_pNorthingEdit, SIGNAL(editingFinished()), this, SLOT(onNorthingChanged()));
|
|
|
}
|
|
|
|
|
|
// 根据经纬度和UTM zone计算Northing坐标的辅助函数
|
|
|
double nmWxGeoRefDlg::calculateNorthing(double latitude, double longitude, int zone)
|
|
|
{
|
|
|
// UTM投影的简化计算公式
|
|
|
// 这里使用基本的UTM投影算法
|
|
|
|
|
|
const double a = 6378137.0; // WGS84长半轴
|
|
|
const double e2 = 0.00669437999014; // WGS84第一偏心率的平方
|
|
|
const double k0 = 0.9996; // UTM比例因子
|
|
|
|
|
|
// 转换为弧度
|
|
|
double lat_rad = latitude * M_PI / 180.0;
|
|
|
double lon_rad = longitude * M_PI / 180.0;
|
|
|
|
|
|
// 中央经线
|
|
|
double lon0_rad = (-183.0 + zone * 6.0) * M_PI / 180.0;
|
|
|
double dlon = lon_rad - lon0_rad;
|
|
|
|
|
|
// UTM投影计算
|
|
|
double N = a / sqrt(1 - e2 * sin(lat_rad) * sin(lat_rad));
|
|
|
double T = tan(lat_rad) * tan(lat_rad);
|
|
|
double C = e2 * cos(lat_rad) * cos(lat_rad) / (1 - e2);
|
|
|
double A = cos(lat_rad) * dlon;
|
|
|
|
|
|
double M = a * ((1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256) * lat_rad
|
|
|
- (3 * e2 / 8 + 3 * e2 * e2 / 32 + 45 * e2 * e2 * e2 / 1024) * sin(2 * lat_rad)
|
|
|
+ (15 * e2 * e2 / 256 + 45 * e2 * e2 * e2 / 1024) * sin(4 * lat_rad)
|
|
|
- (35 * e2 * e2 * e2 / 3072) * sin(6 * lat_rad));
|
|
|
|
|
|
double northing = k0 * (M + N * tan(lat_rad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24
|
|
|
+ (61 - 58 * T + T * T + 600 * C - 330 * e2) * A * A * A * A * A * A / 720));
|
|
|
|
|
|
// 南半球需要加上假北值
|
|
|
if(latitude < 0) {
|
|
|
northing += 10000000.0;
|
|
|
}
|
|
|
|
|
|
return northing;
|
|
|
}
|
|
|
|
|
|
// 经度编辑完成时的处理
|
|
|
void nmWxGeoRefDlg::onLongitudeChanged()
|
|
|
{
|
|
|
if(m_bUpdating) return; // 防止递归更新
|
|
|
|
|
|
QString lonText = m_pLongitudeEdit->text();
|
|
|
QString latText = m_pLatitudeEdit->text();
|
|
|
|
|
|
// 检查经度值是否真的发生了变化
|
|
|
if(lonText == m_lastLongitude) {
|
|
|
return; // 值没有变化,不进行转换
|
|
|
}
|
|
|
|
|
|
if(!lonText.isEmpty() && !latText.isEmpty()) {
|
|
|
updateFromLatLon();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 纬度编辑完成时的处理
|
|
|
void nmWxGeoRefDlg::onLatitudeChanged()
|
|
|
{
|
|
|
if(m_bUpdating) return; // 防止递归更新
|
|
|
|
|
|
QString lonText = m_pLongitudeEdit->text();
|
|
|
QString latText = m_pLatitudeEdit->text();
|
|
|
|
|
|
// 检查纬度值是否真的发生了变化
|
|
|
if(latText == m_lastLatitude) {
|
|
|
return; // 值没有变化,不进行转换
|
|
|
}
|
|
|
|
|
|
if(!lonText.isEmpty() && !latText.isEmpty()) {
|
|
|
updateFromLatLon();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 东向坐标编辑完成时的处理
|
|
|
void nmWxGeoRefDlg::onEastingChanged()
|
|
|
{
|
|
|
if(m_bUpdating) return; // 防止递归更新
|
|
|
|
|
|
QString eastingText = m_pEastingEdit->text();
|
|
|
QString northingText = m_pNorthingEdit->text();
|
|
|
QString zoneText = m_pUtmZoneCombo->currentText();
|
|
|
|
|
|
// 检查东向坐标值是否真的发生了变化
|
|
|
if(eastingText == m_lastEasting) {
|
|
|
return; // 值没有变化,不进行转换
|
|
|
}
|
|
|
|
|
|
if(!eastingText.isEmpty() && !northingText.isEmpty() && !zoneText.isEmpty()) {
|
|
|
updateFromUTM();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 北向坐标编辑完成时的处理
|
|
|
void nmWxGeoRefDlg::onNorthingChanged()
|
|
|
{
|
|
|
if(m_bUpdating) return; // 防止递归更新
|
|
|
|
|
|
QString eastingText = m_pEastingEdit->text();
|
|
|
QString northingText = m_pNorthingEdit->text();
|
|
|
QString zoneText = m_pUtmZoneCombo->currentText();
|
|
|
|
|
|
// 检查北向坐标值是否真的发生了变化
|
|
|
if(northingText == m_lastNorthing) {
|
|
|
return; // 值没有变化,不进行转换
|
|
|
}
|
|
|
|
|
|
if(!eastingText.isEmpty() && !northingText.isEmpty() && !zoneText.isEmpty()) {
|
|
|
updateFromUTM();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 更新记录的值
|
|
|
void nmWxGeoRefDlg::updateFromLatLon()
|
|
|
{
|
|
|
m_bUpdating = true; // 设置更新标志
|
|
|
|
|
|
bool ok1, ok2;
|
|
|
double latitude = m_pLatitudeEdit->text().toDouble(&ok1);
|
|
|
double longitude = m_pLongitudeEdit->text().toDouble(&ok2);
|
|
|
|
|
|
if(!ok1 || !ok2) {
|
|
|
QMessageBox::warning(this, tr("Input Error"), tr("Please enter valid numbers"));
|
|
|
// 恢复之前的有效值
|
|
|
m_pLongitudeEdit->setText(m_lastLongitude);
|
|
|
m_pLatitudeEdit->setText(m_lastLatitude);
|
|
|
m_bUpdating = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 验证经纬度范围
|
|
|
if(!isValidLatitude(latitude)) {
|
|
|
QMessageBox::warning(this, tr("Input Error"), tr("Latitude must be between - 90o and + 90o"));
|
|
|
// 恢复之前的有效值
|
|
|
m_pLatitudeEdit->setText(m_lastLatitude);
|
|
|
m_bUpdating = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if(!isValidLongitude(longitude)) {
|
|
|
QMessageBox::warning(this, tr("Input Error"), tr("Longitude must be between - 180o and + 180o"));
|
|
|
// 恢复之前的有效值
|
|
|
m_pLongitudeEdit->setText(m_lastLongitude);
|
|
|
m_bUpdating = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
int zone;
|
|
|
char band;
|
|
|
double easting, northing;
|
|
|
|
|
|
// 执行坐标转换
|
|
|
latLonToUTM(latitude, longitude, zone, band, easting, northing);
|
|
|
|
|
|
// 更新UTM Zone下拉框
|
|
|
QString zoneStr = QString("%1%2").arg(zone, 2, 10, QLatin1Char('0')).arg(band);
|
|
|
int index = m_pUtmZoneCombo->findText(zoneStr);
|
|
|
|
|
|
if(index >= 0) {
|
|
|
m_pUtmZoneCombo->setCurrentIndex(index);
|
|
|
}
|
|
|
|
|
|
// 更新东向和北向坐标
|
|
|
QString eastingStr = QString::number(easting, 'f', 2);
|
|
|
QString northingStr = QString::number(northing, 'f', 2);
|
|
|
|
|
|
m_pEastingEdit->setText(eastingStr);
|
|
|
m_pNorthingEdit->setText(northingStr);
|
|
|
|
|
|
// 转换成功后,更新所有记录的值
|
|
|
m_lastLongitude = m_pLongitudeEdit->text(); // 使用当前输入框的值
|
|
|
m_lastLatitude = m_pLatitudeEdit->text();
|
|
|
m_lastEasting = eastingStr;
|
|
|
m_lastNorthing = northingStr;
|
|
|
|
|
|
m_bUpdating = false; // 清除更新标志
|
|
|
}
|
|
|
|
|
|
|
|
|
// 更新记录的值
|
|
|
void nmWxGeoRefDlg::updateFromUTM()
|
|
|
{
|
|
|
m_bUpdating = true; // 设置更新标志
|
|
|
|
|
|
bool ok1, ok2;
|
|
|
double easting = m_pEastingEdit->text().toDouble(&ok1);
|
|
|
double northing = m_pNorthingEdit->text().toDouble(&ok2);
|
|
|
|
|
|
QString zoneText = m_pUtmZoneCombo->currentText();
|
|
|
|
|
|
if(!ok1 || !ok2) {
|
|
|
QMessageBox::warning(this, tr("Input Error"), tr("Please enter valid numbers"));
|
|
|
restoreLastValidValues();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 验证UTM坐标范围
|
|
|
if(!isValidEasting(easting)) {
|
|
|
QMessageBox::warning(this, tr("Input Error"), tr("Easting must be between 0 and 1,000,000 meters"));
|
|
|
restoreLastValidValues();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if(!isValidNorthing(northing)) {
|
|
|
QMessageBox::warning(this, tr("Input Error"), tr("Northing must be between 0 and 10,000,000 meters"));
|
|
|
restoreLastValidValues();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if(zoneText.isEmpty()) {
|
|
|
QMessageBox::warning(this, tr("Input Error"), tr("Please select a valid UTM Zone"));
|
|
|
m_bUpdating = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 解析UTM Zone
|
|
|
int zone = zoneText.left(2).toInt();
|
|
|
char band = zoneText.right(1).toLocal8Bit().at(0);
|
|
|
|
|
|
double latitude, longitude;
|
|
|
|
|
|
// 执行坐标转换,检查返回值
|
|
|
bool conversionSuccess = utmToLatLon(zone, band, easting, northing, latitude, longitude);
|
|
|
|
|
|
if (!conversionSuccess) {
|
|
|
// 根据具体失败原因给出不同的错误信息
|
|
|
if (band == 'C' || band == 'X' || zone <= 2 || zone >= 59) {
|
|
|
QMessageBox::warning(this, tr("Conversion Error"),
|
|
|
tr("The UTM coordinates you entered correspond to an extreme polar region where coordinate conversion may be inaccurate.\n"
|
|
|
"Please try coordinates closer to the center of the UTM zone."));
|
|
|
} else {
|
|
|
QMessageBox::warning(this, tr("Conversion Error"),
|
|
|
tr("UTM coordinate conversion failed.\n"
|
|
|
"The coordinates may be outside the valid range for this UTM zone.\n"
|
|
|
"Please check your input coordinates."));
|
|
|
}
|
|
|
restoreLastValidValues();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 验证转换结果
|
|
|
if(!isValidLatitude(latitude) || !isValidLongitude(longitude)) {
|
|
|
QMessageBox::warning(this, tr("Conversion Error"), tr("UTM coordinate conversion result is out of valid range, please check input"));
|
|
|
restoreLastValidValues();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 更新经纬度
|
|
|
QString longitudeStr = QString::number(longitude, 'f', 6);
|
|
|
QString latitudeStr = QString::number(latitude, 'f', 6);
|
|
|
|
|
|
m_pLongitudeEdit->setText(longitudeStr);
|
|
|
m_pLatitudeEdit->setText(latitudeStr);
|
|
|
|
|
|
// 转换成功后,更新所有记录的值
|
|
|
m_lastLongitude = longitudeStr;
|
|
|
m_lastLatitude = latitudeStr;
|
|
|
m_lastEasting = m_pEastingEdit->text(); // 使用当前输入框的值
|
|
|
m_lastNorthing = m_pNorthingEdit->text();
|
|
|
|
|
|
m_bUpdating = false; // 清除更新标志
|
|
|
}
|
|
|
|
|
|
// 恢复上次有效值
|
|
|
void nmWxGeoRefDlg::restoreLastValidValues()
|
|
|
{
|
|
|
m_pEastingEdit->setText(m_lastEasting);
|
|
|
m_pNorthingEdit->setText(m_lastNorthing);
|
|
|
m_bUpdating = false;
|
|
|
}
|
|
|
|
|
|
// 更新记录的值
|
|
|
void nmWxGeoRefDlg::onUtmZoneChanged(int index)
|
|
|
{
|
|
|
// 添加这行检查!如果正在更新过程中,直接返回
|
|
|
if(m_bUpdating) return;
|
|
|
|
|
|
// 设置更新标志,防止触发其他转换函数
|
|
|
m_bUpdating = true;
|
|
|
|
|
|
bool enabled = (index >= 0); // 选择了有效的 UTM Zone 时启用
|
|
|
m_pLongitudeEdit->setEnabled(enabled);
|
|
|
m_pLatitudeEdit ->setEnabled(enabled);
|
|
|
m_pEastingEdit ->setEnabled(enabled);
|
|
|
m_pNorthingEdit ->setEnabled(enabled);
|
|
|
|
|
|
if(enabled) {
|
|
|
// 获取选中的 UTM Zone
|
|
|
QString selectedZone = m_pUtmZoneCombo->itemText(index);
|
|
|
|
|
|
// 从选中的 UTM Zone 提取 Zone 和 Band
|
|
|
int zone = selectedZone.left(2).toInt();
|
|
|
QString band = selectedZone.right(1);
|
|
|
|
|
|
// 计算经度(Longitude)
|
|
|
double longitude = -177.0 + (zone - 1) * 6.0;
|
|
|
QString longitudeStr = QString::number(longitude, 'f', 6);
|
|
|
m_pLongitudeEdit->setText(longitudeStr);
|
|
|
|
|
|
// 计算纬度(Latitude)
|
|
|
QStringList bands;
|
|
|
bands << "C" << "D" << "E" << "F" << "G" << "H" << "J" << "K" << "L" << "M"
|
|
|
<< "N" << "P" << "Q" << "R" << "S" << "T" << "U" << "V" << "W" << "X";
|
|
|
|
|
|
int bandIndex = bands.indexOf(band);
|
|
|
double latitude = -76.0;
|
|
|
|
|
|
if(bandIndex >= 0) {
|
|
|
if(bandIndex < bands.indexOf("X")) {
|
|
|
// X之前的所有band,每个递增8度
|
|
|
latitude += bandIndex * 8.0;
|
|
|
} else {
|
|
|
// X band:W到X跨度为10度
|
|
|
latitude += (bandIndex - 1) * 8.0 + 10.0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QString latitudeStr = QString::number(latitude, 'f', 6);
|
|
|
m_pLatitudeEdit->setText(latitudeStr);
|
|
|
|
|
|
// 设置东向坐标(Easting)
|
|
|
double easting = 50000.00;
|
|
|
QString eastingStr = QString::number(easting, 'f', 2);
|
|
|
m_pEastingEdit->setText(eastingStr);
|
|
|
|
|
|
// 设置北向坐标(Northing)
|
|
|
double northing = calculateNorthing(latitude, longitude, zone);
|
|
|
QString northingStr = QString::number(northing, 'f', 2);
|
|
|
m_pNorthingEdit->setText(northingStr);
|
|
|
|
|
|
// 更新记录的所有值
|
|
|
m_lastLongitude = longitudeStr;
|
|
|
m_lastLatitude = latitudeStr;
|
|
|
m_lastEasting = eastingStr;
|
|
|
m_lastNorthing = northingStr;
|
|
|
}
|
|
|
|
|
|
// 清除更新标志
|
|
|
m_bUpdating = false;
|
|
|
}
|
|
|
|
|
|
// 经纬度转UTM坐标
|
|
|
void nmWxGeoRefDlg::latLonToUTM(double lat, double lon, int& zone, char& band, double& easting, double& northing)
|
|
|
{
|
|
|
// 确定UTM Zone
|
|
|
zone = getUTMZoneFromLongitude(lon);
|
|
|
band = getUTMBandFromLatitude(lat);
|
|
|
|
|
|
// WGS84椭球体参数
|
|
|
const double a = 6378137.0; // 长半轴
|
|
|
const double e2 = 0.00669437999014; // 第一偏心率的平方
|
|
|
const double k0 = 0.9996; // 比例因子
|
|
|
|
|
|
// 转换为弧度
|
|
|
double lat_rad = lat * M_PI / 180.0;
|
|
|
double lon_rad = lon * M_PI / 180.0;
|
|
|
|
|
|
// 中央经线
|
|
|
double lon0 = -183.0 + zone * 6.0;
|
|
|
double lon0_rad = lon0 * M_PI / 180.0;
|
|
|
double dlon = lon_rad - lon0_rad;
|
|
|
|
|
|
// UTM投影计算
|
|
|
double N = a / sqrt(1 - e2 * sin(lat_rad) * sin(lat_rad));
|
|
|
double T = tan(lat_rad) * tan(lat_rad);
|
|
|
double C = e2 * cos(lat_rad) * cos(lat_rad) / (1 - e2);
|
|
|
double A = cos(lat_rad) * dlon;
|
|
|
|
|
|
double M = a * ((1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256) * lat_rad
|
|
|
- (3 * e2 / 8 + 3 * e2 * e2 / 32 + 45 * e2 * e2 * e2 / 1024) * sin(2 * lat_rad)
|
|
|
+ (15 * e2 * e2 / 256 + 45 * e2 * e2 * e2 / 1024) * sin(4 * lat_rad)
|
|
|
- (35 * e2 * e2 * e2 / 3072) * sin(6 * lat_rad));
|
|
|
|
|
|
// 计算东向坐标
|
|
|
easting = k0 * N * (A + (1 - T + C) * A * A * A / 6
|
|
|
+ (5 - 18 * T + T * T + 72 * C - 58 * e2) * A * A * A * A * A / 120) + 500000.0;
|
|
|
|
|
|
// 计算北向坐标
|
|
|
northing = k0 * (M + N * tan(lat_rad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24
|
|
|
+ (61 - 58 * T + T * T + 600 * C - 330 * e2) * A * A * A * A * A * A / 720));
|
|
|
|
|
|
// 南半球需要加上假北值
|
|
|
if(lat < 0) {
|
|
|
northing += 10000000.0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// UTM坐标转经纬度
|
|
|
bool nmWxGeoRefDlg::utmToLatLon(int zone, char band, double easting, double northing, double& lat, double& lon)
|
|
|
{
|
|
|
// WGS84椭球体参数
|
|
|
const double a = 6378137.0; // 长半轴 (米)
|
|
|
const double f = 1.0 / 298.257223563; // 扁率
|
|
|
const double e2 = 2 * f - f * f; // 第一偏心率的平方
|
|
|
const double k0 = 0.9996; // UTM比例因子
|
|
|
const double e1 = (1 - sqrt(1 - e2)) / (1 + sqrt(1 - e2));
|
|
|
|
|
|
// 中央经线经度
|
|
|
double lon0 = -183.0 + zone * 6.0;
|
|
|
|
|
|
// 调整北向坐标(南半球处理)
|
|
|
double y = northing;
|
|
|
if(band < 'N') { // 南半球
|
|
|
y -= 10000000.0;
|
|
|
}
|
|
|
|
|
|
// 调整东向坐标
|
|
|
double x = easting - 500000.0;
|
|
|
|
|
|
// 计算足点纬度
|
|
|
double M = y / k0;
|
|
|
double mu = M / (a * (1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256));
|
|
|
|
|
|
// 使用级数展开计算足点纬度
|
|
|
double phi1 = mu + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * sin(2 * mu)
|
|
|
+ (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * sin(4 * mu)
|
|
|
+ (151 * e1 * e1 * e1 / 96) * sin(6 * mu)
|
|
|
+ (1097 * e1 * e1 * e1 * e1 / 512) * sin(8 * mu);
|
|
|
|
|
|
// cos(phi1)是否过小
|
|
|
if (fabs(cos(phi1)) < 1e-6) {
|
|
|
// 设置默认值,让调用方知道转换失败
|
|
|
lat = 0.0;
|
|
|
lon = 0.0;
|
|
|
return false; // 返回false表示转换失败
|
|
|
}
|
|
|
|
|
|
// 计算各种参数
|
|
|
double C1 = e2 * cos(phi1) * cos(phi1) / (1 - e2);
|
|
|
double T1 = tan(phi1) * tan(phi1);
|
|
|
double N1 = a / sqrt(1 - e2 * sin(phi1) * sin(phi1));
|
|
|
double R1 = a * (1 - e2) / pow(1 - e2 * sin(phi1) * sin(phi1), 1.5);
|
|
|
double D = x / (N1 * k0);
|
|
|
|
|
|
// 计算纬度(弧度)
|
|
|
double lat_rad = phi1 - (N1 * tan(phi1) / R1) *
|
|
|
(D * D / 2 - (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * e2) * D * D * D * D / 24
|
|
|
+ (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * e2 - 3 * C1 * C1) * D * D * D * D * D * D / 720);
|
|
|
|
|
|
// 计算经度(弧度)
|
|
|
double lon_rad = (D - (1 + 2 * T1 + C1) * D * D * D / 6
|
|
|
+ (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * e2 + 24 * T1 * T1) * D * D * D * D * D / 120) / cos(phi1);
|
|
|
|
|
|
// 转换为度并加上中央经线
|
|
|
lat = lat_rad * 180.0 / M_PI;
|
|
|
lon = lon0 + lon_rad * 180.0 / M_PI;
|
|
|
|
|
|
// 第二个检查:确保结果在合理范围内
|
|
|
if (lat != lat || lon != lon || // 检查NaN
|
|
|
fabs(lat) > 90.0 || // 纬度必须在-90到90之间
|
|
|
fabs(lon - lon0) > 180.0) { // 经度距离中央经线不应超过180度
|
|
|
|
|
|
// 设置默认值
|
|
|
lat = 0.0;
|
|
|
lon = 0.0;
|
|
|
return false; // 返回false表示转换失败
|
|
|
}
|
|
|
|
|
|
// 确保经度在有效范围内
|
|
|
while(lon > 180.0) lon -= 360.0;
|
|
|
while(lon < -180.0) lon += 360.0;
|
|
|
|
|
|
return true; // 转换成功
|
|
|
}
|
|
|
|
|
|
// 根据经度确定UTM Zone
|
|
|
int nmWxGeoRefDlg::getUTMZoneFromLongitude(double longitude)
|
|
|
{
|
|
|
return (int)floor((longitude + 180.0) / 6.0) + 1;
|
|
|
}
|
|
|
|
|
|
// 根据纬度确定UTM Band
|
|
|
char nmWxGeoRefDlg::getUTMBandFromLatitude(double latitude)
|
|
|
{
|
|
|
const char bands[] = "CDEFGHJKLMNPQRSTUVWX";
|
|
|
int index = (int)floor((latitude + 80.0) / 8.0);
|
|
|
|
|
|
if(index < 0) return 'C';
|
|
|
|
|
|
if(index >= 20) return 'X';
|
|
|
|
|
|
return bands[index];
|
|
|
}
|
|
|
|
|
|
bool nmWxGeoRefDlg::isValidLatitude(double lat)
|
|
|
{
|
|
|
return (lat >= -90.0 && lat <= 90.0);
|
|
|
}
|
|
|
|
|
|
// 验证经度范围
|
|
|
bool nmWxGeoRefDlg::isValidLongitude(double lon)
|
|
|
{
|
|
|
return (lon >= -180.0 && lon <= 180.0);
|
|
|
}
|
|
|
|
|
|
// 验证东向坐标范围
|
|
|
bool nmWxGeoRefDlg::isValidEasting(double easting)
|
|
|
{
|
|
|
// 基本UTM范围检查
|
|
|
if (easting < 0.0 || easting > 1000000.0) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 获取坐标轴数据,考虑画布范围
|
|
|
nmDataAxis* axisData = nmDataAnalyzeManager::getCurrentInstance()->getAxisData();
|
|
|
if (axisData) {
|
|
|
double xMin = axisData->getXMin().getValue().toDouble();
|
|
|
double xMax = axisData->getXMax().getValue().toDouble();
|
|
|
|
|
|
// 计算基准坐标的安全范围
|
|
|
double safeEastingMin = 160000.0 - xMin; // UTM东向最小值 - 画布最小X
|
|
|
double safeEastingMax = 840000.0 - xMax; // UTM东向最大值 - 画布最大X
|
|
|
|
|
|
// 确保范围有效
|
|
|
if (safeEastingMin < 0.0) safeEastingMin = 0.0;
|
|
|
if (safeEastingMax > 1000000.0) safeEastingMax = 1000000.0;
|
|
|
if (safeEastingMin >= safeEastingMax) {
|
|
|
safeEastingMin = 0.0;
|
|
|
safeEastingMax = 1000000.0;
|
|
|
}
|
|
|
|
|
|
return (easting >= safeEastingMin && easting <= safeEastingMax);
|
|
|
}
|
|
|
|
|
|
return true; // 如果没有坐标轴数据,使用默认验证
|
|
|
}
|
|
|
|
|
|
// 验证北向坐标范围
|
|
|
bool nmWxGeoRefDlg::isValidNorthing(double northing)
|
|
|
{
|
|
|
// 基本UTM范围检查
|
|
|
if (northing < 0.0 || northing > 10000000.0) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 获取坐标轴数据,考虑画布范围
|
|
|
nmDataAxis* axisData = nmDataAnalyzeManager::getCurrentInstance()->getAxisData();
|
|
|
if (axisData) {
|
|
|
double yMin = axisData->getYMin().getValue().toDouble();
|
|
|
double yMax = axisData->getYMax().getValue().toDouble();
|
|
|
|
|
|
// 计算基准坐标的安全范围
|
|
|
double safeNorthingMin = 0.0 - yMin; // UTM北向最小值 - 画布最小Y
|
|
|
double safeNorthingMax = 9000000.0 - yMax; // UTM北向最大值 - 画布最大Y
|
|
|
|
|
|
// 确保范围有效
|
|
|
if (safeNorthingMin < 0.0) safeNorthingMin = 0.0;
|
|
|
if (safeNorthingMax > 10000000.0) safeNorthingMax = 10000000.0;
|
|
|
if (safeNorthingMin >= safeNorthingMax) {
|
|
|
safeNorthingMin = 0.0;
|
|
|
safeNorthingMax = 10000000.0;
|
|
|
}
|
|
|
|
|
|
return (northing >= safeNorthingMin && northing <= safeNorthingMax);
|
|
|
}
|
|
|
|
|
|
return true; // 如果没有坐标轴数据,使用默认验证
|
|
|
}
|
|
|
|
|
|
void nmWxGeoRefDlg::loadDataToUI()
|
|
|
{
|
|
|
m_bUpdating = true;
|
|
|
|
|
|
// 检查UTM Zone是否有效
|
|
|
QString utmZone = m_geoRefData.getUtmZone().getValue().toString();
|
|
|
bool hasValidData = !utmZone.isEmpty();
|
|
|
|
|
|
if(hasValidData) {
|
|
|
// 有有效数据时,加载所有数据
|
|
|
QString lonStr = QString::number(m_geoRefData.getLongitude().getValue().toDouble(), 'f', 6);
|
|
|
m_pLongitudeEdit->setText(lonStr);
|
|
|
m_lastLongitude = lonStr;
|
|
|
|
|
|
QString latStr = QString::number(m_geoRefData.getLatitude().getValue().toDouble(), 'f', 6);
|
|
|
m_pLatitudeEdit->setText(latStr);
|
|
|
m_lastLatitude = latStr;
|
|
|
|
|
|
int index = m_pUtmZoneCombo->findText(utmZone);
|
|
|
if(index >= 0) {
|
|
|
m_pUtmZoneCombo->setCurrentIndex(index);
|
|
|
}
|
|
|
|
|
|
QString eastingStr = QString::number(m_geoRefData.getEasting().getValue().toDouble(), 'f', 2);
|
|
|
m_pEastingEdit->setText(eastingStr);
|
|
|
m_lastEasting = eastingStr;
|
|
|
|
|
|
QString northingStr = QString::number(m_geoRefData.getNorthing().getValue().toDouble(), 'f', 2);
|
|
|
m_pNorthingEdit->setText(northingStr);
|
|
|
m_lastNorthing = northingStr;
|
|
|
|
|
|
// 启用所有控件
|
|
|
m_pLongitudeEdit->setEnabled(true);
|
|
|
m_pLatitudeEdit->setEnabled(true);
|
|
|
m_pEastingEdit->setEnabled(true);
|
|
|
m_pNorthingEdit->setEnabled(true);
|
|
|
} else {
|
|
|
// 无有效数据时,清空所有文本框,初始化last值
|
|
|
m_pLongitudeEdit->setText("");
|
|
|
m_pLatitudeEdit->setText("");
|
|
|
m_pEastingEdit->setText("");
|
|
|
m_pNorthingEdit->setText("");
|
|
|
|
|
|
m_lastLongitude = "";
|
|
|
m_lastLatitude = "";
|
|
|
m_lastEasting = "";
|
|
|
m_lastNorthing = "";
|
|
|
}
|
|
|
|
|
|
m_bUpdating = false;
|
|
|
}
|
|
|
|
|
|
void nmWxGeoRefDlg::saveDataUI()
|
|
|
{
|
|
|
// 保存经度
|
|
|
nmDataAttribute longitude = m_geoRefData.getLongitude();
|
|
|
double lonValue = m_pLongitudeEdit->text().toDouble();
|
|
|
longitude.setValue(lonValue);
|
|
|
m_geoRefData.setLongitude(longitude);
|
|
|
|
|
|
// 保存纬度
|
|
|
nmDataAttribute latitude = m_geoRefData.getLatitude();
|
|
|
double latValue = m_pLatitudeEdit->text().toDouble();
|
|
|
latitude.setValue(latValue);
|
|
|
m_geoRefData.setLatitude(latitude);
|
|
|
|
|
|
// 保存UTM Zone
|
|
|
nmDataAttribute utmZone = m_geoRefData.getUtmZone();
|
|
|
utmZone.setValue(QVariant(m_pUtmZoneCombo->currentText()));
|
|
|
m_geoRefData.setUtmZone(utmZone);
|
|
|
|
|
|
// 保存东向坐标
|
|
|
nmDataAttribute easting = m_geoRefData.getEasting();
|
|
|
double estValue = m_pEastingEdit->text().toDouble();
|
|
|
easting.setValue(estValue);
|
|
|
m_geoRefData.setEasting(easting);
|
|
|
|
|
|
// 保存北向坐标
|
|
|
nmDataAttribute northing = m_geoRefData.getNorthing();
|
|
|
double norValue = m_pNorthingEdit->text().toDouble();
|
|
|
northing.setValue(norValue);
|
|
|
m_geoRefData.setNorthing(northing);
|
|
|
}
|
|
|
|
|
|
void nmWxGeoRefDlg::okAccept()
|
|
|
{
|
|
|
|
|
|
saveDataUI();
|
|
|
|
|
|
// 更新到全局数据管理器
|
|
|
nmDataAnalyzeManager::getCurrentInstance()->updateGeoRefData(m_geoRefData);
|
|
|
|
|
|
// 调用基类的accept
|
|
|
iDlgBase::accept();
|
|
|
}
|
|
|
|
|
|
void nmWxGeoRefDlg::cancelReject()
|
|
|
{
|
|
|
// 调用基类的reject
|
|
|
iDlgBase::reject();
|
|
|
}
|