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