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