Skip to content
Permalink
Browse files

Flesh out QgsAbstractLayoutIterator interface

  • Loading branch information
nyalldawson committed Dec 23, 2017
1 parent e312d02 commit b6f1425828b8176dcb277e50c2641534389894e1
@@ -28,6 +28,11 @@ the Free Software Foundation; either version 2 of the License, or *

virtual ~QgsAbstractLayoutIterator();

virtual QgsLayout *layout() = 0;
%Docstring
Returns the layout associated with the iterator.
%End

virtual bool beginRender() = 0;
%Docstring
Called when rendering begins, before iteration commences. Returns true if successful, false if no iteration
@@ -49,6 +54,12 @@ Returns the number of features to iterate over.
virtual bool next() = 0;
%Docstring
Iterates to next feature, returning false if no more features exist to iterate over.
%End

virtual QString filePath( const QString &baseFilePath, const QString &extension ) = 0;
%Docstring
Returns the file path for the current feature, based on a
specified base file path and extension.
%End

};
@@ -270,6 +270,8 @@ number of matching features.

virtual int count() const;

virtual QString filePath( const QString &baseFilePath, const QString &extension );


int currentFeatureNumber() const;
%Docstring
@@ -17,7 +17,9 @@
#define QGSABSTRACTLAYOUTITERATOR_H

#include "qgis_core.h"
#include <QString>

class QgsLayout;

class CORE_EXPORT QgsAbstractLayoutIterator
{
@@ -26,6 +28,11 @@ class CORE_EXPORT QgsAbstractLayoutIterator

virtual ~QgsAbstractLayoutIterator() = default;

/**
* Returns the layout associated with the iterator.
*/
virtual QgsLayout *layout() = 0;

/**
* Called when rendering begins, before iteration commences. Returns true if successful, false if no iteration
* is available or required.
@@ -48,6 +55,12 @@ class CORE_EXPORT QgsAbstractLayoutIterator
*/
virtual bool next() = 0;

/**
* Returns the file path for the current feature, based on a
* specified base file path and extension.
*/
virtual QString filePath( const QString &baseFilePath, const QString &extension ) = 0;

};

#endif //QGSABSTRACTLAYOUTITERATOR_H
@@ -338,6 +338,15 @@ int QgsLayoutAtlas::count() const
return mFeatureIds.size();
}

QString QgsLayoutAtlas::filePath( const QString &baseFilePath, const QString &extension )
{
QString base = QDir( baseFilePath ).filePath( mCurrentFilename );
if ( !extension.startsWith( '.' ) )
base += '.';
base += extension;
return base;
}

bool QgsLayoutAtlas::next()
{
int newFeatureNo = mCurrentFeatureNo + 1;
@@ -497,8 +506,6 @@ bool QgsLayoutAtlas::prepareForFeature( const int featureI )
return false;
}

mGeometryCache.clear();

mLayout->context().blockSignals( true ); // setFeature emits changed, we don't want 2 signals
mLayout->context().setLayer( mCoverageLayer.get() );
mLayout->context().blockSignals( false );
@@ -507,59 +514,6 @@ bool QgsLayoutAtlas::prepareForFeature( const int featureI )
emit featureChanged( mCurrentFeature );
emit messagePushed( QString( tr( "Atlas feature %1 of %2" ) ).arg( featureI + 1 ).arg( mFeatureIds.size() ) );

if ( !mCurrentFeature.isValid() )
{
//bad feature
return false;
}

#if 0 //TODO - move to map
//update composer maps

//build a list of atlas-enabled composer maps
QList<QgsComposerMap *> maps;
QList<QgsComposerMap *> atlasMaps;
mComposition->composerItems( maps );
if ( maps.isEmpty() )
{
return true;
}
for ( QList<QgsComposerMap *>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
{
QgsComposerMap *currentMap = ( *mit );
if ( !currentMap->atlasDriven() )
{
continue;
}
atlasMaps << currentMap;
}

if ( !atlasMaps.isEmpty() )
{
//clear the transformed bounds of the previous feature
mTransformedFeatureBounds = QgsRectangle();

// compute extent of current feature in the map CRS. This should be set on a per-atlas map basis,
// but given that it's not currently possible to have maps with different CRSes we can just
// calculate it once based on the first atlas maps' CRS.
computeExtent( atlasMaps[0] );
}

for ( QList<QgsComposerMap *>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
{
if ( ( *mit )->atlasDriven() )
{
// map is atlas driven, so update it's bounds (causes a redraw)
prepareMap( *mit );
}
else
{
// map is not atlas driven, so manually force a redraw (to reflect possibly atlas
// dependent symbology)
( *mit )->invalidateCache();
}
}
#endif
return true;
return mCurrentFeature.isValid();
}

@@ -242,6 +242,7 @@ class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsAbstractLayoutItera
bool beginRender() override;
bool endRender() override;
int count() const override;
QString filePath( const QString &baseFilePath, const QString &extension ) override;

/**
* Returns the current feature number, where a value of 0 corresponds to the first feature.
@@ -367,9 +368,6 @@ class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsAbstractLayoutItera
int mCurrentFeatureNo = 0;
QgsFeature mCurrentFeature;

// projected geometry cache
mutable QMap<long, QgsGeometry> mGeometryCache;

QgsExpressionContext createExpressionContext();

friend class AtlasFeatureSorter;
@@ -198,12 +198,19 @@ def testFileName(self):
self.assertEqual(atlas.count(), 4)
atlas.first()
self.assertEqual(atlas.currentFilename(), 'output_Basse-Normandie')
self.assertEqual(atlas.filePath('/tmp/output', 'png'), '/tmp/output/output_Basse-Normandie.png')
self.assertEqual(atlas.filePath('/tmp/output', '.png'), '/tmp/output/output_Basse-Normandie.png')
self.assertEqual(atlas.filePath('/tmp/output/', 'svg'), '/tmp/output/output_Basse-Normandie.svg')

atlas.next()
self.assertEqual(atlas.currentFilename(), 'output_Bretagne')
self.assertEqual(atlas.filePath('/tmp/output', 'png'), '/tmp/output/output_Bretagne.png')
atlas.next()
self.assertEqual(atlas.currentFilename(), 'output_Pays de la Loire')
self.assertEqual(atlas.filePath('/tmp/output', 'png'), '/tmp/output/output_Pays de la Loire.png')
atlas.next()
self.assertEqual(atlas.currentFilename(), 'output_Centre')
self.assertEqual(atlas.filePath('/tmp/output', 'png'), '/tmp/output/output_Centre.png')

# try changing expression, filename should be updated instantly
atlas.setFilenameExpression("'export_' || \"NAME_1\"")

0 comments on commit b6f1425

Please sign in to comment.
You can’t perform that action at this time.