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