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