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