LCOV - code coverage report
Current view: top level - core/layout - qgslayoutgeopdfexporter.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 94 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :                               qgslayoutgeopdfexporter.cpp
       3                 :            :                              --------------------------
       4                 :            :     begin                : August 2019
       5                 :            :     copyright            : (C) 2019 by Nyall Dawson
       6                 :            :     email                : nyall dot dawson at gmail dot com
       7                 :            :  ***************************************************************************/
       8                 :            : /***************************************************************************
       9                 :            :  *                                                                         *
      10                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      11                 :            :  *   it under the terms of the GNU General Public License as published by  *
      12                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      13                 :            :  *   (at your option) any later version.                                   *
      14                 :            :  *                                                                         *
      15                 :            :  ***************************************************************************/
      16                 :            : 
      17                 :            : #include "qgslayoutgeopdfexporter.h"
      18                 :            : #include "qgsrenderedfeaturehandlerinterface.h"
      19                 :            : #include "qgsfeaturerequest.h"
      20                 :            : #include "qgslayout.h"
      21                 :            : #include "qgslogger.h"
      22                 :            : #include "qgsgeometry.h"
      23                 :            : #include "qgsvectorlayer.h"
      24                 :            : #include "qgsvectorfilewriter.h"
      25                 :            : #include "qgslayertree.h"
      26                 :            : 
      27                 :            : #include <gdal.h>
      28                 :            : #include "qgsgdalutils.h"
      29                 :            : #include "cpl_string.h"
      30                 :            : #include "qgslayoutpagecollection.h"
      31                 :            : 
      32                 :            : #include <QMutex>
      33                 :            : #include <QMutexLocker>
      34                 :            : #include <QDomDocument>
      35                 :            : #include <QDomElement>
      36                 :            : 
      37                 :            : ///@cond PRIVATE
      38                 :          0 : class QgsGeoPdfRenderedFeatureHandler: public QgsRenderedFeatureHandlerInterface
      39                 :            : {
      40                 :            :   public:
      41                 :            : 
      42                 :          0 :     QgsGeoPdfRenderedFeatureHandler( QgsLayoutItemMap *map, QgsLayoutGeoPdfExporter *exporter, const QStringList &layerIds )
      43                 :          0 :       : mExporter( exporter )
      44                 :          0 :       , mMap( map )
      45                 :          0 :       , mLayerIds( layerIds )
      46                 :          0 :     {
      47                 :            :       // get page size
      48                 :          0 :       const QgsLayoutSize pageSize = map->layout()->pageCollection()->page( map->page() )->pageSize();
      49                 :          0 :       QSizeF pageSizeLayoutUnits = map->layout()->convertToLayoutUnits( pageSize );
      50                 :          0 :       const QgsLayoutSize pageSizeInches = map->layout()->renderContext().measurementConverter().convert( pageSize, QgsUnitTypes::LayoutInches );
      51                 :            : 
      52                 :            :       // PDF assumes 72 dpi -- this is hardcoded!!
      53                 :          0 :       const double pageHeightPdfUnits = pageSizeInches.height() * 72;
      54                 :          0 :       const double pageWidthPdfUnits = pageSizeInches.width() * 72;
      55                 :            : 
      56                 :          0 :       QTransform mapTransform;
      57                 :          0 :       QPolygonF mapRectPoly = QPolygonF( QRectF( 0, 0, map->rect().width(), map->rect().height() ) );
      58                 :            :       //workaround QT Bug #21329
      59                 :          0 :       mapRectPoly.pop_back();
      60                 :            : 
      61                 :          0 :       QPolygonF mapRectInLayout = map->mapToScene( mapRectPoly );
      62                 :            : 
      63                 :            :       //create transform from layout coordinates to map coordinates
      64                 :          0 :       QTransform::quadToQuad( mapRectPoly, mapRectInLayout, mMapToLayoutTransform );
      65                 :            : 
      66                 :            :       // and a transform to PDF coordinate space
      67                 :          0 :       mLayoutToPdfTransform = QTransform::fromTranslate( 0, pageHeightPdfUnits ).scale( pageWidthPdfUnits / pageSizeLayoutUnits.width(),
      68                 :          0 :                               -pageHeightPdfUnits / pageSizeLayoutUnits.height() );
      69                 :          0 :     }
      70                 :            : 
      71                 :          0 :     void handleRenderedFeature( const QgsFeature &feature, const QgsGeometry &renderedBounds, const QgsRenderedFeatureHandlerInterface::RenderedFeatureContext &context ) override
      72                 :            :     {
      73                 :            :       // is it a hack retrieving the layer ID from an expression context like this? possibly... BUT
      74                 :            :       // the alternative is adding a layer ID member to QgsRenderContext, and that's just asking for people to abuse it
      75                 :            :       // and use it to retrieve QgsMapLayers mid-way through a render operation. Lesser of two evils it is!
      76                 :          0 :       const QString layerId = context.renderContext.expressionContext().variable( QStringLiteral( "layer_id" ) ).toString();
      77                 :          0 :       if ( !mLayerIds.contains( layerId ) )
      78                 :          0 :         return;
      79                 :            : 
      80                 :          0 :       const QString theme = ( mMap->mExportThemes.isEmpty() || mMap->mExportThemeIt == mMap->mExportThemes.end() ) ? QString() : *mMap->mExportThemeIt;
      81                 :            : 
      82                 :            :       // transform from pixels to map item coordinates
      83                 :          0 :       QTransform pixelToMapItemTransform = QTransform::fromScale( 1.0 / context.renderContext.scaleFactor(), 1.0 / context.renderContext.scaleFactor() );
      84                 :          0 :       QgsGeometry transformed = renderedBounds;
      85                 :          0 :       transformed.transform( pixelToMapItemTransform );
      86                 :            :       // transform from map item coordinates to page coordinates
      87                 :          0 :       transformed.transform( mMapToLayoutTransform );
      88                 :            :       // ...and then to PDF coordinate space
      89                 :          0 :       transformed.transform( mLayoutToPdfTransform );
      90                 :            : 
      91                 :            :       // always convert to multitype, to make things consistent
      92                 :          0 :       transformed.convertToMultiType();
      93                 :            : 
      94                 :          0 :       mExporter->pushRenderedFeature( layerId, QgsLayoutGeoPdfExporter::RenderedFeature( feature, transformed ), theme );
      95                 :          0 :     }
      96                 :            : 
      97                 :          0 :     QSet<QString> usedAttributes( QgsVectorLayer *, const QgsRenderContext & ) const override
      98                 :            :     {
      99                 :          0 :       return QSet< QString >() << QgsFeatureRequest::ALL_ATTRIBUTES;
     100                 :          0 :     }
     101                 :            : 
     102                 :            :   private:
     103                 :            :     QTransform mMapToLayoutTransform;
     104                 :            :     QTransform mLayoutToPdfTransform;
     105                 :            :     QgsLayoutGeoPdfExporter *mExporter = nullptr;
     106                 :            :     QgsLayoutItemMap *mMap = nullptr;
     107                 :            :     QStringList mLayerIds;
     108                 :            : };
     109                 :            : ///@endcond
     110                 :            : 
     111                 :          0 : QgsLayoutGeoPdfExporter::QgsLayoutGeoPdfExporter( QgsLayout *layout )
     112                 :          0 :   : mLayout( layout )
     113                 :          0 : {
     114                 :            :   // build a list of exportable feature layers in advance
     115                 :          0 :   QStringList exportableLayerIds;
     116                 :          0 :   const QMap< QString, QgsMapLayer * > layers = mLayout->project()->mapLayers( true );
     117                 :          0 :   for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
     118                 :            :   {
     119                 :          0 :     if ( QgsMapLayer *ml = it.value() )
     120                 :            :     {
     121                 :          0 :       const QVariant visibility = ml->customProperty( QStringLiteral( "geopdf/initiallyVisible" ), true );
     122                 :          0 :       mInitialLayerVisibility.insert( ml->id(), !visibility.isValid() ? true : visibility.toBool() );
     123                 :          0 :       if ( ml->type() == QgsMapLayerType::VectorLayer )
     124                 :            :       {
     125                 :          0 :         const QVariant v = ml->customProperty( QStringLiteral( "geopdf/includeFeatures" ) );
     126                 :          0 :         if ( !v.isValid() || v.toBool() )
     127                 :            :         {
     128                 :          0 :           exportableLayerIds << ml->id();
     129                 :          0 :         }
     130                 :          0 :       }
     131                 :            : 
     132                 :          0 :       const QString groupName = ml->customProperty( QStringLiteral( "geopdf/groupName" ) ).toString();
     133                 :          0 :       if ( !groupName.isEmpty() )
     134                 :          0 :         mCustomLayerTreeGroups.insert( ml->id(), groupName );
     135                 :          0 :     }
     136                 :          0 :   }
     137                 :            : 
     138                 :            :   // on construction, we install a rendered feature handler on layout item maps
     139                 :          0 :   QList< QgsLayoutItemMap * > maps;
     140                 :          0 :   mLayout->layoutItems( maps );
     141                 :          0 :   for ( QgsLayoutItemMap *map : std::as_const( maps ) )
     142                 :            :   {
     143                 :          0 :     QgsGeoPdfRenderedFeatureHandler *handler = new QgsGeoPdfRenderedFeatureHandler( map, this, exportableLayerIds );
     144                 :          0 :     mMapHandlers.insert( map, handler );
     145                 :          0 :     map->addRenderedFeatureHandler( handler );
     146                 :            :   }
     147                 :            : 
     148                 :            :   // start with project layer order, and then apply custom layer order if set
     149                 :          0 :   QStringList geoPdfLayerOrder;
     150                 :          0 :   const QString presetLayerOrder = mLayout->customProperty( QStringLiteral( "pdfLayerOrder" ) ).toString();
     151                 :          0 :   if ( !presetLayerOrder.isEmpty() )
     152                 :          0 :     geoPdfLayerOrder = presetLayerOrder.split( QStringLiteral( "~~~" ) );
     153                 :            : 
     154                 :          0 :   QList< QgsMapLayer * > layerOrder = mLayout->project()->layerTreeRoot()->layerOrder();
     155                 :          0 :   for ( auto it = geoPdfLayerOrder.rbegin(); it != geoPdfLayerOrder.rend(); ++it )
     156                 :            :   {
     157                 :          0 :     for ( int i = 0; i < layerOrder.size(); ++i )
     158                 :            :     {
     159                 :          0 :       if ( layerOrder.at( i )->id() == *it )
     160                 :            :       {
     161                 :          0 :         layerOrder.move( i, 0 );
     162                 :          0 :         break;
     163                 :            :       }
     164                 :          0 :     }
     165                 :          0 :   }
     166                 :            : 
     167                 :          0 :   for ( const QgsMapLayer *layer : layerOrder )
     168                 :          0 :     mLayerOrder << layer->id();
     169                 :          0 : }
     170                 :            : 
     171                 :          0 : QgsLayoutGeoPdfExporter::~QgsLayoutGeoPdfExporter()
     172                 :          0 : {
     173                 :            :   // cleanup - remove rendered feature handler from all maps
     174                 :          0 :   for ( auto it = mMapHandlers.constBegin(); it != mMapHandlers.constEnd(); ++it )
     175                 :            :   {
     176                 :          0 :     it.key()->removeRenderedFeatureHandler( it.value() );
     177                 :          0 :     delete it.value();
     178                 :          0 :   }
     179                 :          0 : }
     180                 :            : 
     181                 :          0 : QgsAbstractGeoPdfExporter::VectorComponentDetail QgsLayoutGeoPdfExporter::componentDetailForLayerId( const QString &layerId )
     182                 :            : {
     183                 :          0 :   QgsProject *project = mLayout->project();
     184                 :          0 :   VectorComponentDetail detail;
     185                 :          0 :   const QgsMapLayer *layer = project->mapLayer( layerId );
     186                 :          0 :   detail.name = layer ? layer->name() : layerId;
     187                 :          0 :   detail.mapLayerId = layerId;
     188                 :          0 :   if ( const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer * >( layer ) )
     189                 :            :   {
     190                 :          0 :     detail.displayAttribute = vl->displayField();
     191                 :          0 :   }
     192                 :          0 :   return detail;
     193                 :          0 : }
     194                 :            : 

Generated by: LCOV version 1.14