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