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