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