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