QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
qgslayoutmapwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutmapwidget.cpp
3 ----------------------
4 begin : October 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19#include "qgslayoutmapwidget.h"
20
21#include "qgsbookmarkmodel.h"
22#include "qgsfillsymbol.h"
23#include "qgsguiutils.h"
24#include "qgslayertree.h"
25#include "qgslayout.h"
26#include "qgslayoutatlas.h"
28#include "qgslayoutitemmap.h"
30#include "qgslayoutundostack.h"
31#include "qgsmapcanvas.h"
32#include "qgsmaplayermodel.h"
34#include "qgsproject.h"
38
39#include <QMenu>
40#include <QMessageBox>
41#include <QString>
42#include <QStringListModel>
43
44#include "moc_qgslayoutmapwidget.cpp"
45
46using namespace Qt::StringLiterals;
47
49 : QgsLayoutItemBaseWidget( nullptr, item )
50 , mMapItem( item )
51 , mMapCanvas( mapCanvas )
52{
53 Q_ASSERT( mMapItem );
54
55 setupUi( this );
56 connect( mScaleLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mScaleLineEdit_editingFinished );
57 connect( mActionSetToCanvasExtent, &QAction::triggered, this, &QgsLayoutMapWidget::setToMapCanvasExtent );
58 connect( mActionViewExtentInCanvas, &QAction::triggered, this, &QgsLayoutMapWidget::viewExtentInCanvas );
59 connect( mActionSetToCanvasScale, &QAction::triggered, this, &QgsLayoutMapWidget::setToMapCanvasScale );
60 connect( mActionViewScaleInCanvas, &QAction::triggered, this, &QgsLayoutMapWidget::viewScaleInCanvas );
61 connect( mActionUpdatePreview, &QAction::triggered, this, &QgsLayoutMapWidget::updatePreview );
62 connect( mFollowVisibilityPresetCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mFollowVisibilityPresetCheckBox_stateChanged );
63 connect( mKeepLayerListCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mKeepLayerListCheckBox_stateChanged );
64 connect( mKeepLayerStylesCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mKeepLayerStylesCheckBox_stateChanged );
65 connect( mDrawCanvasItemsCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutMapWidget::mDrawCanvasItemsCheckBox_stateChanged );
66 connect( mOverviewBlendModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::mOverviewBlendModeComboBox_currentIndexChanged );
67 connect( mOverviewInvertCheckbox, &QCheckBox::toggled, this, &QgsLayoutMapWidget::mOverviewInvertCheckbox_toggled );
68 connect( mOverviewCenterCheckbox, &QCheckBox::toggled, this, &QgsLayoutMapWidget::mOverviewCenterCheckbox_toggled );
69 connect( mOverviewPositionComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::overviewStackingChanged );
70 connect( mOverviewStackingLayerComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsLayoutMapWidget::overviewStackingLayerChanged );
71 connect( mXMinLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mXMinLineEdit_editingFinished );
72 connect( mXMaxLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mXMaxLineEdit_editingFinished );
73 connect( mYMinLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mYMinLineEdit_editingFinished );
74 connect( mYMaxLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutMapWidget::mYMaxLineEdit_editingFinished );
75 connect( mAtlasMarginRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasMarginRadio_toggled );
76 connect( mAtlasCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mAtlasCheckBox_toggled );
77 connect( mAtlasMarginSpinBox, static_cast<void ( QSpinBox::* )( int )>( &QSpinBox::valueChanged ), this, &QgsLayoutMapWidget::mAtlasMarginSpinBox_valueChanged );
78 connect( mAtlasFixedScaleRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasFixedScaleRadio_toggled );
79 connect( mAtlasPredefinedScaleRadio, &QRadioButton::toggled, this, &QgsLayoutMapWidget::mAtlasPredefinedScaleRadio_toggled );
80 connect( mAddGridPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mAddGridPushButton_clicked );
81 connect( mRemoveGridPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mRemoveGridPushButton_clicked );
82 connect( mCopyGridPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mCopyGridPushButton_clicked );
83 connect( mGridUpButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridUpButton_clicked );
84 connect( mGridDownButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridDownButton_clicked );
85 connect( mGridListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutMapWidget::mGridListWidget_currentItemChanged );
86 connect( mGridListWidget, &QListWidget::itemChanged, this, &QgsLayoutMapWidget::mGridListWidget_itemChanged );
87 connect( mGridPropertiesButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mGridPropertiesButton_clicked );
88 connect( mAddOverviewPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mAddOverviewPushButton_clicked );
89 connect( mRemoveOverviewPushButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mRemoveOverviewPushButton_clicked );
90 connect( mOverviewUpButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mOverviewUpButton_clicked );
91 connect( mOverviewDownButton, &QPushButton::clicked, this, &QgsLayoutMapWidget::mOverviewDownButton_clicked );
92 connect( mOverviewCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mOverviewCheckBox_toggled );
93 connect( mOverviewListWidget, &QListWidget::currentItemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_currentItemChanged );
94 connect( mOverviewListWidget, &QListWidget::itemChanged, this, &QgsLayoutMapWidget::mOverviewListWidget_itemChanged );
95 connect( mActionLabelSettings, &QAction::triggered, this, &QgsLayoutMapWidget::showLabelSettings );
96 connect( mActionClipSettings, &QAction::triggered, this, &QgsLayoutMapWidget::showClipSettings );
97
98 connect( mTemporalCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mTemporalCheckBox_toggled );
99 connect( mStartDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsLayoutMapWidget::updateTemporalExtent );
100 connect( mEndDateTime, &QDateTimeEdit::dateTimeChanged, this, &QgsLayoutMapWidget::updateTemporalExtent );
101
102 mZLowerSpin->setClearValueMode( QgsDoubleSpinBox::ClearValueMode::MinimumValue, tr( "Not set" ) );
103 mZUpperSpin->setClearValueMode( QgsDoubleSpinBox::ClearValueMode::MinimumValue, tr( "Not set" ) );
104 connect( mElevationRangeCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutMapWidget::mElevationRangeCheckBox_toggled );
105 connect( mZLowerSpin, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutMapWidget::updateZRange );
106 connect( mZUpperSpin, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutMapWidget::updateZRange );
107
108 mStartDateTime->setDateTimeRange( QDateTime( QDate( 1, 1, 1 ), QTime( 0, 0, 0 ) ), mStartDateTime->maximumDateTime() );
109 mEndDateTime->setDateTimeRange( QDateTime( QDate( 1, 1, 1 ), QTime( 0, 0, 0 ) ), mStartDateTime->maximumDateTime() );
110 mStartDateTime->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
111 mEndDateTime->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
112
113 connect( mActionMoveContent, &QAction::triggered, this, &QgsLayoutMapWidget::switchToMoveContentTool );
114 setPanelTitle( tr( "Map Properties" ) );
115 mMapRotationSpinBox->setClearValue( 0 );
116
117 mDockToolbar->setIconSize( QgsGuiUtils::iconSize( true ) );
118
119 mLayersMenu = new QMenu( this );
120 QToolButton *btnLayers = new QToolButton( this );
121 btnLayers->setAutoRaise( true );
122 btnLayers->setToolTip( tr( "Set Map Extent to Layer Extent" ) );
123 btnLayers->setIcon( QgsApplication::getThemeIcon( u"/mActionLayers.svg"_s ) );
124 btnLayers->setPopupMode( QToolButton::InstantPopup );
125 btnLayers->setMenu( mLayersMenu );
126
127 mDockToolbar->insertWidget( mActionMoveContent, btnLayers );
128 connect( mLayersMenu, &QMenu::aboutToShow, this, &QgsLayoutMapWidget::aboutToShowLayersMenu );
129
130 mBookmarkMenu = new QMenu( this );
131 QToolButton *btnBookmarks = new QToolButton( this );
132 btnBookmarks->setAutoRaise( true );
133 btnBookmarks->setToolTip( tr( "Set Map Extent to Bookmark Extent" ) );
134 btnBookmarks->setIcon( QgsApplication::getThemeIcon( u"/mActionShowBookmarks.svg"_s ) );
135 btnBookmarks->setPopupMode( QToolButton::InstantPopup );
136 btnBookmarks->setMenu( mBookmarkMenu );
137
138 mDockToolbar->insertWidget( mActionMoveContent, btnBookmarks );
139 connect( mBookmarkMenu, &QMenu::aboutToShow, this, &QgsLayoutMapWidget::aboutToShowBookmarkMenu );
140
141 //add widget for general composer item properties
142 mItemPropertiesWidget = new QgsLayoutItemPropertiesWidget( this, item );
143 mainLayout->addWidget( mItemPropertiesWidget );
144
145 mScaleLineEdit->setValidator( new QDoubleValidator( mScaleLineEdit ) );
146
147 mXMinLineEdit->setValidator( new QDoubleValidator( mXMinLineEdit ) );
148 mXMaxLineEdit->setValidator( new QDoubleValidator( mXMaxLineEdit ) );
149 mYMinLineEdit->setValidator( new QDoubleValidator( mYMinLineEdit ) );
150 mYMaxLineEdit->setValidator( new QDoubleValidator( mYMaxLineEdit ) );
151
152 mOverviewPositionComboBox->addItem( tr( "Below Map" ), QgsLayoutItemMapItem::StackBelowMap );
153 mOverviewPositionComboBox->addItem( tr( "Below Map Layer" ), QgsLayoutItemMapItem::StackBelowMapLayer );
154 mOverviewPositionComboBox->addItem( tr( "Above Map Layer" ), QgsLayoutItemMapItem::StackAboveMapLayer );
155 mOverviewPositionComboBox->addItem( tr( "Below Map Labels" ), QgsLayoutItemMapItem::StackBelowMapLabels );
156 mOverviewPositionComboBox->addItem( tr( "Above Map Labels" ), QgsLayoutItemMapItem::StackAboveMapLabels );
157
158 blockAllSignals( true );
159
160 mCrsSelector->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, true );
161 mCrsSelector->setNotSetText( tr( "Use Project CRS" ) );
162 mCrsSelector->setDialogTitle( tr( "Map Item CRS" ) );
163
164 mOverviewFrameStyleButton->setSymbolType( Qgis::SymbolType::Fill );
165
166 // follow preset combo
167 mFollowVisibilityPresetCombo->setModel( new QStringListModel( mFollowVisibilityPresetCombo ) );
168 connect( mFollowVisibilityPresetCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutMapWidget::followVisibilityPresetSelected );
169 connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemesChanged, this, &QgsLayoutMapWidget::onMapThemesChanged );
170 onMapThemesChanged();
171
172 // keep layers from preset button
173 QMenu *menuKeepLayers = new QMenu( this );
174 mLayerListFromPresetButton->setMenu( menuKeepLayers );
175 mLayerListFromPresetButton->setIcon( QgsApplication::getThemeIcon( u"/mActionShowAllLayers.svg"_s ) );
176 mLayerListFromPresetButton->setToolTip( tr( "Set layer list from a map theme" ) );
177 connect( menuKeepLayers, &QMenu::aboutToShow, this, &QgsLayoutMapWidget::aboutToShowKeepLayersVisibilityPresetsMenu );
178
179 connect( item, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements );
180
181 connect( &item->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutMapWidget::atlasLayerChanged );
182 if ( QgsLayoutAtlas *atlas = layoutAtlas() )
183 {
184 connect( atlas, &QgsLayoutAtlas::toggled, this, &QgsLayoutMapWidget::compositionAtlasToggled );
185 compositionAtlasToggled( atlas->enabled() );
186 }
187
188 mOverviewFrameMapComboBox->setCurrentLayout( item->layout() );
189 mOverviewFrameMapComboBox->setItemType( QgsLayoutItemRegistry::LayoutMap );
190 mOverviewFrameStyleButton->registerExpressionContextGenerator( item );
191
192 connect( mOverviewFrameMapComboBox, &QgsLayoutItemComboBox::itemChanged, this, &QgsLayoutMapWidget::overviewMapChanged );
193 connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsLayoutMapWidget::mapCrsChanged );
194 connect( mOverviewFrameStyleButton, &QgsSymbolButton::changed, this, &QgsLayoutMapWidget::overviewSymbolChanged );
195
196 mOverviewFrameStyleButton->registerExpressionContextGenerator( item );
197 mOverviewFrameStyleButton->setLayer( coverageLayer() );
198 if ( item->layout() )
199 {
200 connect( &item->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, mOverviewFrameStyleButton, &QgsSymbolButton::setLayer );
201 }
202
203
218
219 updateGuiElements();
220 loadGridEntries();
221 loadOverviewEntries();
222
223 connect( mMapRotationSpinBox, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutMapWidget::rotationChanged );
224 connect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
226
227 blockAllSignals( false );
228}
229
231{
232 if ( mItemPropertiesWidget )
233 mItemPropertiesWidget->setMasterLayout( masterLayout );
234}
235
236void QgsLayoutMapWidget::setReportTypeString( const QString &string )
237{
238 mReportTypeString = string;
239 mAtlasCheckBox->setTitle( tr( "Controlled by %1" ).arg( string == tr( "atlas" ) ? tr( "Atlas" ) : tr( "Report" ) ) );
240 mAtlasPredefinedScaleRadio->setToolTip( tr( "Use one of the predefined scales of the project where the %1 feature best fits." ).arg( string ) );
241
242 if ( mClipWidget )
243 mClipWidget->setReportTypeString( string );
244 if ( mLabelWidget )
245 mLabelWidget->setReportTypeString( string );
246}
247
253
255{
256 if ( item->type() != QgsLayoutItemRegistry::LayoutMap )
257 return false;
258
259 if ( mMapItem )
260 {
261 disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements );
262 disconnect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
263 disconnect( mMapItem, &QgsLayoutItemMap::mapRotationChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
264 }
265
266 mMapItem = qobject_cast<QgsLayoutItemMap *>( item );
267 mItemPropertiesWidget->setItem( mMapItem );
268 if ( mLabelWidget )
269 mLabelWidget->setItem( mMapItem );
270 if ( mClipWidget )
271 mClipWidget->setItem( mMapItem );
272
273 if ( mMapItem )
274 {
275 connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapWidget::updateGuiElements );
276 connect( mMapItem, &QgsLayoutItemMap::extentChanged, mItemPropertiesWidget, &QgsLayoutItemPropertiesWidget::updateVariables );
278 mOverviewFrameStyleButton->registerExpressionContextGenerator( mMapItem );
279 }
280
281 updateGuiElements();
282
283 return true;
284}
285
287{
288 updateDataDefinedButton( mScaleDDBtn );
289 updateDataDefinedButton( mMapRotationDDBtn );
290 updateDataDefinedButton( mXMinDDBtn );
291 updateDataDefinedButton( mYMinDDBtn );
292 updateDataDefinedButton( mXMaxDDBtn );
293 updateDataDefinedButton( mYMaxDDBtn );
294 updateDataDefinedButton( mAtlasMarginDDBtn );
295 updateDataDefinedButton( mStylePresetsDDBtn );
296 updateDataDefinedButton( mLayersDDBtn );
297 updateDataDefinedButton( mCRSDDBtn );
298 updateDataDefinedButton( mStartDateTimeDDBtn );
299 updateDataDefinedButton( mEndDateTimeDDBtn );
300}
301
302void QgsLayoutMapWidget::compositionAtlasToggled( bool atlasEnabled )
303{
304 if ( atlasEnabled && mMapItem && mMapItem->layout() && mMapItem->layout()->reportContext().layer() && mMapItem->layout()->reportContext().layer()->wkbType() != Qgis::WkbType::NoGeometry )
305 {
306 mAtlasCheckBox->setEnabled( true );
307 }
308 else
309 {
310 mAtlasCheckBox->setEnabled( false );
311 mAtlasCheckBox->setChecked( false );
312 }
313}
314
315void QgsLayoutMapWidget::aboutToShowKeepLayersVisibilityPresetsMenu()
316{
317 // this menu is for the case when setting "keep layers" and "keep layer styles"
318 // and the preset configuration is copied. The preset is not followed further.
319
320 QMenu *menu = qobject_cast<QMenu *>( sender() );
321 if ( !menu )
322 return;
323
324 menu->clear();
325 const auto constMapThemes = QgsProject::instance()->mapThemeCollection()->mapThemes();
326 for ( const QString &presetName : constMapThemes )
327 {
328 menu->addAction( presetName, this, &QgsLayoutMapWidget::keepLayersVisibilityPresetSelected );
329 }
330
331 if ( menu->actions().isEmpty() )
332 menu->addAction( tr( "No presets defined" ) )->setEnabled( false );
333}
334
335void QgsLayoutMapWidget::followVisibilityPresetSelected( int currentIndex )
336{
337 if ( !mMapItem )
338 return;
339
340 if ( mBlockThemeComboChanges != 0 )
341 return;
342
343 if ( currentIndex == -1 )
344 return; // doing combo box model reset
345
346 QString presetName;
347 if ( currentIndex != 0 )
348 {
349 presetName = mFollowVisibilityPresetCombo->currentText();
350 }
351
352 if ( presetName == mMapItem->followVisibilityPresetName() )
353 return;
354
355 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
356 mFollowVisibilityPresetCheckBox->setChecked( true );
357 mMapItem->setFollowVisibilityPresetName( presetName );
358 mMapItem->layout()->undoStack()->endCommand();
359
360 mMapItem->invalidateCache();
361}
362
363void QgsLayoutMapWidget::keepLayersVisibilityPresetSelected()
364{
365 QAction *action = qobject_cast<QAction *>( sender() );
366 if ( !action )
367 return;
368
369 const QString presetName = action->text();
370 const QList<QgsMapLayer *> lst = orderedPresetVisibleLayers( presetName );
371 if ( mMapItem )
372 {
373 mKeepLayerListCheckBox->setChecked( true );
374 mMapItem->setLayers( lst );
375
376 mKeepLayerStylesCheckBox->setChecked( true );
377
378 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
379 mMapItem->setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( presetName ) );
380 mMapItem->layout()->undoStack()->endCommand();
381
382 mMapItem->invalidateCache();
383 }
384}
385
386void QgsLayoutMapWidget::onMapThemesChanged()
387{
388 if ( QStringListModel *model = qobject_cast<QStringListModel *>( mFollowVisibilityPresetCombo->model() ) )
389 {
390 mBlockThemeComboChanges++;
391 QStringList lst;
392 lst.append( tr( "(none)" ) );
394 model->setStringList( lst );
395
396 // select the previously selected item again
397 const int presetModelIndex = mFollowVisibilityPresetCombo->findText( mMapItem->followVisibilityPresetName() );
398 mFollowVisibilityPresetCombo->blockSignals( true );
399 mFollowVisibilityPresetCombo->setCurrentIndex( presetModelIndex != -1 ? presetModelIndex : 0 ); // 0 == none
400 mFollowVisibilityPresetCombo->blockSignals( false );
401 mBlockThemeComboChanges--;
402 }
403}
404
405void QgsLayoutMapWidget::mapCrsChanged( const QgsCoordinateReferenceSystem &crs )
406{
407 if ( !mMapItem )
408 {
409 return;
410 }
411
412 if ( mMapItem->presetCrs() == crs )
413 return;
414
415 // try to reproject to maintain extent
416 const QgsCoordinateReferenceSystem oldCrs = mMapItem->crs();
417
418 bool updateExtent = false;
419 QgsRectangle newExtent;
420 try
421 {
422 QgsCoordinateTransform xForm( oldCrs, crs.isValid() ? crs : QgsProject::instance()->crs(), QgsProject::instance() );
423 xForm.setBallparkTransformsAreAppropriate( true );
424 const QgsRectangle prevExtent = mMapItem->extent();
425 newExtent = xForm.transformBoundingBox( prevExtent );
426 updateExtent = true;
427 }
428 catch ( QgsCsException & )
429 {
430 //transform failed, don't update extent
431 }
432
433 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map CRS" ) );
434 mMapItem->setCrs( crs );
435 if ( updateExtent )
436 mMapItem->zoomToExtent( newExtent );
437 mMapItem->layout()->undoStack()->endCommand();
438 mMapItem->invalidateCache();
439}
440
441void QgsLayoutMapWidget::overviewSymbolChanged()
442{
443 QgsLayoutItemMapOverview *overview = currentOverview();
444 if ( !overview )
445 return;
446
447 mMapItem->beginCommand( tr( "Change Overview Style" ), QgsLayoutItem::UndoOverviewStyle );
448 overview->setFrameSymbol( mOverviewFrameStyleButton->clonedSymbol<QgsFillSymbol>() );
449 mMapItem->endCommand();
450 mMapItem->invalidateCache();
451}
452
453void QgsLayoutMapWidget::showLabelSettings()
454{
455 mLabelWidget = new QgsLayoutMapLabelingWidget( mMapItem );
456
457 if ( !mReportTypeString.isEmpty() )
458 mLabelWidget->setReportTypeString( mReportTypeString );
459
460 openPanel( mLabelWidget );
461}
462
463void QgsLayoutMapWidget::showClipSettings()
464{
465 mClipWidget = new QgsLayoutMapClippingWidget( mMapItem );
466 if ( !mReportTypeString.isEmpty() )
467 mClipWidget->setReportTypeString( mReportTypeString );
468 openPanel( mClipWidget );
469}
470
471void QgsLayoutMapWidget::switchToMoveContentTool()
472{
473 if ( mInterface )
474 mInterface->activateTool( QgsLayoutDesignerInterface::ToolMoveItemContent );
475}
476
477void QgsLayoutMapWidget::aboutToShowLayersMenu()
478{
479 mLayersMenu->clear();
480
481 if ( !mMapLayerModel )
482 {
483 mMapLayerModel = new QgsMapLayerProxyModel( this );
484 mMapLayerModel->setFilters( Qgis::LayerFilter::SpatialLayer );
485 }
486
487 if ( mMapLayerModel->rowCount() == 0 )
488 {
489 QAction *action = new QAction( tr( "No spatial layers available" ) );
490 action->setEnabled( false );
491 mLayersMenu->addAction( action );
492 return;
493 }
494
495 for ( int i = 0; i < mMapLayerModel->rowCount(); ++i )
496 {
497 const QModelIndex index = mMapLayerModel->index( i, 0 );
498 const QIcon icon = qvariant_cast<QIcon>( mMapLayerModel->data( index, Qt::DecorationRole ) );
499 const QString text = mMapLayerModel->data( index, Qt::DisplayRole ).toString();
500 const QString tooltip = mMapLayerModel->data( index, Qt::ToolTipRole ).toString();
501 const QString layerId = mMapLayerModel->data( index, static_cast<int>( QgsMapLayerModel::CustomRole::LayerId ) ).toString();
502
503 QAction *action = new QAction( icon, text, mLayersMenu );
504 action->setToolTip( tooltip );
505 connect( action, &QAction::triggered, this, [this, layerId] {
506 if ( QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerId ) )
507 {
508 setToCustomExtent( QgsReferencedRectangle( layer->extent(), layer->crs() ) );
509 }
510 } );
511 mLayersMenu->addAction( action );
512 }
513}
514
515void QgsLayoutMapWidget::aboutToShowBookmarkMenu()
516{
517 mBookmarkMenu->clear();
518
519 // query the bookmarks now? or once during widget creation... Hmm. Either way, there's potentially a
520 // delay if there's LOTS of bookmarks. Let's avoid the cost until bookmarks are actually required.
521 if ( !mBookmarkModel )
522 {
523 mBookmarkModel = new QgsBookmarkManagerProxyModel( QgsApplication::bookmarkManager(), QgsProject::instance()->bookmarkManager(), this );
524 }
525
526 if ( mBookmarkModel->rowCount() == 0 )
527 {
528 QAction *action = new QAction( tr( "No bookmarks available" ) );
529 action->setEnabled( false );
530 mBookmarkMenu->addAction( action );
531 return;
532 }
533
534 QMap<QString, QMenu *> groupMenus;
535 for ( int i = 0; i < mBookmarkModel->rowCount(); ++i )
536 {
537 const QString group = mBookmarkModel->data( mBookmarkModel->index( i, 0 ), static_cast<int>( QgsBookmarkManagerModel::CustomRole::Group ) ).toString();
538 QMenu *destMenu = mBookmarkMenu;
539 if ( !group.isEmpty() )
540 {
541 destMenu = groupMenus.value( group );
542 if ( !destMenu )
543 {
544 destMenu = new QMenu( group, mBookmarkMenu );
545 groupMenus[group] = destMenu;
546 }
547 }
548 QAction *action = new QAction( mBookmarkModel->data( mBookmarkModel->index( i, 0 ), static_cast<int>( QgsBookmarkManagerModel::CustomRole::Name ) ).toString(), mBookmarkMenu );
549 const QgsReferencedRectangle extent = mBookmarkModel->data( mBookmarkModel->index( i, 0 ), static_cast<int>( QgsBookmarkManagerModel::CustomRole::Extent ) ).value<QgsReferencedRectangle>();
550 connect( action, &QAction::triggered, this, [this, extent] { setToCustomExtent( extent ); } );
551 destMenu->addAction( action );
552 }
553
554 QStringList groupKeys = groupMenus.keys();
555 groupKeys.sort( Qt::CaseInsensitive );
556 for ( int i = 0; i < groupKeys.count(); ++i )
557 {
558 if ( mBookmarkMenu->actions().value( i ) )
559 mBookmarkMenu->insertMenu( mBookmarkMenu->actions().at( i ), groupMenus.value( groupKeys.at( i ) ) );
560 else
561 mBookmarkMenu->addMenu( groupMenus.value( groupKeys.at( i ) ) );
562 }
563}
564
565void QgsLayoutMapWidget::setToCustomExtent( const QgsReferencedRectangle &referencedExtent )
566{
567 if ( !mMapItem || referencedExtent.isEmpty() )
568 {
569 return;
570 }
571
572 QgsRectangle extent = referencedExtent;
573 if ( referencedExtent.crs() != mMapItem->crs() )
574 {
575 try
576 {
577 QgsCoordinateTransform coordinateTransform( referencedExtent.crs(), mMapItem->crs(), QgsProject::instance() );
578 coordinateTransform.setBallparkTransformsAreAppropriate( true );
579 extent = coordinateTransform.transformBoundingBox( extent );
580 }
581 catch ( QgsCsException & )
582 {
583 //transform failed, better not proceed
584 return;
585 }
586 }
587
588 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) );
589 mMapItem->zoomToExtent( extent );
590 mMapItem->layout()->undoStack()->endCommand();
591}
592
593void QgsLayoutMapWidget::mTemporalCheckBox_toggled( bool checked )
594{
595 if ( !mMapItem )
596 {
597 return;
598 }
599
600 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Toggle Temporal Range" ) );
601 mMapItem->setIsTemporal( checked );
602 mMapItem->layout()->undoStack()->endCommand();
603
604 if ( checked )
605 {
606 whileBlocking( mStartDateTime )->setDateTime( mMapItem->temporalRange().begin() );
607 whileBlocking( mEndDateTime )->setDateTime( mMapItem->temporalRange().end() );
608 }
609
610 updatePreview();
611}
612
613void QgsLayoutMapWidget::updateTemporalExtent()
614{
615 if ( !mMapItem )
616 {
617 return;
618 }
619
620 const QDateTime begin = mStartDateTime->dateTime();
621 const QDateTime end = mEndDateTime->dateTime();
622 const QgsDateTimeRange range = QgsDateTimeRange( begin, end, true, begin == end );
623
624 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Set Temporal Range" ) );
625 mMapItem->setTemporalRange( range );
626 mMapItem->layout()->undoStack()->endCommand();
627
628 updatePreview();
629}
630
631void QgsLayoutMapWidget::mElevationRangeCheckBox_toggled( bool checked )
632{
633 if ( !mMapItem )
634 {
635 return;
636 }
637
638 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Toggle Z Range" ) );
639 mMapItem->setZRangeEnabled( checked );
640 mMapItem->layout()->undoStack()->endCommand();
641
642 updatePreview();
643}
644
645void QgsLayoutMapWidget::updateZRange()
646{
647 if ( !mMapItem )
648 {
649 return;
650 }
651
652 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Set Z Range" ) );
653 double zLower = mZLowerSpin->value();
654 if ( zLower == mZLowerSpin->clearValue() )
655 zLower = std::numeric_limits<double>::lowest();
656 double zUpper = mZUpperSpin->value();
657 if ( zUpper == mZUpperSpin->clearValue() )
658 zUpper = std::numeric_limits<double>::max();
659
660 mMapItem->setZRange( QgsDoubleRange( zLower, zUpper ) );
661 mMapItem->layout()->undoStack()->endCommand();
662
663 updatePreview();
664}
665
666void QgsLayoutMapWidget::mAtlasCheckBox_toggled( bool checked )
667{
668 if ( !mMapItem )
669 {
670 return;
671 }
672
673 mAtlasFixedScaleRadio->setEnabled( checked );
674 mAtlasMarginRadio->setEnabled( checked );
675
676 if ( mAtlasMarginRadio->isEnabled() && mAtlasMarginRadio->isChecked() )
677 {
678 mAtlasMarginSpinBox->setEnabled( true );
679 }
680 else
681 {
682 mAtlasMarginSpinBox->setEnabled( false );
683 }
684
685 mAtlasPredefinedScaleRadio->setEnabled( checked );
686
687 if ( checked )
688 {
689 //check atlas coverage layer type
690 if ( mMapItem->layout() )
691 {
692 toggleAtlasScalingOptionsByLayerType();
693 }
694 }
695
696 // disable predefined scales if none are defined
697 if ( !hasPredefinedScales() )
698 {
699 mAtlasPredefinedScaleRadio->setEnabled( false );
700 }
701
702 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Set Atlas Driven" ) );
703 mMapItem->setAtlasDriven( checked );
704 mMapItem->layout()->undoStack()->endCommand();
705 updateMapForAtlas();
706}
707
708void QgsLayoutMapWidget::updateMapForAtlas()
709{
710 //update map if in atlas preview mode
711 if ( mMapItem->atlasDriven() )
712 {
713 mMapItem->refresh();
714 }
715 else
716 {
717 //redraw map
718 mMapItem->invalidateCache();
719 }
720}
721
722void QgsLayoutMapWidget::mAtlasMarginRadio_toggled( bool checked )
723{
724 mAtlasMarginSpinBox->setEnabled( checked );
725
726 if ( checked && mMapItem )
727 {
728 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) );
729 mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Auto );
730 mMapItem->layout()->undoStack()->endCommand();
731 updateMapForAtlas();
732 }
733}
734
735void QgsLayoutMapWidget::mAtlasMarginSpinBox_valueChanged( int value )
736{
737 if ( !mMapItem )
738 {
739 return;
740 }
741
742 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Margin" ), QgsLayoutItem::UndoAtlasMargin );
743 mMapItem->setAtlasMargin( value / 100. );
744 mMapItem->layout()->undoStack()->endCommand();
745 updateMapForAtlas();
746}
747
748void QgsLayoutMapWidget::mAtlasFixedScaleRadio_toggled( bool checked )
749{
750 if ( !mMapItem )
751 {
752 return;
753 }
754
755 if ( checked )
756 {
757 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) );
758 mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Fixed );
759 mMapItem->layout()->undoStack()->endCommand();
760 updateMapForAtlas();
761 }
762}
763
764void QgsLayoutMapWidget::mAtlasPredefinedScaleRadio_toggled( bool checked )
765{
766 if ( !mMapItem )
767 {
768 return;
769 }
770
771 if ( hasPredefinedScales() )
772 {
773 if ( checked )
774 {
775 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Scales" ) );
776 mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Predefined );
777 mMapItem->layout()->undoStack()->endCommand();
778 updateMapForAtlas();
779 }
780 }
781 else
782 {
783 // restore to fixed scale if no predefined scales exist
784 whileBlocking( mAtlasFixedScaleRadio )->setChecked( true );
785 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Atlas Mode" ) );
786 mMapItem->setAtlasScalingMode( QgsLayoutItemMap::Fixed );
787 mMapItem->layout()->undoStack()->endCommand();
788 }
789}
790
791void QgsLayoutMapWidget::mScaleLineEdit_editingFinished()
792{
793 if ( !mMapItem )
794 {
795 return;
796 }
797
798 bool conversionSuccess = false;
799 const double scaleDenominator = QLocale().toDouble( mScaleLineEdit->text(), &conversionSuccess );
800 if ( !conversionSuccess )
801 {
802 return;
803 }
804
805 if ( qgsDoubleNear( scaleDenominator, mMapItem->scale() ) )
806 return;
807
808 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Scale" ) );
809 mMapItem->setScale( scaleDenominator );
810 mMapItem->layout()->undoStack()->endCommand();
811}
812
813void QgsLayoutMapWidget::rotationChanged( double value )
814{
815 if ( !mMapItem )
816 {
817 return;
818 }
819
820 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Rotation" ), QgsLayoutItem::UndoMapRotation );
821 mMapItem->setMapRotation( value );
822 mMapItem->layout()->undoStack()->endCommand();
823 mMapItem->invalidateCache();
824}
825
826void QgsLayoutMapWidget::setToMapCanvasExtent()
827{
828 if ( !mMapItem )
829 {
830 return;
831 }
832
833 QgsRectangle newExtent = mMapCanvas->mapSettings().visibleExtent();
834
835 //transform?
836 if ( mMapCanvas->mapSettings().destinationCrs() != mMapItem->crs() )
837 {
838 try
839 {
840 QgsCoordinateTransform xForm( mMapCanvas->mapSettings().destinationCrs(), mMapItem->crs(), QgsProject::instance() );
841 xForm.setBallparkTransformsAreAppropriate( true );
842 newExtent = xForm.transformBoundingBox( newExtent );
843 }
844 catch ( QgsCsException & )
845 {
846 //transform failed, better not proceed
847 return;
848 }
849 }
850
851 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) );
852 mMapItem->zoomToExtent( newExtent );
853 mMapItem->layout()->undoStack()->endCommand();
854}
855
856void QgsLayoutMapWidget::setToMapCanvasScale()
857{
858 if ( !mMapItem )
859 {
860 return;
861 }
862
863 const double newScale = mMapCanvas->scale();
864
865 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Scale" ) );
866 mMapItem->setScale( newScale );
867 mMapItem->layout()->undoStack()->endCommand();
868}
869
870void QgsLayoutMapWidget::viewExtentInCanvas()
871{
872 if ( !mMapItem )
873 {
874 return;
875 }
876
877 const QgsRectangle currentMapExtent = mMapItem->extent();
878
879 if ( !currentMapExtent.isEmpty() )
880 {
881 try
882 {
883 mMapCanvas->setReferencedExtent( QgsReferencedRectangle( currentMapExtent, mMapItem->crs() ) );
884 }
885 catch ( QgsCsException & )
886 {
887 //transform failed, better not proceed
888 return;
889 }
890 mMapCanvas->refresh();
891 }
892}
893
894void QgsLayoutMapWidget::viewScaleInCanvas()
895{
896 if ( !mMapItem )
897 {
898 return;
899 }
900
901 const double currentScale = mMapItem->scale();
902 mMapCanvas->zoomScale( currentScale, true );
903}
904
905void QgsLayoutMapWidget::mXMinLineEdit_editingFinished()
906{
907 updateComposerExtentFromGui();
908}
909
910void QgsLayoutMapWidget::mXMaxLineEdit_editingFinished()
911{
912 updateComposerExtentFromGui();
913}
914
915void QgsLayoutMapWidget::mYMinLineEdit_editingFinished()
916{
917 updateComposerExtentFromGui();
918}
919
920void QgsLayoutMapWidget::mYMaxLineEdit_editingFinished()
921{
922 updateComposerExtentFromGui();
923}
924
925void QgsLayoutMapWidget::updateGuiElements()
926{
927 if ( !mMapItem )
928 {
929 return;
930 }
931
932 blockAllSignals( true );
933 mLabel->setText( mMapItem->displayName() );
934
935 whileBlocking( mCrsSelector )->setCrs( mMapItem->presetCrs() );
936
937 //width, height, scale
938 const double scale = mMapItem->scale();
939
940 //round scale to an appropriate number of decimal places
941 if ( scale >= 10000 )
942 {
943 //round scale to integer if it's greater than 10000
944 mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 0 ) );
945 }
946 else if ( scale >= 10 )
947 {
948 mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 3 ) );
949 }
950 else if ( scale >= 1 )
951 {
952 //don't round scale if it's less than 10, instead use 4 decimal places
953 mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 4 ) );
954 }
955 else
956 {
957 //if scale < 1 then use 10 decimal places
958 mScaleLineEdit->setText( QLocale().toString( mMapItem->scale(), 'f', 10 ) );
959 }
960
961 //composer map extent
962 const QgsRectangle composerMapExtent = mMapItem->extent();
963 mXMinLineEdit->setText( QLocale().toString( composerMapExtent.xMinimum(), 'f', 3 ) );
964 mXMaxLineEdit->setText( QLocale().toString( composerMapExtent.xMaximum(), 'f', 3 ) );
965 mYMinLineEdit->setText( QLocale().toString( composerMapExtent.yMinimum(), 'f', 3 ) );
966 mYMaxLineEdit->setText( QLocale().toString( composerMapExtent.yMaximum(), 'f', 3 ) );
967
968 mMapRotationSpinBox->setValue( mMapItem->mapRotation( QgsLayoutObject::OriginalValue ) );
969
970 // follow preset checkbox
971 mFollowVisibilityPresetCheckBox->setCheckState( mMapItem->followVisibilityPreset() ? Qt::Checked : Qt::Unchecked );
972 const int presetModelIndex = mFollowVisibilityPresetCombo->findText( mMapItem->followVisibilityPresetName() );
973 mFollowVisibilityPresetCombo->setCurrentIndex( presetModelIndex != -1 ? presetModelIndex : 0 ); // 0 == none
974
975 //keep layer list checkbox
976 if ( mMapItem->keepLayerSet() )
977 {
978 mKeepLayerListCheckBox->setCheckState( Qt::Checked );
979 }
980 else
981 {
982 mKeepLayerListCheckBox->setCheckState( Qt::Unchecked );
983 }
984
985 mKeepLayerStylesCheckBox->setEnabled( mMapItem->keepLayerSet() );
986 mKeepLayerStylesCheckBox->setCheckState( mMapItem->keepLayerStyles() ? Qt::Checked : Qt::Unchecked );
987
988 //draw canvas items
989 if ( mMapItem->drawAnnotations() )
990 {
991 mDrawCanvasItemsCheckBox->setCheckState( Qt::Checked );
992 }
993 else
994 {
995 mDrawCanvasItemsCheckBox->setCheckState( Qt::Unchecked );
996 }
997
998 //atlas controls
999 mAtlasCheckBox->setChecked( mMapItem->atlasDriven() );
1000 mAtlasMarginSpinBox->setValue( static_cast<int>( mMapItem->atlasMargin( QgsLayoutObject::OriginalValue ) * 100 ) );
1001
1002 mAtlasFixedScaleRadio->setEnabled( mMapItem->atlasDriven() );
1003 mAtlasFixedScaleRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Fixed );
1004 mAtlasMarginSpinBox->setEnabled( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Auto );
1005 mAtlasMarginRadio->setEnabled( mMapItem->atlasDriven() );
1006 mAtlasMarginRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Auto );
1007 mAtlasPredefinedScaleRadio->setEnabled( mMapItem->atlasDriven() );
1008 mAtlasPredefinedScaleRadio->setChecked( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Predefined );
1009
1010 if ( mMapItem->atlasDriven() )
1011 {
1012 toggleAtlasScalingOptionsByLayerType();
1013 }
1014 // disable predefined scales if none are defined
1015 if ( !hasPredefinedScales() )
1016 {
1017 mAtlasPredefinedScaleRadio->setEnabled( false );
1018 }
1019
1020 mTemporalCheckBox->setChecked( mMapItem->isTemporal() );
1021 mTemporalCheckBox->setCollapsed( !mMapItem->isTemporal() );
1022 mStartDateTime->setEnabled( mMapItem->isTemporal() );
1023 mEndDateTime->setEnabled( mMapItem->isTemporal() );
1024 if ( mMapItem->isTemporal() )
1025 {
1026 mStartDateTime->setDateTime( mMapItem->temporalRange().begin() );
1027 mEndDateTime->setDateTime( mMapItem->temporalRange().end() );
1028 }
1029
1030 whileBlocking( mElevationRangeCheckBox )->setChecked( mMapItem->zRangeEnabled() );
1031 mElevationRangeCheckBox->setCollapsed( !mMapItem->zRangeEnabled() );
1032 if ( mMapItem->zRange().lower() != std::numeric_limits<double>::lowest() )
1033 whileBlocking( mZLowerSpin )->setValue( mMapItem->zRange().lower() );
1034 else
1035 whileBlocking( mZLowerSpin )->clear();
1036 if ( mMapItem->zRange().upper() != std::numeric_limits<double>::max() )
1037 whileBlocking( mZUpperSpin )->setValue( mMapItem->zRange().upper() );
1038 else
1039 whileBlocking( mZUpperSpin )->clear();
1040
1042 loadGridEntries();
1043 loadOverviewEntries();
1044
1045 blockAllSignals( false );
1046}
1047
1048void QgsLayoutMapWidget::toggleAtlasScalingOptionsByLayerType()
1049{
1050 if ( !mMapItem )
1051 {
1052 return;
1053 }
1054
1055 //get atlas coverage layer
1056 QgsVectorLayer *layer = coverageLayer();
1057 if ( !layer )
1058 {
1059 return;
1060 }
1061
1063 {
1064 //For point layers buffer setting makes no sense, so set "fixed scale" on and disable margin control
1065 if ( mMapItem->atlasScalingMode() == QgsLayoutItemMap::Auto )
1066 mAtlasFixedScaleRadio->setChecked( true );
1067 mAtlasMarginRadio->setEnabled( false );
1068 }
1069 else
1070 {
1071 //Not a point layer, so enable changes to fixed scale control
1072 mAtlasMarginRadio->setEnabled( true );
1073 }
1074}
1075
1076void QgsLayoutMapWidget::updateComposerExtentFromGui()
1077{
1078 if ( !mMapItem )
1079 {
1080 return;
1081 }
1082
1083 double xmin, ymin, xmax, ymax;
1084 bool conversionSuccess;
1085
1086 xmin = QLocale().toDouble( mXMinLineEdit->text(), &conversionSuccess );
1087 if ( !conversionSuccess )
1088 return;
1089 xmax = QLocale().toDouble( mXMaxLineEdit->text(), &conversionSuccess );
1090 if ( !conversionSuccess )
1091 return;
1092 ymin = QLocale().toDouble( mYMinLineEdit->text(), &conversionSuccess );
1093 if ( !conversionSuccess )
1094 return;
1095 ymax = QLocale().toDouble( mYMaxLineEdit->text(), &conversionSuccess );
1096 if ( !conversionSuccess )
1097 return;
1098
1099 const QgsRectangle newExtent( xmin, ymin, xmax, ymax );
1100
1101 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Extent" ) );
1102 mMapItem->setExtent( newExtent );
1103 mMapItem->layout()->undoStack()->endCommand();
1104}
1105
1106void QgsLayoutMapWidget::blockAllSignals( bool b )
1107{
1108 mScaleLineEdit->blockSignals( b );
1109 mXMinLineEdit->blockSignals( b );
1110 mXMaxLineEdit->blockSignals( b );
1111 mYMinLineEdit->blockSignals( b );
1112 mYMaxLineEdit->blockSignals( b );
1113 mDrawCanvasItemsCheckBox->blockSignals( b );
1114 mOverviewFrameMapComboBox->blockSignals( b );
1115 mOverviewFrameStyleButton->blockSignals( b );
1116 mOverviewBlendModeComboBox->blockSignals( b );
1117 mOverviewInvertCheckbox->blockSignals( b );
1118 mOverviewCenterCheckbox->blockSignals( b );
1119 mAtlasCheckBox->blockSignals( b );
1120 mAtlasMarginSpinBox->blockSignals( b );
1121 mAtlasFixedScaleRadio->blockSignals( b );
1122 mAtlasMarginRadio->blockSignals( b );
1123 mFollowVisibilityPresetCheckBox->blockSignals( b );
1124 mFollowVisibilityPresetCombo->blockSignals( b );
1125 mKeepLayerListCheckBox->blockSignals( b );
1126 mKeepLayerStylesCheckBox->blockSignals( b );
1127 mActionSetToCanvasExtent->blockSignals( b );
1128 mActionUpdatePreview->blockSignals( b );
1129 mTemporalCheckBox->blockSignals( b );
1130 mStartDateTime->blockSignals( b );
1131 mEndDateTime->blockSignals( b );
1132
1133 blockOverviewItemsSignals( b );
1134}
1135
1136void QgsLayoutMapWidget::updatePreview()
1137{
1138 if ( !mMapItem )
1139 {
1140 return;
1141 }
1142 mMapItem->refresh();
1143}
1144
1145void QgsLayoutMapWidget::mFollowVisibilityPresetCheckBox_stateChanged( int state )
1146{
1147 if ( !mMapItem )
1148 {
1149 return;
1150 }
1151
1152 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
1153 if ( state == Qt::Checked )
1154 {
1155 mMapItem->setFollowVisibilityPreset( true );
1156
1157 // mutually exclusive with keeping custom layer list
1158 mKeepLayerListCheckBox->setCheckState( Qt::Unchecked );
1159 mKeepLayerStylesCheckBox->setCheckState( Qt::Unchecked );
1160
1161 mMapItem->invalidateCache();
1162 }
1163 else
1164 {
1165 mMapItem->setFollowVisibilityPreset( false );
1166 }
1167 mMapItem->layout()->undoStack()->endCommand();
1168}
1169
1170void QgsLayoutMapWidget::mKeepLayerListCheckBox_stateChanged( int state )
1171{
1172 if ( !mMapItem )
1173 {
1174 return;
1175 }
1176
1177 // update map
1178 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Map Preset Changed" ) );
1179 storeCurrentLayerSet();
1180 mMapItem->setKeepLayerSet( state == Qt::Checked );
1181 if ( state == Qt::Unchecked )
1182 {
1183 mMapItem->setLayers( QList<QgsMapLayer *>() );
1184 }
1185 mMapItem->layout()->undoStack()->endCommand();
1186
1187 // update gui
1188 if ( state == Qt::Checked )
1189 {
1190 // mutually exclusive with following a preset
1191 mFollowVisibilityPresetCheckBox->setCheckState( Qt::Unchecked );
1192 }
1193 else
1194 {
1195 mKeepLayerStylesCheckBox->setChecked( Qt::Unchecked );
1196 mMapItem->invalidateCache();
1197 }
1198
1199 mKeepLayerStylesCheckBox->setEnabled( state == Qt::Checked );
1200}
1201
1202void QgsLayoutMapWidget::mKeepLayerStylesCheckBox_stateChanged( int state )
1203{
1204 if ( !mMapItem )
1205 {
1206 return;
1207 }
1208
1209 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Map Preset" ) );
1210 if ( state == Qt::Checked )
1211 {
1212 mMapItem->storeCurrentLayerStyles();
1213 mMapItem->setKeepLayerStyles( true );
1214 }
1215 else
1216 {
1217 mMapItem->setLayerStyleOverrides( QMap<QString, QString>() );
1218 mMapItem->setKeepLayerStyles( false );
1219 }
1220 mMapItem->layout()->undoStack()->endCommand();
1221}
1222
1223void QgsLayoutMapWidget::mDrawCanvasItemsCheckBox_stateChanged( int state )
1224{
1225 if ( !mMapItem )
1226 {
1227 return;
1228 }
1229
1230 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Toggle Map Item" ) );
1231 mMapItem->setDrawAnnotations( state == Qt::Checked );
1232 mMapItem->invalidateCache();
1233 mMapItem->layout()->undoStack()->endCommand();
1234}
1235
1236void QgsLayoutMapWidget::atlasLayerChanged( QgsVectorLayer *layer )
1237{
1238 if ( !layer || layer->wkbType() == Qgis::WkbType::NoGeometry )
1239 {
1240 //geometryless layer, disable atlas control
1241 mAtlasCheckBox->setChecked( false );
1242 mAtlasCheckBox->setEnabled( false );
1243 return;
1244 }
1245 else
1246 {
1247 mAtlasCheckBox->setEnabled( true );
1248 }
1249
1250 // enable or disable fixed scale control based on layer type
1251 if ( mAtlasCheckBox->isChecked() )
1252 toggleAtlasScalingOptionsByLayerType();
1253}
1254
1255bool QgsLayoutMapWidget::hasPredefinedScales() const
1256{
1257 // first look at project's scales
1258 const QVector<double> scales( QgsProject::instance()->viewSettings()->mapScales() );
1259 const bool hasProjectScales( QgsProject::instance()->viewSettings()->useProjectScales() );
1260 if ( !hasProjectScales || scales.isEmpty() )
1261 {
1262 // default to global map tool scales
1263 const QgsSettings settings;
1264 const QStringList scales = QgsSettingsRegistryCore::settingsMapScales->value();
1265 return !scales.isEmpty() && !scales[0].isEmpty();
1266 }
1267 return true;
1268}
1269
1270void QgsLayoutMapWidget::mAddGridPushButton_clicked()
1271{
1272 if ( !mMapItem )
1273 {
1274 return;
1275 }
1276
1277 const QString itemName = tr( "Grid %1" ).arg( mMapItem->grids()->size() + 1 );
1278 QgsLayoutItemMapGrid *grid = new QgsLayoutItemMapGrid( itemName, mMapItem );
1279 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Add Map Grid" ) );
1280 mMapItem->grids()->addGrid( grid );
1281 mMapItem->layout()->undoStack()->endCommand();
1282 mMapItem->updateBoundingRect();
1283 mMapItem->update();
1284
1285 addGridListItem( grid->id(), grid->name() );
1286 mGridListWidget->setCurrentRow( 0 );
1287 mGridListWidget_currentItemChanged( mGridListWidget->currentItem(), nullptr );
1288}
1289
1290void QgsLayoutMapWidget::mRemoveGridPushButton_clicked()
1291{
1292 QListWidgetItem *item = mGridListWidget->currentItem();
1293 if ( !item )
1294 {
1295 return;
1296 }
1297
1298 mMapItem->beginCommand( tr( "Remove Grid" ) );
1299 mMapItem->grids()->removeGrid( item->data( Qt::UserRole ).toString() );
1300 QListWidgetItem *delItem = mGridListWidget->takeItem( mGridListWidget->row( item ) );
1301 delete delItem;
1302 mMapItem->endCommand();
1303 mMapItem->updateBoundingRect();
1304 mMapItem->update();
1305}
1306
1307void QgsLayoutMapWidget::mCopyGridPushButton_clicked()
1308{
1309 QListWidgetItem *item = mGridListWidget->currentItem();
1310 if ( !item )
1311 {
1312 return;
1313 }
1314
1315 QgsLayoutItemMapGrid *sourceGrid = mMapItem->grids()->grid( item->data( Qt::UserRole ).toString() );
1316 if ( !sourceGrid )
1317 {
1318 return;
1319 }
1320 int i = 0;
1321 QString itemName = tr( "%1 - Copy" ).arg( sourceGrid->name() );
1322 QList<QgsLayoutItemMapGrid *> grids = mMapItem->grids()->asList();
1323 while ( true )
1324 {
1325 const auto it = std::find_if( grids.begin(), grids.end(), [&itemName]( const QgsLayoutItemMapGrid *grd ) { return grd->name() == itemName; } );
1326 if ( it != grids.end() )
1327 {
1328 i++;
1329 itemName = tr( "%1 - Copy %2" ).arg( sourceGrid->name() ).arg( i );
1330 continue;
1331 }
1332 break;
1333 }
1334 QgsLayoutItemMapGrid *grid = new QgsLayoutItemMapGrid( itemName, mMapItem );
1335 grid->copyProperties( sourceGrid );
1336
1337 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Duplicate Map Grid" ) );
1338 mMapItem->grids()->addGrid( grid );
1339 mMapItem->layout()->undoStack()->endCommand();
1340 mMapItem->updateBoundingRect();
1341 mMapItem->update();
1342
1343 addGridListItem( grid->id(), grid->name() );
1344 mGridListWidget->setCurrentRow( 0 );
1345 mGridListWidget_currentItemChanged( mGridListWidget->currentItem(), nullptr );
1346}
1347
1348void QgsLayoutMapWidget::mGridUpButton_clicked()
1349{
1350 QListWidgetItem *item = mGridListWidget->currentItem();
1351 if ( !item )
1352 {
1353 return;
1354 }
1355
1356 const int row = mGridListWidget->row( item );
1357 if ( row < 1 )
1358 {
1359 return;
1360 }
1361 mGridListWidget->takeItem( row );
1362 mGridListWidget->insertItem( row - 1, item );
1363 mGridListWidget->setCurrentItem( item );
1364 mMapItem->beginCommand( tr( "Move Grid Up" ) );
1365 mMapItem->grids()->moveGridUp( item->data( Qt::UserRole ).toString() );
1366 mMapItem->endCommand();
1367 mMapItem->update();
1368}
1369
1370void QgsLayoutMapWidget::mGridDownButton_clicked()
1371{
1372 QListWidgetItem *item = mGridListWidget->currentItem();
1373 if ( !item )
1374 {
1375 return;
1376 }
1377
1378 const int row = mGridListWidget->row( item );
1379 if ( mGridListWidget->count() <= row )
1380 {
1381 return;
1382 }
1383 mGridListWidget->takeItem( row );
1384 mGridListWidget->insertItem( row + 1, item );
1385 mGridListWidget->setCurrentItem( item );
1386 mMapItem->beginCommand( tr( "Move Grid Down" ) );
1387 mMapItem->grids()->moveGridDown( item->data( Qt::UserRole ).toString() );
1388 mMapItem->endCommand();
1389 mMapItem->update();
1390}
1391
1392QgsLayoutItemMapGrid *QgsLayoutMapWidget::currentGrid()
1393{
1394 if ( !mMapItem )
1395 {
1396 return nullptr;
1397 }
1398
1399 QListWidgetItem *item = mGridListWidget->currentItem();
1400 if ( !item )
1401 {
1402 return nullptr;
1403 }
1404 return mMapItem->grids()->grid( item->data( Qt::UserRole ).toString() );
1405}
1406
1407void QgsLayoutMapWidget::mGridListWidget_currentItemChanged( QListWidgetItem *current, QListWidgetItem * )
1408{
1409 mGridPropertiesButton->setEnabled( static_cast<bool>( current ) );
1410}
1411
1412void QgsLayoutMapWidget::mGridListWidget_itemChanged( QListWidgetItem *item )
1413{
1414 if ( !mMapItem )
1415 {
1416 return;
1417 }
1418
1419 QgsLayoutItemMapGrid *grid = mMapItem->grids()->grid( item->data( Qt::UserRole ).toString() );
1420 if ( !grid )
1421 {
1422 return;
1423 }
1424
1425 mMapItem->beginCommand( tr( "Rename Grid" ) );
1426 grid->setName( item->text() );
1427 mMapItem->endCommand();
1428}
1429
1430void QgsLayoutMapWidget::mGridPropertiesButton_clicked()
1431{
1432 if ( !mMapItem )
1433 {
1434 return;
1435 }
1436 QgsLayoutItemMapGrid *grid = currentGrid();
1437 if ( !grid )
1438 {
1439 return;
1440 }
1441
1442 QgsLayoutMapGridWidget *w = new QgsLayoutMapGridWidget( grid, mMapItem );
1443 w->setDesignerInterface( mInterface );
1444 openPanel( w );
1445}
1446
1447QListWidgetItem *QgsLayoutMapWidget::addGridListItem( const QString &id, const QString &name )
1448{
1449 QListWidgetItem *item = new QListWidgetItem( name, nullptr );
1450 item->setData( Qt::UserRole, id );
1451 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable );
1452 mGridListWidget->insertItem( 0, item );
1453 return item;
1454}
1455
1456void QgsLayoutMapWidget::loadGridEntries()
1457{
1458 //save selection
1459 QSet<QString> selectedIds;
1460 const QList<QListWidgetItem *> itemSelection = mGridListWidget->selectedItems();
1461 QList<QListWidgetItem *>::const_iterator sIt = itemSelection.constBegin();
1462 for ( ; sIt != itemSelection.constEnd(); ++sIt )
1463 {
1464 selectedIds.insert( ( *sIt )->data( Qt::UserRole ).toString() );
1465 }
1466
1467 mGridListWidget->clear();
1468 if ( !mMapItem )
1469 {
1470 return;
1471 }
1472 //load all composer grids into list widget
1473 const QList<QgsLayoutItemMapGrid *> grids = mMapItem->grids()->asList();
1474 QList<QgsLayoutItemMapGrid *>::const_iterator gridIt = grids.constBegin();
1475 for ( ; gridIt != grids.constEnd(); ++gridIt )
1476 {
1477 QListWidgetItem *item = addGridListItem( ( *gridIt )->id(), ( *gridIt )->name() );
1478 if ( selectedIds.contains( ( *gridIt )->id() ) )
1479 {
1480 item->setSelected( true );
1481 mGridListWidget->setCurrentItem( item );
1482 }
1483 }
1484
1485 if ( mGridListWidget->currentItem() )
1486 {
1487 mGridListWidget_currentItemChanged( mGridListWidget->currentItem(), nullptr );
1488 }
1489 else
1490 {
1491 mGridListWidget_currentItemChanged( nullptr, nullptr );
1492 }
1493}
1494
1495void QgsLayoutMapWidget::mAddOverviewPushButton_clicked()
1496{
1497 if ( !mMapItem )
1498 {
1499 return;
1500 }
1501
1502 const QString itemName = tr( "Overview %1" ).arg( mMapItem->overviews()->size() + 1 );
1503 QgsLayoutItemMapOverview *overview = new QgsLayoutItemMapOverview( itemName, mMapItem );
1504 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Add Map Overview" ) );
1505 mMapItem->overviews()->addOverview( overview );
1506 mMapItem->layout()->undoStack()->endCommand();
1507 mMapItem->invalidateCache();
1508
1509 addOverviewListItem( overview->id(), overview->name() );
1510
1511 mOverviewListWidget->setCurrentRow( 0 );
1512}
1513
1514void QgsLayoutMapWidget::mRemoveOverviewPushButton_clicked()
1515{
1516 QListWidgetItem *item = mOverviewListWidget->currentItem();
1517 if ( !item )
1518 {
1519 return;
1520 }
1521 mMapItem->beginCommand( tr( "Remove Map Overview" ) );
1522 mMapItem->overviews()->removeOverview( item->data( Qt::UserRole ).toString() );
1523 mMapItem->endCommand();
1524 QListWidgetItem *delItem = mOverviewListWidget->takeItem( mOverviewListWidget->row( item ) );
1525 delete delItem;
1526 mMapItem->invalidateCache();
1527}
1528
1529void QgsLayoutMapWidget::mOverviewUpButton_clicked()
1530{
1531 QListWidgetItem *item = mOverviewListWidget->currentItem();
1532 if ( !item )
1533 {
1534 return;
1535 }
1536
1537 const int row = mOverviewListWidget->row( item );
1538 if ( row < 1 )
1539 {
1540 return;
1541 }
1542 mOverviewListWidget->takeItem( row );
1543 mOverviewListWidget->insertItem( row - 1, item );
1544 mOverviewListWidget->setCurrentItem( item );
1545 mMapItem->beginCommand( tr( "Move Overview Up" ) );
1546 mMapItem->overviews()->moveOverviewUp( item->data( Qt::UserRole ).toString() );
1547 mMapItem->endCommand();
1548 mMapItem->invalidateCache();
1549}
1550
1551void QgsLayoutMapWidget::mOverviewDownButton_clicked()
1552{
1553 QListWidgetItem *item = mOverviewListWidget->currentItem();
1554 if ( !item )
1555 {
1556 return;
1557 }
1558
1559 const int row = mOverviewListWidget->row( item );
1560 if ( mOverviewListWidget->count() <= row )
1561 {
1562 return;
1563 }
1564 mOverviewListWidget->takeItem( row );
1565 mOverviewListWidget->insertItem( row + 1, item );
1566 mOverviewListWidget->setCurrentItem( item );
1567 mMapItem->beginCommand( tr( "Move Overview Down" ) );
1568 mMapItem->overviews()->moveOverviewDown( item->data( Qt::UserRole ).toString() );
1569 mMapItem->endCommand();
1570 mMapItem->invalidateCache();
1571}
1572
1573QgsLayoutItemMapOverview *QgsLayoutMapWidget::currentOverview()
1574{
1575 if ( !mMapItem )
1576 {
1577 return nullptr;
1578 }
1579
1580 QListWidgetItem *item = mOverviewListWidget->currentItem();
1581 if ( !item )
1582 {
1583 return nullptr;
1584 }
1585
1586 return mMapItem->overviews()->overview( item->data( Qt::UserRole ).toString() );
1587}
1588
1589void QgsLayoutMapWidget::mOverviewListWidget_currentItemChanged( QListWidgetItem *current, QListWidgetItem *previous )
1590{
1591 Q_UNUSED( previous )
1592 if ( !current )
1593 {
1594 mOverviewCheckBox->setEnabled( false );
1595 return;
1596 }
1597
1598 mOverviewCheckBox->setEnabled( true );
1599 setOverviewItems( mMapItem->overviews()->overview( current->data( Qt::UserRole ).toString() ) );
1600}
1601
1602void QgsLayoutMapWidget::mOverviewListWidget_itemChanged( QListWidgetItem *item )
1603{
1604 if ( !mMapItem )
1605 {
1606 return;
1607 }
1608
1609 QgsLayoutItemMapOverview *overview = mMapItem->overviews()->overview( item->data( Qt::UserRole ).toString() );
1610 if ( !overview )
1611 {
1612 return;
1613 }
1614
1615 mMapItem->beginCommand( u"Rename Overview"_s );
1616 overview->setName( item->text() );
1617 mMapItem->endCommand();
1618 if ( item->isSelected() )
1619 {
1620 //update checkbox title if item is current item
1621 mOverviewCheckBox->setTitle( tr( "Draw \"%1\" overview" ).arg( overview->name() ) );
1622 }
1623}
1624
1625void QgsLayoutMapWidget::setOverviewItemsEnabled( bool enabled )
1626{
1627 mOverviewFrameMapLabel->setEnabled( enabled );
1628 mOverviewFrameMapComboBox->setEnabled( enabled );
1629 mOverviewFrameStyleLabel->setEnabled( enabled );
1630 mOverviewFrameStyleButton->setEnabled( enabled );
1631 mOverviewBlendModeLabel->setEnabled( enabled );
1632 mOverviewBlendModeComboBox->setEnabled( enabled );
1633 mOverviewInvertCheckbox->setEnabled( enabled );
1634 mOverviewCenterCheckbox->setEnabled( enabled );
1635 mOverviewPositionComboBox->setEnabled( enabled );
1636
1637 const QgsLayoutItemMapItem::StackingPosition currentStackingPos = static_cast<QgsLayoutItemMapItem::StackingPosition>( mOverviewPositionComboBox->currentData().toInt() );
1638 mOverviewStackingLayerComboBox->setEnabled( enabled && ( currentStackingPos == QgsLayoutItemMapItem::StackAboveMapLayer || currentStackingPos == QgsLayoutItemMapItem::StackBelowMapLayer ) );
1639}
1640
1641void QgsLayoutMapWidget::blockOverviewItemsSignals( const bool block )
1642{
1643 mOverviewFrameMapComboBox->blockSignals( block );
1644 mOverviewFrameStyleButton->blockSignals( block );
1645 mOverviewBlendModeComboBox->blockSignals( block );
1646 mOverviewInvertCheckbox->blockSignals( block );
1647 mOverviewCenterCheckbox->blockSignals( block );
1648 mOverviewPositionComboBox->blockSignals( block );
1649 mOverviewStackingLayerComboBox->blockSignals( block );
1650}
1651
1652void QgsLayoutMapWidget::setOverviewItems( QgsLayoutItemMapOverview *overview )
1653{
1654 if ( !overview )
1655 {
1656 return;
1657 }
1658
1659 blockOverviewItemsSignals( true );
1660
1661 mOverviewCheckBox->setTitle( tr( "Draw \"%1\" overview" ).arg( overview->name() ) );
1662 mOverviewCheckBox->setChecked( overview->enabled() );
1663
1664 //overview frame
1665 mOverviewFrameMapComboBox->setItem( overview->linkedMap() );
1666
1667 //overview frame blending mode
1668 mOverviewBlendModeComboBox->setBlendMode( overview->blendMode() );
1669 //overview inverted
1670 mOverviewInvertCheckbox->setChecked( overview->inverted() );
1671 //center overview
1672 mOverviewCenterCheckbox->setChecked( overview->centered() );
1673
1674 mOverviewPositionComboBox->setCurrentIndex( mOverviewPositionComboBox->findData( overview->stackingPosition() ) );
1675 mOverviewStackingLayerComboBox->setLayer( overview->stackingLayer() );
1676 mOverviewStackingLayerComboBox->setEnabled(
1677 mOverviewPositionComboBox->isEnabled() && ( overview->stackingPosition() == QgsLayoutItemMapItem::StackAboveMapLayer || overview->stackingPosition() == QgsLayoutItemMapItem::StackBelowMapLayer )
1678 );
1679
1680 mOverviewFrameStyleButton->setSymbol( overview->frameSymbol()->clone() );
1681
1682 blockOverviewItemsSignals( false );
1683}
1684
1685void QgsLayoutMapWidget::storeCurrentLayerSet()
1686{
1687 if ( !mMapItem )
1688 return;
1689
1690 QList<QgsMapLayer *> layers = mMapCanvas->mapSettings().layers();
1691
1692 mMapItem->setLayers( layers );
1693
1694 if ( mMapItem->keepLayerStyles() )
1695 {
1696 // also store styles associated with the layers
1697 mMapItem->storeCurrentLayerStyles();
1698 }
1699}
1700
1701QList<QgsMapLayer *> QgsLayoutMapWidget::orderedPresetVisibleLayers( const QString &name ) const
1702{
1703 const QStringList visibleIds = QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayerIds( name );
1704
1705 // also make sure to order the layers according to map canvas order
1706 QList<QgsMapLayer *> lst;
1707 const auto constLayerOrder = QgsProject::instance()->layerTreeRoot()->layerOrder();
1708 for ( QgsMapLayer *layer : constLayerOrder )
1709 {
1710 if ( visibleIds.contains( layer->id() ) )
1711 {
1712 lst << layer;
1713 }
1714 }
1715 return lst;
1716}
1717
1718QListWidgetItem *QgsLayoutMapWidget::addOverviewListItem( const QString &id, const QString &name )
1719{
1720 QListWidgetItem *item = new QListWidgetItem( name, nullptr );
1721 item->setData( Qt::UserRole, id );
1722 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable );
1723 mOverviewListWidget->insertItem( 0, item );
1724 return item;
1725}
1726
1727void QgsLayoutMapWidget::loadOverviewEntries()
1728{
1729 //save selection
1730 QSet<QString> selectedIds;
1731 const QList<QListWidgetItem *> itemSelection = mOverviewListWidget->selectedItems();
1732 QList<QListWidgetItem *>::const_iterator sIt = itemSelection.constBegin();
1733 for ( ; sIt != itemSelection.constEnd(); ++sIt )
1734 {
1735 selectedIds.insert( ( *sIt )->data( Qt::UserRole ).toString() );
1736 }
1737
1738 mOverviewListWidget->clear();
1739 if ( !mMapItem )
1740 {
1741 return;
1742 }
1743
1744 mOverviewFrameMapComboBox->setExceptedItemList( QList<QgsLayoutItem *>() << mMapItem );
1745
1746 //load all composer overviews into list widget
1747 const QList<QgsLayoutItemMapOverview *> overviews = mMapItem->overviews()->asList();
1748 QList<QgsLayoutItemMapOverview *>::const_iterator overviewIt = overviews.constBegin();
1749 for ( ; overviewIt != overviews.constEnd(); ++overviewIt )
1750 {
1751 QListWidgetItem *item = addOverviewListItem( ( *overviewIt )->id(), ( *overviewIt )->name() );
1752 if ( selectedIds.contains( ( *overviewIt )->id() ) )
1753 {
1754 item->setSelected( true );
1755 mOverviewListWidget->setCurrentItem( item );
1756 }
1757 }
1758
1759 if ( mOverviewListWidget->currentItem() )
1760 {
1761 mOverviewListWidget_currentItemChanged( mOverviewListWidget->currentItem(), nullptr );
1762 }
1763 else
1764 {
1765 mOverviewListWidget_currentItemChanged( nullptr, nullptr );
1766 }
1767}
1768
1769void QgsLayoutMapWidget::mOverviewCheckBox_toggled( bool state )
1770{
1771 QgsLayoutItemMapOverview *overview = currentOverview();
1772 if ( !overview )
1773 {
1774 return;
1775 }
1776
1777 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Overview Display Toggled" ) );
1778 overview->setEnabled( state );
1779 mMapItem->invalidateCache();
1780 mMapItem->layout()->undoStack()->endCommand();
1781}
1782
1783void QgsLayoutMapWidget::overviewMapChanged( QgsLayoutItem *item )
1784{
1785 QgsLayoutItemMapOverview *overview = currentOverview();
1786 if ( !overview )
1787 {
1788 return;
1789 }
1790
1791 QgsLayoutItemMap *map = dynamic_cast<QgsLayoutItemMap *>( item );
1792 if ( !map )
1793 return;
1794
1795 mMapItem->beginCommand( tr( "Change Overview Map" ) );
1796 overview->setLinkedMap( map );
1797 mMapItem->invalidateCache();
1798 mMapItem->endCommand();
1799}
1800
1801void QgsLayoutMapWidget::mOverviewBlendModeComboBox_currentIndexChanged( int index )
1802{
1803 Q_UNUSED( index )
1804 QgsLayoutItemMapOverview *overview = currentOverview();
1805 if ( !overview )
1806 {
1807 return;
1808 }
1809
1810 mMapItem->beginCommand( tr( "Change Overview Blend Mode" ) );
1811 overview->setBlendMode( mOverviewBlendModeComboBox->blendMode() );
1812 mMapItem->invalidateCache();
1813 mMapItem->endCommand();
1814}
1815
1816void QgsLayoutMapWidget::mOverviewInvertCheckbox_toggled( bool state )
1817{
1818 QgsLayoutItemMapOverview *overview = currentOverview();
1819 if ( !overview )
1820 {
1821 return;
1822 }
1823
1824 mMapItem->beginCommand( tr( "Toggle Overview Inverted" ) );
1825 overview->setInverted( state );
1826 mMapItem->invalidateCache();
1827 mMapItem->endCommand();
1828}
1829
1830void QgsLayoutMapWidget::mOverviewCenterCheckbox_toggled( bool state )
1831{
1832 QgsLayoutItemMapOverview *overview = currentOverview();
1833 if ( !overview )
1834 {
1835 return;
1836 }
1837
1838 mMapItem->beginCommand( tr( "Toggle Overview Centered" ) );
1839 overview->setCentered( state );
1840 mMapItem->invalidateCache();
1841 mMapItem->endCommand();
1842}
1843
1844void QgsLayoutMapWidget::overviewStackingChanged( int )
1845{
1846 QgsLayoutItemMapOverview *overview = currentOverview();
1847 if ( !overview )
1848 {
1849 return;
1850 }
1851
1852 mMapItem->beginCommand( tr( "Change Overview Position" ) );
1853 overview->setStackingPosition( static_cast<QgsLayoutItemMapItem::StackingPosition>( mOverviewPositionComboBox->currentData().toInt() ) );
1854 mMapItem->invalidateCache();
1855 mMapItem->endCommand();
1856
1857 switch ( overview->stackingPosition() )
1858 {
1862 mOverviewStackingLayerComboBox->setEnabled( false );
1863 break;
1864
1867 mOverviewStackingLayerComboBox->setEnabled( true );
1868 break;
1869 }
1870}
1871
1872void QgsLayoutMapWidget::overviewStackingLayerChanged( QgsMapLayer *layer )
1873{
1874 QgsLayoutItemMapOverview *overview = currentOverview();
1875 if ( !overview )
1876 {
1877 return;
1878 }
1879
1880 mMapItem->beginCommand( tr( "Change Overview Position" ) );
1881 overview->setStackingLayer( layer );
1882 mMapItem->invalidateCache();
1883 mMapItem->endCommand();
1884}
1885
1886//
1887// QgsLayoutMapLabelingWidget
1888//
1889
1891 : QgsLayoutItemBaseWidget( nullptr, map )
1892 , mMapItem( map )
1893{
1894 setupUi( this );
1895 setPanelTitle( tr( "Label Settings" ) );
1896
1897 mLabelBoundarySpinBox->setClearValue( 0 );
1898 mLabelBoundarySpinBox->setShowClearButton( true );
1899
1900 mLabelBoundaryUnitsCombo->linkToWidget( mLabelBoundarySpinBox );
1901 mLabelBoundaryUnitsCombo->setConverter( &mMapItem->layout()->renderContext().measurementConverter() );
1902
1903 connect( mLabelBoundaryUnitsCombo, &QgsLayoutUnitsComboBox::unitChanged, this, &QgsLayoutMapLabelingWidget::labelMarginUnitsChanged );
1904 connect( mLabelBoundarySpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutMapLabelingWidget::labelMarginChanged );
1905 connect( mShowPartialLabelsCheckBox, &QCheckBox::toggled, this, &QgsLayoutMapLabelingWidget::showPartialsToggled );
1906 connect( mShowUnplacedCheckBox, &QCheckBox::toggled, this, &QgsLayoutMapLabelingWidget::showUnplacedToggled );
1907
1909
1910 setNewItem( map );
1911}
1912
1914{
1915 if ( item->type() != QgsLayoutItemRegistry::LayoutMap )
1916 return false;
1917
1918 if ( mMapItem )
1919 {
1920 disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapLabelingWidget::updateGuiElements );
1921 }
1922
1923 mMapItem = qobject_cast<QgsLayoutItemMap *>( item );
1924
1925 if ( mMapItem )
1926 {
1927 connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapLabelingWidget::updateGuiElements );
1928 }
1929
1930 updateGuiElements();
1931
1932 return true;
1933}
1934
1935void QgsLayoutMapLabelingWidget::updateGuiElements()
1936{
1937 whileBlocking( mLabelBoundarySpinBox )->setValue( mMapItem->labelMargin().length() );
1938 whileBlocking( mLabelBoundaryUnitsCombo )->setUnit( mMapItem->labelMargin().units() );
1939 whileBlocking( mShowPartialLabelsCheckBox )->setChecked( mMapItem->mapFlags() & QgsLayoutItemMap::ShowPartialLabels );
1940 whileBlocking( mShowUnplacedCheckBox )->setChecked( mMapItem->mapFlags() & QgsLayoutItemMap::ShowUnplacedLabels );
1941
1942 if ( mBlockingItemsListView->model() )
1943 {
1944 QAbstractItemModel *oldModel = mBlockingItemsListView->model();
1945 mBlockingItemsListView->setModel( nullptr );
1946 oldModel->deleteLater();
1947 }
1948
1949 QgsLayoutMapItemBlocksLabelsModel *model = new QgsLayoutMapItemBlocksLabelsModel( mMapItem, mMapItem->layout()->itemsModel(), mBlockingItemsListView );
1950 mBlockingItemsListView->setModel( model );
1951
1952 updateDataDefinedButton( mLabelMarginDDBtn );
1953}
1954
1955void QgsLayoutMapLabelingWidget::labelMarginChanged( double val )
1956{
1957 if ( !mMapItem )
1958 return;
1959
1960 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin );
1961 mMapItem->setLabelMargin( QgsLayoutMeasurement( val, mLabelBoundaryUnitsCombo->unit() ) );
1962 mMapItem->layout()->undoStack()->endCommand();
1963 mMapItem->invalidateCache();
1964}
1965
1966void QgsLayoutMapLabelingWidget::labelMarginUnitsChanged()
1967{
1968 if ( !mMapItem )
1969 return;
1970
1971 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Margin" ), QgsLayoutItem::UndoMapLabelMargin );
1972 mMapItem->setLabelMargin( QgsLayoutMeasurement( mLabelBoundarySpinBox->value(), mLabelBoundaryUnitsCombo->unit() ) );
1973 mMapItem->layout()->undoStack()->endCommand();
1974 mMapItem->invalidateCache();
1975}
1976
1977void QgsLayoutMapLabelingWidget::showPartialsToggled( bool checked )
1978{
1979 if ( !mMapItem )
1980 return;
1981
1982 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Visibility" ) );
1983 QgsLayoutItemMap::MapItemFlags flags = mMapItem->mapFlags();
1984 if ( checked )
1986 else
1987 flags &= ~QgsLayoutItemMap::ShowPartialLabels;
1988 mMapItem->setMapFlags( flags );
1989 mMapItem->layout()->undoStack()->endCommand();
1990 mMapItem->invalidateCache();
1991}
1992
1993void QgsLayoutMapLabelingWidget::showUnplacedToggled( bool checked )
1994{
1995 if ( !mMapItem )
1996 return;
1997
1998 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Visibility" ) );
1999 QgsLayoutItemMap::MapItemFlags flags = mMapItem->mapFlags();
2000 if ( checked )
2002 else
2003 flags &= ~QgsLayoutItemMap::ShowUnplacedLabels;
2004 mMapItem->setMapFlags( flags );
2005 mMapItem->layout()->undoStack()->endCommand();
2006 mMapItem->invalidateCache();
2007}
2008
2010 : QSortFilterProxyModel( parent )
2011 , mLayoutModel( layoutModel )
2012 , mMapItem( map )
2013{
2014 setSourceModel( layoutModel );
2015}
2016
2018{
2019 return 1;
2020}
2021
2022QVariant QgsLayoutMapItemBlocksLabelsModel::data( const QModelIndex &i, int role ) const
2023{
2024 if ( !i.isValid() )
2025 return QVariant();
2026
2027 if ( i.column() != 0 )
2028 return QVariant();
2029
2030 const QModelIndex sourceIndex = mapToSource( index( i.row(), QgsLayoutModel::ItemId, i.parent() ) );
2031
2032 QgsLayoutItem *item = mLayoutModel->itemFromIndex( mapToSource( i ) );
2033 if ( !item )
2034 {
2035 return QVariant();
2036 }
2037
2038 switch ( role )
2039 {
2040 case Qt::CheckStateRole:
2041 switch ( i.column() )
2042 {
2043 case 0:
2044 return mMapItem ? ( mMapItem->isLabelBlockingItem( item ) ? Qt::Checked : Qt::Unchecked ) : Qt::Unchecked;
2045 default:
2046 return QVariant();
2047 }
2048
2049 default:
2050 return mLayoutModel->data( sourceIndex, role );
2051 }
2052}
2053
2054bool QgsLayoutMapItemBlocksLabelsModel::setData( const QModelIndex &index, const QVariant &value, int role )
2055{
2056 Q_UNUSED( role )
2057
2058 if ( !index.isValid() )
2059 return false;
2060
2061 QgsLayoutItem *item = mLayoutModel->itemFromIndex( mapToSource( index ) );
2062 if ( !item || !mMapItem )
2063 {
2064 return false;
2065 }
2066
2067 mMapItem->layout()->undoStack()->beginCommand( mMapItem, tr( "Change Label Blocking Items" ) );
2068
2069 if ( value.toBool() )
2070 {
2071 mMapItem->addLabelBlockingItem( item );
2072 }
2073 else
2074 {
2075 mMapItem->removeLabelBlockingItem( item );
2076 }
2077 emit dataChanged( index, index, QVector<int>() << role );
2078
2079 mMapItem->layout()->undoStack()->endCommand();
2080 mMapItem->invalidateCache();
2081
2082 return true;
2083}
2084
2085Qt::ItemFlags QgsLayoutMapItemBlocksLabelsModel::flags( const QModelIndex &index ) const
2086{
2087 Qt::ItemFlags flags = QSortFilterProxyModel::flags( index );
2088
2089 if ( !index.isValid() )
2090 {
2091 return flags;
2092 }
2093
2094 switch ( index.column() )
2095 {
2096 case 0:
2097 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
2098 default:
2099 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
2100 }
2101}
2102
2103bool QgsLayoutMapItemBlocksLabelsModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
2104{
2105 QgsLayoutItem *item = mLayoutModel->itemFromIndex( mLayoutModel->index( source_row, 0, source_parent ) );
2106 if ( !item || item == mMapItem )
2107 {
2108 return false;
2109 }
2110
2111 return true;
2112}
2113
2114
2115//
2116// QgsLayoutMapClippingWidget
2117//
2118
2120 : QgsLayoutItemBaseWidget( nullptr, map )
2121 , mMapItem( map )
2122{
2123 setupUi( this );
2124 setPanelTitle( tr( "Clipping Settings" ) );
2125
2126 mLayerModel = new QgsMapLayerModel( this );
2127 mLayerModel->setItemsCheckable( true );
2128 mLayersTreeView->setModel( mLayerModel );
2129
2130 mAtlasClippingTypeComboBox->addItem( tr( "Clip During Render Only" ), static_cast<int>( QgsMapClippingRegion::FeatureClippingType::ClipPainterOnly ) );
2131 mAtlasClippingTypeComboBox->addItem( tr( "Clip Feature Before Render" ), static_cast<int>( QgsMapClippingRegion::FeatureClippingType::ClipToIntersection ) );
2132 mAtlasClippingTypeComboBox->addItem( tr( "Render Intersecting Features Unchanged" ), static_cast<int>( QgsMapClippingRegion::FeatureClippingType::NoClipping ) );
2133
2134 for ( int i = 0; i < mAtlasClippingTypeComboBox->count(); ++i )
2135 {
2136 mItemClippingTypeComboBox->addItem( mAtlasClippingTypeComboBox->itemText( i ), mAtlasClippingTypeComboBox->itemData( i ) );
2137 }
2138
2139 mClipItemComboBox->setCurrentLayout( map->layout() );
2140 mClipItemComboBox->setItemFlags( QgsLayoutItem::FlagProvidesClipPath );
2141
2142 connect( mRadioClipSelectedLayers, &QRadioButton::toggled, this, &QgsLayoutMapClippingWidget::toggleLayersSelectionGui );
2143 mLayersTreeView->setEnabled( false );
2144 mSelectAllButton->setEnabled( false );
2145 mDeselectAllButton->setEnabled( false );
2146 mInvertSelectionButton->setEnabled( false );
2147 mRadioClipAllLayers->setChecked( true );
2148
2149 connect( mClipToAtlasCheckBox, &QGroupBox::toggled, this, [this]( bool active ) {
2150 if ( !mBlockUpdates )
2151 {
2152 mMapItem->beginCommand( tr( "Toggle Atlas Clipping" ) );
2153 mMapItem->atlasClippingSettings()->setEnabled( active );
2154 mMapItem->endCommand();
2155 }
2156 } );
2157 connect( mForceLabelsInsideCheckBox, &QCheckBox::toggled, this, [this]( bool active ) {
2158 if ( !mBlockUpdates )
2159 {
2160 mMapItem->beginCommand( tr( "Change Atlas Clipping Label Behavior" ) );
2161 mMapItem->atlasClippingSettings()->setForceLabelsInsideFeature( active );
2162 mMapItem->endCommand();
2163 }
2164 } );
2165 connect( mClipFrameCheckBox, &QCheckBox::toggled, this, [this]( bool active ) {
2166 if ( !mBlockUpdates )
2167 {
2168 mMapItem->beginCommand( tr( "Change Atlas Clipping Label Behavior" ) );
2169 mMapItem->atlasClippingSettings()->setClipItemShape( active );
2170 mMapItem->endCommand();
2171 }
2172 } );
2173 connect( mAtlasClippingTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this] {
2174 if ( !mBlockUpdates )
2175 {
2176 mMapItem->beginCommand( tr( "Change Atlas Clipping Behavior" ) );
2177 mMapItem->atlasClippingSettings()->setFeatureClippingType( static_cast<QgsMapClippingRegion::FeatureClippingType>( mAtlasClippingTypeComboBox->currentData().toInt() ) );
2178 mMapItem->endCommand();
2179 }
2180 } );
2181
2182 connect( mRadioClipSelectedLayers, &QCheckBox::toggled, this, [this]( bool active ) {
2183 if ( active && !mBlockUpdates )
2184 {
2185 mBlockUpdates = true;
2186 mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) );
2187 mMapItem->atlasClippingSettings()->setRestrictToLayers( true );
2188 mMapItem->endCommand();
2189 mBlockUpdates = false;
2190 }
2191 } );
2192 // layers selection buttons
2193 connect( mSelectAllButton, &QPushButton::clicked, this, &QgsLayoutMapClippingWidget::selectAll );
2194 connect( mDeselectAllButton, &QPushButton::clicked, this, &QgsLayoutMapClippingWidget::deselectAll );
2195 connect( mInvertSelectionButton, &QPushButton::clicked, this, &QgsLayoutMapClippingWidget::invertSelection );
2196
2197 connect( mRadioClipAllLayers, &QCheckBox::toggled, this, [this]( bool active ) {
2198 if ( active && !mBlockUpdates )
2199 {
2200 mBlockUpdates = true;
2201 mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) );
2202 mMapItem->atlasClippingSettings()->setRestrictToLayers( false );
2203 mMapItem->endCommand();
2204 mBlockUpdates = false;
2205 }
2206 } );
2207 connect( mLayerModel, &QgsMapLayerModel::dataChanged, this, [this]( const QModelIndex &, const QModelIndex &, const QVector<int> &roles = QVector<int>() ) {
2208 if ( !roles.contains( Qt::CheckStateRole ) )
2209 return;
2210
2211 if ( !mBlockUpdates )
2212 {
2213 mBlockUpdates = true;
2214 mMapItem->beginCommand( tr( "Change Atlas Clipping Layers" ) );
2215 mMapItem->atlasClippingSettings()->setLayersToClip( mLayerModel->layersChecked() );
2216 mMapItem->endCommand();
2217 mBlockUpdates = false;
2218 }
2219 } );
2220
2221 // item clipping widgets
2222
2223 connect( mClipToItemCheckBox, &QGroupBox::toggled, this, [this]( bool active ) {
2224 if ( !mBlockUpdates )
2225 {
2226 mMapItem->beginCommand( tr( "Toggle Map Clipping" ) );
2227 mMapItem->itemClippingSettings()->setEnabled( active );
2228 mMapItem->endCommand();
2229 }
2230 } );
2231 connect( mItemClippingTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, [this] {
2232 if ( !mBlockUpdates )
2233 {
2234 mMapItem->beginCommand( tr( "Change Map Clipping Behavior" ) );
2235 mMapItem->itemClippingSettings()->setFeatureClippingType( static_cast<QgsMapClippingRegion::FeatureClippingType>( mItemClippingTypeComboBox->currentData().toInt() ) );
2236 mMapItem->endCommand();
2237 }
2238 } );
2239 connect( mForceLabelsInsideItemCheckBox, &QCheckBox::toggled, this, [this]( bool active ) {
2240 if ( !mBlockUpdates )
2241 {
2242 mMapItem->beginCommand( tr( "Change Map Clipping Label Behavior" ) );
2243 mMapItem->itemClippingSettings()->setForceLabelsInsideClipPath( active );
2244 mMapItem->endCommand();
2245 }
2246 } );
2247 connect( mClipItemComboBox, &QgsLayoutItemComboBox::itemChanged, this, [this]( QgsLayoutItem *item ) {
2248 if ( !mBlockUpdates )
2249 {
2250 mMapItem->beginCommand( tr( "Change Map Clipping Item" ) );
2251 mMapItem->itemClippingSettings()->setSourceItem( item );
2252 mMapItem->endCommand();
2253 }
2254 } );
2255
2256 setNewItem( map );
2257
2258 connect( &map->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutMapClippingWidget::atlasLayerChanged );
2259 if ( QgsLayoutAtlas *atlas = layoutAtlas() )
2260 {
2261 connect( atlas, &QgsLayoutAtlas::toggled, this, &QgsLayoutMapClippingWidget::atlasToggled );
2262 atlasToggled( atlas->enabled() );
2263 }
2264}
2265
2267{
2268 mClipToAtlasCheckBox->setTitle( tr( "Clip to %1 feature" ).arg( string ) );
2269 mClipToAtlasLabel->setText( tr( "<b>When enabled, map layers will be automatically clipped to the boundary of the current %1 feature.</b>" ).arg( string ) );
2270 mForceLabelsInsideCheckBox->setText( tr( "Force labels inside %1 feature" ).arg( string ) );
2271 mClipFrameCheckBox->setText( tr( "Clip item frame to match %1 feature" ).arg( string ) );
2272}
2273
2275{
2276 if ( item->type() != QgsLayoutItemRegistry::LayoutMap )
2277 return false;
2278
2279 if ( mMapItem )
2280 {
2281 disconnect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapClippingWidget::updateGuiElements );
2282 }
2283
2284 mMapItem = qobject_cast<QgsLayoutItemMap *>( item );
2285
2286 if ( mMapItem )
2287 {
2288 connect( mMapItem, &QgsLayoutObject::changed, this, &QgsLayoutMapClippingWidget::updateGuiElements );
2289 }
2290
2291 updateGuiElements();
2292
2293 return true;
2294}
2295
2296void QgsLayoutMapClippingWidget::updateGuiElements()
2297{
2298 if ( mBlockUpdates )
2299 return;
2300
2301 mBlockUpdates = true;
2302 mClipToAtlasCheckBox->setChecked( mMapItem->atlasClippingSettings()->enabled() );
2303 mAtlasClippingTypeComboBox->setCurrentIndex( mAtlasClippingTypeComboBox->findData( static_cast<int>( mMapItem->atlasClippingSettings()->featureClippingType() ) ) );
2304 mForceLabelsInsideCheckBox->setChecked( mMapItem->atlasClippingSettings()->forceLabelsInsideFeature() );
2305 mClipFrameCheckBox->setChecked( mMapItem->atlasClippingSettings()->clipItemShape() );
2306
2307 mRadioClipAllLayers->setChecked( !mMapItem->atlasClippingSettings()->restrictToLayers() );
2308 mRadioClipSelectedLayers->setChecked( mMapItem->atlasClippingSettings()->restrictToLayers() );
2309 mLayerModel->setLayersChecked( mMapItem->atlasClippingSettings()->layersToClip() );
2310
2311 mClipToItemCheckBox->setChecked( mMapItem->itemClippingSettings()->enabled() );
2312 mItemClippingTypeComboBox->setCurrentIndex( mItemClippingTypeComboBox->findData( static_cast<int>( mMapItem->itemClippingSettings()->featureClippingType() ) ) );
2313 mForceLabelsInsideItemCheckBox->setChecked( mMapItem->itemClippingSettings()->forceLabelsInsideClipPath() );
2314 mClipItemComboBox->setItem( mMapItem->itemClippingSettings()->sourceItem() );
2315
2316 mBlockUpdates = false;
2317}
2318
2319void QgsLayoutMapClippingWidget::atlasLayerChanged( QgsVectorLayer *layer )
2320{
2321 if ( !layer || layer->geometryType() != Qgis::GeometryType::Polygon )
2322 {
2323 //non-polygon layer, disable atlas control
2324 mClipToAtlasCheckBox->setChecked( false );
2325 mClipToAtlasCheckBox->setEnabled( false );
2326 return;
2327 }
2328 else
2329 {
2330 mClipToAtlasCheckBox->setEnabled( true );
2331 }
2332}
2333
2334void QgsLayoutMapClippingWidget::atlasToggled( bool atlasEnabled )
2335{
2336 if ( atlasEnabled && mMapItem && mMapItem->layout() && mMapItem->layout()->reportContext().layer() && mMapItem->layout()->reportContext().layer()->geometryType() == Qgis::GeometryType::Polygon )
2337 {
2338 mClipToAtlasCheckBox->setEnabled( true );
2339 }
2340 else
2341 {
2342 mClipToAtlasCheckBox->setEnabled( false );
2343 mClipToAtlasCheckBox->setChecked( false );
2344 }
2345}
2346
2347void QgsLayoutMapClippingWidget::invertSelection()
2348{
2349 for ( int i = 0; i < mLayerModel->rowCount( QModelIndex() ); i++ )
2350 {
2351 QModelIndex index = mLayerModel->index( i, 0 );
2352 Qt::CheckState currentState = Qt::CheckState( mLayerModel->data( index, Qt::CheckStateRole ).toInt() );
2353 Qt::CheckState newState = ( currentState == Qt::Checked ) ? Qt::Unchecked : Qt::Checked;
2354 mLayerModel->setData( index, newState, Qt::CheckStateRole );
2355 }
2356}
2357
2358void QgsLayoutMapClippingWidget::selectAll()
2359{
2360 for ( int i = 0; i < mLayerModel->rowCount( QModelIndex() ); i++ )
2361 {
2362 QModelIndex index = mLayerModel->index( i, 0 );
2363 mLayerModel->setData( index, Qt::Checked, Qt::CheckStateRole );
2364 }
2365}
2366
2367void QgsLayoutMapClippingWidget::deselectAll()
2368{
2369 for ( int i = 0; i < mLayerModel->rowCount( QModelIndex() ); i++ )
2370 {
2371 QModelIndex index = mLayerModel->index( i, 0 );
2372 mLayerModel->setData( index, Qt::Unchecked, Qt::CheckStateRole );
2373 }
2374}
2375
2376void QgsLayoutMapClippingWidget::toggleLayersSelectionGui( bool toggled )
2377{
2378 mLayersTreeView->setEnabled( toggled );
2379 mSelectAllButton->setEnabled( toggled );
2380 mDeselectAllButton->setEnabled( toggled );
2381 mInvertSelectionButton->setEnabled( toggled );
2382}
@ SpatialLayer
All spatial layers.
Definition qgis.h:241
@ Point
Points.
Definition qgis.h:380
@ Polygon
Polygons.
Definition qgis.h:382
@ Fill
Fill symbol.
Definition qgis.h:639
@ NoGeometry
No geometry.
Definition qgis.h:312
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsBookmarkManager * bookmarkManager()
Returns the application's bookmark manager, used for storing installation-wide bookmarks.
@ Extent
Bookmark extent as a QgsReferencedRectangle.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
@ MinimumValue
Reset value to minimum().
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
Used to render QgsLayout as an atlas, by iterating over the features from an associated vector layer.
void toggled(bool enabled)
Emitted when atlas is enabled or disabled.
A common interface for layout designer dialogs and widgets.
@ ToolMoveItemContent
Move item content tool.
void updateDataDefinedButton(QgsPropertyOverrideButton *button)
Updates a previously registered data defined button to reflect the item's current properties.
QgsVectorLayer * coverageLayer() const
Returns the current layout context coverage layer (if set).
void registerDataDefinedButton(QgsPropertyOverrideButton *button, QgsLayoutObject::DataDefinedProperty property)
Registers a data defined button, setting up its initial value, connections and description.
virtual void setDesignerInterface(QgsLayoutDesignerInterface *iface)
Sets the the layout designer interface in which the widget is being shown.
QgsLayoutAtlas * layoutAtlas() const
Returns the atlas for the layout (if available).
QgsLayoutItemBaseWidget(QWidget *parent SIP_TRANSFERTHIS, QgsLayoutObject *layoutObject)
Constructor for QgsLayoutItemBaseWidget, linked with the specified layoutObject.
void itemChanged(QgsLayoutItem *item)
Emitted whenever the currently selected item changes.
An individual grid which is drawn above the map content in a QgsLayoutItemMap.
void setEnabled(bool enabled) override
Controls whether the item will be drawn.
void copyProperties(const QgsLayoutItemMapGrid *other)
Copies properties from specified map grid.
QString id() const
Returns the unique id for the map item.
StackingPosition
Item stacking position, specifies where the in the map's stack the item should be rendered.
@ StackBelowMapLabels
Render above all map layers, but below map labels.
@ StackAboveMapLabels
Render above all map layers and labels.
@ StackBelowMapLayer
Render below a specific map layer (see stackingLayer()).
@ StackAboveMapLayer
Render above a specific map layer (see stackingLayer()).
@ StackBelowMap
Render below all map layers.
void setStackingLayer(QgsMapLayer *layer)
Sets the item's stacking layer, which specifies where the in the map's stack the item should be rende...
virtual void setEnabled(bool enabled)
Controls whether the item will be drawn.
void setStackingPosition(StackingPosition position)
Sets the item's stacking position, which specifies where the in the map's stack the item should be re...
StackingPosition stackingPosition() const
Returns the item's stacking position, which specifies where the in the map's stack the item should be...
QString name() const
Returns the friendly display name for the item.
void setName(const QString &name)
Sets the friendly display name for the item.
bool enabled() const
Returns whether the item will be drawn.
QgsMapLayer * stackingLayer() const
Returns the item's stacking layer, which specifies where the in the map's stack the item should be re...
An individual overview which is drawn above the map content in a QgsLayoutItemMap,...
QgsFillSymbol * frameSymbol()
Returns the fill symbol used for drawing the overview extent.
QPainter::CompositionMode blendMode() const
Retrieves the blending mode used for drawing the overview.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the overview.
bool centered() const
Returns whether the extent of the map is forced to center on the overview.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map to show the overview extent of.
void setFrameSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used for drawing the overview extent.
bool inverted() const
Returns whether the overview frame is inverted, ie, whether the shaded area is drawn outside the exte...
void setInverted(bool inverted)
Sets whether the overview frame is inverted, ie, whether the shaded area is drawn outside the extent ...
void setCentered(bool centered)
Sets whether the extent of the map is forced to center on the overview.
QgsLayoutItemMap * linkedMap()
Returns the source map to show the overview extent of.
Layout graphical items for displaying a map.
void extentChanged()
Emitted when the map's extent changes.
@ Predefined
A scale is chosen from the predefined scales.
@ Auto
The extent is adjusted so that each feature is fully visible.
@ Fixed
The current scale of the map is used for each feature of the atlas.
void mapRotationChanged(double newRotation)
Emitted when the map's rotation changes.
QFlags< MapItemFlag > MapItemFlags
@ ShowPartialLabels
Whether to draw labels which are partially outside of the map view.
@ ShowUnplacedLabels
Whether to render unplaced labels in the map view.
A widget for controlling the common properties of layout items (e.g.
void updateVariables()
Updates the variables widget, refreshing the values of variables shown.
Base class for graphical items within a QgsLayout.
@ UndoOverviewStyle
Map overview style.
@ UndoMapRotation
Map rotation changed.
@ UndoAtlasMargin
Map atlas margin changed.
@ UndoMapLabelMargin
Margin for labels from edge of map.
int type() const override
Returns a unique graphics item type identifier.
@ FlagProvidesClipPath
Item can act as a clipping path provider (see clipPath()).
QgsLayoutMapClippingWidget(QgsLayoutItemMap *map)
constructor
void setReportTypeString(const QString &string) override
Sets the string to use to describe the current report type (e.g.
bool setNewItem(QgsLayoutItem *item) final
Attempts to update the widget to show the properties for the specified item.
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
QgsLayoutMapItemBlocksLabelsModel(QgsLayoutItemMap *map, QgsLayoutModel *layoutModel, QObject *parent=nullptr)
constructor
Qt::ItemFlags flags(const QModelIndex &index) const override
bool setData(const QModelIndex &index, const QVariant &value, int role) override
QVariant data(const QModelIndex &index, int role) const override
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QgsLayoutMapLabelingWidget(QgsLayoutItemMap *map)
constructor
bool setNewItem(QgsLayoutItem *item) final
Attempts to update the widget to show the properties for the specified item.
void setMasterLayout(QgsMasterLayoutInterface *masterLayout) override
Sets the master layout associated with the item.
bool setNewItem(QgsLayoutItem *item) override
Attempts to update the widget to show the properties for the specified item.
void setReportTypeString(const QString &string) override
Sets the string to use to describe the current report type (e.g.
void populateDataDefinedButtons()
Initializes data defined buttons to current atlas coverage layer.
QgsLayoutMapWidget(QgsLayoutItemMap *item, QgsMapCanvas *mapCanvas)
constructor
void setDesignerInterface(QgsLayoutDesignerInterface *iface) override
Sets the the layout designer interface in which the widget is being shown.
A model for items attached to a layout.
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
@ MapZRangeUpper
Map frame Z-range lower value.
@ StartDateTime
Temporal range's start DateTime.
@ MapZRangeLower
Map frame Z-range lower value.
@ MapStylePreset
Layer and style map theme.
@ EndDateTime
Temporal range's end DateTime.
@ OriginalValue
Return the original, user set value.
void layerChanged(QgsVectorLayer *layer)
Emitted when the context's layer is changed.
void beginCommand(QgsLayoutUndoObjectInterface *object, const QString &commandText, int id=0)
Begins a new undo command for the specified object.
void unitChanged(Qgis::LayoutUnit unit)
Emitted when the unit is changed.
QgsLayoutReportContext & reportContext()
Returns a reference to the layout's report context, which stores information relating to the current ...
QgsLayoutUndoStack * undoStack()
Returns a pointer to the layout's undo stack, which manages undo/redo states for the layout and it's ...
Map canvas is a class for displaying all GIS data types on a canvas.
FeatureClippingType
Feature clipping behavior, which controls how features from vector layers will be clipped.
@ NoClipping
Only render features which intersect the clipping region, but do not clip these features to the regio...
@ ClipPainterOnly
Applying clipping on the painter only (i.e. feature boundaries will be unchanged, but may be invisibl...
@ ClipToIntersection
Clip the geometry of these features to the region prior to rendering (i.e. feature boundaries will fo...
void layerChanged(QgsMapLayer *layer)
Emitted whenever the currently selected layer changes.
A model for display of map layers in widgets.
void setLayersChecked(const QList< QgsMapLayer * > &layers)
Sets which layers are checked in the model.
@ LayerId
Stores the map layer ID.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString id
Definition qgsmaplayer.h:86
void mapThemesChanged()
Emitted when map themes within the collection are changed.
QStringList mapThemeVisibleLayerIds(const QString &name) const
Returns the list of layer IDs that are visible for the specified map theme.
Interface for master layout type objects, such as print layouts and reports.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsMapThemeCollection * mapThemeCollection
Definition qgsproject.h:122
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
QgsCoordinateReferenceSystem crs
Definition qgsproject.h:119
@ CrsNotSet
Not set (hidden by default).
void crsChanged(const QgsCoordinateReferenceSystem &crs)
Emitted when the selected CRS is changed.
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
QgsCoordinateReferenceSystem crs() const
Returns the associated coordinate reference system, or an invalid CRS if no reference system is set.
A QgsRectangle with associated coordinate reference system.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryStringList * settingsMapScales
void setLayer(QgsVectorLayer *layer)
Sets a layer to associate with the widget.
void changed()
Emitted when the symbol's settings are changed.
Represents a vector layer which manages a vector based dataset.
Q_INVOKABLE Qgis::WkbType wkbType() const final
Returns the WKBType or WKBUnknown in case of error.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:6880
QgsTemporalRange< QDateTime > QgsDateTimeRange
QgsRange which stores a range of date times.
Definition qgsrange.h:705