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