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