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