Files
qt5-legatus-legacy/Lts/Plugins/SituationMapPlugin/Gui/SituationMap/sitmap_layer_control.cpp
T
2026-08-13 18:33:10 +02:00

598 lines
19 KiB
C++
Executable File

/**
* @RDE_HEADER@
* SituationMap - Layer Control Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-06-01
*/
#include "sitmap_layer_control.h"
#include "ui_sitmap_layer_control.h"
#include <Map/layer_parameter.h>
#include <FilterManagement/Gui/FilterChange/flt_filter_change.h>
#include <LtsUtilsSettings/settings.h>
#include <QTimer>
#include <QStyledItemDelegate>
namespace SituationMap
{
//
// class SpinBoxDelegate
//
class SpinBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
using BaseClass = QStyledItemDelegate;
explicit SpinBoxDelegate(LayerControlDialog *lcDialog)
: BaseClass(lcDialog), m_layerControlDialog(lcDialog)
{
}
~SpinBoxDelegate()
{
if (m_layerControlDialog)
m_layerControlDialog->m_ignoreLayerParameterChanged = false;
}
using QStyledItemDelegate::QStyledItemDelegate;
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
if (index.column() == LayersModel::Layer)
{
QStyledItemDelegate::paint(painter, option, index);
return;
}
if (!displayValue(index))
return;
int progress = index.data().toInt();
QStyleOptionProgressBar progressBarOption;
progressBarOption.rect = option.rect;
progressBarOption.minimum = minSpinboxValue(index);
progressBarOption.maximum = 100;
progressBarOption.progress = progress;
if (index.column() == LayersModel::Opacity)
progressBarOption.text = QString::number(progress) + QLatin1Char('%');
else
progressBarOption.text = QString::number(progress);
progressBarOption.textVisible = true;
QApplication::style()->drawControl(QStyle::CE_ProgressBar,
&progressBarOption, painter);
}
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
if (m_layerControlDialog)
m_layerControlDialog->m_ignoreLayerParameterChanged = true;
m_modelIdx = QModelIndex();
if (index.column() == 0)
return QStyledItemDelegate::createEditor(parent, option, index);
if (!displayValue(index))
return nullptr;
QSlider *sl = new QSlider(Qt::Horizontal, parent);
sl->setAutoFillBackground(true);
sl->setMinimum(minSpinboxValue(index));
sl->setMaximum(100);
sl->setToolTipDuration(0);
connect(sl, &QSlider::valueChanged, this, [sl](int value) {
sl->setToolTip(QString::number(value));
});
return sl;
}
void destroyEditor(QWidget *editor, const QModelIndex &index) const override
{
BaseClass::destroyEditor(editor, index);
if (m_layerControlDialog)
m_layerControlDialog->m_ignoreLayerParameterChanged = false;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const override
{
QSlider *sl = index.column() == 0 ? nullptr : qobject_cast<QSlider *>(editor);
if (!sl)
QStyledItemDelegate::setEditorData(editor, index);
else
{
m_modelIdx = index;
connect(sl, &QSlider::valueChanged, this, &SpinBoxDelegate::onValueChanged, Qt::UniqueConnection);
sl->setValue(index.data(Qt::EditRole).toInt());
}
}
private Q_SLOTS:
void onValueChanged(int value)
{
m_value = value;
//wait until user stops movement
if (!m_timer)
{
m_timer = new QTimer(this);
m_timer->setSingleShot(true);
m_timer->setInterval(500); // msec
connect(m_timer, &QTimer::timeout, this, &SpinBoxDelegate::onValueChangedTimout);
}
m_timer->start();
}
void onValueChangedTimout()
{
if (!m_layerControlDialog || !m_layerControlDialog->layersModel() || !m_modelIdx.isValid())
return;
m_layerControlDialog->layersModel()->setData(m_modelIdx, m_value, Qt::EditRole);
}
void setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const override
{
QSlider *sl = index.column() == 0 ? nullptr : qobject_cast<QSlider *>(editor);
if (!sl)
QStyledItemDelegate::setModelData(editor, model, index);
else
model->setData(index, sl->value(), Qt::EditRole);
}
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
QSlider *sl = index.column() == 0 ? nullptr : qobject_cast<QSlider *>(editor);
if (!sl)
QStyledItemDelegate::updateEditorGeometry(editor, option, index);
else
sl->setGeometry(option.rect);
}
private:
static bool displayValue(const QModelIndex &index)
{
if (index.column() == LayersModel::Opacity)
return true;
LayerType lt = static_cast<LayerType>(index.data(Qt::UserRole).toInt());
return (lt == LayerType::Basemap);
}
static int minSpinboxValue(const QModelIndex &idx)
{
return idx.column() == LayersModel::Opacity ? 0 : -100;
}
// Slider: to enable on-the-fly update :
mutable QModelIndex m_modelIdx;
QTimer *m_timer = nullptr;
LayerControlDialog *m_layerControlDialog = nullptr;
int m_value = 0;
// : Slider: to enable on-the-fly update
}; // SpinBoxDelegate
//
// class LayerControlDialog
//
LayerControlDialog::LayerControlDialog(QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_session(nullptr)
, m_situationMap(nullptr)
, m_layersModel(nullptr)
, m_ui(new Ui::LayerControlDialog)
, m_logger(getLogger("LayerControlDialog"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_ui->setupUi(this);
m_ui->tvLayer->setItemDelegate(new SpinBoxDelegate(this));
setOnlineHelpFilename(QStringLiteral("Situation-Layer_Control"));
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
LayerControlDialog::~LayerControlDialog()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
delete m_layersModel;
delete m_ui;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LayerControlDialog::retranslate()
{
translate(m_ui, tr("Layer Control"));
}
bool LayerControlDialog::init(LtsSession::LtsSession *session, SituationMap::Map *map)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START " << " map=" << map;
if (!session || !map)
return false;
m_session = session;
m_situationMap = map;
if (m_situationMap)
connect(map, &Map::beginDestroy, this, &LayerControlDialog::onMapDestroyed, Qt::UniqueConnection);
// setup initial position of splitter:
m_ui->splitter->setStretchFactor(0, 1);
m_ui->splitter->setStretchFactor(1, 2);
m_layersModel = new LayersModel(m_session, this);
m_layersModel->initModel(m_situationMap);
m_ui->tvLayer->setModel(m_layersModel);
m_ui->tvLayer->resizeColumnsToContents();
m_ui->tvLayer->setColumnWidth(1, 100); // Column 1 -> Opacity ....
m_ui->tvLayer->setColumnWidth(2, 100);
m_ui->tvLayer->setColumnWidth(3, 100);
m_ui->tvLayer->setColumnWidth(4, 100);
connect(m_ui->pbUp, &QPushButton::clicked, this, &LayerControlDialog::onPushButtonUpClicked);
connect(m_ui->pbDown, &QPushButton::clicked, this, &LayerControlDialog::onPushButtonDownClicked);
connect(m_layersModel, &LayersModel::layerDataChanged, this, &LayerControlDialog::onLayerDataChanged);
connect(m_layersModel, &LayersModel::isVisibleChanged, this, &LayerControlDialog::layerOnOff);
//set values
onZoomSliderInit();
m_ui->slRotation->setValue(static_cast<int>(m_situationMap->rotation()));
m_ui->lblRotation->setToolTip(QString::number(m_ui->slRotation->value()));
onNavigateBelowGroundOfMapChanged(m_situationMap->navigationConstraint());
onBaseSurfaceOpacityOfMapChanged(m_situationMap->baseSurfaceOpacity());
const bool isMode2D = m_situationMap->mode() == Mode::Mode_2D;
m_ui->slZoom->setEnabled(isMode2D);
m_ui->tbResetZoom->setEnabled(isMode2D);
m_ui->slRotation->setEnabled(isMode2D);
m_ui->tbResetRotation->setEnabled(isMode2D);
const bool isMode3D = m_situationMap->mode() == Mode::Mode_3D;
m_ui->lbNavigateBelowGround->setEnabled(isMode3D);
m_ui->cbNavigateBelowGround->setEnabled(isMode3D);
m_ui->slBaseSurfaceOpacity->setEnabled(isMode3D);
m_ui->tbBaseSurfaceOpacity->setEnabled(isMode3D);
//connects
if (m_situationMap->mode() == Mode::Mode_2D)
{
//rotation
connect(m_ui->slRotation, &QSlider::valueChanged, this, &LayerControlDialog::onRotationSliderValueChanged);
connect(m_situationMap, &Map::rotationChanged, this, &LayerControlDialog::onRotationOfMapChanged);
//zoom
// Attention: use identical signal at any other zoom/scale-Slider!!!
connect(m_situationMap, &Map::extentChangedDelayed, this, &LayerControlDialog::onMapExtentChanged);
connect(m_ui->slZoom, &QSlider::valueChanged, this, &LayerControlDialog::onZoomSliderValueChanged);
}
if (m_situationMap->mode() == Mode::Mode_3D)
{
// navigationConstraint
connect(m_situationMap, &Map::navigationConstraintChanged, this, &LayerControlDialog::onNavigateBelowGroundOfMapChanged);
connect(m_ui->cbNavigateBelowGround, &QCheckBox::stateChanged, this, &LayerControlDialog::onNavigateBelowGroundValueChanged);
// baseSurfaceOpacity
connect(m_situationMap, &Map::baseSurfaceOpacityChanged, this, &LayerControlDialog::onBaseSurfaceOpacityOfMapChanged);
connect(m_ui->slBaseSurfaceOpacity, &QSlider::valueChanged, this, &LayerControlDialog::onBaseSurfaceOpacitySliderValueChanged);
}
//updatePanel();
connect(m_ui->tbResetZoom, &QToolButton::clicked, this, [this]()
{
m_ui->slZoom->setValue(100);
});
connect(m_ui->tbResetRotation, &QToolButton::clicked, this, [this]()
{
m_ui->slRotation->setValue(0);
m_ui->lblRotation->setToolTip(QString::number(m_ui->slRotation->value()));
});
connect(m_ui->tbBaseSurfaceOpacity, &QToolButton::clicked, this, [this]()
{
m_ui->slBaseSurfaceOpacity->setValue(100);
m_ui->slBaseSurfaceOpacity->setToolTip(QString::number(m_ui->slBaseSurfaceOpacity->value()));
});
connect(m_situationMap, &Map::mapReady, this, &LayerControlDialog::onMapReady, Qt::ConnectionType::QueuedConnection);
// if (this->physicalDpiX() <= LtsWidgets::HIGHDPI)
// m_ui->horizontalLayout->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
connect(m_situationMap, &Map::layerParameterChanged, this, &LayerControlDialog::onLayerParamterChanged);
// Load Filter
if (FilterManagement::FilterChangeDialog::isAvailableAtApp(m_session->app()))
{
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setText(tr("Load filter"));
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setVisible(true);
connect(m_buttonBox->getButton(LtsWidgets::DialogButton::Load), &QPushButton::clicked,
this, &LayerControlDialog::onLoadButtonClicked, Qt::UniqueConnection);
}
// show only if baseSurfaceOpacity is supported / arcgis >= 100.5.0.
// and if item is enabled via ProjectSettings
const NavigationConstraint navConstraint = m_situationMap->navigationConstraint();
LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance();
const bool enableNavigateBelowGround = ps->value(QStringLiteral("SituationMap"), QStringLiteral("enableNavigateBelowGround")).toBool();
const bool enableBaseSurfaceOpacity = ps->value(QStringLiteral("SituationMap"), QStringLiteral("enableBaseSurfaceOpacity")).toBool();
if (navConstraint == NavigationConstraint::Undefined || // < 105.5.0
!enableNavigateBelowGround)
{
m_ui->lbNavigateBelowGround->hide();
m_ui->cbNavigateBelowGround->hide();
}
if (navConstraint == NavigationConstraint::Undefined || // < 105.5.0
!enableBaseSurfaceOpacity)
{
m_ui->lblBaseSurfaceOpacity->hide();
m_ui->slBaseSurfaceOpacity->hide();
m_ui->tbBaseSurfaceOpacity->hide();
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END " << " map=" << map;
return true;
}
void LayerControlDialog::onPushButtonUpClicked()
{
int currentRow = m_ui->tvLayer->currentIndex().row();
if (currentRow - 1 < 0)
return;
if (m_layersModel->canBeMovedUp(m_ui->tvLayer->currentIndex()))
{
QModelIndex idx = m_layersModel->moveRow(m_ui->tvLayer->currentIndex(), currentRow - 1);
m_ui->tvLayer->setCurrentIndex(idx);
QStringList sl = m_layersModel->orderLayerNames();
m_situationMap->layerFactory().orderLayerTo(sl);
}
}
void LayerControlDialog::onPushButtonDownClicked()
{
int currentRow = m_ui->tvLayer->currentIndex().row();
if (m_layersModel->canBeMovedDown(m_ui->tvLayer->currentIndex()))
{
QModelIndex idx = m_layersModel->moveRow(m_ui->tvLayer->currentIndex(), currentRow + 1);
m_ui->tvLayer->setCurrentIndex(idx);
QStringList sl = m_layersModel->orderLayerNames();
m_situationMap->layerFactory().orderLayerTo(sl);
}
}
void LayerControlDialog::onLayerDataChanged(const QString &layerName, int newValue, int column)
{
LayerParameter lp = m_situationMap->layerFactory().layerParameter(layerName);
switch (column)
{
case LayersModel::Opacity:
lp.opacity = newValue;
break;
case LayersModel::Brightness:
lp.brightness = newValue;
break;
case LayersModel::Gamma:
lp.gamma = newValue;
break;
case LayersModel::Contrast:
lp.contrast = newValue;
break;
default:
break;
}
m_situationMap->layerFactory().setLayerParameter(layerName, lp);
}
void LayerControlDialog::onLoadButtonClicked()
{
FilterManagement::FilterChangeDialog *dialog = new FilterManagement::FilterChangeDialog(this);
if (!dialog->init(m_session, FilterManagement::SymbolAppearanceSettings::Part::All, false, false, true))
{
delete dialog;
return;
}
dialog->setAttribute(Qt::WA_DeleteOnClose);
QObject::connect(dialog, &FilterManagement::FilterChangeDialog::rejected, dialog, &FilterManagement::FilterChangeDialog::deleteLater);
dialog->show();
}
void LayerControlDialog::onMapReady()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
//set values
onZoomSliderInit();
m_ui->slRotation->setValue(static_cast<int>(m_situationMap->rotation()));
m_ui->lblRotation->setToolTip(QString::number(m_ui->slRotation->value()));
m_ui->slBaseSurfaceOpacity->setValue(static_cast<int>(m_situationMap->baseSurfaceOpacity()*100.0f));
m_ui->slBaseSurfaceOpacity->setToolTip(QString::number(m_ui->slBaseSurfaceOpacity->value()));
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LayerControlDialog::onMapDestroyed()
{
delete this;
}
void LayerControlDialog::onLayerParamterChanged()
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " XXXXXXXXX ";
if (!m_situationMap)
return;
if (m_ignoreLayerParameterChanged)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " skip due to m_ignoreLayerParameterChanged";
return;
}
if (m_layersModel)
m_layersModel->initModel(m_situationMap);
// zoom and rotation slider will be updated by theirself
}
//SLOTS
void LayerControlDialog::onRotationSliderValueChanged()
{
if (m_situationMap->isReady())
m_situationMap->setRotation(m_ui->slRotation->value());
}
void LayerControlDialog::onRotationOfMapChanged(double rotation)
{
if (m_situationMap->isReady() && !m_ui->slRotation->isSliderDown())
{
const QSignalBlocker blocker(m_ui->slRotation);
m_ui->slRotation->setValue(static_cast<int>(rotation));
m_ui->lblRotation->setToolTip(QString::number(m_ui->slRotation->value()));
}
}
void LayerControlDialog::onZoomSliderInit()
{
TRACE_LOG(m_logger) << LOGFUNCTION << "'" << " START";
if (!m_ui || !m_ui->slZoom)
return;
m_ui->slZoom->blockSignals(true);
m_ui->slZoom->setMinimum(static_cast<int>(sqrt(m_situationMap->maxScale())));
m_ui->slZoom->setMaximum(static_cast<int>(sqrt(m_situationMap->minScale())));
if (m_ui->slZoom->minimum() == m_ui->slZoom->maximum())
{
WARNING_LOG(m_logger) << LOGFUNCTION << " min/max value are identical"
<< " minimum=" << m_ui->slZoom->minimum()
<< " maximum=" << m_ui->slZoom->maximum()
<< " value=" << m_ui->slZoom->value();
m_ui->slZoom->setMinimum(static_cast<int>(sqrt(500)));
m_ui->slZoom->setMaximum(static_cast<int>(sqrt(100000)));
WARNING_LOG(m_logger) << LOGFUNCTION << " min/max value are identical"
<< " minimum=" << m_ui->slZoom->minimum()
<< " maximum=" << m_ui->slZoom->maximum()
<< " value=" << m_ui->slZoom->value();
}
QString toolTipStr = QStringLiteral("min=%1 - max=%2").arg(m_ui->slZoom->minimum()).arg(m_ui->slZoom->maximum());
m_ui->slZoom->setToolTip(toolTipStr);
onMapExtentChanged();
m_ui->slZoom->blockSignals(false);
TRACE_LOG(m_logger) << LOGFUNCTION << "'" << " END";
}
void LayerControlDialog::onZoomSliderValueChanged()
{
if (m_situationMap->isReady())
m_situationMap->setScale(m_ui->slZoom->value() * m_ui->slZoom->value());
}
void LayerControlDialog::onMapExtentChanged()
{
if (m_ui->slZoom && m_ui->slZoom->isEnabled() && m_situationMap->isReady() && (!m_ui->slZoom->isSliderDown()))
{
const QSignalBlocker blocker(m_ui->slZoom);
double newVal = sqrt(m_situationMap->scale());
m_ui->slZoom->setValue(static_cast<int>(newVal));
m_ui->lblZoom->setToolTip(QString::number(m_situationMap->scale()));
}
}
void LayerControlDialog::onNavigateBelowGroundValueChanged(int state)
{
if (!m_situationMap->isReady()) // required ?
return;
Qt::CheckState cs = static_cast<Qt::CheckState>(state);
switch (cs) {
case Qt::CheckState::PartiallyChecked:
m_situationMap->setNavigationConstraint(NavigationConstraint::Undefined);
break;
case Qt::CheckState::Checked:
m_situationMap->setNavigationConstraint(NavigationConstraint::None);
break;
case Qt::CheckState::Unchecked:
m_situationMap->setNavigationConstraint(NavigationConstraint::StayAbove);
break;
} // switch
}
void LayerControlDialog::onNavigateBelowGroundOfMapChanged(NavigationConstraint newNavigationConstraint)
{
switch (newNavigationConstraint)
{
case NavigationConstraint::Undefined:
m_ui->cbNavigateBelowGround->setCheckState(Qt::CheckState::PartiallyChecked); // should never happen
break;
case NavigationConstraint::None:
m_ui->cbNavigateBelowGround->setCheckState(Qt::CheckState::Checked);
break;
case NavigationConstraint::StayAbove:
m_ui->cbNavigateBelowGround->setCheckState(Qt::CheckState::Unchecked);
break;
} // switch
}
void LayerControlDialog::onBaseSurfaceOpacitySliderValueChanged()
{
if (m_situationMap->isReady()) // required ?
m_situationMap->setBaseSurfaceOpacity(m_ui->slBaseSurfaceOpacity->value()/100.0f);
}
void LayerControlDialog::onBaseSurfaceOpacityOfMapChanged(float opacity)
{
if (m_situationMap->isReady() && !m_ui->slBaseSurfaceOpacity->isSliderDown())
{
const QSignalBlocker blocker(m_ui->slBaseSurfaceOpacity);
m_ui->slBaseSurfaceOpacity->setValue(static_cast<int>(opacity*100));
m_ui->slBaseSurfaceOpacity->setToolTip(QString::number(m_ui->slBaseSurfaceOpacity->value()));
}
}
void LayerControlDialog::layerOnOff(const QString &layerName, bool isVisible)
{
// get LayerParameter, update data and set:
LayerParameter lp = m_situationMap->layerFactory().layerParameter(layerName);
lp.visible = isVisible;
m_situationMap->layerFactory().setLayerParameter(layerName, lp);
}
} //namespace SituationMap
#include "sitmap_layer_control.moc"