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