QGIS API Documentation 3.36.0-Maidenhead (09951dc0acf)
Loading...
Searching...
No Matches
qgswmsrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgswmsrenderer.cpp
3 -------------------
4 begin : May 14, 2006
5 copyright : (C) 2006 by Marco Hugentobler
6 (C) 2017 by David Marteau
7 email : marco dot hugentobler at karto dot baug dot ethz dot ch
8 david dot marteau at 3liz dot com
9 ***************************************************************************/
10
11/***************************************************************************
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 * *
18 ***************************************************************************/
19
20#include "qgsjsonutils.h"
21#include "qgswmsrenderer.h"
22#include "qgsfilterrestorer.h"
23#include "qgsexception.h"
24#include "qgsfields.h"
25#include "qgsfieldformatter.h"
27#include "qgsfeatureiterator.h"
28#include "qgsgeometry.h"
29#include "qgslayertree.h"
30#include "qgslayoututils.h"
31#include "qgslayertreemodel.h"
32#include "qgslegendrenderer.h"
33#include "qgsmaplayer.h"
34#include "qgsmaprenderertask.h"
36#include "qgsmaptopixel.h"
37#include "qgsproject.h"
39#include "qgsrasterlayer.h"
40#include "qgsrasterrenderer.h"
41#include "qgsscalecalculator.h"
45#include "qgsvectorlayer.h"
46#include "qgsvectortilelayer.h"
47#include "qgsmessagelog.h"
48#include "qgsrenderer.h"
49#include "qgsfeature.h"
50#include "qgsaccesscontrol.h"
51#include "qgsfeaturerequest.h"
55#include "qgsserverfeatureid.h"
57#include "qgswkbtypes.h"
59#include "qgsannotation.h"
61#include "qgspallabeling.h"
62#include "qgswmsrestorer.h"
63#include "qgsdxfexport.h"
64#include "qgssymbollayerutils.h"
65#include "qgsserverexception.h"
66#include "qgsserverapiutils.h"
68#include "qgsfeaturestore.h"
72#include "qgsdimensionfilter.h"
73
74#include <QImage>
75#include <QPainter>
76#include <QStringList>
77#include <QTemporaryFile>
78#include <QDir>
79#include <QUrl>
80#include <nlohmann/json.hpp>
81
82//for printing
83#include "qgslayoutatlas.h"
84#include "qgslayoutmanager.h"
85#include "qgslayoutexporter.h"
86#include "qgslayoutsize.h"
89#include "qgsprintlayout.h"
91#include "qgslayoutitempage.h"
92#include "qgslayoutitemlabel.h"
93#include "qgslayoutitemlegend.h"
94#include "qgslayoutitemmap.h"
96#include "qgslayoutframe.h"
97#include "qgslayoutitemhtml.h"
99#include "qgsogcutils.h"
100
101namespace QgsWms
102{
104 : mContext( context )
105 {
106 mProject = mContext.project();
107
108 mWmsParameters = mContext.parameters();
109 mWmsParameters.dump();
110 }
111
113 {
114 removeTemporaryLayers();
115 }
116
118 {
119 // get layers
120 std::unique_ptr<QgsWmsRestorer> restorer;
121 restorer.reset( new QgsWmsRestorer( mContext ) );
122
123 // configure layers
124 QList<QgsMapLayer *> layers = mContext.layersToRender();
125 configureLayers( layers );
126
127 // init renderer
128 QgsLegendSettings settings = legendSettings();
129 QgsLegendRenderer renderer( &model, settings );
130
131 // create context
132 QgsRenderContext context;
133 if ( !mWmsParameters.bbox().isEmpty() )
134 {
135 QgsMapSettings mapSettings;
137 std::unique_ptr<QImage> tmp( createImage( mContext.mapSize( false ) ) );
138 configureMapSettings( tmp.get(), mapSettings );
139 context = QgsRenderContext::fromMapSettings( mapSettings );
140 }
141 else
142 {
143 //use default scale settings
144 context = configureDefaultRenderContext();
145 }
146
147 // create image according to context
148 std::unique_ptr<QImage> image;
149 const qreal dpmm = mContext.dotsPerMm();
150 const QSizeF minSize = renderer.minimumSize( &context );
151 const QSize size( static_cast<int>( minSize.width() * dpmm ), static_cast<int>( minSize.height() * dpmm ) );
152 if ( !mContext.isValidWidthHeight( size.width(), size.height() ) )
153 {
154 throw QgsServerException( QStringLiteral( "Legend image is too large" ) );
155 }
156 image.reset( createImage( size ) );
157
158 // configure painter and addapt to the context
159 QPainter painter( image.get() );
160
161 context.setPainter( &painter );
162 if ( painter.renderHints() & QPainter::SmoothPixmapTransform )
164 if ( painter.renderHints() & QPainter::LosslessImageRendering )
166
168 QgsScopedRenderContextScaleToMm scaleContext( context );
169
170 // rendering
171 renderer.drawLegend( context );
172 painter.end();
173
174 return image.release();
175 }
176
178 {
179 // get layers
180 std::unique_ptr<QgsWmsRestorer> restorer;
181 restorer.reset( new QgsWmsRestorer( mContext ) );
182
183 // configure layers
184 QList<QgsMapLayer *> layers = mContext.layersToRender();
185 configureLayers( layers );
186
187 // create image
188 const QSize size( mWmsParameters.widthAsInt(), mWmsParameters.heightAsInt() );
189 //test if legend image is larger than max width/height
190 if ( !mContext.isValidWidthHeight( size.width(), size.height() ) )
191 {
192 throw QgsServerException( QStringLiteral( "Legend image is too large" ) );
193 }
194 std::unique_ptr<QImage> image( createImage( size ) );
195
196 // configure painter
197 const qreal dpmm = mContext.dotsPerMm();
198 std::unique_ptr<QPainter> painter;
199 painter.reset( new QPainter( image.get() ) );
200 painter->setRenderHint( QPainter::Antialiasing, true );
201 painter->scale( dpmm, dpmm );
202
203 // rendering
204 QgsLegendSettings settings = legendSettings();
206 ctx.painter = painter.get();
207
208 // create context
209 QgsRenderContext context = configureDefaultRenderContext( painter.get() );
210 ctx.context = &context;
211
212 nodeModel.drawSymbol( settings, &ctx, size.height() / dpmm );
213 painter->end();
214
215 return image.release();
216 }
217
218 QJsonObject QgsRenderer::getLegendGraphicsAsJson( QgsLayerTreeModel &model, const Qgis::LegendJsonRenderFlags &jsonRenderFlags )
219 {
220 // get layers
221 std::unique_ptr<QgsWmsRestorer> restorer;
222 restorer.reset( new QgsWmsRestorer( mContext ) );
223
224 // configure layers
225 QList<QgsMapLayer *> layers = mContext.layersToRender();
226 configureLayers( layers );
227
228 // init renderer
229 QgsLegendSettings settings = legendSettings();
230 settings.setJsonRenderFlags( jsonRenderFlags );
231 QgsLegendRenderer renderer( &model, settings );
232
233 // rendering
234 QgsRenderContext renderContext;
235 return renderer.exportLegendToJson( renderContext );
236 }
237
238 QJsonObject QgsRenderer::getLegendGraphicsAsJson( QgsLayerTreeModelLegendNode &legendNode, const Qgis::LegendJsonRenderFlags &jsonRenderFlags )
239 {
240 // get layers
241 std::unique_ptr<QgsWmsRestorer> restorer;
242 restorer.reset( new QgsWmsRestorer( mContext ) );
243
244 // configure layers
245 QList<QgsMapLayer *> layers = mContext.layersToRender();
246 configureLayers( layers );
247
248 // init renderer
249 QgsLegendSettings settings = legendSettings();
250 settings.setJsonRenderFlags( jsonRenderFlags );
251
252 // rendering
253 QgsRenderContext renderContext;
254 QJsonObject jsonSymbol { legendNode.exportSymbolToJson( settings, renderContext ) };
255
256 if ( jsonRenderFlags.testFlag( Qgis::LegendJsonRenderFlag::ShowRuleDetails ) )
257 {
259 if ( QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() ) )
260 {
261 if ( vLayer->renderer() )
262 {
263 const QString ruleKey { legendNode.data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString() };
264 bool ok;
265 const QString ruleExp { vLayer->renderer()->legendKeyToExpression( ruleKey, vLayer, ok ) };
266 if ( ok )
267 {
268 jsonSymbol[ QStringLiteral( "rule" ) ] = ruleExp;
269 }
270 }
271 }
272 }
273
274 return jsonSymbol;
275 }
276
277 void QgsRenderer::runHitTest( const QgsMapSettings &mapSettings, HitTest &hitTest ) const
278 {
280
281 for ( const QString &id : mapSettings.layerIds() )
282 {
283 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mProject->mapLayer( id ) );
284 if ( !vl || !vl->renderer() )
285 continue;
286
287 if ( vl->hasScaleBasedVisibility() && vl->isInScaleRange( mapSettings.scale() ) )
288 {
289 hitTest[vl] = SymbolSet(); // no symbols -> will not be shown
290 continue;
291 }
292
293 QgsCoordinateTransform tr = mapSettings.layerTransform( vl );
294 context.setCoordinateTransform( tr );
296
297 SymbolSet &usedSymbols = hitTest[vl];
298 runHitTestLayer( vl, usedSymbols, context );
299 }
300 }
301
302 void QgsRenderer::runHitTestLayer( QgsVectorLayer *vl, SymbolSet &usedSymbols, QgsRenderContext &context ) const
303 {
304 std::unique_ptr< QgsFeatureRenderer > r( vl->renderer()->clone() );
305 bool moreSymbolsPerFeature = r->capabilities() & QgsFeatureRenderer::MoreSymbolsPerFeature;
306 r->startRender( context, vl->fields() );
307 QgsFeature f;
308 QgsFeatureRequest request( context.extent() );
310 QgsFeatureIterator fi = vl->getFeatures( request );
311 while ( fi.nextFeature( f ) )
312 {
313 context.expressionContext().setFeature( f );
314 if ( moreSymbolsPerFeature )
315 {
316 for ( QgsSymbol *s : r->originalSymbolsForFeature( f, context ) )
317 usedSymbols.insert( QgsSymbolLayerUtils::symbolProperties( s ) );
318 }
319 else
320 usedSymbols.insert( QgsSymbolLayerUtils::symbolProperties( r->originalSymbolForFeature( f, context ) ) );
321 }
322 r->stopRender( context );
323 }
324
326 {
327 // check size
328 if ( ! mContext.isValidWidthHeight() )
329 {
331 QStringLiteral( "The requested map size is too large" ) );
332 }
333
334 // init layer restorer before doing anything
335 std::unique_ptr<QgsWmsRestorer> restorer;
336 restorer.reset( new QgsWmsRestorer( mContext ) );
337
338 // configure layers
339 QgsMapSettings mapSettings;
341 QList<QgsMapLayer *> layers = mContext.layersToRender();
342 configureLayers( layers, &mapSettings );
343
344 // create the output image and the painter
345 std::unique_ptr<QPainter> painter;
346 std::unique_ptr<QImage> image( createImage( mContext.mapSize() ) );
347
348 // configure map settings (background, DPI, ...)
349 configureMapSettings( image.get(), mapSettings );
350
351 // add layers to map settings
352 mapSettings.setLayers( layers );
353
354 // run hit tests
356 runHitTest( mapSettings, symbols );
357
358 return symbols;
359 }
360
362 {
363 // init layer restorer before doing anything
364 std::unique_ptr<QgsWmsRestorer> restorer;
365 restorer.reset( new QgsWmsRestorer( mContext ) );
366
367 // GetPrint request needs a template parameter
368 const QString templateName = mWmsParameters.composerTemplate();
369 if ( templateName.isEmpty() )
370 {
373 }
374 else if ( QgsServerProjectUtils::wmsRestrictedComposers( *mProject ).contains( templateName ) )
375 {
377 mWmsParameters[QgsWmsParameter::TEMPLATE ] );
378 }
379
380 // check template
381 const QgsLayoutManager *lManager = mProject->layoutManager();
382 QgsPrintLayout *sourceLayout( dynamic_cast<QgsPrintLayout *>( lManager->layoutByName( templateName ) ) );
383 if ( !sourceLayout )
384 {
386 mWmsParameters[QgsWmsParameter::TEMPLATE ] );
387 }
388
389 // Check that layout has at least one page
390 if ( sourceLayout->pageCollection()->pageCount() < 1 )
391 {
393 QStringLiteral( "The template has no pages" ) );
394 }
395
396 std::unique_ptr<QgsPrintLayout> layout( sourceLayout->clone() );
397
398 //atlas print?
399 QgsLayoutAtlas *atlas = nullptr;
400 QStringList atlasPk = mWmsParameters.atlasPk();
401 if ( !atlasPk.isEmpty() ) //atlas print requested?
402 {
403 atlas = layout->atlas();
404 if ( !atlas || !atlas->enabled() )
405 {
406 //error
408 QStringLiteral( "The template has no atlas enabled" ) );
409 }
410
411 QgsVectorLayer *cLayer = atlas->coverageLayer();
412 if ( !cLayer )
413 {
415 QStringLiteral( "The atlas has no coverage layer" ) );
416 }
417
418 int maxAtlasFeatures = QgsServerProjectUtils::wmsMaxAtlasFeatures( *mProject );
419 if ( atlasPk.size() == 1 && atlasPk.at( 0 ) == QLatin1String( "*" ) )
420 {
421 atlas->setFilterFeatures( false );
422 atlas->updateFeatures();
423 if ( atlas->count() > maxAtlasFeatures )
424 {
426 QString( "The project configuration allows printing maximum %1 atlas features at a time" ).arg( maxAtlasFeatures ) );
427 }
428 }
429 else
430 {
431 const QgsAttributeList pkIndexes = cLayer->primaryKeyAttributes();
432 if ( pkIndexes.size() == 0 )
433 {
434 QgsDebugMsgLevel( QStringLiteral( "Atlas print: layer %1 has no primary key attributes" ).arg( cLayer->name() ), 2 );
435 }
436
437 // Handles the pk-less case
438 const int pkIndexesSize {std::max< int >( pkIndexes.size(), 1 )};
439
440 QStringList pkAttributeNames;
441 for ( int pkIndex : std::as_const( pkIndexes ) )
442 {
443 pkAttributeNames.append( cLayer->fields().at( pkIndex ).name() );
444 }
445
446 const int nAtlasFeatures = atlasPk.size() / pkIndexesSize;
447 if ( nAtlasFeatures * pkIndexesSize != atlasPk.size() ) //Test if atlasPk.size() is a multiple of pkIndexesSize. Bail out if not
448 {
450 QStringLiteral( "Wrong number of ATLAS_PK parameters" ) );
451 }
452
453 //number of atlas features might be restricted
454 if ( nAtlasFeatures > maxAtlasFeatures )
455 {
457 QString( "%1 atlas features have been requested, but the project configuration only allows printing %2 atlas features at a time" )
458 .arg( nAtlasFeatures ).arg( maxAtlasFeatures ) );
459 }
460
461 QString filterString;
462 int currentAtlasPk = 0;
463
464 for ( int i = 0; i < nAtlasFeatures; ++i )
465 {
466 if ( i > 0 )
467 {
468 filterString.append( " OR " );
469 }
470
471 filterString.append( "( " );
472
473 // If the layer has no PK attributes, assume FID
474 if ( pkAttributeNames.isEmpty() )
475 {
476 filterString.append( QStringLiteral( "$id = %1" ).arg( atlasPk.at( currentAtlasPk ) ) );
477 ++currentAtlasPk;
478 }
479 else
480 {
481 for ( int j = 0; j < pkIndexes.size(); ++j )
482 {
483 if ( j > 0 )
484 {
485 filterString.append( " AND " );
486 }
487 filterString.append( QgsExpression::createFieldEqualityExpression( pkAttributeNames.at( j ), atlasPk.at( currentAtlasPk ) ) );
488 ++currentAtlasPk;
489 }
490 }
491
492 filterString.append( " )" );
493
494 }
495
496 atlas->setFilterFeatures( true );
497
498 QString errorString;
499 atlas->setFilterExpression( filterString, errorString );
500
501 if ( !errorString.isEmpty() )
502 {
503 throw QgsException( QStringLiteral( "An error occurred during the Atlas print: %1" ).arg( errorString ) );
504 }
505
506 }
507 }
508
509 // configure layers
510 QgsMapSettings mapSettings;
512 QList<QgsMapLayer *> layers = mContext.layersToRender();
513 configureLayers( layers, &mapSettings );
514
515 // configure map settings (background, DPI, ...)
516 std::unique_ptr<QImage> image( new QImage() );
517 configureMapSettings( image.get(), mapSettings );
518
519 // add layers to map settings
520 mapSettings.setLayers( layers );
521
522 // configure layout
523 configurePrintLayout( layout.get(), mapSettings, atlas );
524
525 QgsLayoutRenderContext &layoutRendererContext = layout->renderContext();
527 const QList<QgsMapLayer *> lyrs = mapSettings.layers();
528
529#ifdef HAVE_SERVER_PYTHON_PLUGINS
530 mContext.accessControl()->resolveFilterFeatures( lyrs );
531 filters.addProvider( mContext.accessControl() );
532#endif
533
534 QHash<const QgsVectorLayer *, QStringList> fltrs;
535 for ( QgsMapLayer *l : lyrs )
536 {
537 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( l ) )
538 {
539 fltrs.insert( vl, dimensionFilter( vl ) );
540 }
541 }
542
543 QgsDimensionFilter dimFilter( fltrs );
544 filters.addProvider( &dimFilter );
545 layoutRendererContext.setFeatureFilterProvider( &filters );
546
547 // Get the temporary output file
548 const QgsWmsParameters::Format format = mWmsParameters.format();
549 const QString extension = QgsWmsParameters::formatAsString( format ).toLower();
550
551 QTemporaryFile tempOutputFile( QDir::tempPath() + '/' + QStringLiteral( "XXXXXX.%1" ).arg( extension ) );
552 if ( !tempOutputFile.open() )
553 {
554 throw QgsException( QStringLiteral( "Could not open temporary file for the GetPrint request." ) );
555
556 }
557
558 QString exportError;
559 if ( format == QgsWmsParameters::SVG )
560 {
561 // Settings for the layout exporter
563 if ( !mWmsParameters.dpi().isEmpty() )
564 {
565 bool ok;
566 double dpi( mWmsParameters.dpi().toDouble( &ok ) );
567 if ( ok )
568 exportSettings.dpi = dpi;
569 }
570 // Set scales
571 exportSettings.predefinedMapScales = QgsLayoutUtils::predefinedScales( layout.get( ) );
572 // Draw selections
574 if ( atlas )
575 {
576 //export first page of atlas
577 atlas->beginRender();
578 if ( atlas->next() )
579 {
580 QgsLayoutExporter atlasSvgExport( atlas->layout() );
581 atlasSvgExport.exportToSvg( tempOutputFile.fileName(), exportSettings );
582 }
583 }
584 else
585 {
586 QgsLayoutExporter exporter( layout.get() );
587 exporter.exportToSvg( tempOutputFile.fileName(), exportSettings );
588 }
589 }
590 else if ( format == QgsWmsParameters::PNG || format == QgsWmsParameters::JPG )
591 {
592 // Settings for the layout exporter
594
595 // Get the dpi from input or use the default
596 double dpi( layout->renderContext().dpi( ) );
597 if ( !mWmsParameters.dpi().isEmpty() )
598 {
599 bool ok;
600 double _dpi = mWmsParameters.dpi().toDouble( &ok );
601 if ( ok )
602 dpi = _dpi;
603 }
604 exportSettings.dpi = dpi;
605 // Set scales
606 exportSettings.predefinedMapScales = QgsLayoutUtils::predefinedScales( layout.get( ) );
607 // Draw selections
609 // Destination image size in px
610 QgsLayoutSize layoutSize( layout->pageCollection()->page( 0 )->sizeWithUnits() );
611
612 QgsLayoutMeasurement width( layout->convertFromLayoutUnits( layoutSize.width(), Qgis::LayoutUnit::Millimeters ) );
613 QgsLayoutMeasurement height( layout->convertFromLayoutUnits( layoutSize.height(), Qgis::LayoutUnit::Millimeters ) );
614
615 const QSize imageSize = QSize( static_cast<int>( width.length() * dpi / 25.4 ), static_cast<int>( height.length() * dpi / 25.4 ) );
616
617 const QString paramWidth = mWmsParameters.width();
618 const QString paramHeight = mWmsParameters.height();
619
620 // Prefer width and height from the http request
621 // Fallback to predefined values from layout
622 // Preserve aspect ratio if only one value is specified
623 if ( !paramWidth.isEmpty() && !paramHeight.isEmpty() )
624 {
625 exportSettings.imageSize = QSize( paramWidth.toInt(), paramHeight.toInt() );
626 }
627 else if ( !paramWidth.isEmpty() && paramHeight.isEmpty() )
628 {
629 exportSettings.imageSize = QSize( paramWidth.toInt(), static_cast<double>( paramWidth.toInt() ) / imageSize.width() * imageSize.height() );
630 }
631 else if ( paramWidth.isEmpty() && !paramHeight.isEmpty() )
632 {
633 exportSettings.imageSize = QSize( static_cast<double>( paramHeight.toInt() ) / imageSize.height() * imageSize.width(), paramHeight.toInt() );
634 }
635 else
636 {
637 exportSettings.imageSize = imageSize;
638 }
639
640 // Export first page only (unless it's a pdf, see below)
641 exportSettings.pages.append( 0 );
642 if ( atlas )
643 {
644 //only can give back one page in server rendering
645 atlas->beginRender();
646 if ( atlas->next() )
647 {
648 QgsLayoutExporter atlasPngExport( atlas->layout() );
649 atlasPngExport.exportToImage( tempOutputFile.fileName(), exportSettings );
650 }
651 else
652 {
653 throw QgsServiceException( QStringLiteral( "Bad request" ), QStringLiteral( "Atlas error: empty atlas." ), QString(), 400 );
654 }
655 }
656 else
657 {
658 QgsLayoutExporter exporter( layout.get() );
659 exporter.exportToImage( tempOutputFile.fileName(), exportSettings );
660 }
661 }
662 else if ( format == QgsWmsParameters::PDF )
663 {
664 // Settings for the layout exporter
666 // TODO: handle size from input ?
667 if ( !mWmsParameters.dpi().isEmpty() )
668 {
669 bool ok;
670 double dpi( mWmsParameters.dpi().toDouble( &ok ) );
671 if ( ok )
672 exportSettings.dpi = dpi;
673 }
674 // Draw selections
676 // Print as raster
677 exportSettings.rasterizeWholeImage = layout->customProperty( QStringLiteral( "rasterize" ), false ).toBool();
678 // Set scales. 1. Prio: request, 2. Prio: predefined mapscales in layout
679 QVector<qreal> requestMapScales = mWmsParameters.pdfPredefinedMapScales();
680 if ( requestMapScales.size() > 0 )
681 {
682 exportSettings.predefinedMapScales = requestMapScales;
683 }
684 else
685 {
686 exportSettings.predefinedMapScales = QgsLayoutUtils::predefinedScales( layout.get( ) );
687 }
688 // Export themes
689 QStringList exportThemes = mWmsParameters.pdfExportMapThemes();
690 if ( exportThemes.size() > 0 )
691 {
692 exportSettings.exportThemes = exportThemes;
693 }
694 exportSettings.writeGeoPdf = mWmsParameters.writeGeoPdf();
695 exportSettings.textRenderFormat = mWmsParameters.pdfTextRenderFormat();
696 exportSettings.forceVectorOutput = mWmsParameters.pdfForceVectorOutput();
697 exportSettings.appendGeoreference = mWmsParameters.pdfAppendGeoreference();
698 exportSettings.simplifyGeometries = mWmsParameters.pdfSimplifyGeometries();
701 if ( mWmsParameters.pdfLosslessImageCompression() )
702 {
704 }
705 if ( mWmsParameters.pdfDisableTiledRasterRendering() )
706 {
708 }
709
710 // Export all pages
711 if ( atlas )
712 {
713 QgsLayoutExporter::exportToPdf( atlas, tempOutputFile.fileName(), exportSettings, exportError );
714 }
715 else
716 {
717 QgsLayoutExporter exporter( layout.get() );
718 exporter.exportToPdf( tempOutputFile.fileName(), exportSettings );
719 }
720 }
721 else //unknown format
722 {
724 mWmsParameters[QgsWmsParameter::FORMAT] );
725 }
726
727 if ( atlas )
728 {
729 handlePrintErrors( atlas->layout() );
730 }
731 else
732 {
733 handlePrintErrors( layout.get() );
734 }
735
736 return tempOutputFile.readAll();
737 }
738
739 bool QgsRenderer::configurePrintLayout( QgsPrintLayout *c, const QgsMapSettings &mapSettings, QgsLayoutAtlas *atlas )
740 {
741
742 c->renderContext().setSelectionColor( mapSettings.selectionColor() );
743 // Maps are configured first
744 QList<QgsLayoutItemMap *> maps;
745 c->layoutItems<QgsLayoutItemMap>( maps );
746 // Layout maps now use a string UUID as "id", let's assume that the first map
747 // has id 0 and so on ...
748 int mapId = 0;
749
750 for ( const auto &map : std::as_const( maps ) )
751 {
752 QgsWmsParametersComposerMap cMapParams = mWmsParameters.composerMapParameters( mapId );
753 mapId++;
754
755 // If there are no configured layers, we take layers from unprefixed LAYER(S) if any
756 if ( cMapParams.mLayers.isEmpty() )
757 {
758 cMapParams.mLayers = mWmsParameters.composerMapParameters( -1 ).mLayers;
759 }
760
761 if ( !atlas || !map->atlasDriven() ) //No need to extent, scale, rotation set with atlas feature
762 {
763 //map extent is mandatory
764 if ( !cMapParams.mHasExtent )
765 {
766 //remove map from composition if not referenced by the request
767 c->removeLayoutItem( map );
768 continue;
769 }
770 // Change CRS of map set to "project CRS" to match requested CRS
771 // (if map has a valid preset crs then we keep this crs and don't use the
772 // requested crs for this map item)
773 if ( mapSettings.destinationCrs().isValid() && !map->presetCrs().isValid() )
774 map->setCrs( mapSettings.destinationCrs() );
775
776 QgsRectangle r( cMapParams.mExtent );
777 if ( mWmsParameters.versionAsNumber() >= QgsProjectVersion( 1, 3, 0 ) &&
778 mapSettings.destinationCrs().hasAxisInverted() )
779 {
780 r.invert();
781 }
782 map->setExtent( r );
783
784 // scale
785 if ( cMapParams.mScale > 0 )
786 {
787 map->setScale( static_cast<double>( cMapParams.mScale ) );
788 }
789
790 // rotation
791 if ( cMapParams.mRotation )
792 {
793 map->setMapRotation( cMapParams.mRotation );
794 }
795 }
796
797 if ( !map->keepLayerSet() )
798 {
799
800 QList<QgsMapLayer *> layerSet;
801
802 for ( const auto &layer : std::as_const( cMapParams.mLayers ) )
803 {
804 if ( mContext.isValidGroup( layer.mNickname ) )
805 {
806 QList<QgsMapLayer *> layersFromGroup;
807
808 const QList<QgsMapLayer *> cLayersFromGroup = mContext.layersFromGroup( layer.mNickname );
809 for ( QgsMapLayer *layerFromGroup : cLayersFromGroup )
810 {
811
812 if ( ! layerFromGroup )
813 {
814 continue;
815 }
816
817 layersFromGroup.push_front( layerFromGroup );
818 }
819
820 if ( !layersFromGroup.isEmpty() )
821 {
822 layerSet.append( layersFromGroup );
823 }
824 }
825 else
826 {
827 QgsMapLayer *mlayer = mContext.layer( layer.mNickname );
828
829 if ( ! mlayer )
830 {
831 continue;
832 }
833
834 setLayerStyle( mlayer, layer.mStyle );
835 layerSet << mlayer;
836 }
837 }
838
839 std::reverse( layerSet.begin(), layerSet.end() );
840
841 // If the map is set to follow preset we need to disable follow preset and manually
842 // configure the layers here or the map item internal logic will override and get
843 // the layers from the map theme.
844 QMap<QString, QString> layersStyle;
845 if ( map->followVisibilityPreset() )
846 {
847
848 if ( atlas )
849 {
850 // Possibly triggers a refresh of the DD visibility preset (theme) name
851 // see issue GH #54475
852 atlas->updateFeatures();
853 atlas->first();
854 }
855
856 const QString presetName = map->followVisibilityPresetName();
857 if ( layerSet.isEmpty() )
858 {
859 // Get the layers from the theme
860 const QgsExpressionContext ex { map->createExpressionContext() };
861 layerSet = map->layersToRender( &ex );
862 }
863 // Disable the theme
864 map->setFollowVisibilityPreset( false );
865
866 // Collect the style of each layer in the theme that has been disabled
867 const QList<QgsMapThemeCollection::MapThemeLayerRecord> mapThemeRecords = QgsProject::instance()->mapThemeCollection()->mapThemeState( presetName ).layerRecords();
868 for ( const auto &layerMapThemeRecord : std::as_const( mapThemeRecords ) )
869 {
870 if ( layerSet.contains( layerMapThemeRecord.layer() ) )
871 {
872 layersStyle.insert( layerMapThemeRecord.layer()->id(),
873 layerMapThemeRecord.layer()->styleManager()->style( layerMapThemeRecord.currentStyle ).xmlData() );
874 }
875 }
876 }
877
878 // Handle highlight layers
879 const QList< QgsMapLayer *> highlights = highlightLayers( cMapParams.mHighlightLayers );
880 for ( const auto &hl : std::as_const( highlights ) )
881 {
882 layerSet.prepend( hl );
883 }
884
885 map->setLayers( layerSet );
886 map->setKeepLayerSet( true );
887
888 // Set style override if a particular style should be used due to a map theme.
889 // It will actualize linked legend symbols too.
890 if ( !layersStyle.isEmpty() )
891 {
892 map->setLayerStyleOverrides( layersStyle );
893 map->setKeepLayerStyles( true );
894 }
895 }
896
897 //grid space x / y
898 if ( cMapParams.mGridX > 0 && cMapParams.mGridY > 0 )
899 {
900 map->grid()->setIntervalX( static_cast<double>( cMapParams.mGridX ) );
901 map->grid()->setIntervalY( static_cast<double>( cMapParams.mGridY ) );
902 }
903 }
904
905 // Labels
906 QList<QgsLayoutItemLabel *> labels;
907 c->layoutItems<QgsLayoutItemLabel>( labels );
908 for ( const auto &label : std::as_const( labels ) )
909 {
910 bool ok = false;
911 const QString labelId = label->id();
912 const QString labelParam = mWmsParameters.layoutParameter( labelId, ok );
913
914 if ( !ok )
915 continue;
916
917 if ( labelParam.isEmpty() )
918 {
919 //remove exported labels referenced in the request
920 //but with empty string
921 c->removeItem( label );
922 delete label;
923 continue;
924 }
925
926 label->setText( labelParam );
927 }
928
929 // HTMLs
930 QList<QgsLayoutItemHtml *> htmls;
931 c->layoutObjects<QgsLayoutItemHtml>( htmls );
932 for ( const auto &html : std::as_const( htmls ) )
933 {
934 if ( html->frameCount() == 0 )
935 continue;
936
937 QgsLayoutFrame *htmlFrame = html->frame( 0 );
938 bool ok = false;
939 const QString htmlId = htmlFrame->id();
940 const QString url = mWmsParameters.layoutParameter( htmlId, ok );
941
942 if ( !ok )
943 {
944 html->update();
945 continue;
946 }
947
948 //remove exported Htmls referenced in the request
949 //but with empty string
950 if ( url.isEmpty() )
951 {
952 c->removeMultiFrame( html );
953 delete html;
954 continue;
955 }
956
957 QUrl newUrl( url );
958 html->setUrl( newUrl );
959 html->update();
960 }
961
962
963 // legends
964 QList<QgsLayoutItemLegend *> legends;
965 c->layoutItems<QgsLayoutItemLegend>( legends );
966 for ( const auto &legend : std::as_const( legends ) )
967 {
968 if ( legend->autoUpdateModel() )
969 {
970 // the legend has an auto-update model
971 // we will update it with map's layers
972 const QgsLayoutItemMap *map = legend->linkedMap();
973 if ( !map )
974 {
975 continue;
976 }
977
978 legend->setAutoUpdateModel( false );
979
980 // get model and layer tree root of the legend
981 QgsLegendModel *model = legend->model();
982 QStringList layerSet;
983 QList<QgsMapLayer *> mapLayers;
984 if ( map->layers().isEmpty() )
985 {
986 // in QGIS desktop, each layer has its legend, including invisible layers
987 // and using maptheme, legend items are automatically filtered
988 mapLayers = mProject->mapLayers( true ).values();
989 }
990 else
991 {
992 mapLayers = map->layers();
993 }
994 const QList<QgsMapLayer *> layerList = mapLayers;
995 for ( const auto &layer : layerList )
996 layerSet << layer->id();
997
998 //setLayerIdsToLegendModel( model, layerSet, map->scale() );
999
1000 // get model and layer tree root of the legend
1001 QgsLayerTree *root = model->rootGroup();
1002
1003 // get layerIds find in the layer tree root
1004 const QStringList layerIds = root->findLayerIds();
1005
1006 // find the layer in the layer tree
1007 // remove it if the layer id is not in map layerIds
1008 for ( const auto &layerId : layerIds )
1009 {
1010 QgsLayerTreeLayer *nodeLayer = root->findLayer( layerId );
1011 if ( !nodeLayer )
1012 {
1013 continue;
1014 }
1015 if ( !layerSet.contains( layerId ) )
1016 {
1017 qobject_cast<QgsLayerTreeGroup *>( nodeLayer->parent() )->removeChildNode( nodeLayer );
1018 }
1019 else
1020 {
1021 QgsMapLayer *layer = nodeLayer->layer();
1022 if ( !layer->isInScaleRange( map->scale() ) )
1023 {
1024 qobject_cast<QgsLayerTreeGroup *>( nodeLayer->parent() )->removeChildNode( nodeLayer );
1025 }
1026 }
1027 }
1029 }
1030 }
1031 return true;
1032 }
1033
1035 {
1036 // check size
1037 if ( ! mContext.isValidWidthHeight() )
1038 {
1040 QStringLiteral( "The requested map size is too large" ) );
1041 }
1042
1043 // init layer restorer before doing anything
1044 std::unique_ptr<QgsWmsRestorer> restorer;
1045 restorer.reset( new QgsWmsRestorer( mContext ) );
1046
1047 // configure layers
1048 QList<QgsMapLayer *> layers = mContext.layersToRender();
1049
1050 QgsMapSettings mapSettings;
1052 configureLayers( layers, &mapSettings );
1053
1054 // create the output image and the painter
1055 std::unique_ptr<QPainter> painter;
1056 std::unique_ptr<QImage> image( createImage( mContext.mapSize() ) );
1057
1058 // configure map settings (background, DPI, ...)
1059 configureMapSettings( image.get(), mapSettings );
1060
1061 // add layers to map settings
1062 mapSettings.setLayers( layers );
1063
1064 // rendering step for layers
1065 QPainter *renderedPainter = layersRendering( mapSettings, *image );
1066 if ( !renderedPainter ) // job has been canceled
1067 {
1068 return nullptr;
1069 }
1070
1071 painter.reset( renderedPainter );
1072
1073 // rendering step for annotations
1074 annotationsRendering( painter.get(), mapSettings );
1075
1076 // painting is terminated
1077 painter->end();
1078
1079 // scale output image if necessary (required by WMS spec)
1080 QImage *scaledImage = scaleImage( image.get() );
1081 if ( scaledImage )
1082 image.reset( scaledImage );
1083
1084 // return
1085 return image.release();
1086 }
1087
1088 std::unique_ptr<QgsDxfExport> QgsRenderer::getDxf()
1089 {
1090 // configure layers
1091 QList<QgsMapLayer *> layers = mContext.layersToRender();
1092 configureLayers( layers );
1093
1094 // get dxf layers
1095 const QStringList attributes = mWmsParameters.dxfLayerAttributes();
1096 QList< QgsDxfExport::DxfLayer > dxfLayers;
1097 int layerIdx = -1;
1098 for ( QgsMapLayer *layer : layers )
1099 {
1100 layerIdx++;
1101 if ( layer->type() != Qgis::LayerType::Vector )
1102 continue;
1103
1104 // cast for dxf layers
1105 QgsVectorLayer *vlayer = static_cast<QgsVectorLayer *>( layer );
1106
1107 // get the layer attribute used in dxf
1108 int layerAttribute = -1;
1109 if ( attributes.size() > layerIdx )
1110 {
1111 layerAttribute = vlayer->fields().indexFromName( attributes[ layerIdx ] );
1112 }
1113
1114 dxfLayers.append( QgsDxfExport::DxfLayer( vlayer, layerAttribute ) );
1115 }
1116
1117 //map extent
1118 QgsRectangle mapExtent = mWmsParameters.bboxAsRectangle();
1119
1120 QString crs = mWmsParameters.crs();
1121 if ( crs.compare( QStringLiteral( "CRS:84" ), Qt::CaseInsensitive ) == 0 )
1122 {
1123 crs = QStringLiteral( "EPSG:4326" );
1124 mapExtent.invert();
1125 }
1126 else if ( crs.isEmpty() )
1127 {
1128 crs = QStringLiteral( "EPSG:4326" );
1129 }
1130
1132
1133 if ( !outputCRS.isValid() )
1134 {
1136 QgsWmsParameter parameter;
1137
1138 if ( mWmsParameters.versionAsNumber() >= QgsProjectVersion( 1, 3, 0 ) )
1139 {
1141 parameter = mWmsParameters[ QgsWmsParameter::CRS ];
1142 }
1143 else
1144 {
1146 parameter = mWmsParameters[ QgsWmsParameter::SRS ];
1147 }
1148
1149 throw QgsBadRequestException( code, parameter );
1150 }
1151
1152 //then set destinationCrs
1153
1154 // Change x- and y- of BBOX for WMS 1.3.0 if axis inverted
1155 if ( mWmsParameters.versionAsNumber() >= QgsProjectVersion( 1, 3, 0 ) && outputCRS.hasAxisInverted() )
1156 {
1157 mapExtent.invert();
1158 }
1159
1160
1161 // add layers to dxf
1162 std::unique_ptr<QgsDxfExport> dxf = std::make_unique<QgsDxfExport>();
1163 dxf->setExtent( mapExtent );
1164 dxf->setDestinationCrs( outputCRS );
1165 dxf->addLayers( dxfLayers );
1166 dxf->setLayerTitleAsName( mWmsParameters.dxfUseLayerTitleAsName() );
1167 dxf->setSymbologyExport( mWmsParameters.dxfMode() );
1169 {
1170 dxf->setSymbologyScale( mWmsParameters.dxfScale() );
1171 }
1172
1173 dxf->setForce2d( mWmsParameters.isForce2D() );
1174 QgsDxfExport::Flags flags;
1175 if ( mWmsParameters.noMText() )
1176 flags.setFlag( QgsDxfExport::Flag::FlagNoMText );
1177
1178 dxf->setFlags( flags );
1179
1180 return dxf;
1181 }
1182
1183 std::unique_ptr<QgsMapRendererTask> QgsRenderer::getPdf( const QString &tmpFileName )
1184 {
1185 QgsMapSettings ms;
1186 ms.setExtent( mWmsParameters.bboxAsRectangle() );
1187 ms.setLayers( mContext.layersToRender() );
1189 ms.setOutputSize( QSize( mWmsParameters.widthAsInt(), mWmsParameters.heightAsInt() ) );
1190 ms.setDpiTarget( mWmsParameters.dpiAsDouble() );
1191
1193 if ( mWmsParameters.pdfExportMetadata() )
1194 {
1195 pdfExportDetails.author = QgsProject::instance()->metadata().author();
1196 pdfExportDetails.producer = QStringLiteral( "QGIS %1" ).arg( Qgis::version() );
1197 pdfExportDetails.creator = QStringLiteral( "QGIS %1" ).arg( Qgis::version() );
1198 pdfExportDetails.creationDateTime = QDateTime::currentDateTime();
1199 pdfExportDetails.subject = QgsProject::instance()->metadata().abstract();
1200 pdfExportDetails.title = QgsProject::instance()->metadata().title();
1201 pdfExportDetails.keywords = QgsProject::instance()->metadata().keywords();
1202 }
1205 const bool geoPdf = mWmsParameters.pdfAppendGeoreference();
1206 std::unique_ptr<QgsMapRendererTask> pdf = std::make_unique<QgsMapRendererTask>( ms, tmpFileName, QStringLiteral( "PDF" ), false, QgsTask::Hidden, geoPdf, pdfExportDetails );
1207 if ( mWmsParameters.pdfAppendGeoreference() )
1208 {
1209 pdf->setSaveWorldFile( true );
1210 }
1211 return pdf;
1212 }
1213
1214 static void infoPointToMapCoordinates( int i, int j, QgsPointXY *infoPoint, const QgsMapSettings &mapSettings )
1215 {
1216 //check if i, j are in the pixel range of the image
1217 if ( i < 0 || i > mapSettings.outputSize().width() )
1218 {
1220 param.mValue = i;
1222 param );
1223 }
1224
1225 if ( j < 0 || j > mapSettings.outputSize().height() )
1226 {
1227 QgsWmsParameter param( QgsWmsParameter::J );
1228 param.mValue = j;
1230 param );
1231 }
1232
1233 double xRes = mapSettings.extent().width() / mapSettings.outputSize().width();
1234 double yRes = mapSettings.extent().height() / mapSettings.outputSize().height();
1235 infoPoint->setX( mapSettings.extent().xMinimum() + i * xRes + xRes / 2.0 );
1236 infoPoint->setY( mapSettings.extent().yMaximum() - j * yRes - yRes / 2.0 );
1237 }
1238
1239 QByteArray QgsRenderer::getFeatureInfo( const QString &version )
1240 {
1241 // Verifying Mandatory parameters
1242 // The QUERY_LAYERS parameter is Mandatory
1243 if ( mWmsParameters.queryLayersNickname().isEmpty() )
1244 {
1246 mWmsParameters[QgsWmsParameter::QUERY_LAYERS] );
1247 }
1248
1249 // The I/J parameters are Mandatory if they are not replaced by X/Y or FILTER or FILTER_GEOM
1250 const bool ijDefined = !mWmsParameters.i().isEmpty() && !mWmsParameters.j().isEmpty();
1251 const bool xyDefined = !mWmsParameters.x().isEmpty() && !mWmsParameters.y().isEmpty();
1252 const bool filtersDefined = !mWmsParameters.filters().isEmpty();
1253 const bool filterGeomDefined = !mWmsParameters.filterGeom().isEmpty();
1254
1255 if ( !ijDefined && !xyDefined && !filtersDefined && !filterGeomDefined )
1256 {
1257 QgsWmsParameter parameter = mWmsParameters[QgsWmsParameter::I];
1258
1259 if ( mWmsParameters.j().isEmpty() )
1260 parameter = mWmsParameters[QgsWmsParameter::J];
1261
1263 }
1264
1265 const QgsWmsParameters::Format infoFormat = mWmsParameters.infoFormat();
1266 if ( infoFormat == QgsWmsParameters::Format::NONE )
1267 {
1269 mWmsParameters[QgsWmsParameter::INFO_FORMAT] );
1270 }
1271
1272 // create the mapSettings and the output image
1273 std::unique_ptr<QImage> outputImage( createImage( mContext.mapSize() ) );
1274
1275 // init layer restorer before doing anything
1276 std::unique_ptr<QgsWmsRestorer> restorer;
1277 restorer.reset( new QgsWmsRestorer( mContext ) );
1278
1279 // The CRS parameter is considered as mandatory in configureMapSettings
1280 // but in the case of filter parameter, CRS parameter has not to be mandatory
1281 bool mandatoryCrsParam = true;
1282 if ( filtersDefined && !ijDefined && !xyDefined && mWmsParameters.crs().isEmpty() )
1283 {
1284 mandatoryCrsParam = false;
1285 }
1286
1287 // configure map settings (background, DPI, ...)
1288 QgsMapSettings mapSettings;
1290 configureMapSettings( outputImage.get(), mapSettings, mandatoryCrsParam );
1291
1292 // compute scale denominator
1293 QgsScaleCalculator scaleCalc( ( outputImage->logicalDpiX() + outputImage->logicalDpiY() ) / 2, mapSettings.destinationCrs().mapUnits() );
1294 const double scaleDenominator = scaleCalc.calculate( mWmsParameters.bboxAsRectangle(), outputImage->width() );
1295
1296 // configure layers
1297 QgsWmsRenderContext context = mContext;
1298 context.setScaleDenominator( scaleDenominator );
1299
1300 QList<QgsMapLayer *> layers = context.layersToRender();
1301 configureLayers( layers, &mapSettings );
1302
1303 // add layers to map settings
1304 mapSettings.setLayers( layers );
1305
1306#ifdef HAVE_SERVER_PYTHON_PLUGINS
1307 mContext.accessControl()->resolveFilterFeatures( mapSettings.layers() );
1308#endif
1309
1310 QDomDocument result = featureInfoDocument( layers, mapSettings, outputImage.get(), version );
1311
1312 QByteArray ba;
1313
1314 if ( infoFormat == QgsWmsParameters::Format::TEXT )
1315 ba = convertFeatureInfoToText( result );
1316 else if ( infoFormat == QgsWmsParameters::Format::HTML )
1317 ba = convertFeatureInfoToHtml( result );
1318 else if ( infoFormat == QgsWmsParameters::Format::JSON )
1319 ba = convertFeatureInfoToJson( layers, result );
1320 else
1321 ba = result.toByteArray();
1322
1323 return ba;
1324 }
1325
1326 QImage *QgsRenderer::createImage( const QSize &size ) const
1327 {
1328 std::unique_ptr<QImage> image;
1329
1330 // use alpha channel only if necessary because it slows down performance
1331 QgsWmsParameters::Format format = mWmsParameters.format();
1332 bool transparent = mWmsParameters.transparentAsBool();
1333
1334 if ( transparent && format != QgsWmsParameters::JPG )
1335 {
1336 image = std::make_unique<QImage>( size, QImage::Format_ARGB32_Premultiplied );
1337 image->fill( 0 );
1338 }
1339 else
1340 {
1341 image = std::make_unique<QImage>( size, QImage::Format_RGB32 );
1342 image->fill( mWmsParameters.backgroundColorAsColor() );
1343 }
1344
1345 // Check that image was correctly created
1346 if ( image->isNull() )
1347 {
1348 throw QgsException( QStringLiteral( "createImage: image could not be created, check for out of memory conditions" ) );
1349 }
1350
1351 const int dpm = static_cast<int>( mContext.dotsPerMm() * 1000.0 );
1352 image->setDotsPerMeterX( dpm );
1353 image->setDotsPerMeterY( dpm );
1354
1355 return image.release();
1356 }
1357
1358 void QgsRenderer::configureMapSettings( const QPaintDevice *paintDevice, QgsMapSettings &mapSettings, bool mandatoryCrsParam )
1359 {
1360 if ( !paintDevice )
1361 {
1362 throw QgsException( QStringLiteral( "configureMapSettings: no paint device" ) );
1363 }
1364
1365 mapSettings.setOutputSize( QSize( paintDevice->width(), paintDevice->height() ) );
1366 // Recalculate from input DPI: do not take the (integer) value from paint device
1367 // because it loose precision!
1368 mapSettings.setOutputDpi( mContext.dotsPerMm() * 25.4 );
1369
1370 //map extent
1371 QgsRectangle mapExtent = mWmsParameters.bboxAsRectangle();
1372 if ( !mWmsParameters.bbox().isEmpty() && mapExtent.isEmpty() )
1373 {
1375 mWmsParameters[QgsWmsParameter::BBOX] );
1376 }
1377
1378 QString crs = mWmsParameters.crs();
1379 if ( crs.compare( "CRS:84", Qt::CaseInsensitive ) == 0 )
1380 {
1381 crs = QString( "EPSG:4326" );
1382 mapExtent.invert();
1383 }
1384 else if ( crs.isEmpty() && !mandatoryCrsParam )
1385 {
1386 crs = QString( "EPSG:4326" );
1387 }
1388
1390
1391 //wms spec says that CRS parameter is mandatory.
1393 if ( !outputCRS.isValid() )
1394 {
1396 QgsWmsParameter parameter;
1397
1398 if ( mWmsParameters.versionAsNumber() >= QgsProjectVersion( 1, 3, 0 ) )
1399 {
1401 parameter = mWmsParameters[ QgsWmsParameter::CRS ];
1402 }
1403 else
1404 {
1406 parameter = mWmsParameters[ QgsWmsParameter::SRS ];
1407 }
1408
1409 throw QgsBadRequestException( code, parameter );
1410 }
1411
1412 //then set destinationCrs
1413 mapSettings.setDestinationCrs( outputCRS );
1414
1415 // Change x- and y- of BBOX for WMS 1.3.0 if axis inverted
1416 if ( mWmsParameters.versionAsNumber() >= QgsProjectVersion( 1, 3, 0 ) && outputCRS.hasAxisInverted() )
1417 {
1418 mapExtent.invert();
1419 }
1420
1421 mapSettings.setExtent( mapExtent );
1422
1423 // set the extent buffer
1424 mapSettings.setExtentBuffer( mContext.mapTileBuffer( paintDevice->width() ) );
1425
1426 /* Define the background color
1427 * Transparent or colored
1428 */
1429 QgsWmsParameters::Format format = mWmsParameters.format();
1430 bool transparent = mWmsParameters.transparentAsBool();
1431 QColor backgroundColor = mWmsParameters.backgroundColorAsColor();
1432
1433 //set background color
1434 if ( transparent && format != QgsWmsParameters::JPG )
1435 {
1436 mapSettings.setBackgroundColor( QColor( 0, 0, 0, 0 ) );
1437 }
1438 else if ( backgroundColor.isValid() )
1439 {
1440 mapSettings.setBackgroundColor( backgroundColor );
1441 }
1442
1443 // add context from project (global variables, ...)
1444 QgsExpressionContext context = mProject->createExpressionContext();
1445 context << QgsExpressionContextUtils::mapSettingsScope( mapSettings );
1446 mapSettings.setExpressionContext( context );
1447
1448 // add labeling engine settings
1449 mapSettings.setLabelingEngineSettings( mProject->labelingEngineSettings() );
1450
1451 // enable rendering optimization
1453
1455
1456 // enable profiling
1457 if ( mContext.settings().logProfile() )
1458 {
1460 }
1461
1462 // set selection color
1463 mapSettings.setSelectionColor( mProject->selectionColor() );
1464
1465 // Set WMS temporal properties
1466 // Note that this cannot parse multiple time instants while the vector dimensions implementation can
1467 const QString timeString { mWmsParameters.dimensionValues().value( QStringLiteral( "TIME" ), QString() ) };
1468 if ( ! timeString.isEmpty() )
1469 {
1470 bool isValidTemporalRange { true };
1471 QgsDateTimeRange range;
1472 // First try with a simple date/datetime instant
1473 const QDateTime dt { QDateTime::fromString( timeString, Qt::DateFormat::ISODateWithMs ) };
1474 if ( dt.isValid() )
1475 {
1476 range = QgsDateTimeRange( dt, dt );
1477 }
1478 else // parse as an interval
1479 {
1480 try
1481 {
1483 }
1484 catch ( const QgsServerApiBadRequestException &ex )
1485 {
1486 isValidTemporalRange = false;
1487 QgsMessageLog::logMessage( QStringLiteral( "Could not parse TIME parameter into a temporal range" ), "Server", Qgis::MessageLevel::Warning );
1488 }
1489 }
1490
1491 if ( isValidTemporalRange )
1492 {
1493 mIsTemporal = true;
1494 mapSettings.setIsTemporal( true );
1495 mapSettings.setTemporalRange( range );
1496 }
1497
1498 }
1499 }
1500
1501 QgsRenderContext QgsRenderer::configureDefaultRenderContext( QPainter *painter )
1502 {
1504 context.setScaleFactor( mContext.dotsPerMm() );
1505 const double mmPerMapUnit = 1 / QgsServerProjectUtils::wmsDefaultMapUnitsPerMm( *mProject );
1506 context.setMapToPixel( QgsMapToPixel( 1 / ( mmPerMapUnit * context.scaleFactor() ) ) );
1507 QgsDistanceArea distanceArea = QgsDistanceArea();
1508 distanceArea.setSourceCrs( QgsCoordinateReferenceSystem( mWmsParameters.crs() ), mProject->transformContext() );
1509 distanceArea.setEllipsoid( geoNone() );
1510 context.setDistanceArea( distanceArea );
1511 return context;
1512 }
1513
1514 QDomDocument QgsRenderer::featureInfoDocument( QList<QgsMapLayer *> &layers, const QgsMapSettings &mapSettings,
1515 const QImage *outputImage, const QString &version ) const
1516 {
1517 const QStringList queryLayers = mContext.flattenedQueryLayers( mContext.parameters().queryLayersNickname() );
1518
1519 bool ijDefined = ( !mWmsParameters.i().isEmpty() && !mWmsParameters.j().isEmpty() );
1520
1521 bool xyDefined = ( !mWmsParameters.x().isEmpty() && !mWmsParameters.y().isEmpty() );
1522
1523 bool filtersDefined = !mWmsParameters.filters().isEmpty();
1524
1525 bool filterGeomDefined = !mWmsParameters.filterGeom().isEmpty();
1526
1527 int featureCount = mWmsParameters.featureCountAsInt();
1528 if ( featureCount < 1 )
1529 {
1530 featureCount = 1;
1531 }
1532
1533 int i = mWmsParameters.iAsInt();
1534 int j = mWmsParameters.jAsInt();
1535 if ( xyDefined && !ijDefined )
1536 {
1537 i = mWmsParameters.xAsInt();
1538 j = mWmsParameters.yAsInt();
1539 }
1540 int width = mWmsParameters.widthAsInt();
1541 int height = mWmsParameters.heightAsInt();
1542 if ( ( i != -1 && j != -1 && width != 0 && height != 0 ) && ( width != outputImage->width() || height != outputImage->height() ) )
1543 {
1544 i *= ( outputImage->width() / static_cast<double>( width ) );
1545 j *= ( outputImage->height() / static_cast<double>( height ) );
1546 }
1547
1548 // init search variables
1549 std::unique_ptr<QgsRectangle> featuresRect;
1550 std::unique_ptr<QgsGeometry> filterGeom;
1551 std::unique_ptr<QgsPointXY> infoPoint;
1552
1553 if ( i != -1 && j != -1 )
1554 {
1555 infoPoint.reset( new QgsPointXY() );
1556 infoPointToMapCoordinates( i, j, infoPoint.get(), mapSettings );
1557 }
1558 else if ( filtersDefined )
1559 {
1560 featuresRect.reset( new QgsRectangle() );
1561 }
1562 else if ( filterGeomDefined )
1563 {
1564 filterGeom.reset( new QgsGeometry( QgsGeometry::fromWkt( mWmsParameters.filterGeom() ) ) );
1565 }
1566
1567 QDomDocument result;
1568 const QDomNode header = result.createProcessingInstruction( QStringLiteral( "xml" ), QStringLiteral( "version=\"1.0\" encoding=\"UTF-8\"" ) );
1569 result.appendChild( header );
1570
1571 QDomElement getFeatureInfoElement;
1572 QgsWmsParameters::Format infoFormat = mWmsParameters.infoFormat();
1573 if ( infoFormat == QgsWmsParameters::Format::GML )
1574 {
1575 getFeatureInfoElement = result.createElement( QStringLiteral( "wfs:FeatureCollection" ) );
1576 getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:wfs" ), QStringLiteral( "http://www.opengis.net/wfs" ) );
1577 getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) );
1578 getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:gml" ), QStringLiteral( "http://www.opengis.net/gml" ) );
1579 getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:ows" ), QStringLiteral( "http://www.opengis.net/ows" ) );
1580 getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1581 getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:qgs" ), QStringLiteral( "http://qgis.org/gml" ) );
1582 getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
1583 getFeatureInfoElement.setAttribute( QStringLiteral( "xsi:schemaLocation" ), QStringLiteral( "http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd http://qgis.org/gml" ) );
1584 }
1585 else
1586 {
1587 QString featureInfoElemName = QgsServerProjectUtils::wmsFeatureInfoDocumentElement( *mProject );
1588 if ( featureInfoElemName.isEmpty() )
1589 {
1590 featureInfoElemName = QStringLiteral( "GetFeatureInfoResponse" );
1591 }
1592 QString featureInfoElemNs = QgsServerProjectUtils::wmsFeatureInfoDocumentElementNs( *mProject );
1593 if ( featureInfoElemNs.isEmpty() )
1594 {
1595 getFeatureInfoElement = result.createElement( featureInfoElemName );
1596 }
1597 else
1598 {
1599 getFeatureInfoElement = result.createElementNS( featureInfoElemNs, featureInfoElemName );
1600 }
1601 //feature info schema
1602 QString featureInfoSchema = QgsServerProjectUtils::wmsFeatureInfoSchema( *mProject );
1603 if ( !featureInfoSchema.isEmpty() )
1604 {
1605 getFeatureInfoElement.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
1606 getFeatureInfoElement.setAttribute( QStringLiteral( "xsi:schemaLocation" ), featureInfoSchema );
1607 }
1608 }
1609 result.appendChild( getFeatureInfoElement );
1610
1611 //Render context is needed to determine feature visibility for vector layers
1612 QgsRenderContext renderContext = QgsRenderContext::fromMapSettings( mapSettings );
1613
1614 bool sia2045 = QgsServerProjectUtils::wmsInfoFormatSia2045( *mProject );
1615
1616 //layers can have assigned a different name for GetCapabilities
1617 QHash<QString, QString> layerAliasMap = QgsServerProjectUtils::wmsFeatureInfoLayerAliasMap( *mProject );
1618
1619 for ( const QString &queryLayer : queryLayers )
1620 {
1621 bool validLayer = false;
1622 bool queryableLayer = true;
1623 for ( QgsMapLayer *layer : std::as_const( layers ) )
1624 {
1625 if ( queryLayer == mContext.layerNickname( *layer ) )
1626 {
1627 validLayer = true;
1628 queryableLayer = layer->flags().testFlag( QgsMapLayer::Identifiable );
1629 if ( !queryableLayer )
1630 {
1631 break;
1632 }
1633
1634 QDomElement layerElement;
1635 if ( infoFormat == QgsWmsParameters::Format::GML )
1636 {
1637 layerElement = getFeatureInfoElement;
1638 }
1639 else
1640 {
1641 layerElement = result.createElement( QStringLiteral( "Layer" ) );
1642 QString layerName = queryLayer;
1643
1644 //check if the layer is given a different name for GetFeatureInfo output
1645 QHash<QString, QString>::const_iterator layerAliasIt = layerAliasMap.constFind( layerName );
1646 if ( layerAliasIt != layerAliasMap.constEnd() )
1647 {
1648 layerName = layerAliasIt.value();
1649 }
1650
1651 layerElement.setAttribute( QStringLiteral( "name" ), layerName );
1652 const QString layerTitle = layer->title();
1653 if ( !layerTitle.isEmpty() )
1654 {
1655 layerElement.setAttribute( QStringLiteral( "title" ), layerTitle );
1656 }
1657 else
1658 {
1659 layerElement.setAttribute( QStringLiteral( "title" ), layerName );
1660 }
1661 getFeatureInfoElement.appendChild( layerElement );
1662 if ( sia2045 ) //the name might not be unique after alias replacement
1663 {
1664 layerElement.setAttribute( QStringLiteral( "id" ), layer->id() );
1665 }
1666 }
1667
1668 if ( layer->type() == Qgis::LayerType::Vector )
1669 {
1670 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
1671 if ( vectorLayer )
1672 {
1673 ( void )featureInfoFromVectorLayer( vectorLayer, infoPoint.get(), featureCount, result, layerElement, mapSettings, renderContext, version, featuresRect.get(), filterGeom.get() );
1674 break;
1675 }
1676 }
1677 else
1678 {
1679 QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layer );
1680 if ( !rasterLayer )
1681 {
1682 break;
1683 }
1684 if ( !infoPoint )
1685 {
1686 break;
1687 }
1688 QgsPointXY layerInfoPoint = mapSettings.mapToLayerCoordinates( layer, *( infoPoint.get() ) );
1689 if ( !rasterLayer->extent().contains( layerInfoPoint ) )
1690 {
1691 break;
1692 }
1693 if ( infoFormat == QgsWmsParameters::Format::GML )
1694 {
1695 layerElement = result.createElement( QStringLiteral( "gml:featureMember" )/*wfs:FeatureMember*/ );
1696 getFeatureInfoElement.appendChild( layerElement );
1697 }
1698
1699 ( void )featureInfoFromRasterLayer( rasterLayer, mapSettings, &layerInfoPoint, renderContext, result, layerElement, version );
1700 }
1701 break;
1702 }
1703 }
1704 if ( !validLayer && !mContext.isValidLayer( queryLayer ) && !mContext.isValidGroup( queryLayer ) )
1705 {
1706 QgsWmsParameter param( QgsWmsParameter::LAYER );
1707 param.mValue = queryLayer;
1709 param );
1710 }
1711 else if ( ( validLayer && !queryableLayer ) || ( !validLayer && mContext.isValidGroup( queryLayer ) ) )
1712 {
1713 QgsWmsParameter param( QgsWmsParameter::LAYER );
1714 param.mValue = queryLayer;
1715 // Check if this layer belongs to a group and the group has any queryable layers
1716 bool hasGroupAndQueryable { false };
1717 if ( ! mContext.parameters().queryLayersNickname().contains( queryLayer ) )
1718 {
1719 // Find which group this layer belongs to
1720 const QStringList constNicks { mContext.parameters().queryLayersNickname() };
1721 for ( const QString &ql : constNicks )
1722 {
1723 if ( mContext.layerGroups().contains( ql ) )
1724 {
1725 const QList<QgsMapLayer *> constLayers { mContext.layerGroups()[ql] };
1726 for ( const QgsMapLayer *ml : constLayers )
1727 {
1728 if ( ( ! ml->shortName().isEmpty() && ml->shortName() == queryLayer ) || ( ml->name() == queryLayer ) )
1729 {
1730 param.mValue = ql;
1731 }
1732 if ( ml->flags().testFlag( QgsMapLayer::Identifiable ) )
1733 {
1734 hasGroupAndQueryable = true;
1735 break;
1736 }
1737 }
1738 break;
1739 }
1740 }
1741 }
1742 // Only throw if it's not a group or the group has no queryable children
1743 if ( ! hasGroupAndQueryable )
1744 {
1746 param );
1747 }
1748 }
1749 }
1750
1751 if ( featuresRect && ! featuresRect->isNull() )
1752 {
1753 if ( infoFormat == QgsWmsParameters::Format::GML )
1754 {
1755 QDomElement bBoxElem = result.createElement( QStringLiteral( "gml:boundedBy" ) );
1756 QDomElement boxElem;
1757 int gmlVersion = mWmsParameters.infoFormatVersion();
1758 if ( gmlVersion < 3 )
1759 {
1760 boxElem = QgsOgcUtils::rectangleToGMLBox( featuresRect.get(), result, 8 );
1761 }
1762 else
1763 {
1764 boxElem = QgsOgcUtils::rectangleToGMLEnvelope( featuresRect.get(), result, 8 );
1765 }
1766
1767 QgsCoordinateReferenceSystem crs = mapSettings.destinationCrs();
1768 if ( crs.isValid() )
1769 {
1770 boxElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() );
1771 }
1772 bBoxElem.appendChild( boxElem );
1773 getFeatureInfoElement.insertBefore( bBoxElem, QDomNode() ); //insert as first child
1774 }
1775 else
1776 {
1777 QDomElement bBoxElem = result.createElement( QStringLiteral( "BoundingBox" ) );
1778 bBoxElem.setAttribute( QStringLiteral( "CRS" ), mapSettings.destinationCrs().authid() );
1779 bBoxElem.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( featuresRect->xMinimum(), 8 ) );
1780 bBoxElem.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( featuresRect->xMaximum(), 8 ) );
1781 bBoxElem.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( featuresRect->yMinimum(), 8 ) );
1782 bBoxElem.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( featuresRect->yMaximum(), 8 ) );
1783 getFeatureInfoElement.insertBefore( bBoxElem, QDomNode() ); //insert as first child
1784 }
1785 }
1786
1787 if ( sia2045 && infoFormat == QgsWmsParameters::Format::XML )
1788 {
1789 convertFeatureInfoToSia2045( result );
1790 }
1791
1792 return result;
1793 }
1794
1795 bool QgsRenderer::featureInfoFromVectorLayer( QgsVectorLayer *layer,
1796 const QgsPointXY *infoPoint,
1797 int nFeatures,
1798 QDomDocument &infoDocument,
1799 QDomElement &layerElement,
1800 const QgsMapSettings &mapSettings,
1801 QgsRenderContext &renderContext,
1802 const QString &version,
1803 QgsRectangle *featureBBox,
1804 QgsGeometry *filterGeom ) const
1805 {
1806 if ( !layer )
1807 {
1808 return false;
1809 }
1810
1811 QgsFeatureRequest fReq;
1812
1813 // Transform filter geometry to layer CRS
1814 std::unique_ptr<QgsGeometry> layerFilterGeom;
1815 if ( filterGeom )
1816 {
1817 layerFilterGeom.reset( new QgsGeometry( *filterGeom ) );
1818 layerFilterGeom->transform( QgsCoordinateTransform( mapSettings.destinationCrs(), layer->crs(), fReq.transformContext() ) );
1819 }
1820
1821 //we need a selection rect (0.01 of map width)
1822 QgsRectangle mapRect = mapSettings.extent();
1823 QgsRectangle layerRect = mapSettings.mapToLayerCoordinates( layer, mapRect );
1824
1825
1826 QgsRectangle searchRect;
1827
1828 //info point could be 0 in case there is only an attribute filter
1829 if ( infoPoint )
1830 {
1831 searchRect = featureInfoSearchRect( layer, mapSettings, renderContext, *infoPoint );
1832 }
1833 else if ( layerFilterGeom )
1834 {
1835 searchRect = layerFilterGeom->boundingBox();
1836 }
1837 else if ( !mWmsParameters.bbox().isEmpty() )
1838 {
1839 searchRect = layerRect;
1840 }
1841
1842 //do a select with searchRect and go through all the features
1843
1844 QgsFeature feature;
1845 QgsAttributes featureAttributes;
1846 int featureCounter = 0;
1847 layer->updateFields();
1848 const QgsFields fields = layer->fields();
1849 bool addWktGeometry = ( QgsServerProjectUtils::wmsFeatureInfoAddWktGeometry( *mProject ) && mWmsParameters.withGeometry() );
1850 bool segmentizeWktGeometry = QgsServerProjectUtils::wmsFeatureInfoSegmentizeWktGeometry( *mProject );
1851
1852 bool hasGeometry = QgsServerProjectUtils::wmsFeatureInfoAddWktGeometry( *mProject ) || addWktGeometry || featureBBox || layerFilterGeom;
1853 fReq.setFlags( ( ( hasGeometry ) ? Qgis::FeatureRequestFlag::NoFlags : Qgis::FeatureRequestFlag::NoGeometry ) | Qgis::FeatureRequestFlag::ExactIntersect );
1854
1855 if ( ! searchRect.isEmpty() )
1856 {
1857 fReq.setFilterRect( searchRect );
1858 }
1859 else
1860 {
1861 fReq.setFlags( fReq.flags() & ~ static_cast<int>( Qgis::FeatureRequestFlag::ExactIntersect ) );
1862 }
1863
1864
1865 if ( layerFilterGeom )
1866 {
1867 fReq.setFilterExpression( QString( "intersects( $geometry, geom_from_wkt('%1') )" ).arg( layerFilterGeom->asWkt() ) );
1868 }
1869
1870 mFeatureFilter.filterFeatures( layer, fReq );
1871
1872#ifdef HAVE_SERVER_PYTHON_PLUGINS
1873 mContext.accessControl()->filterFeatures( layer, fReq );
1874
1875 QStringList attributes;
1876 for ( const QgsField &field : fields )
1877 {
1878 attributes.append( field.name() );
1879 }
1880 attributes = mContext.accessControl()->layerAttributes( layer, attributes );
1881 fReq.setSubsetOfAttributes( attributes, layer->fields() );
1882#endif
1883
1884 QgsFeatureIterator fit = layer->getFeatures( fReq );
1885 std::unique_ptr< QgsFeatureRenderer > r2( layer->renderer() ? layer->renderer()->clone() : nullptr );
1886 if ( r2 )
1887 {
1888 r2->startRender( renderContext, layer->fields() );
1889 }
1890
1891 bool featureBBoxInitialized = false;
1892 while ( fit.nextFeature( feature ) )
1893 {
1894 if ( layer->wkbType() == Qgis::WkbType::NoGeometry && ! searchRect.isEmpty() )
1895 {
1896 break;
1897 }
1898
1899 ++featureCounter;
1900 if ( featureCounter > nFeatures )
1901 {
1902 break;
1903 }
1904
1905 renderContext.expressionContext().setFeature( feature );
1906
1907 if ( layer->wkbType() != Qgis::WkbType::NoGeometry && ! searchRect.isEmpty() )
1908 {
1909 if ( !r2 )
1910 {
1911 continue;
1912 }
1913
1914 //check if feature is rendered at all
1915 bool render = r2->willRenderFeature( feature, renderContext );
1916 if ( !render )
1917 {
1918 continue;
1919 }
1920 }
1921
1922 QgsRectangle box;
1923 if ( layer->wkbType() != Qgis::WkbType::NoGeometry && hasGeometry )
1924 {
1925 box = mapSettings.layerExtentToOutputExtent( layer, feature.geometry().boundingBox() );
1926 if ( featureBBox ) //extend feature info bounding box if requested
1927 {
1928 if ( !featureBBoxInitialized && featureBBox->isEmpty() )
1929 {
1930 *featureBBox = box;
1931 featureBBoxInitialized = true;
1932 }
1933 else
1934 {
1935 featureBBox->combineExtentWith( box );
1936 }
1937 }
1938 }
1939
1941 if ( layer->crs() != mapSettings.destinationCrs() )
1942 {
1943 outputCrs = mapSettings.destinationCrs();
1944 }
1945
1946 if ( mWmsParameters.infoFormat() == QgsWmsParameters::Format::GML )
1947 {
1948 bool withGeom = layer->wkbType() != Qgis::WkbType::NoGeometry && addWktGeometry;
1949 int gmlVersion = mWmsParameters.infoFormatVersion();
1950 QString typeName = mContext.layerNickname( *layer );
1951 QDomElement elem = createFeatureGML(
1952 &feature, layer, infoDocument, outputCrs, mapSettings, typeName, withGeom, gmlVersion
1953#ifdef HAVE_SERVER_PYTHON_PLUGINS
1954 , &attributes
1955#endif
1956 );
1957 QDomElement featureMemberElem = infoDocument.createElement( QStringLiteral( "gml:featureMember" )/*wfs:FeatureMember*/ );
1958 featureMemberElem.appendChild( elem );
1959 layerElement.appendChild( featureMemberElem );
1960 continue;
1961 }
1962 else
1963 {
1964 QDomElement featureElement = infoDocument.createElement( QStringLiteral( "Feature" ) );
1965 featureElement.setAttribute( QStringLiteral( "id" ), QgsServerFeatureId::getServerFid( feature, layer->dataProvider()->pkAttributeIndexes() ) );
1966 layerElement.appendChild( featureElement );
1967
1968 featureAttributes = feature.attributes();
1969 QgsEditFormConfig editConfig = layer->editFormConfig();
1971 {
1972 writeAttributesTabLayout( editConfig, layer, fields, featureAttributes, infoDocument, featureElement, renderContext
1973#ifdef HAVE_SERVER_PYTHON_PLUGINS
1974 , &attributes
1975#endif
1976 );
1977 }
1978 else
1979 {
1980 for ( int i = 0; i < featureAttributes.count(); ++i )
1981 {
1982 writeVectorLayerAttribute( i, layer, fields, featureAttributes, infoDocument, featureElement, renderContext
1983#ifdef HAVE_SERVER_PYTHON_PLUGINS
1984 , &attributes
1985#endif
1986 );
1987 }
1988 }
1989
1990 //add maptip attribute based on html/expression (in case there is no maptip attribute)
1991 QString mapTip = layer->mapTipTemplate();
1992 if ( !mapTip.isEmpty() && ( mWmsParameters.withMapTip() || mWmsParameters.htmlInfoOnlyMapTip() ) )
1993 {
1994 QDomElement maptipElem = infoDocument.createElement( QStringLiteral( "Attribute" ) );
1995 maptipElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "maptip" ) );
1996 QgsExpressionContext context { renderContext.expressionContext() };
1997 context.appendScope( QgsExpressionContextUtils::layerScope( layer ) );
1998 maptipElem.setAttribute( QStringLiteral( "value" ), QgsExpression::replaceExpressionText( mapTip, &context ) );
1999 featureElement.appendChild( maptipElem );
2000 }
2001
2002 QgsExpression displayExpression = layer->displayExpression();
2003 if ( displayExpression.isValid() && mWmsParameters.withDisplayName() )
2004 {
2005 QDomElement displayElem = infoDocument.createElement( QStringLiteral( "Attribute" ) );
2006 displayElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "displayName" ) );
2007 QgsExpressionContext context { renderContext.expressionContext() };
2008 context.appendScope( QgsExpressionContextUtils::layerScope( layer ) );
2009 displayExpression.prepare( &context );
2010 displayElem.setAttribute( QStringLiteral( "value" ), displayExpression.evaluate( &context ).toString() );
2011 featureElement.appendChild( displayElem );
2012 }
2013
2014 //append feature bounding box to feature info xml
2016 layer->wkbType() != Qgis::WkbType::NoGeometry && hasGeometry )
2017 {
2018 QDomElement bBoxElem = infoDocument.createElement( QStringLiteral( "BoundingBox" ) );
2019 bBoxElem.setAttribute( version == QLatin1String( "1.1.1" ) ? "SRS" : "CRS", outputCrs.authid() );
2020 bBoxElem.setAttribute( QStringLiteral( "minx" ), qgsDoubleToString( box.xMinimum(), mContext.precision() ) );
2021 bBoxElem.setAttribute( QStringLiteral( "maxx" ), qgsDoubleToString( box.xMaximum(), mContext.precision() ) );
2022 bBoxElem.setAttribute( QStringLiteral( "miny" ), qgsDoubleToString( box.yMinimum(), mContext.precision() ) );
2023 bBoxElem.setAttribute( QStringLiteral( "maxy" ), qgsDoubleToString( box.yMaximum(), mContext.precision() ) );
2024 featureElement.appendChild( bBoxElem );
2025 }
2026
2027 //also append the wkt geometry as an attribute
2028 if ( layer->wkbType() != Qgis::WkbType::NoGeometry && addWktGeometry && hasGeometry )
2029 {
2030 QgsGeometry geom = feature.geometry();
2031 if ( !geom.isNull() )
2032 {
2033 if ( layer->crs() != outputCrs )
2034 {
2035 QgsCoordinateTransform transform = mapSettings.layerTransform( layer );
2036 if ( transform.isValid() )
2037 geom.transform( transform );
2038 }
2039
2040 if ( segmentizeWktGeometry )
2041 {
2042 const QgsAbstractGeometry *abstractGeom = geom.constGet();
2043 if ( abstractGeom )
2044 {
2045 if ( QgsWkbTypes::isCurvedType( abstractGeom->wkbType() ) )
2046 {
2047 QgsAbstractGeometry *segmentizedGeom = abstractGeom->segmentize();
2048 geom.set( segmentizedGeom );
2049 }
2050 }
2051 }
2052 QDomElement geometryElement = infoDocument.createElement( QStringLiteral( "Attribute" ) );
2053 geometryElement.setAttribute( QStringLiteral( "name" ), QStringLiteral( "geometry" ) );
2054 geometryElement.setAttribute( QStringLiteral( "value" ), geom.asWkt( mContext.precision() ) );
2055 geometryElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "derived" ) );
2056 featureElement.appendChild( geometryElement );
2057 }
2058 }
2059 }
2060 }
2061 if ( r2 )
2062 {
2063 r2->stopRender( renderContext );
2064 }
2065
2066 return true;
2067 }
2068
2069 void QgsRenderer::writeAttributesTabGroup( const QgsAttributeEditorElement *group, QgsVectorLayer *layer, const QgsFields &fields, QgsAttributes &featureAttributes, QDomDocument &doc, QDomElement &parentElem, QgsRenderContext &renderContext, QStringList *attributes ) const
2070 {
2071 const QgsAttributeEditorContainer *container = dynamic_cast<const QgsAttributeEditorContainer *>( group );
2072 if ( container )
2073 {
2074 QString groupName = container->name();
2075 QDomElement nameElem;
2076
2077 if ( !groupName.isEmpty() )
2078 {
2079 nameElem = doc.createElement( groupName );
2080 parentElem.appendChild( nameElem );
2081 }
2082
2083 const QList<QgsAttributeEditorElement *> children = container->children();
2084 for ( const QgsAttributeEditorElement *child : children )
2085 {
2086 if ( child->type() == Qgis::AttributeEditorType::Container )
2087 {
2088 writeAttributesTabGroup( child, layer, fields, featureAttributes, doc, nameElem.isNull() ? parentElem : nameElem, renderContext );
2089 }
2090 else if ( child->type() == Qgis::AttributeEditorType::Field )
2091 {
2092 const QgsAttributeEditorField *editorField = dynamic_cast<const QgsAttributeEditorField *>( child );
2093 if ( editorField )
2094 {
2095 const int idx { fields.indexFromName( editorField->name() ) };
2096 if ( idx >= 0 )
2097 {
2098 writeVectorLayerAttribute( idx, layer, fields, featureAttributes, doc, nameElem.isNull() ? parentElem : nameElem, renderContext, attributes );
2099 }
2100 }
2101 }
2102 }
2103 }
2104 }
2105
2106 void QgsRenderer::writeAttributesTabLayout( QgsEditFormConfig &config, QgsVectorLayer *layer, const QgsFields &fields, QgsAttributes &featureAttributes, QDomDocument &doc, QDomElement &featureElem, QgsRenderContext &renderContext, QStringList *attributes ) const
2107 {
2108 QgsAttributeEditorContainer *editorContainer = config.invisibleRootContainer();
2109 if ( !editorContainer )
2110 {
2111 return;
2112 }
2113
2114 writeAttributesTabGroup( editorContainer, layer, fields, featureAttributes, doc, featureElem, renderContext, attributes );
2115 }
2116
2117 void QgsRenderer::writeVectorLayerAttribute( int attributeIndex, QgsVectorLayer *layer, const QgsFields &fields, QgsAttributes &featureAttributes, QDomDocument &doc, QDomElement &featureElem, QgsRenderContext &renderContext, QStringList *attributes ) const
2118 {
2119#ifndef HAVE_SERVER_PYTHON_PLUGINS
2120 Q_UNUSED( attributes );
2121#endif
2122
2123 if ( !layer )
2124 {
2125 return;
2126 }
2127
2128 //skip attribute if it is explicitly excluded from WMS publication
2129 if ( fields.at( attributeIndex ).configurationFlags().testFlag( Qgis::FieldConfigurationFlag::HideFromWms ) )
2130 {
2131 return;
2132 }
2133#ifdef HAVE_SERVER_PYTHON_PLUGINS
2134 //skip attribute if it is excluded by access control
2135 if ( attributes && !attributes->contains( fields.at( attributeIndex ).name() ) )
2136 {
2137 return;
2138 }
2139#endif
2140
2141 QString attributeName = layer->attributeDisplayName( attributeIndex );
2142 QDomElement attributeElement = doc.createElement( QStringLiteral( "Attribute" ) );
2143 attributeElement.setAttribute( QStringLiteral( "name" ), attributeName );
2144 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( attributeIndex );
2145 attributeElement.setAttribute( QStringLiteral( "value" ),
2147 replaceValueMapAndRelation(
2148 layer, attributeIndex,
2149 featureAttributes[attributeIndex] ),
2150 &renderContext.expressionContext() )
2151 );
2152 featureElem.appendChild( attributeElement );
2153 }
2154
2155 bool QgsRenderer::featureInfoFromRasterLayer( QgsRasterLayer *layer,
2156 const QgsMapSettings &mapSettings,
2157 const QgsPointXY *infoPoint,
2158 const QgsRenderContext &renderContext,
2159 QDomDocument &infoDocument,
2160 QDomElement &layerElement,
2161 const QString &version ) const
2162 {
2163 Q_UNUSED( version )
2164
2165 if ( !infoPoint || !layer || !layer->dataProvider() )
2166 {
2167 return false;
2168 }
2169
2170 QgsMessageLog::logMessage( QStringLiteral( "infoPoint: %1 %2" ).arg( infoPoint->x() ).arg( infoPoint->y() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
2171
2174 {
2175 return false;
2176 }
2177
2178 const Qgis::RasterIdentifyFormat identifyFormat(
2179 static_cast<bool>( layer->dataProvider()->capabilities() & QgsRasterDataProvider::IdentifyFeature )
2181 : Qgis::RasterIdentifyFormat::Value );
2182
2183 QgsRasterIdentifyResult identifyResult;
2184 if ( layer->crs() != mapSettings.destinationCrs() )
2185 {
2186 const QgsRectangle extent { mapSettings.extent() };
2187 const QgsCoordinateTransform transform { mapSettings.destinationCrs(), layer->crs(), mapSettings.transformContext() };
2188 if ( ! transform.isValid() )
2189 {
2190 throw QgsBadRequestException( QgsServiceException::OGC_InvalidCRS, QStringLiteral( "CRS transform error from %1 to %2 in layer %3" )
2191 .arg( mapSettings.destinationCrs().authid() )
2192 .arg( layer->crs().authid() )
2193 .arg( layer->name() ) );
2194 }
2195 identifyResult = layer->dataProvider()->identify( *infoPoint, identifyFormat, transform.transform( extent ), mapSettings.outputSize().width(), mapSettings.outputSize().height() );
2196 }
2197 else
2198 {
2199 identifyResult = layer->dataProvider()->identify( *infoPoint, identifyFormat, mapSettings.extent(), mapSettings.outputSize().width(), mapSettings.outputSize().height() );
2200 }
2201
2202 if ( !identifyResult.isValid() )
2203 return false;
2204
2205 QMap<int, QVariant> attributes = identifyResult.results();
2206
2207 if ( mWmsParameters.infoFormat() == QgsWmsParameters::Format::GML )
2208 {
2209 QgsFeature feature;
2210 QgsFields fields;
2211 QgsCoordinateReferenceSystem layerCrs = layer->crs();
2212 int gmlVersion = mWmsParameters.infoFormatVersion();
2213 QString typeName = mContext.layerNickname( *layer );
2214
2215 if ( identifyFormat == Qgis::RasterIdentifyFormat::Value )
2216 {
2217 feature.initAttributes( attributes.count() );
2218 int index = 0;
2219 for ( auto it = attributes.constBegin(); it != attributes.constEnd(); ++it )
2220 {
2221 fields.append( QgsField( layer->bandName( it.key() ), QVariant::Double ) );
2222 feature.setAttribute( index++, QString::number( it.value().toDouble() ) );
2223 }
2224 feature.setFields( fields );
2225 QDomElement elem = createFeatureGML(
2226 &feature, nullptr, infoDocument, layerCrs, mapSettings, typeName, false, gmlVersion, nullptr );
2227 layerElement.appendChild( elem );
2228 }
2229 else
2230 {
2231 const auto values = identifyResult.results();
2232 for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
2233 {
2234 QVariant value = it.value();
2235 if ( value.type() == QVariant::Bool && !value.toBool() )
2236 {
2237 // sublayer not visible or not queryable
2238 continue;
2239 }
2240
2241 if ( value.type() == QVariant::String )
2242 {
2243 continue;
2244 }
2245
2246 // list of feature stores for a single sublayer
2247 const QgsFeatureStoreList featureStoreList = it.value().value<QgsFeatureStoreList>();
2248
2249 for ( const QgsFeatureStore &featureStore : featureStoreList )
2250 {
2251 const QgsFeatureList storeFeatures = featureStore.features();
2252 for ( const QgsFeature &feature : storeFeatures )
2253 {
2254 QDomElement elem = createFeatureGML(
2255 &feature, nullptr, infoDocument, layerCrs, mapSettings, typeName, false, gmlVersion, nullptr );
2256 layerElement.appendChild( elem );
2257 }
2258 }
2259 }
2260 }
2261 }
2262 else
2263 {
2264 if ( identifyFormat == Qgis::RasterIdentifyFormat::Value )
2265 {
2266 for ( auto it = attributes.constBegin(); it != attributes.constEnd(); ++it )
2267 {
2268 QDomElement attributeElement = infoDocument.createElement( QStringLiteral( "Attribute" ) );
2269 attributeElement.setAttribute( QStringLiteral( "name" ), layer->bandName( it.key() ) );
2270
2271 QString value;
2272 if ( ! QgsVariantUtils::isNull( it.value() ) )
2273 {
2274 value = QString::number( it.value().toDouble() );
2275 }
2276
2277 attributeElement.setAttribute( QStringLiteral( "value" ), value );
2278 layerElement.appendChild( attributeElement );
2279 }
2280 }
2281 else // feature
2282 {
2283 const auto values = identifyResult.results();
2284 for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
2285 {
2286 QVariant value = it.value();
2287 if ( value.type() == QVariant::Bool && !value.toBool() )
2288 {
2289 // sublayer not visible or not queryable
2290 continue;
2291 }
2292
2293 if ( value.type() == QVariant::String )
2294 {
2295 continue;
2296 }
2297
2298 // list of feature stores for a single sublayer
2299 const QgsFeatureStoreList featureStoreList = it.value().value<QgsFeatureStoreList>();
2300 for ( const QgsFeatureStore &featureStore : featureStoreList )
2301 {
2302 const QgsFeatureList storeFeatures = featureStore.features();
2303 for ( const QgsFeature &feature : storeFeatures )
2304 {
2305 for ( const auto &fld : feature.fields() )
2306 {
2307 const auto val { feature.attribute( fld.name() )};
2308 if ( val.isValid() )
2309 {
2310 QDomElement attributeElement = infoDocument.createElement( QStringLiteral( "Attribute" ) );
2311 attributeElement.setAttribute( QStringLiteral( "name" ), fld.name() );
2312 attributeElement.setAttribute( QStringLiteral( "value" ), val.toString() );
2313 layerElement.appendChild( attributeElement );
2314 }
2315 }
2316 }
2317 }
2318 }
2319 }
2320 //add maptip attribute based on html/expression
2321 QString mapTip = layer->mapTipTemplate();
2322 if ( !mapTip.isEmpty() && ( mWmsParameters.withMapTip() || mWmsParameters.htmlInfoOnlyMapTip() ) )
2323 {
2324 QDomElement maptipElem = infoDocument.createElement( QStringLiteral( "Attribute" ) );
2325 maptipElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "maptip" ) );
2326 QgsExpressionContext context { renderContext.expressionContext() };
2328 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_cursor_point" ), QVariant::fromValue( QgsGeometry::fromPointXY( QgsPointXY( infoPoint->x(), infoPoint->y() ) ) ) ) );
2329 context.appendScope( scope );
2330 maptipElem.setAttribute( QStringLiteral( "value" ), QgsExpression::replaceExpressionText( mapTip, &context ) );
2331 layerElement.appendChild( maptipElem );
2332 }
2333 }
2334 return true;
2335 }
2336
2337 bool QgsRenderer::testFilterStringSafety( const QString &filter ) const
2338 {
2339 //; too dangerous for sql injections
2340 if ( filter.contains( QLatin1String( ";" ) ) )
2341 {
2342 return false;
2343 }
2344
2345 QStringList tokens = filter.split( ' ', Qt::SkipEmptyParts );
2346 groupStringList( tokens, QStringLiteral( "'" ) );
2347 groupStringList( tokens, QStringLiteral( "\"" ) );
2348
2349 for ( auto tokenIt = tokens.constBegin() ; tokenIt != tokens.constEnd(); ++tokenIt )
2350 {
2351 //allowlist of allowed characters and keywords
2352 if ( tokenIt->compare( QLatin1String( "," ) ) == 0
2353 || tokenIt->compare( QLatin1String( "(" ) ) == 0
2354 || tokenIt->compare( QLatin1String( ")" ) ) == 0
2355 || tokenIt->compare( QLatin1String( "=" ) ) == 0
2356 || tokenIt->compare( QLatin1String( "!=" ) ) == 0
2357 || tokenIt->compare( QLatin1String( "<" ) ) == 0
2358 || tokenIt->compare( QLatin1String( "<=" ) ) == 0
2359 || tokenIt->compare( QLatin1String( ">" ) ) == 0
2360 || tokenIt->compare( QLatin1String( ">=" ) ) == 0
2361 || tokenIt->compare( QLatin1String( "%" ) ) == 0
2362 || tokenIt->compare( QLatin1String( "IS" ), Qt::CaseInsensitive ) == 0
2363 || tokenIt->compare( QLatin1String( "NOT" ), Qt::CaseInsensitive ) == 0
2364 || tokenIt->compare( QLatin1String( "NULL" ), Qt::CaseInsensitive ) == 0
2365 || tokenIt->compare( QLatin1String( "AND" ), Qt::CaseInsensitive ) == 0
2366 || tokenIt->compare( QLatin1String( "OR" ), Qt::CaseInsensitive ) == 0
2367 || tokenIt->compare( QLatin1String( "IN" ), Qt::CaseInsensitive ) == 0
2368 || tokenIt->compare( QLatin1String( "LIKE" ), Qt::CaseInsensitive ) == 0
2369 || tokenIt->compare( QLatin1String( "ILIKE" ), Qt::CaseInsensitive ) == 0
2370 || tokenIt->compare( QLatin1String( "DMETAPHONE" ), Qt::CaseInsensitive ) == 0
2371 || tokenIt->compare( QLatin1String( "SOUNDEX" ), Qt::CaseInsensitive ) == 0
2372 || mContext.settings().allowedExtraSqlTokens().contains( *tokenIt, Qt::CaseSensitivity::CaseInsensitive ) )
2373 {
2374 continue;
2375 }
2376
2377 //numbers are OK
2378 bool isNumeric;
2379 tokenIt->toDouble( &isNumeric );
2380 if ( isNumeric )
2381 {
2382 continue;
2383 }
2384
2385 //numeric strings need to be quoted once either with single or with double quotes
2386
2387 //empty strings are OK
2388 if ( *tokenIt == QLatin1String( "''" ) )
2389 {
2390 continue;
2391 }
2392
2393 //single quote
2394 if ( tokenIt->size() > 2
2395 && ( *tokenIt )[0] == QChar( '\'' )
2396 && ( *tokenIt )[tokenIt->size() - 1] == QChar( '\'' )
2397 && ( *tokenIt )[1] != QChar( '\'' )
2398 && ( *tokenIt )[tokenIt->size() - 2] != QChar( '\'' ) )
2399 {
2400 continue;
2401 }
2402
2403 //double quote
2404 if ( tokenIt->size() > 2
2405 && ( *tokenIt )[0] == QChar( '"' )
2406 && ( *tokenIt )[tokenIt->size() - 1] == QChar( '"' )
2407 && ( *tokenIt )[1] != QChar( '"' )
2408 && ( *tokenIt )[tokenIt->size() - 2] != QChar( '"' ) )
2409 {
2410 continue;
2411 }
2412
2413 return false;
2414 }
2415
2416 return true;
2417 }
2418
2419 void QgsRenderer::groupStringList( QStringList &list, const QString &groupString )
2420 {
2421 //group contents within single quotes together
2422 bool groupActive = false;
2423 int startGroup = -1;
2424 QString concatString;
2425
2426 for ( int i = 0; i < list.size(); ++i )
2427 {
2428 QString &str = list[i];
2429 if ( str.startsWith( groupString ) )
2430 {
2431 startGroup = i;
2432 groupActive = true;
2433 concatString.clear();
2434 }
2435
2436 if ( groupActive )
2437 {
2438 if ( i != startGroup )
2439 {
2440 concatString.append( " " );
2441 }
2442 concatString.append( str );
2443 }
2444
2445 if ( str.endsWith( groupString ) )
2446 {
2447 int endGroup = i;
2448 groupActive = false;
2449
2450 if ( startGroup != -1 )
2451 {
2452 list[startGroup] = concatString;
2453 for ( int j = startGroup + 1; j <= endGroup; ++j )
2454 {
2455 list.removeAt( startGroup + 1 );
2456 --i;
2457 }
2458 }
2459
2460 concatString.clear();
2461 startGroup = -1;
2462 }
2463 }
2464 }
2465
2466 void QgsRenderer::convertFeatureInfoToSia2045( QDomDocument &doc ) const
2467 {
2468 QDomDocument SIAInfoDoc;
2469 QDomElement infoDocElement = doc.documentElement();
2470 QDomElement SIAInfoDocElement = SIAInfoDoc.importNode( infoDocElement, false ).toElement();
2471 SIAInfoDoc.appendChild( SIAInfoDocElement );
2472
2473 QString currentAttributeName;
2474 QString currentAttributeValue;
2475 QDomElement currentAttributeElem;
2476 QString currentLayerName;
2477 QDomElement currentLayerElem;
2478 QDomNodeList layerNodeList = infoDocElement.elementsByTagName( QStringLiteral( "Layer" ) );
2479 for ( int i = 0; i < layerNodeList.size(); ++i )
2480 {
2481 currentLayerElem = layerNodeList.at( i ).toElement();
2482 currentLayerName = currentLayerElem.attribute( QStringLiteral( "name" ) );
2483
2484 QDomElement currentFeatureElem;
2485
2486 QDomNodeList featureList = currentLayerElem.elementsByTagName( QStringLiteral( "Feature" ) );
2487 if ( featureList.isEmpty() )
2488 {
2489 //raster?
2490 QDomNodeList attributeList = currentLayerElem.elementsByTagName( QStringLiteral( "Attribute" ) );
2491 QDomElement rasterLayerElem;
2492 if ( !attributeList.isEmpty() )
2493 {
2494 rasterLayerElem = SIAInfoDoc.createElement( currentLayerName );
2495 }
2496 for ( int j = 0; j < attributeList.size(); ++j )
2497 {
2498 currentAttributeElem = attributeList.at( j ).toElement();
2499 currentAttributeName = currentAttributeElem.attribute( QStringLiteral( "name" ) );
2500 currentAttributeValue = currentAttributeElem.attribute( QStringLiteral( "value" ) );
2501 QDomElement outAttributeElem = SIAInfoDoc.createElement( currentAttributeName );
2502 QDomText outAttributeText = SIAInfoDoc.createTextNode( currentAttributeValue );
2503 outAttributeElem.appendChild( outAttributeText );
2504 rasterLayerElem.appendChild( outAttributeElem );
2505 }
2506 if ( !attributeList.isEmpty() )
2507 {
2508 SIAInfoDocElement.appendChild( rasterLayerElem );
2509 }
2510 }
2511 else //vector
2512 {
2513 //property attributes
2514 QSet<QString> layerPropertyAttributes;
2515 QString currentLayerId = currentLayerElem.attribute( QStringLiteral( "id" ) );
2516 if ( !currentLayerId.isEmpty() )
2517 {
2518 QgsMapLayer *currentLayer = mProject->mapLayer( currentLayerId );
2519 if ( currentLayer )
2520 {
2521 QString WMSPropertyAttributesString = currentLayer->customProperty( QStringLiteral( "WMSPropertyAttributes" ) ).toString();
2522 if ( !WMSPropertyAttributesString.isEmpty() )
2523 {
2524 QStringList propertyList = WMSPropertyAttributesString.split( QStringLiteral( "//" ) );
2525 for ( auto propertyIt = propertyList.constBegin() ; propertyIt != propertyList.constEnd(); ++propertyIt )
2526 {
2527 layerPropertyAttributes.insert( *propertyIt );
2528 }
2529 }
2530 }
2531 }
2532
2533 QDomElement propertyRefChild; //child to insert the next property after (or
2534 for ( int j = 0; j < featureList.size(); ++j )
2535 {
2536 QDomElement SIAFeatureElem = SIAInfoDoc.createElement( currentLayerName );
2537 currentFeatureElem = featureList.at( j ).toElement();
2538 QDomNodeList attributeList = currentFeatureElem.elementsByTagName( QStringLiteral( "Attribute" ) );
2539
2540 for ( int k = 0; k < attributeList.size(); ++k )
2541 {
2542 currentAttributeElem = attributeList.at( k ).toElement();
2543 currentAttributeName = currentAttributeElem.attribute( QStringLiteral( "name" ) );
2544 currentAttributeValue = currentAttributeElem.attribute( QStringLiteral( "value" ) );
2545 if ( layerPropertyAttributes.contains( currentAttributeName ) )
2546 {
2547 QDomElement propertyElem = SIAInfoDoc.createElement( QStringLiteral( "property" ) );
2548 QDomElement identifierElem = SIAInfoDoc.createElement( QStringLiteral( "identifier" ) );
2549 QDomText identifierText = SIAInfoDoc.createTextNode( currentAttributeName );
2550 identifierElem.appendChild( identifierText );
2551 QDomElement valueElem = SIAInfoDoc.createElement( QStringLiteral( "value" ) );
2552 QDomText valueText = SIAInfoDoc.createTextNode( currentAttributeValue );
2553 valueElem.appendChild( valueText );
2554 propertyElem.appendChild( identifierElem );
2555 propertyElem.appendChild( valueElem );
2556 if ( propertyRefChild.isNull() )
2557 {
2558 SIAFeatureElem.insertBefore( propertyElem, QDomNode() );
2559 propertyRefChild = propertyElem;
2560 }
2561 else
2562 {
2563 SIAFeatureElem.insertAfter( propertyElem, propertyRefChild );
2564 }
2565 }
2566 else
2567 {
2568 QDomElement SIAAttributeElem = SIAInfoDoc.createElement( currentAttributeName );
2569 QDomText SIAAttributeText = SIAInfoDoc.createTextNode( currentAttributeValue );
2570 SIAAttributeElem.appendChild( SIAAttributeText );
2571 SIAFeatureElem.appendChild( SIAAttributeElem );
2572 }
2573 }
2574 SIAInfoDocElement.appendChild( SIAFeatureElem );
2575 }
2576 }
2577 }
2578 doc = SIAInfoDoc;
2579 }
2580
2581 QByteArray QgsRenderer::convertFeatureInfoToHtml( const QDomDocument &doc ) const
2582 {
2583 const bool onlyMapTip = mWmsParameters.htmlInfoOnlyMapTip();
2584 QString featureInfoString = QStringLiteral( " <!DOCTYPE html>" );
2585 if ( !onlyMapTip )
2586 {
2587 featureInfoString.append( QStringLiteral( R"HTML(
2588
2589 <head>
2590 <title>Information</title>
2591 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
2592 <style>
2593 body {
2594 font-family: "Open Sans", "Calluna Sans", "Gill Sans MT", "Calibri", "Trebuchet MS", sans-serif;
2595 }
2596
2597 table,
2598 th,
2599 td {
2600 width: 100%;
2601 border: 1px solid black;
2602 border-collapse: collapse;
2603 text-align: left;
2604 padding: 2px;
2605 }
2606
2607 th {
2608 width: 25%;
2609 font-weight: bold;
2610 }
2611
2612 .layer-title {
2613 font-weight: bold;
2614 padding: 2px;
2615 }
2616 </style>
2617 </head>
2618
2619 <body>
2620 )HTML" ) );
2621 }
2622
2623 const QDomNodeList layerList = doc.elementsByTagName( QStringLiteral( "Layer" ) );
2624
2625 //layer loop
2626 for ( int i = 0; i < layerList.size(); ++i )
2627 {
2628 const QDomElement layerElem = layerList.at( i ).toElement();
2629
2630 //feature loop (for vector layers)
2631 const QDomNodeList featureNodeList = layerElem.elementsByTagName( QStringLiteral( "Feature" ) );
2632 const QDomElement currentFeatureElement;
2633
2634 if ( !featureNodeList.isEmpty() ) //vector layer
2635 {
2636 if ( !onlyMapTip )
2637 {
2638 const QString featureInfoLayerTitleString = QStringLiteral( " <div class='layer-title'>%1</div>" ).arg( layerElem.attribute( QStringLiteral( "title" ) ).toHtmlEscaped() );
2639 featureInfoString.append( featureInfoLayerTitleString );
2640 }
2641
2642 for ( int j = 0; j < featureNodeList.size(); ++j )
2643 {
2644 const QDomElement featureElement = featureNodeList.at( j ).toElement();
2645 if ( !onlyMapTip )
2646 {
2647 featureInfoString.append( QStringLiteral( R"HTML(
2648 <table>)HTML" ) );
2649 }
2650
2651 //attribute loop
2652 const QDomNodeList attributeNodeList = featureElement.elementsByTagName( QStringLiteral( "Attribute" ) );
2653 for ( int k = 0; k < attributeNodeList.size(); ++k )
2654 {
2655 const QDomElement attributeElement = attributeNodeList.at( k ).toElement();
2656 const QString name = attributeElement.attribute( QStringLiteral( "name" ) ).toHtmlEscaped();
2657 QString value = attributeElement.attribute( QStringLiteral( "value" ) );
2658 if ( name != QLatin1String( "maptip" ) )
2659 {
2660 value = value.toHtmlEscaped();
2661 }
2662
2663 if ( !onlyMapTip )
2664 {
2665 const QString featureInfoAttributeString = QStringLiteral( R"HTML(
2666 <tr>
2667 <th>%1</th>
2668 <td>%2</td>
2669 </tr>)HTML" ).arg( name, value );
2670
2671 featureInfoString.append( featureInfoAttributeString );
2672 }
2673 else if ( name == QStringLiteral( "maptip" ) )
2674 {
2675 featureInfoString.append( QStringLiteral( R"HTML(
2676 %1)HTML" ).arg( value ) );
2677 break;
2678 }
2679
2680 }
2681 if ( !onlyMapTip )
2682 {
2683 featureInfoString.append( QStringLiteral( R"HTML(
2684 </table>)HTML" ) );
2685 }
2686 }
2687 }
2688 else //no result or raster layer
2689 {
2690 const QDomNodeList attributeNodeList = layerElem.elementsByTagName( QStringLiteral( "Attribute" ) );
2691
2692 // raster layer
2693 if ( !attributeNodeList.isEmpty() )
2694 {
2695 if ( !onlyMapTip )
2696 {
2697 const QString featureInfoLayerTitleString = QStringLiteral( " <div class='layer-title'>%1</div>" ).arg( layerElem.attribute( QStringLiteral( "title" ) ).toHtmlEscaped() );
2698 featureInfoString.append( featureInfoLayerTitleString );
2699
2700 featureInfoString.append( QStringLiteral( R"HTML(
2701 <table>)HTML" ) );
2702 }
2703
2704 for ( int j = 0; j < attributeNodeList.size(); ++j )
2705 {
2706 const QDomElement attributeElement = attributeNodeList.at( j ).toElement();
2707 const QString name = attributeElement.attribute( QStringLiteral( "name" ) ).toHtmlEscaped();
2708 QString value = attributeElement.attribute( QStringLiteral( "value" ) );
2709 if ( value.isEmpty() )
2710 {
2711 value = QStringLiteral( "no data" );
2712 }
2713 if ( name != QLatin1String( "maptip" ) )
2714 {
2715 value = value.toHtmlEscaped();
2716 }
2717
2718 if ( !onlyMapTip )
2719 {
2720 const QString featureInfoAttributeString = QStringLiteral( R"HTML(
2721 <tr>
2722 <th>%1</th>
2723 <td>%2</td>
2724 </tr>)HTML" ).arg( name, value );
2725
2726
2727 featureInfoString.append( featureInfoAttributeString );
2728 }
2729 else if ( name == QStringLiteral( "maptip" ) )
2730 {
2731 featureInfoString.append( QStringLiteral( R"HTML(
2732 %1)HTML" ).arg( value ) );
2733 break;
2734 }
2735
2736 }
2737 if ( !onlyMapTip )
2738 {
2739 featureInfoString.append( QStringLiteral( R"HTML(
2740 </table>)HTML" ) );
2741 }
2742 }
2743 }
2744 }
2745
2746 //end the html body
2747 if ( !onlyMapTip )
2748 {
2749 featureInfoString.append( QStringLiteral( R"HTML(
2750 </body>)HTML" ) );
2751 }
2752
2753 return featureInfoString.toUtf8();
2754 }
2755
2756 QByteArray QgsRenderer::convertFeatureInfoToText( const QDomDocument &doc ) const
2757 {
2758 QString featureInfoString;
2759
2760 //the Text head
2761 featureInfoString.append( "GetFeatureInfo results\n" );
2762 featureInfoString.append( "\n" );
2763
2764 QDomNodeList layerList = doc.elementsByTagName( QStringLiteral( "Layer" ) );
2765
2766 //layer loop
2767 for ( int i = 0; i < layerList.size(); ++i )
2768 {
2769 QDomElement layerElem = layerList.at( i ).toElement();
2770
2771 featureInfoString.append( "Layer '" + layerElem.attribute( QStringLiteral( "name" ) ) + "'\n" );
2772
2773 //feature loop (for vector layers)
2774 QDomNodeList featureNodeList = layerElem.elementsByTagName( QStringLiteral( "Feature" ) );
2775 QDomElement currentFeatureElement;
2776
2777 if ( !featureNodeList.isEmpty() ) //vector layer
2778 {
2779 for ( int j = 0; j < featureNodeList.size(); ++j )
2780 {
2781 QDomElement featureElement = featureNodeList.at( j ).toElement();
2782 featureInfoString.append( "Feature " + featureElement.attribute( QStringLiteral( "id" ) ) + "\n" );
2783
2784 //attribute loop
2785 QDomNodeList attributeNodeList = featureElement.elementsByTagName( QStringLiteral( "Attribute" ) );
2786 for ( int k = 0; k < attributeNodeList.size(); ++k )
2787 {
2788 QDomElement attributeElement = attributeNodeList.at( k ).toElement();
2789 featureInfoString.append( attributeElement.attribute( QStringLiteral( "name" ) ) + " = '" +
2790 attributeElement.attribute( QStringLiteral( "value" ) ) + "'\n" );
2791 }
2792 }
2793 }
2794 else //raster layer
2795 {
2796 QDomNodeList attributeNodeList = layerElem.elementsByTagName( QStringLiteral( "Attribute" ) );
2797 for ( int j = 0; j < attributeNodeList.size(); ++j )
2798 {
2799 QDomElement attributeElement = attributeNodeList.at( j ).toElement();
2800 QString value = attributeElement.attribute( QStringLiteral( "value" ) );
2801 if ( value.isEmpty() )
2802 {
2803 value = QStringLiteral( "no data" );
2804 }
2805 featureInfoString.append( attributeElement.attribute( QStringLiteral( "name" ) ) + " = '" +
2806 value + "'\n" );
2807 }
2808 }
2809
2810 featureInfoString.append( "\n" );
2811 }
2812
2813 return featureInfoString.toUtf8();
2814 }
2815
2816 QByteArray QgsRenderer::convertFeatureInfoToJson( const QList<QgsMapLayer *> &layers, const QDomDocument &doc ) const
2817 {
2818 json json
2819 {
2820 { "type", "FeatureCollection" },
2821 { "features", json::array() },
2822 };
2823 const bool withGeometry = ( QgsServerProjectUtils::wmsFeatureInfoAddWktGeometry( *mProject ) && mWmsParameters.withGeometry() );
2824
2825 const QDomNodeList layerList = doc.elementsByTagName( QStringLiteral( "Layer" ) );
2826 for ( int i = 0; i < layerList.size(); ++i )
2827 {
2828 const QDomElement layerElem = layerList.at( i ).toElement();
2829 const QString layerName = layerElem.attribute( QStringLiteral( "name" ) );
2830
2831 QgsMapLayer *layer = nullptr;
2832 for ( QgsMapLayer *l : layers )
2833 {
2834 if ( mContext.layerNickname( *l ).compare( layerName ) == 0 )
2835 {
2836 layer = l;
2837 }
2838 }
2839
2840 if ( !layer )
2841 continue;
2842
2843 if ( layer->type() == Qgis::LayerType::Vector )
2844 {
2845 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
2846
2847 // search features to export
2848 QgsFeatureList features;
2849 QgsAttributeList attributes;
2850 const QDomNodeList featuresNode = layerElem.elementsByTagName( QStringLiteral( "Feature" ) );
2851 if ( featuresNode.isEmpty() )
2852 continue;
2853
2854 QMap<QgsFeatureId, QString> fidMap;
2855
2856 for ( int j = 0; j < featuresNode.size(); ++j )
2857 {
2858 const QDomElement featureNode = featuresNode.at( j ).toElement();
2859 const QString fid = featureNode.attribute( QStringLiteral( "id" ) );
2860 QgsFeature feature;
2861 const QString expression { QgsServerFeatureId::getExpressionFromServerFid( fid, static_cast<QgsVectorDataProvider *>( layer->dataProvider() ) ) };
2862 if ( expression.isEmpty() )
2863 {
2864 feature = vl->getFeature( fid.toLongLong() );
2865 }
2866 else
2867 {
2868 QgsFeatureRequest request { QgsExpression( expression )};
2869 request.setFlags( Qgis::FeatureRequestFlag::NoGeometry );
2870 vl->getFeatures( request ).nextFeature( feature );
2871 }
2872
2873 fidMap.insert( feature.id(), fid );
2874
2875 QString wkt;
2876 if ( withGeometry )
2877 {
2878 const QDomNodeList attrs = featureNode.elementsByTagName( "Attribute" );
2879 for ( int k = 0; k < attrs.count(); k++ )
2880 {
2881 const QDomElement elm = attrs.at( k ).toElement();
2882 if ( elm.attribute( QStringLiteral( "name" ) ).compare( "geometry" ) == 0 )
2883 {
2884 wkt = elm.attribute( "value" );
2885 break;
2886 }
2887 }
2888
2889 if ( ! wkt.isEmpty() )
2890 {
2891 // CRS in WMS parameters may be different from the layer
2892 feature.setGeometry( QgsGeometry::fromWkt( wkt ) );
2893 }
2894 }
2895 features << feature;
2896
2897 // search attributes to export (one time only)
2898 if ( !attributes.isEmpty() )
2899 continue;
2900
2901 const QDomNodeList attributesNode = featureNode.elementsByTagName( QStringLiteral( "Attribute" ) );
2902 for ( int k = 0; k < attributesNode.size(); ++k )
2903 {
2904 const QDomElement attributeElement = attributesNode.at( k ).toElement();
2905 const QString fieldName = attributeElement.attribute( QStringLiteral( "name" ) );
2906
2907 attributes << feature.fieldNameIndex( fieldName );
2908 }
2909 }
2910
2911 // export
2912 QgsJsonExporter exporter( vl );
2913 exporter.setAttributeDisplayName( true );
2914 exporter.setAttributes( attributes );
2915 exporter.setIncludeGeometry( withGeometry );
2916 exporter.setTransformGeometries( false );
2917
2918 for ( const auto &feature : std::as_const( features ) )
2919 {
2920 const QString id = QStringLiteral( "%1.%2" ).arg( layerName ).arg( fidMap.value( feature.id() ) );
2921 json["features"].push_back( exporter.exportFeatureToJsonObject( feature, QVariantMap(), id ) );
2922 }
2923 }
2924 else // raster layer
2925 {
2926 auto properties = json::object();
2927 const QDomNodeList attributesNode = layerElem.elementsByTagName( QStringLiteral( "Attribute" ) );
2928 for ( int j = 0; j < attributesNode.size(); ++j )
2929 {
2930 const QDomElement attrElmt = attributesNode.at( j ).toElement();
2931 const QString name = attrElmt.attribute( QStringLiteral( "name" ) );
2932
2933 QString value = attrElmt.attribute( QStringLiteral( "value" ) );
2934 if ( value.isEmpty() )
2935 {
2936 value = QStringLiteral( "null" );
2937 }
2938
2939 properties[name.toStdString()] = value.toStdString();
2940 }
2941
2942 json["features"].push_back(
2943 {
2944 {"type", "Feature" },
2945 {"id", layerName.toStdString() },
2946 {"properties", properties }
2947 } );
2948 }
2949 }
2950#ifdef QGISDEBUG
2951 // This is only useful to generate human readable reference files for tests
2952 return QByteArray::fromStdString( json.dump( 2 ) );
2953#else
2954 return QByteArray::fromStdString( json.dump() );
2955#endif
2956 }
2957
2958 QDomElement QgsRenderer::createFeatureGML(
2959 const QgsFeature *feat,
2960 QgsVectorLayer *layer,
2961 QDomDocument &doc,
2963 const QgsMapSettings &mapSettings,
2964 const QString &typeName,
2965 bool withGeom,
2966 int version,
2967 QStringList *attributes ) const
2968 {
2969 //qgs:%TYPENAME%
2970 QDomElement typeNameElement = doc.createElement( "qgs:" + typeName /*qgs:%TYPENAME%*/ );
2971 QString fid;
2972 if ( layer && layer->dataProvider() )
2974 else
2975 fid = FID_TO_STRING( feat->id() );
2976
2977 typeNameElement.setAttribute( QStringLiteral( "fid" ), QStringLiteral( "%1.%2" ).arg( typeName, fid ) );
2978
2979 QgsCoordinateTransform transform;
2980 if ( layer && layer->crs() != crs )
2981 {
2982 transform = mapSettings.layerTransform( layer );
2983 }
2984
2985 QgsGeometry geom = feat->geometry();
2986
2987 QgsExpressionContext expressionContext;
2988 expressionContext << QgsExpressionContextUtils::globalScope()
2990 if ( layer )
2991 expressionContext << QgsExpressionContextUtils::layerScope( layer );
2992 expressionContext.setFeature( *feat );
2993
2994 // always add bounding box info if feature contains geometry and has been
2995 // explicitly configured in the project
2997 !geom.isNull() && geom.type() != Qgis::GeometryType::Unknown &&
2998 geom.type() != Qgis::GeometryType::Null )
2999 {
3000 QgsRectangle box = feat->geometry().boundingBox();
3001 if ( transform.isValid() )
3002 {
3003 try
3004 {
3005 box = transform.transformBoundingBox( box );
3006 }
3007 catch ( QgsCsException &e )
3008 {
3009 QgsMessageLog::logMessage( QStringLiteral( "Transform error caught: %1" ).arg( e.what() ) );
3010 }
3011 }
3012
3013 QDomElement bbElem = doc.createElement( QStringLiteral( "gml:boundedBy" ) );
3014 QDomElement boxElem;
3015 if ( version < 3 )
3016 {
3017 boxElem = QgsOgcUtils::rectangleToGMLBox( &box, doc, mContext.precision() );
3018 }
3019 else
3020 {
3021 boxElem = QgsOgcUtils::rectangleToGMLEnvelope( &box, doc, mContext.precision() );
3022 }
3023
3024 if ( crs.isValid() )
3025 {
3026 boxElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() );
3027 }
3028 bbElem.appendChild( boxElem );
3029 typeNameElement.appendChild( bbElem );
3030 }
3031
3032 if ( withGeom && !geom.isNull() )
3033 {
3034 //add geometry column (as gml)
3035
3036 if ( transform.isValid() )
3037 {
3038 geom.transform( transform );
3039 }
3040
3041 QDomElement geomElem = doc.createElement( QStringLiteral( "qgs:geometry" ) );
3042 QDomElement gmlElem;
3043 if ( version < 3 )
3044 {
3045 gmlElem = QgsOgcUtils::geometryToGML( geom, doc, mContext.precision() );
3046 }
3047 else
3048 {
3049 gmlElem = QgsOgcUtils::geometryToGML( geom, doc, QStringLiteral( "GML3" ), mContext.precision() );
3050 }
3051
3052 if ( !gmlElem.isNull() )
3053 {
3054 if ( crs.isValid() )
3055 {
3056 gmlElem.setAttribute( QStringLiteral( "srsName" ), crs.authid() );
3057 }
3058 geomElem.appendChild( gmlElem );
3059 typeNameElement.appendChild( geomElem );
3060 }
3061 }
3062
3063 //read all allowed attribute values from the feature
3064 QgsAttributes featureAttributes = feat->attributes();
3065 QgsFields fields = feat->fields();
3066 for ( int i = 0; i < fields.count(); ++i )
3067 {
3068 QString attributeName = fields.at( i ).name();
3069 //skip attribute if it is explicitly excluded from WMS publication
3070 if ( fields.at( i ).configurationFlags().testFlag( Qgis::FieldConfigurationFlag::HideFromWms ) )
3071 {
3072 continue;
3073 }
3074 //skip attribute if it is excluded by access control
3075 if ( attributes && !attributes->contains( attributeName ) )
3076 {
3077 continue;
3078 }
3079
3080 QDomElement fieldElem = doc.createElement( "qgs:" + attributeName.replace( ' ', '_' ) );
3081 QString fieldTextString = featureAttributes.at( i ).toString();
3082 if ( layer )
3083 {
3084 fieldTextString = QgsExpression::replaceExpressionText( replaceValueMapAndRelation( layer, i, fieldTextString ), &expressionContext );
3085 }
3086 QDomText fieldText = doc.createTextNode( fieldTextString );
3087 fieldElem.appendChild( fieldText );
3088 typeNameElement.appendChild( fieldElem );
3089 }
3090
3091 //add maptip attribute based on html/expression (in case there is no maptip attribute)
3092 if ( layer )
3093 {
3094 QString mapTip = layer->mapTipTemplate();
3095
3096 if ( !mapTip.isEmpty() && ( mWmsParameters.withMapTip() || mWmsParameters.htmlInfoOnlyMapTip() ) )
3097 {
3098 QString fieldTextString = QgsExpression::replaceExpressionText( mapTip, &expressionContext );
3099 QDomElement fieldElem = doc.createElement( QStringLiteral( "qgs:maptip" ) );
3100 QDomText maptipText = doc.createTextNode( fieldTextString );
3101 fieldElem.appendChild( maptipText );
3102 typeNameElement.appendChild( fieldElem );
3103 }
3104 }
3105
3106 return typeNameElement;
3107 }
3108
3109 QString QgsRenderer::replaceValueMapAndRelation( QgsVectorLayer *vl, int idx, const QVariant &attributeVal )
3110 {
3111 const QgsEditorWidgetSetup setup = vl->editorWidgetSetup( idx );
3113 QString value( fieldFormatter->representValue( vl, idx, setup.config(), QVariant(), attributeVal ) );
3114
3115 if ( setup.config().value( QStringLiteral( "AllowMulti" ) ).toBool() && value.startsWith( QLatin1Char( '{' ) ) && value.endsWith( QLatin1Char( '}' ) ) )
3116 {
3117 value = value.mid( 1, value.size() - 2 );
3118 }
3119 return value;
3120 }
3121
3122 QgsRectangle QgsRenderer::featureInfoSearchRect( QgsVectorLayer *ml, const QgsMapSettings &mapSettings, const QgsRenderContext &rct, const QgsPointXY &infoPoint ) const
3123 {
3124 if ( !ml )
3125 {
3126 return QgsRectangle();
3127 }
3128
3129 double mapUnitTolerance = 0.0;
3131 {
3132 if ( ! mWmsParameters.polygonTolerance().isEmpty()
3133 && mWmsParameters.polygonToleranceAsInt() > 0 )
3134 {
3135 mapUnitTolerance = mWmsParameters.polygonToleranceAsInt() * rct.mapToPixel().mapUnitsPerPixel();
3136 }
3137 else
3138 {
3139 mapUnitTolerance = mapSettings.extent().width() / 400.0;
3140 }
3141 }
3142 else if ( ml->geometryType() == Qgis::GeometryType::Line )
3143 {
3144 if ( ! mWmsParameters.lineTolerance().isEmpty()
3145 && mWmsParameters.lineToleranceAsInt() > 0 )
3146 {
3147 mapUnitTolerance = mWmsParameters.lineToleranceAsInt() * rct.mapToPixel().mapUnitsPerPixel();
3148 }
3149 else
3150 {
3151 mapUnitTolerance = mapSettings.extent().width() / 200.0;
3152 }
3153 }
3154 else //points
3155 {
3156 if ( ! mWmsParameters.pointTolerance().isEmpty()
3157 && mWmsParameters.pointToleranceAsInt() > 0 )
3158 {
3159 mapUnitTolerance = mWmsParameters.pointToleranceAsInt() * rct.mapToPixel().mapUnitsPerPixel();
3160 }
3161 else
3162 {
3163 mapUnitTolerance = mapSettings.extent().width() / 100.0;
3164 }
3165 }
3166
3167 QgsRectangle mapRectangle( infoPoint.x() - mapUnitTolerance, infoPoint.y() - mapUnitTolerance,
3168 infoPoint.x() + mapUnitTolerance, infoPoint.y() + mapUnitTolerance );
3169 return ( mapSettings.mapToLayerCoordinates( ml, mapRectangle ) );
3170 }
3171
3172 QList<QgsMapLayer *> QgsRenderer::highlightLayers( QList<QgsWmsParametersHighlightLayer> params )
3173 {
3174 QList<QgsMapLayer *> highlightLayers;
3175
3176 // try to create highlight layer for each geometry
3177 QString crs = mWmsParameters.crs();
3178 for ( const QgsWmsParametersHighlightLayer &param : params )
3179 {
3180 // create sld document from symbology
3181 QDomDocument sldDoc;
3182 QString errorMsg;
3183 int errorLine;
3184 int errorColumn;
3185 if ( !sldDoc.setContent( param.mSld, true, &errorMsg, &errorLine, &errorColumn ) )
3186 {
3187 QgsMessageLog::logMessage( QStringLiteral( "Error parsing SLD for layer %1 at line %2, column %3:\n%4" )
3188 .arg( param.mName )
3189 .arg( errorLine )
3190 .arg( errorColumn )
3191 .arg( errorMsg ),
3192 QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
3193 continue;
3194 }
3195
3196 // create renderer from sld document
3197 std::unique_ptr<QgsFeatureRenderer> renderer;
3198 QDomElement el = sldDoc.documentElement();
3199 renderer.reset( QgsFeatureRenderer::loadSld( el, param.mGeom.type(), errorMsg ) );
3200 if ( !renderer )
3201 {
3203 continue;
3204 }
3205
3206 // build url for vector layer
3207 const QString typeName = QgsWkbTypes::displayString( param.mGeom.wkbType() );
3208 QString url = typeName + "?crs=" + crs;
3209 if ( ! param.mLabel.isEmpty() )
3210 {
3211 url += "&field=label:string";
3212 }
3213
3214 // create vector layer
3216 std::unique_ptr<QgsVectorLayer> layer = std::make_unique<QgsVectorLayer>( url, param.mName, QLatin1String( "memory" ), options );
3217 if ( !layer->isValid() )
3218 {
3219 continue;
3220 }
3221
3222 // create feature with label if necessary
3223 QgsFeature fet( layer->fields() );
3224 if ( ! param.mLabel.isEmpty() )
3225 {
3226 fet.setAttribute( 0, param.mLabel );
3227
3228 // init labeling engine
3229 QgsPalLayerSettings palSettings;
3230 palSettings.fieldName = "label"; // defined in url
3231 palSettings.priority = 10; // always drawn
3233 palSettings.placementSettings().setAllowDegradedPlacement( true );
3234 palSettings.dist = param.mLabelDistance;
3235
3236 if ( !qgsDoubleNear( param.mLabelRotation, 0 ) )
3237 {
3239 palSettings.dataDefinedProperties().setProperty( pR, param.mLabelRotation );
3240 }
3241
3243 switch ( param.mGeom.type() )
3244 {
3246 {
3247 if ( param.mHali.isEmpty() || param.mVali.isEmpty() || QgsWkbTypes::flatType( param.mGeom.wkbType() ) != Qgis::WkbType::Point )
3248 {
3250 palSettings.lineSettings().setPlacementFlags( Qgis::LabelLinePlacementFlags() );
3251 }
3252 else //set label directly on point if there is hali/vali
3253 {
3254 QgsPointXY pt = param.mGeom.asPoint();
3256 QVariant x( pt.x() );
3257 palSettings.dataDefinedProperties().setProperty( pX, x );
3259 QVariant y( pt.y() );
3260 palSettings.dataDefinedProperties().setProperty( pY, y );
3262 palSettings.dataDefinedProperties().setProperty( pHali, param.mHali );
3264 palSettings.dataDefinedProperties().setProperty( pVali, param.mVali );
3265 }
3266
3267 break;
3268 }
3270 {
3271 QgsGeometry point = param.mGeom.pointOnSurface();
3272 QgsPointXY pt = point.asPoint();
3274
3276 QVariant x( pt.x() );
3277 palSettings.dataDefinedProperties().setProperty( pX, x );
3278
3280 QVariant y( pt.y() );
3281 palSettings.dataDefinedProperties().setProperty( pY, y );
3282
3284 QVariant hali( "Center" );
3285 palSettings.dataDefinedProperties().setProperty( pHali, hali );
3286
3288 QVariant vali( "Half" );
3289 palSettings.dataDefinedProperties().setProperty( pVali, vali );
3290 break;
3291 }
3292 default:
3293 {
3294 placement = Qgis::LabelPlacement::Line;
3296 break;
3297 }
3298 }
3299 palSettings.placement = placement;
3300 QgsTextFormat textFormat;
3301 QgsTextBufferSettings bufferSettings;
3302
3303 if ( param.mColor.isValid() )
3304 {
3305 textFormat.setColor( param.mColor );
3306 }
3307
3308 if ( param.mSize > 0 )
3309 {
3310 textFormat.setSize( param.mSize );
3311 }
3312
3313 // no weight property in PAL settings or QgsTextFormat
3314 /* if ( param.fontWeight > 0 )
3315 {
3316 } */
3317
3318 if ( ! param.mFont.isEmpty() )
3319 {
3320 textFormat.setFont( param.mFont );
3321 }
3322
3323 if ( param.mBufferColor.isValid() )
3324 {
3325 bufferSettings.setColor( param.mBufferColor );
3326 }
3327
3328 if ( param.mBufferSize > 0 )
3329 {
3330 bufferSettings.setEnabled( true );
3331 bufferSettings.setSize( static_cast<double>( param.mBufferSize ) );
3332 }
3333
3334 textFormat.setBuffer( bufferSettings );
3335 palSettings.setFormat( textFormat );
3336
3337 QgsVectorLayerSimpleLabeling *simpleLabeling = new QgsVectorLayerSimpleLabeling( palSettings );
3338 layer->setLabeling( simpleLabeling );
3339 layer->setLabelsEnabled( true );
3340 }
3341 fet.setGeometry( param.mGeom );
3342
3343 // add feature to layer and set the SLD renderer
3344 layer->dataProvider()->addFeatures( QgsFeatureList() << fet );
3345 layer->setRenderer( renderer.release() );
3346
3347 // keep the vector as an highlight layer
3348 if ( layer->isValid() )
3349 {
3350 highlightLayers.append( layer.release() );
3351 }
3352 }
3353
3354 mTemporaryLayers.append( highlightLayers );
3355 return highlightLayers;
3356 }
3357
3358 void QgsRenderer::removeTemporaryLayers()
3359 {
3360 qDeleteAll( mTemporaryLayers );
3361 mTemporaryLayers.clear();
3362 }
3363
3364 QPainter *QgsRenderer::layersRendering( const QgsMapSettings &mapSettings, QImage &image ) const
3365 {
3366 QPainter *painter = nullptr;
3367
3369 filters.addProvider( &mFeatureFilter );
3370#ifdef HAVE_SERVER_PYTHON_PLUGINS
3371 mContext.accessControl()->resolveFilterFeatures( mapSettings.layers() );
3372 filters.addProvider( mContext.accessControl() );
3373#endif
3374 QgsMapRendererJobProxy renderJob( mContext.settings().parallelRendering(), mContext.settings().maxThreads(), &filters );
3375
3376 renderJob.render( mapSettings, &image, mContext.socketFeedback() );
3377 painter = renderJob.takePainter();
3378
3379 if ( !renderJob.errors().isEmpty() )
3380 {
3381 const QgsMapRendererJob::Error e = renderJob.errors().at( 0 );
3382
3383 QString layerWMSName;
3384 QgsMapLayer *errorLayer = mProject->mapLayer( e.layerID );
3385 if ( errorLayer )
3386 {
3387 layerWMSName = mContext.layerNickname( *errorLayer );
3388 }
3389
3390 QString errorMessage = QStringLiteral( "Rendering error : '%1'" ).arg( e.message );
3391 if ( ! layerWMSName.isEmpty() )
3392 {
3393 errorMessage = QStringLiteral( "Rendering error : '%1' in layer '%2'" ).arg( e.message, layerWMSName );
3394 }
3395 throw QgsException( errorMessage );
3396 }
3397
3398 return painter;
3399 }
3400
3401 void QgsRenderer::setLayerOpacity( QgsMapLayer *layer, int opacity ) const
3402 {
3403 if ( opacity >= 0 && opacity <= 255 )
3404 {
3405 switch ( layer->type() )
3406 {
3408 {
3409 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
3410 vl->setOpacity( opacity / 255. );
3411 // Labeling
3412 if ( vl->labelsEnabled() && vl->labeling() )
3413 {
3414 QgsAbstractVectorLayerLabeling *labeling { vl->labeling() };
3415 labeling->multiplyOpacity( opacity / 255. );
3416 }
3417 break;
3418 }
3419
3421 {
3422 QgsRasterLayer *rl = qobject_cast<QgsRasterLayer *>( layer );
3423 QgsRasterRenderer *rasterRenderer = rl->renderer();
3424 rasterRenderer->setOpacity( opacity / 255. );
3425 break;
3426 }
3427
3429 {
3430 QgsVectorTileLayer *vl = qobject_cast<QgsVectorTileLayer *>( layer );
3431 vl->setOpacity( opacity / 255. );
3432 break;
3433 }
3434
3441 break;
3442 }
3443 }
3444 }
3445
3446 void QgsRenderer::setLayerFilter( QgsMapLayer *layer, const QList<QgsWmsParametersFilter> &filters )
3447 {
3448
3449 if ( layer->type() == Qgis::LayerType::Vector )
3450 {
3451 QgsVectorLayer *filteredLayer = qobject_cast<QgsVectorLayer *>( layer );
3452 QStringList expList;
3453 for ( const QgsWmsParametersFilter &filter : filters )
3454 {
3455 if ( filter.mType == QgsWmsParametersFilter::OGC_FE )
3456 {
3457 // OGC filter
3458 QDomDocument filterXml;
3459 QString errorMsg;
3460 if ( !filterXml.setContent( filter.mFilter, true, &errorMsg ) )
3461 {
3463 QStringLiteral( "Filter string rejected. Error message: %1. The XML string was: %2" ).arg( errorMsg, filter.mFilter ) );
3464 }
3465 QDomElement filterElem = filterXml.firstChildElement();
3466 std::unique_ptr<QgsExpression> filterExp( QgsOgcUtils::expressionFromOgcFilter( filterElem, filter.mVersion, filteredLayer ) );
3467
3468 if ( filterExp )
3469 {
3470 expList << filterExp->dump();
3471 }
3472 }
3473 else if ( filter.mType == QgsWmsParametersFilter::SQL )
3474 {
3475 // QGIS (SQL) filter
3476 if ( !testFilterStringSafety( filter.mFilter ) )
3477 {
3478 throw QgsSecurityException( QStringLiteral( "The filter string %1"
3479 " has been rejected because of security reasons."
3480 " Note: Text strings have to be enclosed in single or double quotes."
3481 " A space between each word / special character is mandatory."
3482 " Allowed Keywords and special characters are"
3483 " IS,NOT,NULL,AND,OR,IN,=,<,>=,>,>=,!=,',',(,),DMETAPHONE,SOUNDEX%2."
3484 " Not allowed are semicolons in the filter expression." ).arg(
3485 filter.mFilter, mContext.settings().allowedExtraSqlTokens().isEmpty() ?
3486 QString() :
3487 mContext.settings().allowedExtraSqlTokens().join( ',' ).prepend( ',' ) ) );
3488 }
3489
3490 QString newSubsetString = filter.mFilter;
3491 if ( !filteredLayer->subsetString().isEmpty() )
3492 {
3493 newSubsetString.prepend( ") AND (" );
3494 newSubsetString.append( ")" );
3495 newSubsetString.prepend( filteredLayer->subsetString() );
3496 newSubsetString.prepend( "(" );
3497 }
3498 if ( ! filteredLayer->setSubsetString( newSubsetString ) )
3499 {
3500 QgsMessageLog::logMessage( QStringLiteral( "Error setting subset string from filter for layer %1, filter: %2" ).arg( layer->name(), newSubsetString ),
3501 QStringLiteral( "Server" ),
3504 QStringLiteral( "Filter not valid for layer %1: check the filter syntax and the field names." ).arg( layer->name() ) );
3505
3506 }
3507 }
3508 }
3509
3510 expList.append( dimensionFilter( filteredLayer ) );
3511
3512 // Join and apply expressions provided by OGC filter and Dimensions
3513 QString exp;
3514 if ( expList.size() == 1 )
3515 {
3516 exp = expList[0];
3517 }
3518 else if ( expList.size() > 1 )
3519 {
3520 exp = QStringLiteral( "( %1 )" ).arg( expList.join( QLatin1String( " ) AND ( " ) ) );
3521 }
3522 if ( !exp.isEmpty() )
3523 {
3524 std::unique_ptr<QgsExpression> expression( new QgsExpression( exp ) );
3525 if ( expression )
3526 {
3527 mFeatureFilter.setFilter( filteredLayer, *expression );
3528 }
3529 }
3530 }
3531 }
3532
3533 QStringList QgsRenderer::dimensionFilter( QgsVectorLayer *layer ) const
3534 {
3535 QStringList expList;
3536 // WMS Dimension filters
3537 QgsMapLayerServerProperties *serverProperties = static_cast<QgsMapLayerServerProperties *>( layer->serverProperties() );
3538 const QList<QgsMapLayerServerProperties::WmsDimensionInfo> wmsDims = serverProperties->wmsDimensions();
3539 if ( wmsDims.isEmpty() )
3540 {
3541 return expList;
3542 }
3543
3544 QMap<QString, QString> dimParamValues = mContext.parameters().dimensionValues();
3545 for ( const QgsMapLayerServerProperties::WmsDimensionInfo &dim : wmsDims )
3546 {
3547 // Skip temporal properties for this layer, give precedence to the dimensions implementation
3548 if ( mIsTemporal && dim.name.toUpper() == QLatin1String( "TIME" ) && layer->temporalProperties()->isActive() )
3549 {
3550 layer->temporalProperties()->setIsActive( false );
3551 }
3552 // Check field index
3553 int fieldIndex = layer->fields().indexOf( dim.fieldName );
3554 if ( fieldIndex == -1 )
3555 {
3556 continue;
3557 }
3558 // Check end field index
3559 int endFieldIndex = -1;
3560 if ( !dim.endFieldName.isEmpty() )
3561 {
3562 endFieldIndex = layer->fields().indexOf( dim.endFieldName );
3563 if ( endFieldIndex == -1 )
3564 {
3565 continue;
3566 }
3567 }
3568 // Apply dimension filtering
3569 if ( !dimParamValues.contains( dim.name.toUpper() ) )
3570 {
3571 // Default value based on type configured by user
3572 QVariant defValue;
3573 if ( dim.defaultDisplayType == QgsMapLayerServerProperties::WmsDimensionInfo::AllValues )
3574 {
3575 continue; // no filter by default for this dimension
3576 }
3577 else if ( dim.defaultDisplayType == QgsMapLayerServerProperties::WmsDimensionInfo::ReferenceValue )
3578 {
3579 defValue = dim.referenceValue;
3580 }
3581 else
3582 {
3583 // get unique values
3584 QSet<QVariant> uniqueValues = layer->uniqueValues( fieldIndex );
3585 if ( endFieldIndex != -1 )
3586 {
3587 uniqueValues.unite( layer->uniqueValues( endFieldIndex ) );
3588 }
3589 // sort unique values
3590 QList<QVariant> values = qgis::setToList( uniqueValues );
3591 std::sort( values.begin(), values.end() );
3592 if ( dim.defaultDisplayType == QgsMapLayerServerProperties::WmsDimensionInfo::MinValue )
3593 {
3594 defValue = values.first();
3595 }
3596 else if ( dim.defaultDisplayType == QgsMapLayerServerProperties::WmsDimensionInfo::MaxValue )
3597 {
3598 defValue = values.last();
3599 }
3600 }
3601 // build expression
3602 if ( endFieldIndex == -1 )
3603 {
3604 expList << QgsExpression::createFieldEqualityExpression( dim.fieldName, defValue );
3605 }
3606 else
3607 {
3608 QStringList expElems;
3609 expElems << QgsExpression::quotedColumnRef( dim.fieldName )
3610 << QStringLiteral( "<=" ) << QgsExpression::quotedValue( defValue )
3611 << QStringLiteral( "AND" ) << QgsExpression::quotedColumnRef( dim.endFieldName )
3612 << QStringLiteral( ">=" ) << QgsExpression::quotedValue( defValue );
3613 expList << expElems.join( ' ' );
3614 }
3615 }
3616 else
3617 {
3618 // Get field to convert value provided in parameters
3619 QgsField dimField = layer->fields().at( fieldIndex );
3620 // Value provided in parameters
3621 QString dimParamValue = dimParamValues[dim.name.toUpper()];
3622 // The expression list for this dimension
3623 QStringList dimExplist;
3624 // Multiple values are separated by ,
3625 QStringList dimValues = dimParamValue.split( ',' );
3626 for ( int i = 0; i < dimValues.size(); ++i )
3627 {
3628 QString dimValue = dimValues[i];
3629 // Trim value if necessary
3630 if ( dimValue.size() > 1 )
3631 {
3632 dimValue = dimValue.trimmed();
3633 }
3634 // Range value is separated by / for example 0/1
3635 if ( dimValue.contains( '/' ) )
3636 {
3637 QStringList rangeValues = dimValue.split( '/' );
3638 // Check range value size
3639 if ( rangeValues.size() != 2 )
3640 {
3641 continue; // throw an error
3642 }
3643 // Get range values
3644 QVariant rangeMin = QVariant( rangeValues[0] );
3645 QVariant rangeMax = QVariant( rangeValues[1] );
3646 // Convert and check range values
3647 if ( !dimField.convertCompatible( rangeMin ) )
3648 {
3649 continue; // throw an error
3650 }
3651 if ( !dimField.convertCompatible( rangeMax ) )
3652 {
3653 continue; // throw an error
3654 }
3655 // Build expression for this range
3656 QStringList expElems;
3657 if ( endFieldIndex == -1 )
3658 {
3659 // The field values are between min and max range
3660 expElems << QgsExpression::quotedColumnRef( dim.fieldName )
3661 << QStringLiteral( ">=" ) << QgsExpression::quotedValue( rangeMin )
3662 << QStringLiteral( "AND" ) << QgsExpression::quotedColumnRef( dim.fieldName )
3663 << QStringLiteral( "<=" ) << QgsExpression::quotedValue( rangeMax );
3664 }
3665 else
3666 {
3667 // The start field or the end field are lesser than min range
3668 // or the start field or the end field are greater than min range
3669 expElems << QStringLiteral( "(" ) << QgsExpression::quotedColumnRef( dim.fieldName )
3670 << QStringLiteral( ">=" ) << QgsExpression::quotedValue( rangeMin )
3671 << QStringLiteral( "OR" ) << QgsExpression::quotedColumnRef( dim.endFieldName )
3672 << QStringLiteral( ">=" ) << QgsExpression::quotedValue( rangeMin )
3673 << QStringLiteral( ") AND (" ) << QgsExpression::quotedColumnRef( dim.fieldName )
3674 << QStringLiteral( "<=" ) << QgsExpression::quotedValue( rangeMax )
3675 << QStringLiteral( "OR" ) << QgsExpression::quotedColumnRef( dim.endFieldName )
3676 << QStringLiteral( "<=" ) << QgsExpression::quotedValue( rangeMax )
3677 << QStringLiteral( ")" );
3678 }
3679 dimExplist << expElems.join( ' ' );
3680 }
3681 else
3682 {
3683 QVariant dimVariant = QVariant( dimValue );
3684 if ( !dimField.convertCompatible( dimVariant ) )
3685 {
3686 continue; // throw an error
3687 }
3688 // Build expression for this value
3689 if ( endFieldIndex == -1 )
3690 {
3691 // Field is equal to
3692 dimExplist << QgsExpression::createFieldEqualityExpression( dim.fieldName, dimVariant );
3693 }
3694 else
3695 {
3696 // The start field is lesser or equal to
3697 // and the end field is greater or equal to
3698 QStringList expElems;
3699 expElems << QgsExpression::quotedColumnRef( dim.fieldName )
3700 << QStringLiteral( "<=" ) << QgsExpression::quotedValue( dimVariant )
3701 << QStringLiteral( "AND" ) << QgsExpression::quotedColumnRef( dim.endFieldName )
3702 << QStringLiteral( ">=" ) << QgsExpression::quotedValue( dimVariant );
3703 dimExplist << expElems.join( ' ' );
3704 }
3705 }
3706 }
3707 // Build the expression for this dimension
3708 if ( dimExplist.size() == 1 )
3709 {
3710 expList << dimExplist;
3711 }
3712 else if ( dimExplist.size() > 1 )
3713 {
3714 expList << QStringLiteral( "( %1 )" ).arg( dimExplist.join( QLatin1String( " ) OR ( " ) ) );
3715 }
3716 }
3717 }
3718 return expList;
3719 }
3720
3721 void QgsRenderer::setLayerSelection( QgsMapLayer *layer, const QStringList &fids ) const
3722 {
3723 if ( !fids.empty() && layer->type() == Qgis::LayerType::Vector )
3724 {
3725 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
3726
3727 QgsFeatureRequest request;
3729 const QgsFeatureIds selectedIds = request.filterFids();
3730
3731 if ( selectedIds.empty() )
3732 {
3734 }
3735 else
3736 {
3737 vl->selectByIds( selectedIds );
3738 }
3739 }
3740 }
3741
3742 void QgsRenderer::setLayerAccessControlFilter( QgsMapLayer *layer ) const
3743 {
3744#ifdef HAVE_SERVER_PYTHON_PLUGINS
3745 QgsOWSServerFilterRestorer::applyAccessControlLayerFilters( mContext.accessControl(), layer );
3746#else
3747 Q_UNUSED( layer )
3748#endif
3749 }
3750
3751 void QgsRenderer::updateExtent( const QgsMapLayer *layer, QgsMapSettings &mapSettings ) const
3752 {
3753 QgsRectangle layerExtent = mapSettings.layerToMapCoordinates( layer, layer->extent() );
3754 QgsRectangle mapExtent = mapSettings.extent();
3755 if ( !layerExtent.isEmpty() )
3756 {
3757 mapExtent.combineExtentWith( layerExtent );
3758 mapSettings.setExtent( mapExtent );
3759 }
3760 }
3761
3762 void QgsRenderer::annotationsRendering( QPainter *painter, const QgsMapSettings &mapSettings ) const
3763 {
3764 const QgsAnnotationManager *annotationManager = mProject->annotationManager();
3765 const QList< QgsAnnotation * > annotations = annotationManager->annotations();
3766
3767 QgsRenderContext renderContext = QgsRenderContext::fromQPainter( painter );
3769 renderContext.setFeedback( mContext.socketFeedback() );
3770
3771 for ( QgsAnnotation *annotation : annotations )
3772 {
3773 if ( mContext.socketFeedback() && mContext.socketFeedback()->isCanceled() )
3774 break;
3775 if ( !annotation || !annotation->isVisible() )
3776 continue;
3777
3778 //consider item position
3779 double offsetX = 0;
3780 double offsetY = 0;
3781 if ( annotation->hasFixedMapPosition() )
3782 {
3783 QgsPointXY mapPos = annotation->mapPosition();
3784 if ( mapSettings.destinationCrs() != annotation->mapPositionCrs() )
3785 {
3786 QgsCoordinateTransform coordTransform( annotation->mapPositionCrs(), mapSettings.destinationCrs(), mapSettings.transformContext() );
3787 try
3788 {
3789 mapPos = coordTransform.transform( mapPos );
3790 }
3791 catch ( const QgsCsException &e )
3792 {
3793 QgsMessageLog::logMessage( QStringLiteral( "Error transforming coordinates of annotation item: %1" ).arg( e.what() ) );
3794 }
3795 }
3796 const QgsPointXY devicePos = mapSettings.mapToPixel().transform( mapPos );
3797 offsetX = devicePos.x();
3798 offsetY = devicePos.y();
3799 }
3800 else
3801 {
3802 const QPointF relativePos = annotation->relativePosition();
3803 offsetX = mapSettings.outputSize().width() * relativePos.x();
3804 offsetY = mapSettings.outputSize().height() * relativePos.y();
3805 }
3806
3807 painter->save();
3808 painter->translate( offsetX, offsetY );
3809 annotation->render( renderContext );
3810 painter->restore();
3811 }
3812 }
3813
3814 QImage *QgsRenderer::scaleImage( const QImage *image ) const
3815 {
3816 // Test if width / height ratio of image is the same as the ratio of
3817 // WIDTH / HEIGHT parameters. If not, the image has to be scaled (required
3818 // by WMS spec)
3819 QImage *scaledImage = nullptr;
3820 const int width = mWmsParameters.widthAsInt();
3821 const int height = mWmsParameters.heightAsInt();
3822 if ( width != image->width() || height != image->height() )
3823 {
3824 scaledImage = new QImage( image->scaled( width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
3825 }
3826
3827 return scaledImage;
3828 }
3829
3830 void QgsRenderer::handlePrintErrors( const QgsLayout *layout ) const
3831 {
3832 if ( !layout )
3833 {
3834 return;
3835 }
3836 QList< QgsLayoutItemMap * > mapList;
3837 layout->layoutItems( mapList );
3838
3839 QList< QgsLayoutItemMap * >::const_iterator mapIt = mapList.constBegin();
3840 for ( ; mapIt != mapList.constEnd(); ++mapIt )
3841 {
3842 if ( !( *mapIt )->renderingErrors().isEmpty() )
3843 {
3844 const QgsMapRendererJob::Error e = ( *mapIt )->renderingErrors().at( 0 );
3845 throw QgsException( QStringLiteral( "Rendering error : '%1' in layer %2" ).arg( e.message, e.layerID ) );
3846 }
3847 }
3848 }
3849
3850 void QgsRenderer::configureLayers( QList<QgsMapLayer *> &layers, QgsMapSettings *settings )
3851 {
3852 const bool useSld = !mContext.parameters().sldBody().isEmpty();
3853
3854 for ( auto layer : layers )
3855 {
3856 const QgsWmsParametersLayer param = mContext.parameters( *layer );
3857
3858 if ( ! mContext.layersToRender().contains( layer ) )
3859 {
3860 continue;
3861 }
3862
3863 if ( mContext.isExternalLayer( param.mNickname ) )
3864 {
3865 if ( mContext.testFlag( QgsWmsRenderContext::UseOpacity ) )
3866 {
3867 setLayerOpacity( layer, param.mOpacity );
3868 }
3869 continue;
3870 }
3871
3872 if ( useSld )
3873 {
3874 setLayerSld( layer, mContext.sld( *layer ) );
3875 }
3876 else
3877 {
3878 setLayerStyle( layer, mContext.style( *layer ) );
3879 }
3880
3881 if ( mContext.testFlag( QgsWmsRenderContext::UseOpacity ) )
3882 {
3883 setLayerOpacity( layer, param.mOpacity );
3884 }
3885
3886 if ( mContext.testFlag( QgsWmsRenderContext::UseFilter ) )
3887 {
3888 setLayerFilter( layer, param.mFilter );
3889 }
3890
3892 {
3893 setLayerAccessControlFilter( layer );
3894 }
3895
3897 {
3898 setLayerSelection( layer, param.mSelection );
3899 }
3900
3901 if ( settings && mContext.updateExtent() )
3902 {
3903 updateExtent( layer, *settings );
3904 }
3905 }
3906
3908 {
3909 layers = highlightLayers( mWmsParameters.highlightLayersParameters() ) << layers;
3910 }
3911 }
3912
3913 void QgsRenderer::setLayerStyle( QgsMapLayer *layer, const QString &style ) const
3914 {
3915 if ( style.isEmpty() )
3916 {
3917 return;
3918 }
3919
3920 bool rc = layer->styleManager()->setCurrentStyle( style );
3921 if ( ! rc )
3922 {
3924 QStringLiteral( "Style '%1' does not exist for layer '%2'" ).arg( style, layer->name() ) );
3925 }
3926 }
3927
3928 void QgsRenderer::setLayerSld( QgsMapLayer *layer, const QDomElement &sld ) const
3929 {
3930 QString err;
3931 // Defined sld style name
3932 const QStringList styles = layer->styleManager()->styles();
3933 QString sldStyleName = "__sld_style";
3934 while ( styles.contains( sldStyleName ) )
3935 {
3936 sldStyleName.append( '@' );
3937 }
3938 layer->styleManager()->addStyleFromLayer( sldStyleName );
3939 layer->styleManager()->setCurrentStyle( sldStyleName );
3940 layer->readSld( sld, err );
3941 layer->setCustomProperty( "sldStyleName", sldStyleName );
3942 }
3943
3944 QgsLegendSettings QgsRenderer::legendSettings()
3945 {
3946 // getting scale from bbox or default size
3947 QgsLegendSettings settings = mWmsParameters.legendSettings();
3948
3949 if ( !mWmsParameters.bbox().isEmpty() )
3950 {
3951 QgsMapSettings mapSettings;
3953 std::unique_ptr<QImage> tmp( createImage( mContext.mapSize( false ) ) );
3954 configureMapSettings( tmp.get(), mapSettings );
3955 // QGIS 4.0 - require correct use of QgsRenderContext instead of these
3957 settings.setMapScale( mapSettings.scale() );
3958 settings.setMapUnitsPerPixel( mapSettings.mapUnitsPerPixel() );
3960 }
3961 else
3962 {
3963 // QGIS 4.0 - require correct use of QgsRenderContext instead of these
3965 const double defaultMapUnitsPerPixel = QgsServerProjectUtils::wmsDefaultMapUnitsPerMm( *mContext.project() ) / mContext.dotsPerMm();
3966 settings.setMapUnitsPerPixel( defaultMapUnitsPerPixel );
3968 }
3969
3970 return settings;
3971 }
3972} // namespace QgsWms
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
static QString version()
Version string.
Definition qgis.cpp:258
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
@ Millimeters
Millimeters.
LabelPlacement
Placement modes which determine how label candidates are generated for a feature.
Definition qgis.h:914
@ AroundPoint
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
@ DragAndDrop
"Drag and drop" layout. Needs to be configured.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ NoFlags
No flags are set.
@ Warning
Warning message.
Definition qgis.h:101
@ Info
Information message.
Definition qgis.h:100
@ ShowRuleDetails
If set, the rule expression of a rule based renderer legend item will be added to the JSON.
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
@ Antialiasing
Use antialiasing while drawing.
@ HighQualityImageTransforms
Enable high quality image transformations, which results in better appearance of scaled or rotated ra...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
RasterIdentifyFormat
Raster identify formats.
Definition qgis.h:3949
@ Feature
WMS GML/JSON -> feature.
@ Value
Numerical pixel value.
@ NoGeometry
No geometry.
@ HideFromWms
Field is not available if layer is served as WMS from QGIS server.
@ AllowOverlapIfRequired
Avoids overlapping labels when possible, but permit overlaps if labels for features cannot otherwise ...
@ Reverse
Reverse/inverse transform (from destination to source)
@ RenderMapTile
Draw map such that there are no problems between adjacent tiles.
@ RecordProfile
Enable run-time profiling while rendering (since QGIS 3.34)
@ UseRenderingOptimization
Enable vector simplification and other rendering optimizations.
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
Abstract base class for all geometries.
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
QString abstract() const
Returns a free-form description of the resource.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource.
Abstract base class - its implementations define different approaches to the labeling of a vector lay...
virtual void multiplyOpacity(double opacityFactor)
Multiply opacity by opacityFactor.
Manages storage of a set of QgsAnnotation annotation objects.
QList< QgsAnnotation * > annotations() const
Returns a list of all annotations contained in the manager.
Abstract base class for annotation items which are drawn over a map.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
This is an abstract base class for any elements of a drag and drop form.
QString name() const
Returns the name of this element.
This element will load a field's widget onto the form.
A vector of attributes.
Exception thrown in case of malformed request.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool hasAxisInverted() const
Returns whether the axis order is inverted for the CRS compared to the order east/north (longitude/la...
Class for doing transforms between two map coordinate systems.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Custom exception class for Coordinate Reference System related exceptions.
A server filter to apply a dimension filter to a request.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
@ FlagNoMText
Export text as TEXT elements. If not set, text will be exported as MTEXT elements.
Contains configuration settings for an editor form.
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
Qgis::AttributeFormLayout layout() const
Gets the active layout style for the attribute editor for this layer.
Holder for the widget type and its configuration for a field.
QVariantMap config() const
Defines a QGIS exception class.
QString what() const
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QString expression() const
Returns the original, unmodified expression string.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QVariant::Type fieldType=QVariant::Type::Invalid)
Create an expression allowing to evaluate if a field is equal to a value.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
QVariant evaluate()
Evaluate the feature and return the result.
bool isValid() const
Checks if this expression is valid.
A filter filter provider grouping several filter providers.
QgsFeatureFilterProviderGroup & addProvider(const QgsFeatureFilterProvider *provider)
Add another filter provider to the group.
void setFilter(const QgsVectorLayer *layer, const QgsExpression &expression)
Set a filter for the given layer.
void filterFeatures(const QgsVectorLayer *layer, QgsFeatureRequest &filterFeatures) const override
Filter the features of the layer.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
@ MoreSymbolsPerFeature
May use more than one symbol to render a feature: symbolsForFeature() will return them.
static QgsFeatureRenderer * loadSld(const QDomNode &node, Qgis::GeometryType geomType, QString &errorMessage)
Create a new renderer according to the information contained in the UserStyle element of a SLD style ...
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
Qgis::FeatureRequestFlags flags() const
Returns the flags which affect how features are fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsExpression * filterExpression() const
Returns the filter expression (if set).
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsCoordinateTransformContext transformContext() const
Returns the transform context, for use when a destinationCrs() has been set and reprojection is requi...
const QgsFeatureIds & filterFids() const
Returns the feature IDs that should be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
A container for features with the same fields and crs.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:56
bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
QgsAttributes attributes
Definition qgsfeature.h:65
QgsFields fields
Definition qgsfeature.h:66
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
QgsFeatureId id
Definition qgsfeature.h:64
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
QgsGeometry geometry
Definition qgsfeature.h:67
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QString name
Definition qgsfield.h:62
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition qgsfield.cpp:452
Qgis::FieldConfigurationFlags configurationFlags
Definition qgsfield.h:66
Container of fields for a vector layer.
Definition qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition qgsfields.cpp:59
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
int count() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
A geometry is the spatial representation of a feature.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
Qgis::GeometryType type
void set(QgsAbstractGeometry *geometry)
Sets the underlying geometry store.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
QString asWkt(int precision=17) const
Exports the geometry to WKT.
Handles exporting QgsFeature features to GeoJSON features.
void setPlacementFlags(Qgis::LabelLinePlacementFlags flags)
Returns the line placement flags, which dictate how line labels can be placed above or below the line...
void setOverlapHandling(Qgis::LabelOverlapHandling handling)
Sets the technique used to handle overlapping labels.
void setAllowDegradedPlacement(bool allow)
Sets whether labels can be placed in inferior fallback positions if they cannot otherwise be placed.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
void removeChildrenGroupWithoutLayers()
Remove all child group nodes without layers.
Layer tree node points to a map layer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
@ RuleKey
Rule key of the node (QString)
virtual QJsonObject exportSymbolToJson(const QgsLegendSettings &settings, const QgsRenderContext &context) const
Adds a symbol in base64 string within a JSON object with the key "icon".
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
virtual QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const
Draws symbol on the left side of the item.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
Namespace with helper functions for layer tree operations.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Class used to render QgsLayout as an atlas, by iterating over the features from an associated vector ...
bool beginRender() override
Called when rendering begins, before iteration commences.
bool setFilterExpression(const QString &expression, QString &errorString)
Sets the expression used for filtering features in the coverage layer.
bool first()
Seeks to the first feature, returning false if no feature was found.
QgsLayout * layout() override
Returns the layout associated with the iterator.
bool enabled() const
Returns whether the atlas generation is enabled.
int count() const override
Returns the number of features to iterate over.
void setFilterFeatures(bool filtered)
Sets whether features should be filtered in the coverage layer.
QgsVectorLayer * coverageLayer() const
Returns the coverage layer used for the atlas features.
bool next() override
int updateFeatures()
Requeries the current atlas coverage layer and applies filtering and sorting.
Handles rendering and exports of layouts to various formats.
ExportResult exportToSvg(const QString &filePath, const QgsLayoutExporter::SvgExportSettings &settings)
Exports the layout as an SVG to the filePath, using the specified export settings.
ExportResult exportToImage(const QString &filePath, const QgsLayoutExporter::ImageExportSettings &settings)
Exports the layout to the filePath, using the specified export settings.
ExportResult exportToPdf(const QString &filePath, const QgsLayoutExporter::PdfExportSettings &settings)
Exports the layout as a PDF to the filePath, using the specified export settings.
Base class for frame items, which form a layout multiframe item.
A layout multiframe subclass for HTML content.
A layout item subclass for text labels.
A layout item subclass for map legends.
Layout graphical items for displaying a map.
double scale() const
Returns the map scale.
QList< QgsMapLayer * > layers() const
Returns the stored layer set.
QString id() const
Returns the item's ID name.
Manages storage of a set of layouts.
QgsMasterLayoutInterface * layoutByName(const QString &name) const
Returns the layout with a matching name, or nullptr if no matching layouts were found.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
double length() const
Returns the length of the measurement.
int pageCount() const
Returns the number of pages in the collection.
Stores information relating to the current rendering settings for a layout.
void setFeatureFilterProvider(QgsFeatureFilterProvider *featureFilterProvider)
Sets feature filter provider to featureFilterProvider.
@ FlagLosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
@ FlagDisableTiledRasterLayerRenders
If set, then raster layers will not be drawn as separate tiles. This may improve the appearance in ex...
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
double height() const
Returns the height of the size.
double width() const
Returns the width of the size.
static QVector< double > predefinedScales(const QgsLayout *layout)
Returns a list of predefined scales associated with a layout.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition qgslayout.h:49
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout.
void layoutItems(QList< T * > &itemList) const
Returns a list of layout items of a specific type.
Definition qgslayout.h:120
Item model implementation based on layer tree model for layout legend.
The QgsLegendRenderer class handles automatic layout and rendering of legend.
QSizeF minimumSize(QgsRenderContext *renderContext=nullptr)
Runs the layout algorithm and returns the minimum size required for the legend.
QJsonObject exportLegendToJson(const QgsRenderContext &context)
Renders the legend in a json object.
Q_DECL_DEPRECATED void drawLegend(QPainter *painter)
Draws the legend with given painter.
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
Q_DECL_DEPRECATED void setMapScale(double scale)
Sets the legend map scale.
Q_DECL_DEPRECATED void setMapUnitsPerPixel(double mapUnitsPerPixel)
Sets the mmPerMapUnit calculated by mapUnitsPerPixel mostly taken from the map settings.
void setJsonRenderFlags(const Qgis::LegendJsonRenderFlags &jsonRenderFlags)
Sets the the JSON export flags to jsonRenderFlags.
Manages QGIS Server properties for a map layer.
QStringList styles() const
Returns list of all defined style names.
bool setCurrentStyle(const QString &name)
Set a different style as the current style - will apply it to the layer.
bool addStyleFromLayer(const QString &name)
Add style by cloning the current one.
Base class for all map layer types.
Definition qgsmaplayer.h:75
QString name
Definition qgsmaplayer.h:78
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
virtual QgsRectangle extent() const
Returns the extent of the layer.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:81
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
Qgis::LayerType type
Definition qgsmaplayer.h:82
virtual void setOpacity(double opacity)
Sets the opacity for the layer, where opacity is a value between 0 (totally transparent) and 1....
Q_INVOKABLE void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
@ Identifiable
If the layer is identifiable using the identify map tool and as a WMS layer.
virtual bool readSld(const QDomNode &node, QString &errorMessage)
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer's style manager.
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
QString mapTipTemplate
Definition qgsmaplayer.h:85
The QgsMapSettings class contains configuration for rendering of the map.
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer's CRS to output CRS
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
void setSelectionColor(const QColor &color)
Sets the color that is used for drawing of selected vector features.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
double scale() const
Returns the calculated map scale.
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer's CRS to destination CRS.
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer's CRS to output CRS
void setDpiTarget(double dpi)
Sets the target dpi (dots per inch) to be taken into consideration when rendering.
void setOutputDpi(double dpi)
Sets the dpi (dots per inch) used for conversion between real world units (e.g.
const QgsMapToPixel & mapToPixel() const
double mapUnitsPerPixel() const
Returns the distance in geographical coordinates that equals to one pixel in the map.
QSize outputSize() const
Returns the size of the resulting map image, in pixels.
QgsRectangle extent() const
Returns geographical coordinates of the rectangle that should be rendered.
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QColor selectionColor() const
Returns the color that is used for drawing of selected vector features.
void setExtentBuffer(double buffer)
Sets the buffer in map units to use around the visible extent for rendering symbols whose correspondi...
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets the global configuration of the labeling engine.
void setOutputSize(QSize size)
Sets the size of the resulting map image, in pixels.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
void setFlag(Qgis::MapSettingsFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs (coordinate reference system) for the map render.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
QList< QgsMapThemeCollection::MapThemeLayerRecord > layerRecords() const
Returns a list of records for all visible layer belonging to the theme.
QgsMapThemeCollection::MapThemeRecord mapThemeState(const QString &name) const
Returns the recorded state of a map theme.
Perform transforms between map coordinates and device coordinates.
double mapUnitsPerPixel() const
Returns the current map units per pixel.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
static void applyAccessControlLayerFilters(const QgsAccessControl *accessControl, QgsMapLayer *mapLayer, QHash< QgsMapLayer *, QString > &originalLayerFilters)
Apply filter from AccessControl.
static QDomElement geometryToGML(const QgsGeometry &geometry, QDomDocument &doc, QgsOgcUtils::GMLVersion gmlVersion, const QString &srsName, bool invertAxisOrientation, const QString &gmlIdBase, int precision=17)
Exports the geometry to GML.
static QDomElement rectangleToGMLEnvelope(QgsRectangle *env, QDomDocument &doc, int precision=17)
Exports the rectangle to GML3 Envelope.
static QgsExpression * expressionFromOgcFilter(const QDomElement &element, QgsVectorLayer *layer=nullptr)
Parse XML with OGC filter into QGIS expression.
static QDomElement rectangleToGMLBox(QgsRectangle *box, QDomDocument &doc, int precision=17)
Exports the rectangle to GML2 Box.
Contains settings for how a map layer will be labeled.
const QgsLabelPlacementSettings & placementSettings() const
Returns the label placement settings.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
Qgis::LabelPlacement placement
Label placement mode.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the label's property collection, used for data defined overrides.
int priority
Label priority.
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
double dist
Distance from feature to the label.
Property
Data definable properties.
@ PositionX
X-coordinate data defined label position.
@ LabelRotation
Label rotation.
@ PositionY
Y-coordinate data defined label position.
@ Vali
Vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top)
@ Hali
Horizontal alignment for data defined label position (Left, Center, Right)
QString fieldName
Name of field (or an expression) to use for label text.
A class to represent a 2D point.
Definition qgspointxy.h:60
void setY(double y)
Sets the y value of the point.
Definition qgspointxy.h:130
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
void setX(double x)
Sets the x value of the point.
Definition qgspointxy.h:120
Print layout, a QgsLayout subclass for static or atlas-based layouts.
QgsPrintLayout * clone() const override
Creates a clone of the layout.
QString author() const
Returns the project author string.
A class to describe the version of a project.
QColor selectionColor
Definition qgsproject.h:122
static QgsProject * instance()
Returns the QgsProject singleton instance.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QgsMapThemeCollection * mapThemeCollection
Definition qgsproject.h:115
QgsAnnotationManager * annotationManager()
Returns pointer to the project's annotation manager.
QgsProjectMetadata metadata
Definition qgsproject.h:120
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:113
const QgsLayoutManager * layoutManager() const
Returns the project's layout manager, which manages print layouts, atlases and reports within the pro...
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project's global labeling engine settings.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
virtual QgsRasterIdentifyResult identify(const QgsPointXY &point, Qgis::RasterIdentifyFormat format, const QgsRectangle &boundingBox=QgsRectangle(), int width=0, int height=0, int dpi=96)
Identify raster value(s) found on the point position.
Raster identify results container.
bool isValid() const
Returns true if valid.
QMap< int, QVariant > results() const
Returns the identify results.
@ IdentifyValue
Numerical values.
@ IdentifyFeature
WMS GML -> feature.
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
Represents a raster layer.
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
QString bandName(int bandNoInt) const
Returns the name of a band given its number.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Raster renderer pipe that applies colors to a raster.
void setOpacity(double opacity)
Sets the opacity for the renderer, where opacity is a value between 0 (totally transparent) and 1....
A rectangle specified with double values.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
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 width() const
Returns the width of the 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).
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
bool isEmpty() const
Returns true if the rectangle has no area.
double height() const
Returns the height of the rectangle.
void invert()
Swap x/y coordinates in the rectangle.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
void setCoordinateTransform(const QgsCoordinateTransform &t)
Sets the current coordinate transform for the context.
void setDistanceArea(const QgsDistanceArea &distanceArea)
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly during rendering to check if rendering should ...
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
void setExtent(const QgsRectangle &extent)
When rendering a map layer, calling this method sets the "clipping" extent for the layer (in the laye...
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
Calculates scale for a given combination of canvas size, map extent, and monitor dpi.
double calculate(const QgsRectangle &mapExtent, double canvasWidth) const
Calculate the scale denominator.
Scoped object for temporary scaling of a QgsRenderContext for millimeter based rendering.
Bad request error API exception.
static QgsDateTimeRange parseTemporalDateTimeInterval(const QString &interval)
Parses a datetime interval and returns a QgsDateTimeRange.
Exception base class for server exceptions.
int maxThreads() const
Returns the maximum number of threads to use.
bool logProfile() const
Returns true if profile information has to be added to the logs, default value is false.
QStringList allowedExtraSqlTokens() const
Returns the list of strings that represent the allowed extra SQL tokens accepted as components of a f...
bool parallelRendering() const
Returns parallel rendering setting.
const QList< QgsServerWmsDimensionProperties::WmsDimensionInfo > wmsDimensions() const
Returns the QGIS Server WMS Dimension list.
static QString symbolProperties(QgsSymbol *symbol)
Returns a string representing the symbol.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:94
@ Hidden
Hide task from GUI (since QGIS 3.26)
bool isActive() const
Returns true if the temporal property is active.
void setIsActive(bool active)
Sets whether the temporal property is active.
void setIsTemporal(bool enabled)
Sets whether the temporal range is enabled (i.e.
void setTemporalRange(const QgsDateTimeRange &range)
Sets the temporal range for the object.
Container for settings relating to a text buffer.
void setColor(const QColor &color)
Sets the color for the buffer.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
void setSize(double size)
Sets the size of the buffer.
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setSize(double size)
Sets the size for rendered text.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setBuffer(const QgsTextBufferSettings &bufferSettings)
Sets the text's buffer settings.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
This is the base class for vector data providers.
virtual QgsAttributeList pkAttributeIndexes() const
Returns list of indexes of fields that make up the primary key.
Basic implementation of the labeling interface.
Represents a vector layer which manages a vector based data sets.
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else.
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
QgsMapLayerTemporalProperties * temporalProperties() override
Returns the layer's temporal properties.
void updateFields()
Will regenerate the fields property of this layer by obtaining all fields from the dataProvider,...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Q_INVOKABLE void selectByExpression(const QString &expression, Qgis::SelectBehavior behavior=Qgis::SelectBehavior::SetSelection, QgsExpressionContext *context=nullptr)
Selects matching features using an expression.
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
QString displayExpression
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QgsEditorWidgetSetup editorWidgetSetup(int index) const
The editor widget setup defines which QgsFieldFormatter and editor widget will be used for the field ...
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
Q_INVOKABLE void selectByIds(const QgsFeatureIds &ids, Qgis::SelectBehavior behavior=Qgis::SelectBehavior::SetSelection)
Selects matching features using a list of feature IDs.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
virtual bool setSubsetString(const QString &subset)
Sets the string (typically sql) used to define a subset of the layer.
QgsAttributeList primaryKeyAttributes() const
Returns the list of attributes which make up the layer's primary keys.
QgsEditFormConfig editFormConfig
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
Implements a map layer that is dedicated to rendering of vector tiles.
static QString displayString(Qgis::WkbType type)
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
static bool isCurvedType(Qgis::WkbType type)
Returns true if the WKB type is a curved type or can contain curved geometries.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Exception thrown in case of malformed request.
QgsRenderer(const QgsWmsRenderContext &context)
Constructor for QgsRenderer.
QHash< QgsVectorLayer *, SymbolSet > HitTest
QByteArray getPrint()
Returns printed page as binary.
HitTest symbols()
Returns the hit test according to the current context.
std::unique_ptr< QgsDxfExport > getDxf()
Returns the map as DXF data.
QSet< QString > SymbolSet
void configureLayers(QList< QgsMapLayer * > &layers, QgsMapSettings *settings=nullptr)
Configures layers for rendering optionally considering the map settings.
std::unique_ptr< QgsMapRendererTask > getPdf(const QString &tmpFileName)
Returns a configured pdf export task.
QByteArray getFeatureInfo(const QString &version="1.3.0")
Creates an xml document that describes the result of the getFeatureInfo request.
QImage * getLegendGraphics(QgsLayerTreeModel &model)
Returns the map legend as an image (or nullptr in case of error).
QJsonObject getLegendGraphicsAsJson(QgsLayerTreeModel &model, const Qgis::LegendJsonRenderFlags &jsonRenderFlags=Qgis::LegendJsonRenderFlags())
Returns the map legend as a JSON object.
QImage * getMap()
Returns the map as an image (or nullptr in case of error).
~QgsRenderer()
Destructor for QgsRenderer.
Exception class for WMS service exceptions.
ExceptionCode
Exception codes as defined in OGC scpecifications for WMS 1.1.1 and WMS 1.3.0.
WMS parameter received from the client.
bool htmlInfoOnlyMapTip() const
Returns true if only maptip information is requested for HTML feature info response.
double dxfScale() const
Returns the DXF SCALE parameter.
bool transparentAsBool() const
Returns TRANSPARENT parameter as a bool or its default value if not defined.
QString x() const
Returns X parameter or an empty string if not defined.
QString formatAsString() const
Returns FORMAT parameter as a string.
QgsProjectVersion versionAsNumber() const
Returns VERSION parameter if defined or its default value.
QgsWmsParametersComposerMap composerMapParameters(int mapId) const
Returns the requested parameters for a composer map parameter.
QgsRectangle bboxAsRectangle() const
Returns BBOX as a rectangle if defined and valid.
bool withGeometry() const
Returns if the client wants the feature info response with geometry information.
DxfFormatOption
Options for DXF format.
QString pointTolerance() const
Returns FI_POINT_TOLERANCE parameter or an empty string if not defined.
QString filterGeom() const
Returns the filter geometry found in FILTER_GEOM parameter.
QString composerTemplate() const
Returns TEMPLATE parameter or an empty string if not defined.
Format infoFormat() const
Returns infoFormat.
QString y() const
Returns Y parameter or an empty string if not defined.
double dpiAsDouble() const
Returns DPI parameter as an int or its default value if not defined.
void dump() const
Dumps parameters.
int pointToleranceAsInt() const
Returns FI_POINT_TOLERANCE parameter as an integer.
bool withMapTip() const
withMapTip
QString polygonTolerance() const
Returns FI_POLYGON_TOLERANCE parameter or an empty string if not defined.
QString i() const
Returns I parameter or an empty string if not defined.
bool pdfLosslessImageCompression() const
Returns true if images embedded in pdf must be compressed using a lossless algorithm.
int lineToleranceAsInt() const
Returns FI_LINE_TOLERANCE parameter as an integer.
QString lineTolerance() const
Returns FI_LINE_TOLERANCE parameter or an empty string if not defined.
QStringList pdfExportMapThemes() const
Returns map themes for GeoPDF export.
bool pdfUseOgcBestPracticeFormatGeoreferencing() const
Returns true if OGC best practice georeferencing shall be used.
bool pdfExportMetadata() const
Returns true if metadata shall be added to the pdf.
QString j() const
Returns J parameter or an empty string if not defined.
int xAsInt() const
Returns X parameter as an int or its default value if not defined.
QString bbox() const
Returns BBOX if defined or an empty string.
int heightAsInt() const
Returns HEIGHT parameter as an int or its default value if not defined.
QColor backgroundColorAsColor() const
Returns BGCOLOR parameter as a QColor or its default value if not defined.
Format format() const
Returns format.
QStringList atlasPk() const
Returns the ATLAS_PK parameter.
QList< QgsWmsParametersHighlightLayer > highlightLayersParameters() const
Returns parameters for each highlight layer.
int iAsInt() const
Returns I parameter as an int or its default value if not defined.
bool pdfAppendGeoreference() const
Returns true if georeference info shall be added to the pdf.
int polygonToleranceAsInt() const
Returns FI_POLYGON_TOLERANCE parameter as an integer.
int widthAsInt() const
Returns WIDTH parameter as an int or its default value if not defined.
QString sldBody() const
Returns SLD_body if defined or an empty string.
bool pdfDisableTiledRasterRendering() const
Returns true if rasters shall be untiled in the pdf.
QString layoutParameter(const QString &id, bool &ok) const
Returns a layout parameter thanks to its id.
bool dxfUseLayerTitleAsName() const
Returns the DXF USE_TITLE_AS_LAYERNAME parameter.
bool pdfForceVectorOutput() const
Returns if pdf should be exported as vector.
bool pdfUseIso32000ExtensionFormatGeoreferencing() const
Returns true, if Iso32000 georeferencing shall be used.
QMap< QString, QString > dimensionValues() const
Returns the dimensions parameter.
bool withDisplayName() const
withDisplayName
int infoFormatVersion() const
Returns the infoFormat version for GML.
QgsLegendSettings legendSettings() const
Returns legend settings.
Qgis::TextRenderFormat pdfTextRenderFormat() const
Returns text render format for pdf export.
QStringList dxfLayerAttributes() const
Returns the DXF LAYERATTRIBUTES parameter.
bool writeGeoPdf() const
Returns if a GeoPDF shall be exported.
Qgis::FeatureSymbologyExport dxfMode() const
Returns the DXF MODE parameter.
QString height() const
Returns HEIGHT parameter or an empty string if not defined.
QString crs() const
Returns CRS or an empty string if none is defined.
int featureCountAsInt() const
Returns FEATURE_COUNT as an integer.
int yAsInt() const
Returns Y parameter as an int or its default value if not defined.
QMap< T, QString > formatOptions() const
Returns the format options for an output format.
Format
Output format for the response.
QString width() const
Returns WIDTH parameter or an empty string if not defined.
QStringList filters() const
Returns the list of filters found in FILTER parameter.
QString dpi() const
Returns DPI parameter or an empty string if not defined.
int jAsInt() const
Returns J parameter as an int or its default value if not defined.
QVector< qreal > pdfPredefinedMapScales() const
Returns list of map scales.
bool pdfSimplifyGeometries() const
Returns if geometries shall to be simplified.
QStringList queryLayersNickname() const
Returns nickname of layers found in QUERY_LAYERS parameter.
Rendering context for the WMS renderer.
QSize mapSize(bool aspectRatio=true) const
Returns the size (in pixels) of the map to render, according to width and height WMS parameters as we...
bool isExternalLayer(const QString &name) const
Returns true if the layer is an external layer, false otherwise.
bool isValidGroup(const QString &name) const
Returns true if name is a group.
QStringList flattenedQueryLayers(const QStringList &layerNames) const
Returns a list of query layer names where group names are replaced by the names of their layer compon...
QgsFeedback * socketFeedback() const
Returns the response feedback if any.
QList< QgsMapLayer * > layersToRender() const
Returns a list of all layers to actually render according to the current configuration.
QgsMapLayer * layer(const QString &nickname) const
Returns the layer corresponding to the nickname, or a nullptr if not found or if the layer do not nee...
bool updateExtent() const
Returns true if the extent has to be updated before the rendering, false otherwise.
const QgsServerSettings & settings() const
Returns settings of the server.
bool isValidWidthHeight() const
Returns true if width and height are valid according to the maximum values defined within the project...
QList< QgsMapLayer * > layersFromGroup(const QString &nickname) const
Returns the group's layers list corresponding to the nickname, or an empty list if not found.
QgsWmsParameters parameters() const
Returns WMS parameters.
void setScaleDenominator(double scaleDenominator)
Sets a custom scale denominator.
QString style(const QgsMapLayer &layer) const
Returns a style's name for a specific layer.
QMap< QString, QList< QgsMapLayer * > > layerGroups() const
Returns a map having layer group names as keys and a list of layers as values.
double mapTileBuffer(int mapWidth) const
Returns the tile buffer in geographical units for the given map width in pixels.
QString layerNickname(const QgsMapLayer &layer) const
Returns the nickname (short name, id or name) of the layer according to the current configuration.
qreal dotsPerMm() const
Returns default dots per mm according to the current configuration.
bool testFlag(Flag flag) const
Returns the status of a rendering flag.
QDomElement sld(const QgsMapLayer &layer) const
Returns a SLD document for a specific layer.
bool isValidLayer(const QString &nickname) const
Returns true if the layer has to be rendered, false otherwise.
const QgsProject * project() const
Returns the project.
int precision() const
Returns the precision to use according to the current configuration.
bool renderMapTiles() const
Returns true if WMS requests should use the QgsMapSettings::RenderMapTile flag, so that no visible ar...
RAII class to restore the rendering context configuration on destruction.
SERVER_EXPORT QString getExpressionFromServerFid(const QString &serverFid, const QgsVectorDataProvider *provider)
Returns the expression feature id based on primary keys.
SERVER_EXPORT QgsFeatureRequest updateFeatureRequestFromServerFids(QgsFeatureRequest &featureRequest, const QStringList &serverFids, const QgsVectorDataProvider *provider)
Returns the feature request based on feature ids build with primary keys.
SERVER_EXPORT QString getServerFid(const QgsFeature &feature, const QgsAttributeList &pkAttributes)
Returns the feature id based on primary keys.
SERVER_EXPORT QString wmsFeatureInfoSchema(const QgsProject &project)
Returns the schema URL for XML GetFeatureInfo request.
SERVER_EXPORT bool wmsInfoFormatSia2045(const QgsProject &project)
Returns if the info format is SIA20145.
SERVER_EXPORT QString wmsFeatureInfoDocumentElementNs(const QgsProject &project)
Returns the document element namespace for XML GetFeatureInfo request.
SERVER_EXPORT QStringList wmsRestrictedComposers(const QgsProject &project)
Returns the restricted composer list.
SERVER_EXPORT bool wmsFeatureInfoSegmentizeWktGeometry(const QgsProject &project)
Returns if the geometry has to be segmentize in GetFeatureInfo request.
SERVER_EXPORT bool wmsFeatureInfoUseAttributeFormSettings(const QgsProject &project)
Returns if feature form settings should be considered for the format of the feature info response.
SERVER_EXPORT QHash< QString, QString > wmsFeatureInfoLayerAliasMap(const QgsProject &project)
Returns the mapping between layer name and wms layer name for GetFeatureInfo request.
SERVER_EXPORT bool wmsFeatureInfoAddWktGeometry(const QgsProject &project)
Returns if the geometry is displayed as Well Known Text in GetFeatureInfo request.
SERVER_EXPORT double wmsDefaultMapUnitsPerMm(const QgsProject &project)
Returns the default number of map units per millimeters in case of the scale is not given.
SERVER_EXPORT QString wmsFeatureInfoDocumentElement(const QgsProject &project)
Returns the document element name for XML GetFeatureInfo request.
SERVER_EXPORT int wmsMaxAtlasFeatures(const QgsProject &project)
Returns the maximum number of atlas features which can be printed in a request.
Median cut implementation.
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define str(x)
Definition qgis.cpp:38
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition qgis.h:5631
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:5713
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:5061
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:5712
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5144
QList< QgsFeature > QgsFeatureList
Definition qgsfeature.h:917
QSet< QgsFeatureId > QgsFeatureIds
#define FID_TO_STRING(fid)
QVector< QgsFeatureStore > QgsFeatureStoreList
QList< int > QgsAttributeList
Definition qgsfield.h:27
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
QgsTemporalRange< QDateTime > QgsDateTimeRange
QgsRange which stores a range of date times.
Definition qgsrange.h:688
const QgsCoordinateReferenceSystem & outputCrs
const QgsCoordinateReferenceSystem & crs
const QString & typeName
bool withGeom
QgsAbstractMetadataBase::KeywordMap keywords
Metadata keyword map.
bool useIso32000ExtensionFormatGeoreferencing
true if ISO32000 extension format georeferencing should be used.
bool useOgcBestPracticeFormatGeoreferencing
true if OGC "best practice" format georeferencing should be used.
QDateTime creationDateTime
Metadata creation datetime.
Layers and optional attribute index to split into multiple layers using attribute value as layer name...
Single variable definition for use within a QgsExpressionContextScope.
Q_NOWARN_DEPRECATED_POP QgsRenderContext * context
Render context, if available.
Contains settings relating to exporting layouts to raster images.
QList< int > pages
List of specific pages to export, or an empty list to export all pages.
QSize imageSize
Manual size in pixels for output image.
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
Contains settings relating to exporting layouts to PDF.
bool useIso32000ExtensionFormatGeoreferencing
true if ISO3200 extension format georeferencing should be used.
bool forceVectorOutput
Set to true to force vector object exports, even when the resultant appearance will differ from the l...
bool rasterizeWholeImage
Set to true to force whole layout to be rasterized while exporting.
QStringList exportThemes
Optional list of map themes to export as GeoPDF layer groups.
bool appendGeoreference
Indicates whether PDF export should append georeference data.
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
bool writeGeoPdf
true if GeoPDF files should be created, instead of normal PDF files.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
bool simplifyGeometries
Indicates whether vector geometries should be simplified to avoid redundant extraneous detail,...
bool useOgcBestPracticeFormatGeoreferencing
true if OGC "best practice" format georeferencing should be used.
Qgis::TextRenderFormat textRenderFormat
Text rendering format, which controls how text should be rendered in the export (e....
Contains settings relating to exporting layouts to SVG.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
Setting to define QGIS Server WMS Dimension.
Setting options for loading vector layers.
QList< QgsWmsParametersLayer > mLayers
QList< QgsWmsParametersHighlightLayer > mHighlightLayers
QList< QgsWmsParametersFilter > mFilter