geopro/src/app/ContourLevelDialog.cpp

255 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ContourLevelDialog.hpp"
#include <cmath>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QDoubleValidator>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QLocale>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
namespace geopro::app {
namespace {
constexpr int kMaxLayers = 50; // 原版上限:分层层数 ≤ 50
} // namespace
ContourLevelDialog::ContourLevelDialog(const ContourLevelParams& init, double originMin,
double originMax, double totalArea, QWidget* parent)
: QDialog(parent), originMin_(originMin), originMax_(originMax), totalArea_(totalArea) {
setWindowTitle(QStringLiteral("等值线层级"));
setModal(true);
auto* root = new QVBoxLayout(this);
auto* form = new QFormLayout();
root->addLayout(form);
// 数据范围(原始,只读展示)。
form->addRow(QStringLiteral("数据范围"),
new QLabel(QStringLiteral("%1 ~ %2").arg(originMin_).arg(originMax_)));
// 分层方式。
methodCombo_ = new QComboBox(this);
methodCombo_->addItem(QStringLiteral("一般的"), 0);
methodCombo_->addItem(QStringLiteral("对数"), 1);
methodCombo_->addItem(QStringLiteral("等积"), 2);
methodCombo_->setCurrentIndex(static_cast<int>(init.method));
form->addRow(QStringLiteral("分层方式"), methodCombo_);
auto* validator = new QDoubleValidator(this);
validator->setNotation(QDoubleValidator::StandardNotation);
validator->setLocale(QLocale::c()); // 锁定 C locale与 toDouble() 一致,避免中文系统逗号歧义
// 最大/最小等值线equalArea 时整行隐藏)。
minEdit_ = new QLineEdit(QString::number(init.minValue), this);
maxEdit_ = new QLineEdit(QString::number(init.maxValue), this);
minEdit_->setValidator(validator);
maxEdit_->setValidator(validator);
rangeRow_ = new QWidget(this);
auto* rangeForm = new QFormLayout(rangeRow_);
rangeForm->setContentsMargins(0, 0, 0, 0);
rangeForm->addRow(QStringLiteral("最大等值线"), maxEdit_);
rangeForm->addRow(QStringLiteral("最小等值线"), minEdit_);
root->addWidget(rangeRow_);
// normal间隔数 + 层数(双向联动)。
intervalEdit_ = new QLineEdit(QString::number(init.interval), this);
layerCountEdit_ = new QLineEdit(QString::number(init.layerCount), this);
intervalEdit_->setValidator(validator);
normalRow_ = new QWidget(this);
auto* normalForm = new QFormLayout(normalRow_);
normalForm->setContentsMargins(0, 0, 0, 0);
normalForm->addRow(QStringLiteral("数值间隔"), intervalEdit_);
normalForm->addRow(QStringLiteral("层数"), layerCountEdit_);
root->addWidget(normalRow_);
// logarithmic每数量级次要等值线数。
logLinesEdit_ = new QLineEdit(QString::number(init.logLinesCount), this);
logRow_ = new QWidget(this);
auto* logForm = new QFormLayout(logRow_);
logForm->setContentsMargins(0, 0, 0, 0);
logForm->addRow(QStringLiteral("每数量级次要等值线数"), logLinesEdit_);
root->addWidget(logRow_);
// equalArea等积分层层数 + 区间面积(只读,自动算)。
equalAreaCountEdit_ = new QLineEdit(QString::number(init.equalAreaLayerCount), this);
intervalAreaLabel_ = new QLabel(this);
equalAreaRow_ = new QWidget(this);
auto* eaForm = new QFormLayout(equalAreaRow_);
eaForm->setContentsMargins(0, 0, 0, 0);
eaForm->addRow(QStringLiteral("层数"), equalAreaCountEdit_);
eaForm->addRow(QStringLiteral("区间面积"), intervalAreaLabel_);
root->addWidget(equalAreaRow_);
auto* reset = new QPushButton(QStringLiteral("恢复默认值"), this);
auto* resetRow = new QHBoxLayout();
resetRow->addStretch();
resetRow->addWidget(reset);
root->addLayout(resetRow);
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
buttons->button(QDialogButtonBox::Ok)->setText(QStringLiteral("应用"));
buttons->button(QDialogButtonBox::Cancel)->setText(QStringLiteral("取消"));
root->addWidget(buttons);
// 联动接线normal 双向equalArea 区间面积随层数)。
connect(methodCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
[this](int) { onMethodChanged(); });
connect(intervalEdit_, &QLineEdit::textEdited, this,
[this](const QString&) { recalcLayerCountFromInterval(); });
connect(layerCountEdit_, &QLineEdit::textEdited, this,
[this](const QString&) { recalcIntervalFromLayerCount(); });
connect(minEdit_, &QLineEdit::textEdited, this,
[this](const QString&) { recalcLayerCountFromInterval(); });
connect(maxEdit_, &QLineEdit::textEdited, this,
[this](const QString&) { recalcLayerCountFromInterval(); });
connect(equalAreaCountEdit_, &QLineEdit::textEdited, this,
[this](const QString&) { updateIntervalArea(); });
connect(reset, &QPushButton::clicked, this, &ContourLevelDialog::onReset);
connect(buttons, &QDialogButtonBox::accepted, this, &ContourLevelDialog::onAccept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
updateIntervalArea();
updateVisibility(); // 初始仅按方式显隐(不重算,保留传入初值)
}
double ContourLevelDialog::parsed(const QLineEdit* e, double fallback) const {
bool ok = false;
const double v = e->text().toDouble(&ok);
return ok ? v : fallback;
}
void ContourLevelDialog::updateVisibility() {
const int m = methodCombo_->currentIndex();
const bool equalArea = (m == 2);
rangeRow_->setVisible(!equalArea); // 最大/最小只在 normal/log 显示
normalRow_->setVisible(m == 0);
logRow_->setVisible(m == 1);
equalAreaRow_->setVisible(equalArea);
adjustSize();
}
void ContourLevelDialog::onMethodChanged() {
updateVisibility();
if (methodCombo_->currentIndex() == 0)
recalcIntervalFromLayerCount(); // 切回一般时按层数刷间隔(复刻 watch
}
void ContourLevelDialog::recalcLayerCountFromInterval() {
if (autoUpdating_ || methodCombo_->currentIndex() != 0) return;
const double mn = parsed(minEdit_, NAN), mx = parsed(maxEdit_, NAN);
const double iv = parsed(intervalEdit_, NAN);
if (std::isfinite(mn) && std::isfinite(mx) && std::isfinite(iv) && iv > 0 && mx > mn) {
autoUpdating_ = true;
layerCountEdit_->setText(QString::number(static_cast<int>(std::ceil((mx - mn) / iv))));
autoUpdating_ = false;
}
}
void ContourLevelDialog::recalcIntervalFromLayerCount() {
if (autoUpdating_ || methodCombo_->currentIndex() != 0) return;
const double mn = parsed(minEdit_, NAN), mx = parsed(maxEdit_, NAN);
bool ok = false;
const int cnt = layerCountEdit_->text().toInt(&ok);
if (std::isfinite(mn) && std::isfinite(mx) && ok && cnt > 0 && mx > mn) {
autoUpdating_ = true;
intervalEdit_->setText(QString::number((mx - mn) / cnt, 'f', 2));
autoUpdating_ = false;
}
}
void ContourLevelDialog::updateIntervalArea() {
bool ok = false;
const int cnt = equalAreaCountEdit_->text().toInt(&ok);
if (ok && cnt > 0)
intervalAreaLabel_->setText(QString::number(totalArea_ / cnt, 'f', 2));
else
intervalAreaLabel_->setText(QStringLiteral("0"));
}
void ContourLevelDialog::onReset() {
// 复刻 handleReset统一回 normal + 原始范围 + 间隔=(max-min)/20。
methodCombo_->setCurrentIndex(0);
minEdit_->setText(QString::number(originMin_));
maxEdit_->setText(QString::number(originMax_));
// 常值场max==min默认间隔取 1避免间隔=0 导致"恢复默认→应用即报错"。
const double span = originMax_ - originMin_;
intervalEdit_->setText(QString::number(span > 0 ? span / 20.0 : 1.0, 'f', 2));
logLinesEdit_->setText(QStringLiteral("8"));
equalAreaCountEdit_->setText(QStringLiteral("10"));
recalcLayerCountFromInterval();
updateIntervalArea();
}
void ContourLevelDialog::onAccept() {
const int m = methodCombo_->currentIndex();
const double mn = parsed(minEdit_, NAN), mx = parsed(maxEdit_, NAN);
if (m != 2 && !(mx > mn)) {
QMessageBox::warning(this, QStringLiteral("等值线层级"),
QStringLiteral("最大值必须大于最小值"));
return;
}
ContourLevelParams r;
r.method = static_cast<ContourLevelParams::Method>(m);
r.minValue = mn;
r.maxValue = mx;
if (m == 0) { // normal
const double iv = parsed(intervalEdit_, NAN);
if (!(iv > 0)) {
QMessageBox::warning(this, QStringLiteral("等值线层级"),
QStringLiteral("数据间隔必须大于0"));
return;
}
const int layers = static_cast<int>(std::ceil((mx - mn) / iv));
if (layers > kMaxLayers) {
QMessageBox::warning(this, QStringLiteral("等值线层级"),
QStringLiteral("数据间隔设置过小分层层数不能超过50"));
return;
}
r.interval = iv;
r.layerCount = layers;
} else if (m == 1) { // logarithmic
bool ok = false;
const int lc = logLinesEdit_->text().toInt(&ok);
if (!ok || lc <= 0) {
QMessageBox::warning(this, QStringLiteral("等值线层级"),
QStringLiteral("每数量级次要等值线数必须大于0"));
return;
}
if (!(mx > 0)) {
QMessageBox::warning(this, QStringLiteral("等值线层级"),
QStringLiteral("对数分层方式下最大等值线必须大于0"));
return;
}
if (mn < 0) {
QMessageBox::warning(this, QStringLiteral("等值线层级"),
QStringLiteral("对数分层方式下最小等值线必须大于0"));
return;
}
r.logLinesCount = lc;
} else { // equalArea
bool ok = false;
const int cnt = equalAreaCountEdit_->text().toInt(&ok);
if (!ok || cnt <= 0) {
QMessageBox::warning(this, QStringLiteral("等值线层级"),
QStringLiteral("层数必须大于0"));
return;
}
r.equalAreaLayerCount = cnt;
}
result_ = r;
accept();
}
} // namespace geopro::app