#include "ContourLevelDialog.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include 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(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::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(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(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(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