Prepare for map item porting

This commit is contained in:
Nyall Dawson 2017-10-18 18:18:48 +10:00
parent 87ef5c52b0
commit 9d04e679a2
4 changed files with 663 additions and 0 deletions

View File

@ -797,6 +797,16 @@ QRectF QgsLayoutItem::rectWithFrame() const
return rect().adjusted( -frameBleed, -frameBleed, frameBleed, frameBleed );
}
void QgsLayoutItem::moveContent( double, double )
{
}
void QgsLayoutItem::zoomContent( double, QPointF )
{
}
QgsLayoutPoint QgsLayoutItem::applyDataDefinedPosition( const QgsLayoutPoint &position )
{
if ( !mLayout )

View File

@ -513,6 +513,21 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*/
virtual QRectF rectWithFrame() const;
/**
* Moves the content of the item, by a specified \a dx and \a dy in layout units.
* The default implementation has no effect.
* \see zoomContent()
*/
virtual void moveContent( double dx, double dy );
/**
* Zooms content of item. Does nothing by default.
* \param factor zoom factor, where > 1 results in a zoom in and < 1 results in a zoom out
* \param point item point for zoom center
* \see moveContent()
*/
virtual void zoomContent( double factor, QPointF point );
public slots:
/**

View File

@ -20,6 +20,10 @@
#include "qgis_core.h"
#include "qgslayoutitem.h"
#include "qgslayoutitemregistry.h"
#include "qgsmaplayerref.h"
#include "qgsmaprenderercustompainterjob.h"
class QgsAnnotation;
/**
* \ingroup core
@ -34,12 +38,39 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
public:
/**
* Scaling modes used for the serial rendering (atlas)
*/
enum AtlasScalingMode
{
Fixed, //!< The current scale of the map is used for each feature of the atlas
/**
* A scale is chosen from the predefined scales. The smallest scale from
* the list of scales where the atlas feature is fully visible is chosen.
* \see QgsAtlasComposition::setPredefinedScales.
* \note This mode is only valid for polygon or line atlas coverage layers
*/
Predefined,
/**
* The extent is adjusted so that each feature is fully visible.
* A margin is applied around the center \see setAtlasMargin
* \note This mode is only valid for polygon or line atlas coverage layers
*/
Auto
};
/**
* Constructor for QgsLayoutItemMap, with the specified parent \a layout.
*/
explicit QgsLayoutItemMap( QgsLayout *layout );
~QgsLayoutItemMap();
int type() const override;
QString stringType() const override;
//overridden to show "Map 1" type names
virtual QString displayName() const override;
/**
* Returns a new map item for the specified \a layout.
@ -48,9 +79,605 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
*/
static QgsLayoutItemMap *create( QgsLayout *layout ) SIP_FACTORY;
/**
* Returns the map scale.
* The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
* \see setScale()
*/
double scale() const;
/**
* Sets new map \a scale and changes only the map extent.
*
* The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
*
* \see scale()
*/
void setScale( double scale, bool forceUpdate = true );
/**
* Sets a new \a extent for the map. This method may change the width or height of the map
* item to ensure that the extent exactly matches the specified extent, with no
* overlap or margin. This method implicitly alters the map scale.
* \see zoomToExtent()
*/
void setExtent( const QgsRectangle &extent );
/**
* Zooms the map so that the specified \a extent is fully visible within the map item.
* This method will not change the width or height of the map, and may result in
* an overlap or margin from the specified extent. This method implicitly alters the
* map scale.
* \see setExtent()
*/
void zoomToExtent( const QgsRectangle &extent );
/**
* Returns the current map extent.
* \see visibleExtentPolygon()
*/
QgsRectangle extent() const;
/**
* Returns a polygon representing the current visible map extent, considering map extents and rotation.
* If the map rotation is 0, the result is the same as currentMapExtent
* \returns polygon with the four corner points representing the visible map extent. The points are
* clockwise, starting at the top-left point
* \see extent()
*/
QPolygonF visibleExtentPolygon() const;
/**
* Returns coordinate reference system used for rendering the map.
* This will match the presetCrs() if that is set, or if a preset
* CRS is not set then the map's CRS will follow the composition's
* project's CRS.
* \see presetCrs()
* \see setCrs()
*/
QgsCoordinateReferenceSystem crs() const;
/**
* Returns the map's preset coordinate reference system. If set, this
* CRS will be used to render the map regardless of any project CRS
* setting. If the returned CRS is not valid then the project CRS
* will be used to render the map.
* \see crs()
* \see setCrs()
*/
QgsCoordinateReferenceSystem presetCrs() const { return mCrs; }
/**
* Sets the map's preset \a crs (coordinate reference system). If a valid CRS is
* set, this CRS will be used to render the map regardless of any project CRS
* setting. If the CRS is not valid then the project CRS will be used to render the map.
* \see crs()
* \see presetCrs()
*/
void setCrs( const QgsCoordinateReferenceSystem &crs );
/**
* Returns whether a stored layer set should be used
* or the current layer set from the project associated with the layout. This is just a GUI flag,
* and itself does not change which layers are rendered in the map.
* Instead, use setLayers() to control which layers are rendered.
* \see setKeepLayerSet()
* \see layers()
*/
bool keepLayerSet() const { return mKeepLayerSet; }
/**
* Sets whether the stored layer set should be used
* or the current layer set of the associated project. This is just a GUI flag,
* and itself does not change which layers are rendered in the map.
* Instead, use setLayers() to control which layers are rendered.
* \see keepLayerSet()
* \see layers()
*/
void setKeepLayerSet( bool enabled ) { mKeepLayerSet = enabled; }
/**
* Returns the stored layer set. If empty, the current project layers will
* be used instead.
* \see setLayers()
* \see keepLayerSet()
*/
QList<QgsMapLayer *> layers() const;
/**
* Sets the stored \a layers set. If empty, the current project layers will
* be used instead.
* \see layers()
* \see keepLayerSet()
*/
void setLayers( const QList<QgsMapLayer *> &layers );
/**
* Returns whether current styles of layers should be overridden by previously stored styles.
* \see setKeepLayerStyles()
*/
bool keepLayerStyles() const { return mKeepLayerStyles; }
/**
* Sets whether current styles of layers should be overridden by previously stored styles.
* \see keepLayerStyles()
*/
void setKeepLayerStyles( bool enabled ) { mKeepLayerStyles = enabled; }
/**
* Returns stored overrides of styles for layers.
* \see setLayerStyleOverrides()
*/
QMap<QString, QString> layerStyleOverrides() const { return mLayerStyleOverrides; }
/**
* Sets the stored overrides of styles for layers.
* \see layerStyleOverrides()
*/
void setLayerStyleOverrides( const QMap<QString, QString> &overrides );
/**
* Stores the current project layer styles into style overrides.
*/
void storeCurrentLayerStyles();
/**
* Returns whether the map should follow a map theme. If true, the layers and layer styles
* will be used from given preset name (configured with setFollowVisibilityPresetName() method).
* This means when preset's settings are changed, the new settings are automatically
* picked up next time when rendering, without having to explicitly update them.
* At most one of the flags keepLayerSet() and followVisibilityPreset() should be enabled
* at any time since they are alternative approaches - if both are enabled,
* following map theme has higher priority. If neither is enabled (or if preset name is not set),
* map will use the same configuration as the map canvas uses.
*/
bool followVisibilityPreset() const { return mFollowVisibilityPreset; }
/**
* Sets whether the map should follow a map theme. See followVisibilityPreset() for more details.
*/
void setFollowVisibilityPreset( bool follow ) { mFollowVisibilityPreset = follow; }
/**
* Preset name that decides which layers and layer styles are used for map rendering. It is only
* used when followVisibilityPreset() returns true.
* \see setFollowVisibilityPresetName()
*/
QString followVisibilityPresetName() const { return mFollowVisibilityPresetName; }
/**
* Sets preset name for map rendering. See followVisibilityPresetName() for more details.
* \see followVisibilityPresetName()
*/
void setFollowVisibilityPresetName( const QString &name ) { mFollowVisibilityPresetName = name; }
void moveContent( double dx, double dy ) override;
void zoomContent( double factor, QPointF point ) override;
//! Returns true if the map contains a WMS layer.
bool containsWmsLayer() const;
//! Returns true if the map contains layers with blend modes or flattened layers for vectors
bool containsAdvancedEffects() const;
/**
* Sets the \a rotation for the map - this does not affect the composer item shape, only the
* way the map is drawn within the item. Rotation is in degrees, clockwise.
* \see mapRotation()
*/
void setMapRotation( double rotation );
/**
* Returns the rotation used for drawing the map within the composer item, in degrees clockwise.
* \param valueType controls whether the returned value is the user specified rotation,
* or the current evaluated rotation (which may be affected by data driven rotation
* settings).
* \see setMapRotation()
*/
double mapRotation( QgsLayoutObject::PropertyValueType valueType = QgsLayoutObject::EvaluatedValue ) const;
/**
* Sets whether annotations are drawn within the composer map.
* \see drawAnnotations()
*/
void setDrawAnnotations( bool draw ) { mDrawAnnotations = draw; }
/**
* Returns whether annotations are drawn within the composer map.
* \see setDrawAnnotations()
*/
bool drawAnnotations() const { return mDrawAnnotations; }
/**
* Returns whether the map extent is set to follow the current atlas feature.
* \returns true if map will follow the current atlas feature.
* \see setAtlasDriven
* \see atlasScalingMode
*/
bool atlasDriven() const { return mAtlasDriven; }
/**
* Sets whether the map extent will follow the current atlas feature.
* \param enabled set to true if the map extents should be set by the current atlas feature.
* \see atlasDriven
* \see setAtlasScalingMode
*/
void setAtlasDriven( bool enabled );
/**
* Returns the current atlas scaling mode. This controls how the map's extents
* are calculated for the current atlas feature when an atlas composition
* is enabled.
* \returns the current scaling mode
* \note this parameter is only used if atlasDriven() is true
* \see setAtlasScalingMode
* \see atlasDriven
*/
AtlasScalingMode atlasScalingMode() const { return mAtlasScalingMode; }
/**
* Sets the current atlas scaling mode. This controls how the map's extents
* are calculated for the current atlas feature when an atlas composition
* is enabled.
* \param mode atlas scaling mode to set
* \note this parameter is only used if atlasDriven() is true
* \see atlasScalingMode
* \see atlasDriven
*/
void setAtlasScalingMode( AtlasScalingMode mode ) { mAtlasScalingMode = mode; }
/**
* Returns the margin size (percentage) used when the map is in atlas mode.
* \param valueType controls whether the returned value is the user specified atlas margin,
* or the current evaluated atlas margin (which may be affected by data driven atlas margin
* settings).
* \returns margin size in percentage to leave around the atlas feature's extent
* \note this is only used if atlasScalingMode() is Auto.
* \see atlasScalingMode
* \see setAtlasMargin
*/
double atlasMargin( const QgsLayoutObject::PropertyValueType valueType = QgsLayoutObject::EvaluatedValue );
/**
* Sets the margin size (percentage) used when the map is in atlas mode.
* \param margin size in percentage to leave around the atlas feature's extent
* \note this is only used if atlasScalingMode() is Auto.
* \see atlasScalingMode
* \see atlasMargin
*/
void setAtlasMargin( double margin ) { mAtlasMargin = margin; }
protected:
void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = nullptr ) override;
/**
* \brief Draw to paint device
* \param painter painter
* \param extent map extent
* \param size size in scene coordinates
* \param dpi scene dpi
* \param forceWidthScale force wysiwyg line widths / marker sizes
*/
void draw( QPainter *painter, const QgsRectangle &extent, QSizeF size, double dpi, double *forceWidthScale = nullptr );
void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override;
/**
* Return map settings that would be used for drawing of the map
*/
QgsMapSettings mapSettings( const QgsRectangle &extent, QSizeF size, int dpi ) const;
//! True if a draw is already in progress
bool isDrawing() const {return mDrawing;}
#if 0
//! Sets new scene rectangle bounds and recalculates hight and extent
void setSceneRect( const QRectF &rectangle ) override;
/**
* Sets new Extent for the current atlas preview and changes width, height (and implicitly also scale).
Atlas preview extents are only temporary, and are regenerated whenever the atlas feature changes
*/
void setNewAtlasFeatureExtent( const QgsRectangle &extent );
#endif
#if 0
QgsRectangle extent() const {return mExtent;}
#endif
//! Sets offset values to shift image (useful for live updates when moving item content)
void setOffset( double xOffset, double yOffset );
#if 0
/**
* Stores state in Dom node
* \param elem is Dom element corresponding to 'Composer' tag
* \param doc Dom document
*/
bool writeXml( QDomElement &elem, QDomDocument &doc ) const override;
/**
* Sets state from Dom document
* \param itemElem is Dom node corresponding to 'ComposerMap' tag
* \param doc is Dom document
*/
bool readXml( const QDomElement &itemElem, const QDomDocument &doc ) override;
/**
* Returns the map item's grid stack, which is used to control how grids
* are drawn over the map's contents.
* \returns pointer to grid stack
* \see grid()
*/
QgsComposerMapGridStack *grids() { return mGridStack; }
/**
* Returns the map item's first grid. This is a convenience function.
* \returns pointer to first grid for map item
* \see grids()
*/
QgsComposerMapGrid *grid();
/**
* Returns the map item's overview stack, which is used to control how overviews
* are drawn over the map's contents.
* \returns pointer to overview stack
* \see overview()
*/
QgsComposerMapOverviewStack *overviews() { return mOverviewStack; }
/**
* Returns the map item's first overview. This is a convenience function.
* \returns pointer to first overview for map item
* \see overviews()
*/
QgsComposerMapOverview *overview();
#endif
// In case of annotations, the bounding rectangle can be larger than the map item rectangle
QRectF boundingRect() const override;
#if 0
// reimplement setFrameStrokeWidth, so that updateBoundingRect() is called after setting the frame width
virtual void setFrameStrokeWidth( const double strokeWidth ) override;
#endif
QgsExpressionContext createExpressionContext() const override;
signals:
void extentChanged();
//! Is emitted on rotation change to notify north arrow pictures
void mapRotationChanged( double newRotation );
//! Is emitted when the map has been prepared for atlas rendering, just before actual rendering
void preparedForAtlas();
/**
* Emitted when layer style overrides are changed... a means to let
* associated legend items know they should update
*/
void layerStyleOverridesChanged();
#if 0
//! Returns the conversion factor map units -> mm
double mapUnitsToMM() const;
/**
* Sets mId to a number not yet used in the composition. mId is kept if it is not in use.
Usually, this function is called before adding the composer map to the composition*/
void assignFreeId();
/**
* Get the number of layers that this item requires for exporting as layers
* \returns 0 if this item is to be placed on the same layer as the previous item,
* 1 if it should be placed on its own layer, and >1 if it requires multiple export layers
*/
int numberExportLayers() const override;
#endif
//! Returns extent that considers rotation and shift with mOffsetX / mOffsetY
QPolygonF transformedMapPolygon() const;
//! Transforms map coordinates to item coordinates (considering rotation and move offset)
QPointF mapToItemCoords( QPointF mapCoords ) const;
/**
* Calculates the extent to request and the yShift of the top-left point in case of rotation.
*/
void requestedExtent( QgsRectangle &extent ) const;
public slots:
/**
* Forces a deferred update of the cached map image on next paint.
*/
void invalidateCache();
//! Updates the bounding rect of this item. Call this function before doing any changes related to annotation out of the map rectangle
void updateBoundingRect();
#if 0
virtual void refreshDataDefinedProperty( const QgsComposerObject::DataDefinedProperty property = QgsComposerObject::AllProperties, const QgsExpressionContext *context = nullptr ) override;
#endif
private slots:
void layersAboutToBeRemoved( QList<QgsMapLayer *> layers );
void painterJobFinished();
private:
//! Unique identifier
int mId = 0;
#if 0
QgsComposerMapGridStack *mGridStack = nullptr;
QgsComposerMapOverviewStack *mOverviewStack = nullptr;
#endif
// Map region in map units really used for rendering
// It can be the same as mUserExtent, but it can be bigger in on dimension if mCalculate==Scale,
// so that full rectangle in paper is used.
QgsRectangle mExtent;
//! Map CRS
QgsCoordinateReferenceSystem mCrs;
// Current temporary map region in map units. This is overwritten when atlas feature changes. It's also
// used when the user changes the map extent and an atlas preview is enabled. This allows the user
// to manually tweak each atlas preview page without affecting the actual original map extent.
QgsRectangle mAtlasFeatureExtent;
// We have two images used for rendering/storing cached map images.
// the first (mCacheFinalImage) is used ONLY for storing the most recent completed map render. It's always
// used when drawing map item previews. The second (mCacheRenderingImage) is used temporarily while
// rendering a new preview image in the background. If (and only if) the background render completes, then
// mCacheRenderingImage is pushed into mCacheFinalImage, and used from then on when drawing the item preview.
// This ensures that something is always shown in the map item, even while refreshing the preview image in the
// background
std::unique_ptr< QImage > mCacheFinalImage;
std::unique_ptr< QImage > mCacheRenderingImage;
//! True if cached map image must be recreated
bool mCacheInvalidated = true;
//! \brief Number of layers when cache was created
int mNumCachedLayers;
//! \brief set to true if in state of drawing. Concurrent requests to draw method are returned if set to true
bool mDrawing = false;
//! Offset in x direction for showing map cache image
double mXOffset = 0.0;
//! Offset in y direction for showing map cache image
double mYOffset = 0.0;
double mLastRenderedImageOffsetX = 0.0;
double mLastRenderedImageOffsetY = 0.0;
//! Map rotation
double mMapRotation = 0;
/**
* Temporary evaluated map rotation. Data defined rotation may mean this value
* differs from mMapRotation*/
double mEvaluatedMapRotation = 0;
//! Flag if layers to be displayed should be read from qgis canvas (true) or from stored list in mLayerSet (false)
bool mKeepLayerSet = false;
//! Stored layer list (used if layer live-link mKeepLayerSet is disabled)
QList< QgsMapLayerRef > mLayers;
bool mKeepLayerStyles = false;
//! Stored style names (value) to be used with particular layer IDs (key) instead of default style
QMap<QString, QString> mLayerStyleOverrides;
/**
* Whether layers and styles should be used from a preset (preset name is stored
* in mVisibilityPresetName and may be overridden by data-defined expression).
* This flag has higher priority than mKeepLayerSet. */
bool mFollowVisibilityPreset = false;
/**
* Map theme name to be used for map's layers and styles in case mFollowVisibilityPreset
* is true. May be overridden by data-defined expression. */
QString mFollowVisibilityPresetName;
//! \brief Create cache image
void recreateCachedImageInBackground();
//! Establishes signal/slot connection for update in case of layer change
void connectUpdateSlot();
//! Removes layer ids from mLayerSet that are no longer present in the qgis main map
void syncLayerSet();
#if 0
//! Returns first map grid or creates an empty one if none
const QgsComposerMapGrid *constFirstMapGrid() const;
//! Returns first map overview or creates an empty one if none
const QgsComposerMapOverview *constFirstMapOverview() const;
#endif
//! Current bounding rectangle. This is used to check if notification to the graphics scene is necessary
QRectF mCurrentRectangle;
//! True if annotation items, rubber band, etc. from the main canvas should be displayed
bool mDrawAnnotations = true;
/**
* Adjusts an extent rectangle to match the provided item width and height, so that extent
* center of extent remains the same */
void adjustExtentToItemShape( double itemWidth, double itemHeight, QgsRectangle &extent ) const;
//! True if map is being controlled by an atlas
bool mAtlasDriven = false;
//! Current atlas scaling mode
AtlasScalingMode mAtlasScalingMode = Auto;
//! Margin size for atlas driven extents (percentage of feature size) - when in auto scaling mode
double mAtlasMargin = 0.10;
std::unique_ptr< QPainter > mPainter;
std::unique_ptr< QgsMapRendererCustomPainterJob > mPainterJob;
bool mPainterCancelWait = false;
void init();
//! Resets the item tooltip to reflect current map id
void updateToolTip();
//! Returns a list of the layers to render for this map item
QList<QgsMapLayer *> layersToRender( const QgsExpressionContext *context = nullptr ) const;
//! Returns current layer style overrides for this map item
QMap<QString, QString> layerStyleOverridesToRender( const QgsExpressionContext &context ) const;
//! Returns extent that considers mOffsetX / mOffsetY (during content move)
QgsRectangle transformedExtent() const;
//! MapPolygon variant using a given extent
void mapPolygon( const QgsRectangle &extent, QPolygonF &poly ) const;
/**
* Scales a composer map shift (in MM) and rotates it by mRotation
\param xShift in: shift in x direction (in item units), out: xShift in map units
\param yShift in: shift in y direction (in item units), out: yShift in map units*/
void transformShift( double &xShift, double &yShift ) const;
void drawAnnotations( QPainter *painter );
void drawAnnotation( const QgsAnnotation *item, QgsRenderContext &context );
QPointF composerMapPosForItem( const QgsAnnotation *item ) const;
enum PartType
{
Background,
Layer,
Grid,
OverviewMapExtent,
Frame,
SelectionBoxes
};
//! Test if a part of the copmosermap needs to be drawn, considering mCurrentExportLayer
bool shouldDrawPart( PartType part ) const;
/**
* Refresh the map's extents, considering data defined extent, scale and rotation
* \param context expression context for evaluating data defined map parameters
*/
void refreshMapExtents( const QgsExpressionContext *context = nullptr );
friend class QgsComposerMapOverview; //to access mXOffset, mYOffset
friend class TestQgsComposerMap;
};
#endif //QGSLAYOUTITEMMAP_H

View File

@ -95,6 +95,17 @@ class CORE_EXPORT QgsLayoutObject: public QObject, public QgsExpressionContextGe
ScalebarLineWidth, //!< Scalebar line width
};
/**
* Specifies whether the value returned by a function should be the original, user
* set value, or the current evaluated value for the property. This may differ if
* a property has a data defined expression active.
*/
enum PropertyValueType
{
EvaluatedValue = 0, //!< Return the current evaluated value for the property
OriginalValue //!< Return the original, user set value
};
/**
* Returns the layout object property definitions.
*/