QGIS API Documentation 3.28.14-Firenze (exported)
Loading...
Searching...
No Matches
qgsvectortilelayerproperties.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectortilelayerproperties.cpp
3 --------------------------------------
4 Date : May 2020
5 Copyright : (C) 2020 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsfileutils.h"
19#include "qgshelp.h"
24#include "qgsvectortilelayer.h"
25#include "qgsgui.h"
26#include "qgsnative.h"
27#include "qgsapplication.h"
28#include "qgsmetadatawidget.h"
31#include <QFileDialog>
32#include <QMenu>
33#include <QMessageBox>
34#include <QDesktopServices>
35#include <QTextStream>
36
37QgsVectorTileLayerProperties::QgsVectorTileLayerProperties( QgsVectorTileLayer *lyr, QgsMapCanvas *canvas, QgsMessageBar *messageBar, QWidget *parent, Qt::WindowFlags flags )
38 : QgsOptionsDialogBase( QStringLiteral( "VectorTileLayerProperties" ), parent, flags )
39 , mLayer( lyr )
40 , mMapCanvas( canvas )
41{
42 setupUi( this );
43
44 mRendererWidget = new QgsVectorTileBasicRendererWidget( nullptr, canvas, messageBar, this );
45 mOptsPage_Style->layout()->addWidget( mRendererWidget );
46 mOptsPage_Style->layout()->setContentsMargins( 0, 0, 0, 0 );
47
48 mLabelingWidget = new QgsVectorTileBasicLabelingWidget( nullptr, canvas, messageBar, this );
49 mOptsPage_Labeling->layout()->addWidget( mLabelingWidget );
50 mOptsPage_Labeling->layout()->setContentsMargins( 0, 0, 0, 0 );
51
52 connect( this, &QDialog::accepted, this, &QgsVectorTileLayerProperties::apply );
53 connect( this, &QDialog::rejected, this, &QgsVectorTileLayerProperties::onCancel );
54 connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsVectorTileLayerProperties::apply );
55 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsVectorTileLayerProperties::showHelp );
56
57 // QgsOptionsDialogBase handles saving/restoring of geometry, splitter and current tab states,
58 // switching vertical tabs between icon/text to icon-only modes (splitter collapsed to left),
59 // and connecting QDialogButtonBox's accepted/rejected signals to dialog's accept/reject slots
60 initOptionsBase( false );
61
62#ifdef WITH_QTWEBKIT
63 // Setup information tab
64
65 const int horizontalDpi = logicalDpiX();
66
67 // Adjust zoom: text is ok, but HTML seems rather big at least on Linux/KDE
68 if ( horizontalDpi > 96 )
69 {
70 mMetadataViewer->setZoomFactor( mMetadataViewer->zoomFactor() * 0.9 );
71 }
72 mMetadataViewer->page()->setLinkDelegationPolicy( QWebPage::LinkDelegationPolicy::DelegateAllLinks );
73 connect( mMetadataViewer->page(), &QWebPage::linkClicked, this, &QgsVectorTileLayerProperties::urlClicked );
74 mMetadataViewer->page()->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
75 mMetadataViewer->page()->settings()->setAttribute( QWebSettings::JavascriptEnabled, true );
76
77#endif
78 mOptsPage_Information->setContentsMargins( 0, 0, 0, 0 );
79
80 QVBoxLayout *layout = new QVBoxLayout( metadataFrame );
81 layout->setContentsMargins( 0, 0, 0, 0 );
82 metadataFrame->setContentsMargins( 0, 0, 0, 0 );
83 mMetadataWidget = new QgsMetadataWidget( this, mLayer );
84 mMetadataWidget->layout()->setContentsMargins( 0, 0, 0, 0 );
85 mMetadataWidget->setMapCanvas( mMapCanvas );
86 layout->addWidget( mMetadataWidget );
87 metadataFrame->setLayout( layout );
88 mOptsPage_Metadata->setContentsMargins( 0, 0, 0, 0 );
89
90 // update based on lyr's current state
91 syncToLayer();
92
93 QgsSettings settings;
94 // if dialog hasn't been opened/closed yet, default to Styles tab, which is used most often
95 // this will be read by restoreOptionsBaseUi()
96 if ( !settings.contains( QStringLiteral( "/Windows/VectorTileLayerProperties/tab" ) ) )
97 {
98 settings.setValue( QStringLiteral( "Windows/VectorTileLayerProperties/tab" ),
99 mOptStackedWidget->indexOf( mOptsPage_Style ) );
100 }
101
102 QString title = tr( "Layer Properties - %1" ).arg( mLayer->name() );
103
104 mBtnStyle = new QPushButton( tr( "Style" ) );
105 QMenu *menuStyle = new QMenu( this );
106 menuStyle->addAction( tr( "Load Style…" ), this, &QgsVectorTileLayerProperties::loadStyle );
107 menuStyle->addAction( tr( "Save Style…" ), this, &QgsVectorTileLayerProperties::saveStyleAs );
108 menuStyle->addSeparator();
109 menuStyle->addAction( tr( "Save as Default" ), this, &QgsVectorTileLayerProperties::saveDefaultStyle );
110 menuStyle->addAction( tr( "Restore Default" ), this, &QgsVectorTileLayerProperties::loadDefaultStyle );
111 mBtnStyle->setMenu( menuStyle );
112 connect( menuStyle, &QMenu::aboutToShow, this, &QgsVectorTileLayerProperties::aboutToShowStyleMenu );
113
114 buttonBox->addButton( mBtnStyle, QDialogButtonBox::ResetRole );
115
116 mBtnMetadata = new QPushButton( tr( "Metadata" ), this );
117 QMenu *menuMetadata = new QMenu( this );
118 mActionLoadMetadata = menuMetadata->addAction( tr( "Load Metadata…" ), this, &QgsVectorTileLayerProperties::loadMetadata );
119 mActionSaveMetadataAs = menuMetadata->addAction( tr( "Save Metadata…" ), this, &QgsVectorTileLayerProperties::saveMetadataAs );
120 mBtnMetadata->setMenu( menuMetadata );
121 buttonBox->addButton( mBtnMetadata, QDialogButtonBox::ResetRole );
122
123 if ( !mLayer->styleManager()->isDefault( mLayer->styleManager()->currentStyle() ) )
124 title += QStringLiteral( " (%1)" ).arg( mLayer->styleManager()->currentStyle() );
125 restoreOptionsBaseUi( title );
126}
127
128void QgsVectorTileLayerProperties::apply()
129{
130 mRendererWidget->apply();
131 mLabelingWidget->apply();
132 mMetadataWidget->acceptMetadata();
133}
134
135void QgsVectorTileLayerProperties::onCancel()
136{
137 if ( mOldStyle.xmlData() != mLayer->styleManager()->style( mLayer->styleManager()->currentStyle() ).xmlData() )
138 {
139 // need to reset style to previous - style applied directly to the layer (not in apply())
140 QString myMessage;
141 QDomDocument doc( QStringLiteral( "qgis" ) );
142 int errorLine, errorColumn;
143 doc.setContent( mOldStyle.xmlData(), false, &myMessage, &errorLine, &errorColumn );
144 mLayer->importNamedStyle( doc, myMessage );
145 syncToLayer();
146 }
147}
148
149void QgsVectorTileLayerProperties::syncToLayer()
150{
151 /*
152 * Information Tab
153 */
155 // Inject the stylesheet
156 const QString html { mLayer->htmlMetadata().replace( QLatin1String( "<head>" ), QStringLiteral( R"raw(<head><style type="text/css">%1</style>)raw" ) ).arg( myStyle ) };
157 mMetadataViewer->setHtml( html );
158
159 /*
160 * Symbology Tab
161 */
162 mRendererWidget->setLayer( mLayer );
163
164 /*
165 * Labels Tab
166 */
167 mLabelingWidget->setLayer( mLayer );
168}
169
170
171void QgsVectorTileLayerProperties::loadDefaultStyle()
172{
173 bool defaultLoadedFlag = false;
174 const QString myMessage = mLayer->loadDefaultStyle( defaultLoadedFlag );
175 // reset if the default style was loaded OK only
176 if ( defaultLoadedFlag )
177 {
178 syncToLayer();
179 }
180 else
181 {
182 // otherwise let the user know what went wrong
183 QMessageBox::information( this,
184 tr( "Default Style" ),
185 myMessage
186 );
187 }
188}
189
190void QgsVectorTileLayerProperties::saveDefaultStyle()
191{
192 apply(); // make sure the style to save is up-to-date
193
194 // a flag passed by reference
195 bool defaultSavedFlag = false;
196 // TODO Once the deprecated `saveDefaultStyle()` method is gone, just
197 // remove the NOWARN_DEPRECATED tags
199 // after calling this the above flag will be set true for success
200 // or false if the save operation failed
201 const QString myMessage = mLayer->saveDefaultStyle( defaultSavedFlag );
203 if ( !defaultSavedFlag )
204 {
205 // let the user know what went wrong
206 QMessageBox::information( this,
207 tr( "Default Style" ),
208 myMessage
209 );
210 }
211}
212
213void QgsVectorTileLayerProperties::loadStyle()
214{
215 const QgsSettings settings; // where we keep last used filter in persistent state
216
217 QStringList ids, names, descriptions;
218
219 QgsMapLayerLoadStyleDialog dlg( mLayer );
220
221 if ( dlg.exec() )
222 {
223 mOldStyle = mLayer->styleManager()->style( mLayer->styleManager()->currentStyle() );
224 const QgsMapLayer::StyleCategories categories = dlg.styleCategories();
225 const QString type = dlg.fileExtension();
226 if ( type.compare( QLatin1String( "qml" ), Qt::CaseInsensitive ) == 0 )
227 {
228 QString message;
229 bool defaultLoadedFlag = false;
230 const QString filePath = dlg.filePath();
231 message = mLayer->loadNamedStyle( filePath, defaultLoadedFlag, categories );
232
233 //reset if the default style was loaded OK only
234 if ( defaultLoadedFlag )
235 {
236 syncToLayer();
237 }
238 else
239 {
240 //let the user know what went wrong
241 QMessageBox::warning( this, tr( "Load Style" ), message );
242 }
243 }
244 else if ( type.compare( QLatin1String( "json" ), Qt::CaseInsensitive ) == 0 )
245 {
246 QFile file( dlg.filePath() );
247 if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) )
248 {
249 QMessageBox::warning( this, tr( "Load Style" ), tr( "Could not read %1" ).arg( QDir::toNativeSeparators( dlg.filePath() ) ) );
250 }
251 else
252 {
253 QTextStream in( &file );
254#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
255 in.setCodec( "UTF-8" );
256#endif
257 const QString content = in.readAll();
258
260 // convert automatically from pixel sizes to millimeters, because pixel sizes
261 // are a VERY edge case in QGIS and don't play nice with hidpi map renders or print layouts
263 //assume source uses 96 dpi
264 context.setPixelSizeConversionFactor( 25.4 / 96.0 );
265
267
268 if ( converter.convert( content, &context ) != QgsMapBoxGlStyleConverter::Success )
269 {
270 QMessageBox::warning( this, tr( "Load Style" ), converter.errorMessage() );
271 }
272 else
273 {
274 if ( dlg.styleCategories().testFlag( QgsMapLayer::StyleCategory::Symbology ) )
275 {
276 mLayer->setRenderer( converter.renderer() );
277 }
278 if ( dlg.styleCategories().testFlag( QgsMapLayer::StyleCategory::Labeling ) )
279 {
280 mLayer->setLabeling( converter.labeling() );
281 }
282 syncToLayer();
283 }
284 }
285 }
286 activateWindow(); // set focus back to properties dialog
287 }
288}
289
290void QgsVectorTileLayerProperties::saveStyleAs()
291{
292 QgsSettings settings;
293 const QString lastUsedDir = settings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
294
295 QString outputFileName = QFileDialog::getSaveFileName(
296 this,
297 tr( "Save layer properties as style file" ),
298 lastUsedDir,
299 tr( "QGIS Layer Style File" ) + " (*.qml)" );
300 if ( outputFileName.isEmpty() )
301 return;
302
303 // ensure the user never omits the extension from the file name
304 outputFileName = QgsFileUtils::ensureFileNameHasExtension( outputFileName, QStringList() << QStringLiteral( "qml" ) );
305
306 apply(); // make sure the style to save is up-to-date
307
308 // then export style
309 bool defaultLoadedFlag = false;
310 QString message;
311 message = mLayer->saveNamedStyle( outputFileName, defaultLoadedFlag );
312
313 if ( defaultLoadedFlag )
314 {
315 settings.setValue( QStringLiteral( "style/lastStyleDir" ), QFileInfo( outputFileName ).absolutePath() );
316 }
317 else
318 QMessageBox::information( this, tr( "Save Style" ), message );
319}
320
321void QgsVectorTileLayerProperties::aboutToShowStyleMenu()
322{
323 QMenu *m = qobject_cast<QMenu *>( sender() );
324
326 // re-add style manager actions!
327 m->addSeparator();
329}
330
331void QgsVectorTileLayerProperties::loadMetadata()
332{
333 QgsSettings myQSettings; // where we keep last used filter in persistent state
334 const QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
335
336 const QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load layer metadata from metadata file" ), myLastUsedDir,
337 tr( "QGIS Layer Metadata File" ) + " (*.qmd)" );
338 if ( myFileName.isNull() )
339 {
340 return;
341 }
342
343 QString myMessage;
344 bool defaultLoadedFlag = false;
345 myMessage = mLayer->loadNamedMetadata( myFileName, defaultLoadedFlag );
346
347 //reset if the default style was loaded OK only
348 if ( defaultLoadedFlag )
349 {
350 mMetadataWidget->setMetadata( &mLayer->metadata() );
351 }
352 else
353 {
354 //let the user know what went wrong
355 QMessageBox::warning( this, tr( "Load Metadata" ), myMessage );
356 }
357
358 const QFileInfo myFI( myFileName );
359 const QString myPath = myFI.path();
360 myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
361
362 activateWindow(); // set focus back to properties dialog
363}
364
365void QgsVectorTileLayerProperties::saveMetadataAs()
366{
367 QgsSettings myQSettings; // where we keep last used filter in persistent state
368 const QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
369
370 QString myOutputFileName = QFileDialog::getSaveFileName( this, tr( "Save Layer Metadata as QMD" ),
371 myLastUsedDir, tr( "QMD File" ) + " (*.qmd)" );
372 if ( myOutputFileName.isNull() ) //dialog canceled
373 {
374 return;
375 }
376
377 mMetadataWidget->acceptMetadata();
378
379 //ensure the user never omitted the extension from the file name
380 if ( !myOutputFileName.endsWith( QgsMapLayer::extensionPropertyType( QgsMapLayer::Metadata ), Qt::CaseInsensitive ) )
381 {
383 }
384
385 bool defaultLoadedFlag = false;
386 const QString message = mLayer->saveNamedMetadata( myOutputFileName, defaultLoadedFlag );
387 if ( defaultLoadedFlag )
388 myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), QFileInfo( myOutputFileName ).absolutePath() );
389 else
390 QMessageBox::information( this, tr( "Save Metadata" ), message );
391}
392
393void QgsVectorTileLayerProperties::showHelp()
394{
395 const QVariant helpPage = mOptionsStackedWidget->currentWidget()->property( "helpPage" );
396
397 if ( helpPage.isValid() )
398 {
399 QgsHelp::openHelp( helpPage.toString() );
400 }
401 else
402 {
403 QgsHelp::openHelp( QStringLiteral( "working_with_vector_tiles/vector_tiles_properties.html" ) );
404 }
405}
406
407void QgsVectorTileLayerProperties::urlClicked( const QUrl &url )
408{
409 const QFileInfo file( url.toLocalFile() );
410 if ( file.exists() && !file.isDir() )
411 QgsGui::nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
412 else
413 QDesktopServices::openUrl( url );
414}
415
417{
419
420 const bool isMetadataPanel = ( index == mOptStackedWidget->indexOf( mOptsPage_Metadata ) );
421 mBtnStyle->setVisible( ! isMetadataPanel );
422 mBtnMetadata->setVisible( isMetadataPanel );
423}
static QString reportStyleSheet(QgsApplication::StyleSheetType styleSheetType=QgsApplication::StyleSheetType::Qt)
Returns a css style sheet for reports, the styleSheetType argument determines what type of stylesheet...
@ WebBrowser
StyleSheet for Qt GUI widgets (based on QLabel or QTextBrowser), supports basic CSS and Qt extensions...
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
static QgsNative * nativePlatformInterface()
Returns the global native interface, which offers abstraction to the host OS's underlying public inte...
Definition qgsgui.cpp:73
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:38
Context for a MapBox GL style conversion operation.
void setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
void setTargetUnit(QgsUnitTypes::RenderUnit targetUnit)
Sets the target unit type.
Handles conversion of MapBox GL styles to QGIS vector tile renderers and labeling settings.
QgsVectorTileRenderer * renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
QgsVectorTileLabeling * labeling() const
Returns a new instance of a vector tile labeling representing the converted style,...
Result convert(const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context=nullptr)
Converts a JSON style map, and returns the resultant status of the conversion.
@ Success
Conversion was successful.
QString errorMessage() const
Returns a descriptive error message if an error was encountered during the style conversion,...
Map canvas is a class for displaying all GIS data types on a canvas.
A reusable dialog which allows users to select stored layer styles and categories to load for a map l...
void removesExtraMenuSeparators(QMenu *m)
removes extra separators from the menu
void addStyleManagerActions(QMenu *m, QgsMapLayer *layer)
adds actions to the menu in accordance to the layer
static QgsMapLayerStyleGuiUtils * instance()
returns a singleton instance of this class
QString currentStyle() const
Returns name of the current style.
static bool isDefault(const QString &styleName)
Returns true if this is the default style.
QgsMapLayerStyle style(const QString &name) const
Returns data of a stored style - accessed by its unique name.
QString xmlData() const
Returns XML content of the style.
QString name
Definition qgsmaplayer.h:76
virtual bool importNamedStyle(QDomDocument &doc, QString &errorMsg, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Import the properties of this layer from a QDomDocument.
virtual QString loadNamedMetadata(const QString &uri, bool &resultFlag)
Retrieve a named metadata for this layer if one exists (either as a .qmd file on disk or as a record ...
QgsLayerMetadata metadata
Definition qgsmaplayer.h:78
virtual QString loadNamedStyle(const QString &uri, bool &resultFlag, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Retrieve a named style for this layer if one exists (either as a .qml file on disk or as a record in ...
static QString extensionPropertyType(PropertyType type)
Returns the extension of a Property.
QString saveNamedMetadata(const QString &uri, bool &resultFlag)
Save the current metadata of this layer as a named metadata (either as a .qmd file on disk or as a re...
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer's style manager.
virtual QString saveNamedStyle(const QString &uri, bool &resultFlag, StyleCategories categories=AllStyleCategories)
Save the properties of this layer as a named style (either as a .qml file on disk or as a record in t...
@ Symbology
Symbology.
@ Labeling
Labeling.
virtual QString saveDefaultStyle(bool &resultFlag, StyleCategories categories)
Save the properties of this layer as the default style (either as a .qml file on disk or as a record ...
A bar for displaying non-blocking messages to the user.
A wizard to edit metadata on a map layer.
void acceptMetadata()
Saves the metadata to the layer.
void setMapCanvas(QgsMapCanvas *canvas)
Sets a map canvas associated with the widget.
void setMetadata(const QgsAbstractMetadataBase *metadata)
Sets the metadata to display in the widget.
A base dialog for options and properties dialogs that offers vertical tabs.
virtual void optionsStackedWidget_CurrentChanged(int index)
Select relevant tab on current page change.
void restoreOptionsBaseUi(const QString &title=QString())
Restore the base ui.
QStackedWidget * mOptStackedWidget
void initOptionsBase(bool restoreUi=true, const QString &title=QString())
Set up the base ui connections for vertical tabs.
This class is a composition of two QSettings instances:
Definition qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
bool contains(const QString &key, QgsSettings::Section section=QgsSettings::NoSection) const
Returns true if there exists a setting called key; returns false otherwise.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
@ RenderMillimeters
Millimeters.
void optionsStackedWidget_CurrentChanged(int index) override
QgsVectorTileLayerProperties(QgsVectorTileLayer *lyr, QgsMapCanvas *canvas, QgsMessageBar *messageBar, QWidget *parent=nullptr, Qt::WindowFlags=QgsGuiUtils::ModalDialogFlags)
Constructor.
Implements a map layer that is dedicated to rendering of vector tiles.
void setRenderer(QgsVectorTileRenderer *r)
Sets renderer for the map layer.
void setLabeling(QgsVectorTileLabeling *labeling)
Sets labeling for the map layer.
QString loadDefaultStyle(bool &resultFlag) override
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
QString htmlMetadata() const override
Obtain a formatted HTML string containing assorted metadata for this layer.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:3061
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:3060