Start porting map item
@ -183,6 +183,17 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem, QgsLayoutUndoObjectInt
|
|||||||
.. seealso:: parentGroup()
|
.. seealso:: parentGroup()
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
virtual int numberExportLayers() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the number of layers that this item requires for exporting during layered exports (e.g. SVG).
|
||||||
|
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.
|
||||||
|
|
||||||
|
Items which require multiply layers should check QgsLayoutContext.currentExportLayer() during
|
||||||
|
their rendering to determine which layer should be drawn.
|
||||||
|
:rtype: int
|
||||||
|
%End
|
||||||
|
|
||||||
virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget );
|
virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget );
|
||||||
|
|
||||||
%Docstring
|
%Docstring
|
||||||
@ -514,6 +525,21 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem, QgsLayoutUndoObjectInt
|
|||||||
:rtype: QRectF
|
:rtype: QRectF
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
virtual void moveContent( double dx, double dy );
|
||||||
|
%Docstring
|
||||||
|
Moves the content of the item, by a specified ``dx`` and ``dy`` in layout units.
|
||||||
|
The default implementation has no effect.
|
||||||
|
.. seealso:: zoomContent()
|
||||||
|
%End
|
||||||
|
|
||||||
|
virtual void zoomContent( double factor, QPointF point );
|
||||||
|
%Docstring
|
||||||
|
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
|
||||||
|
.. seealso:: moveContent()
|
||||||
|
%End
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
virtual void refresh();
|
virtual void refresh();
|
||||||
@ -716,6 +742,12 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem, QgsLayoutUndoObjectInt
|
|||||||
:rtype: bool
|
:rtype: bool
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
bool shouldDrawItem() const;
|
||||||
|
%Docstring
|
||||||
|
Returns whether the item should be drawn in the current context.
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class QgsLayoutItemMap : QgsLayoutItem
|
class QgsLayoutItemMap : QgsLayoutItem
|
||||||
{
|
{
|
||||||
%Docstring
|
%Docstring
|
||||||
@ -20,15 +21,33 @@ class QgsLayoutItemMap : QgsLayoutItem
|
|||||||
%End
|
%End
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
enum AtlasScalingMode
|
||||||
|
{
|
||||||
|
Fixed,
|
||||||
|
|
||||||
|
Predefined,
|
||||||
|
|
||||||
|
Auto
|
||||||
|
};
|
||||||
|
|
||||||
explicit QgsLayoutItemMap( QgsLayout *layout );
|
explicit QgsLayoutItemMap( QgsLayout *layout );
|
||||||
%Docstring
|
%Docstring
|
||||||
Constructor for QgsLayoutItemMap, with the specified parent ``layout``.
|
Constructor for QgsLayoutItemMap, with the specified parent ``layout``.
|
||||||
%End
|
%End
|
||||||
|
~QgsLayoutItemMap();
|
||||||
|
|
||||||
virtual int type() const;
|
virtual int type() const;
|
||||||
|
|
||||||
virtual QString stringType() const;
|
virtual QString stringType() const;
|
||||||
|
|
||||||
|
|
||||||
|
void assignFreeId();
|
||||||
|
%Docstring
|
||||||
|
Sets the map id() to a number not yet used in the layout. The existing id() is kept if it is not in use.
|
||||||
|
%End
|
||||||
|
|
||||||
|
virtual QString displayName() const;
|
||||||
|
|
||||||
static QgsLayoutItemMap *create( QgsLayout *layout ) /Factory/;
|
static QgsLayoutItemMap *create( QgsLayout *layout ) /Factory/;
|
||||||
%Docstring
|
%Docstring
|
||||||
Returns a new map item for the specified ``layout``.
|
Returns a new map item for the specified ``layout``.
|
||||||
@ -37,10 +56,410 @@ class QgsLayoutItemMap : QgsLayoutItem
|
|||||||
:rtype: QgsLayoutItemMap
|
:rtype: QgsLayoutItemMap
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget );
|
||||||
|
|
||||||
|
virtual int numberExportLayers() const;
|
||||||
|
|
||||||
|
virtual void setFrameStrokeWidth( const QgsLayoutMeasurement &width );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
double scale() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the map scale.
|
||||||
|
The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
|
||||||
|
.. seealso:: setScale()
|
||||||
|
:rtype: float
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setScale( double scale, bool forceUpdate = true );
|
||||||
|
%Docstring
|
||||||
|
Sets new map ``scale`` and changes only the map extent.
|
||||||
|
|
||||||
|
The ``scale`` value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
|
||||||
|
|
||||||
|
.. seealso:: scale()
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setExtent( const QgsRectangle &extent );
|
||||||
|
%Docstring
|
||||||
|
Sets a new ``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.
|
||||||
|
.. seealso:: zoomToExtent()
|
||||||
|
%End
|
||||||
|
|
||||||
|
void zoomToExtent( const QgsRectangle &extent );
|
||||||
|
%Docstring
|
||||||
|
Zooms the map so that the specified ``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.
|
||||||
|
.. seealso:: setExtent()
|
||||||
|
%End
|
||||||
|
|
||||||
|
QgsRectangle extent() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the current map extent.
|
||||||
|
.. seealso:: visibleExtentPolygon()
|
||||||
|
:rtype: QgsRectangle
|
||||||
|
%End
|
||||||
|
|
||||||
|
|
||||||
|
QPolygonF visibleExtentPolygon() const;
|
||||||
|
%Docstring
|
||||||
|
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
|
||||||
|
:return: polygon with the four corner points representing the visible map extent. The points are
|
||||||
|
clockwise, starting at the top-left point
|
||||||
|
.. seealso:: extent()
|
||||||
|
:rtype: QPolygonF
|
||||||
|
%End
|
||||||
|
|
||||||
|
QgsCoordinateReferenceSystem crs() const;
|
||||||
|
%Docstring
|
||||||
|
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.
|
||||||
|
.. seealso:: presetCrs()
|
||||||
|
.. seealso:: setCrs()
|
||||||
|
:rtype: QgsCoordinateReferenceSystem
|
||||||
|
%End
|
||||||
|
|
||||||
|
QgsCoordinateReferenceSystem presetCrs() const;
|
||||||
|
%Docstring
|
||||||
|
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.
|
||||||
|
.. seealso:: crs()
|
||||||
|
.. seealso:: setCrs()
|
||||||
|
:rtype: QgsCoordinateReferenceSystem
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setCrs( const QgsCoordinateReferenceSystem &crs );
|
||||||
|
%Docstring
|
||||||
|
Sets the map's preset ``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.
|
||||||
|
.. seealso:: crs()
|
||||||
|
.. seealso:: presetCrs()
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool keepLayerSet() const;
|
||||||
|
%Docstring
|
||||||
|
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.
|
||||||
|
.. seealso:: setKeepLayerSet()
|
||||||
|
.. seealso:: layers()
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setKeepLayerSet( bool enabled );
|
||||||
|
%Docstring
|
||||||
|
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.
|
||||||
|
.. seealso:: keepLayerSet()
|
||||||
|
.. seealso:: layers()
|
||||||
|
%End
|
||||||
|
|
||||||
|
QList<QgsMapLayer *> layers() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the stored layer set. If empty, the current project layers will
|
||||||
|
be used instead.
|
||||||
|
.. seealso:: setLayers()
|
||||||
|
.. seealso:: keepLayerSet()
|
||||||
|
:rtype: list of QgsMapLayer
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setLayers( const QList<QgsMapLayer *> &layers );
|
||||||
|
%Docstring
|
||||||
|
Sets the stored ``layers`` set. If empty, the current project layers will
|
||||||
|
be used instead.
|
||||||
|
.. seealso:: layers()
|
||||||
|
.. seealso:: keepLayerSet()
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool keepLayerStyles() const;
|
||||||
|
%Docstring
|
||||||
|
Returns whether current styles of layers should be overridden by previously stored styles.
|
||||||
|
.. seealso:: setKeepLayerStyles()
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setKeepLayerStyles( bool enabled );
|
||||||
|
%Docstring
|
||||||
|
Sets whether current styles of layers should be overridden by previously stored styles.
|
||||||
|
.. seealso:: keepLayerStyles()
|
||||||
|
%End
|
||||||
|
|
||||||
|
QMap<QString, QString> layerStyleOverrides() const;
|
||||||
|
%Docstring
|
||||||
|
Returns stored overrides of styles for layers.
|
||||||
|
.. seealso:: setLayerStyleOverrides()
|
||||||
|
:rtype: QMap<str, QString>
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setLayerStyleOverrides( const QMap<QString, QString> &overrides );
|
||||||
|
%Docstring
|
||||||
|
Sets the stored overrides of styles for layers.
|
||||||
|
.. seealso:: layerStyleOverrides()
|
||||||
|
%End
|
||||||
|
|
||||||
|
void storeCurrentLayerStyles();
|
||||||
|
%Docstring
|
||||||
|
Stores the current project layer styles into style overrides.
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool followVisibilityPreset() const;
|
||||||
|
%Docstring
|
||||||
|
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.
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setFollowVisibilityPreset( bool follow );
|
||||||
|
%Docstring
|
||||||
|
Sets whether the map should follow a map theme. See followVisibilityPreset() for more details.
|
||||||
|
%End
|
||||||
|
|
||||||
|
QString followVisibilityPresetName() const;
|
||||||
|
%Docstring
|
||||||
|
Preset name that decides which layers and layer styles are used for map rendering. It is only
|
||||||
|
used when followVisibilityPreset() returns true.
|
||||||
|
.. seealso:: setFollowVisibilityPresetName()
|
||||||
|
:rtype: str
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setFollowVisibilityPresetName( const QString &name );
|
||||||
|
%Docstring
|
||||||
|
Sets preset name for map rendering. See followVisibilityPresetName() for more details.
|
||||||
|
.. seealso:: followVisibilityPresetName()
|
||||||
|
%End
|
||||||
|
|
||||||
|
virtual void moveContent( double dx, double dy );
|
||||||
|
|
||||||
|
virtual void zoomContent( double factor, QPointF point );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool containsWmsLayer() const;
|
||||||
|
%Docstring
|
||||||
|
Returns true if the map contains a WMS layer.
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool containsAdvancedEffects() const;
|
||||||
|
%Docstring
|
||||||
|
Returns true if the map contains layers with blend modes or flattened layers for vectors
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setMapRotation( double rotation );
|
||||||
|
%Docstring
|
||||||
|
Sets the ``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.
|
||||||
|
.. seealso:: mapRotation()
|
||||||
|
%End
|
||||||
|
|
||||||
|
double mapRotation( QgsLayoutObject::PropertyValueType valueType = QgsLayoutObject::EvaluatedValue ) const;
|
||||||
|
%Docstring
|
||||||
|
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).
|
||||||
|
.. seealso:: setMapRotation()
|
||||||
|
:rtype: float
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setDrawAnnotations( bool draw );
|
||||||
|
%Docstring
|
||||||
|
Sets whether annotations are drawn within the composer map.
|
||||||
|
.. seealso:: drawAnnotations()
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool drawAnnotations() const;
|
||||||
|
%Docstring
|
||||||
|
Returns whether annotations are drawn within the composer map.
|
||||||
|
.. seealso:: setDrawAnnotations()
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
|
|
||||||
|
bool atlasDriven() const;
|
||||||
|
%Docstring
|
||||||
|
Returns whether the map extent is set to follow the current atlas feature.
|
||||||
|
:return: true if map will follow the current atlas feature.
|
||||||
|
.. seealso:: setAtlasDriven
|
||||||
|
.. seealso:: atlasScalingMode
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setAtlasDriven( bool enabled );
|
||||||
|
%Docstring
|
||||||
|
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.
|
||||||
|
.. seealso:: atlasDriven
|
||||||
|
.. seealso:: setAtlasScalingMode
|
||||||
|
%End
|
||||||
|
|
||||||
|
AtlasScalingMode atlasScalingMode() const;
|
||||||
|
%Docstring
|
||||||
|
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.
|
||||||
|
:return: the current scaling mode
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
this parameter is only used if atlasDriven() is true
|
||||||
|
.. seealso:: setAtlasScalingMode
|
||||||
|
.. seealso:: atlasDriven
|
||||||
|
:rtype: AtlasScalingMode
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setAtlasScalingMode( AtlasScalingMode mode );
|
||||||
|
%Docstring
|
||||||
|
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
|
||||||
|
.. seealso:: atlasScalingMode
|
||||||
|
.. seealso:: atlasDriven
|
||||||
|
%End
|
||||||
|
|
||||||
|
double atlasMargin( const QgsLayoutObject::PropertyValueType valueType = QgsLayoutObject::EvaluatedValue );
|
||||||
|
%Docstring
|
||||||
|
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).
|
||||||
|
:return: margin size in percentage to leave around the atlas feature's extent
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
this is only used if atlasScalingMode() is Auto.
|
||||||
|
.. seealso:: atlasScalingMode
|
||||||
|
.. seealso:: setAtlasMargin
|
||||||
|
:rtype: float
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setAtlasMargin( double margin );
|
||||||
|
%Docstring
|
||||||
|
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.
|
||||||
|
.. seealso:: atlasScalingMode
|
||||||
|
.. seealso:: atlasMargin
|
||||||
|
%End
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = 0 );
|
virtual void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = 0 );
|
||||||
|
|
||||||
|
|
||||||
|
QgsMapSettings mapSettings( const QgsRectangle &extent, QSizeF size, int dpi ) const;
|
||||||
|
%Docstring
|
||||||
|
Return map settings that will be used for drawing of the map.
|
||||||
|
:rtype: QgsMapSettings
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool isDrawing() const;
|
||||||
|
%Docstring
|
||||||
|
True if a draw is already in progress
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void setOffset( double xOffset, double yOffset );
|
||||||
|
%Docstring
|
||||||
|
Sets offset values to shift image (useful for live updates when moving item content)
|
||||||
|
%End
|
||||||
|
|
||||||
|
|
||||||
|
virtual QRectF boundingRect() const;
|
||||||
|
|
||||||
|
|
||||||
|
virtual QgsExpressionContext createExpressionContext() const;
|
||||||
|
|
||||||
|
|
||||||
|
double mapUnitsToLayoutUnits() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the conversion factor from map units to layout units.
|
||||||
|
This is calculated using the width of the map item and the width of the
|
||||||
|
current visible map extent.
|
||||||
|
:rtype: float
|
||||||
|
%End
|
||||||
|
|
||||||
|
|
||||||
|
QPolygonF transformedMapPolygon() const;
|
||||||
|
%Docstring
|
||||||
|
Returns extent that considers rotation and shift with mOffsetX / mOffsetY
|
||||||
|
:rtype: QPolygonF
|
||||||
|
%End
|
||||||
|
|
||||||
|
QPointF mapToItemCoords( QPointF mapCoords ) const;
|
||||||
|
%Docstring
|
||||||
|
Transforms map coordinates to item coordinates (considering rotation and move offset)
|
||||||
|
:rtype: QPointF
|
||||||
|
%End
|
||||||
|
|
||||||
|
QgsRectangle requestedExtent() const;
|
||||||
|
%Docstring
|
||||||
|
Calculates the extent to request and the yShift of the top-left point in case of rotation.
|
||||||
|
:rtype: QgsRectangle
|
||||||
|
%End
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void extentChanged();
|
||||||
|
|
||||||
|
void mapRotationChanged( double newRotation );
|
||||||
|
%Docstring
|
||||||
|
Is emitted on rotation change to notify north arrow pictures
|
||||||
|
%End
|
||||||
|
|
||||||
|
void preparedForAtlas();
|
||||||
|
%Docstring
|
||||||
|
Is emitted when the map has been prepared for atlas rendering, just before actual rendering
|
||||||
|
%End
|
||||||
|
|
||||||
|
void layerStyleOverridesChanged();
|
||||||
|
%Docstring
|
||||||
|
Emitted when layer style overrides are changed... a means to let
|
||||||
|
associated legend items know they should update
|
||||||
|
%End
|
||||||
|
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void invalidateCache();
|
||||||
|
%Docstring
|
||||||
|
Forces a deferred update of the cached map image on next paint.
|
||||||
|
%End
|
||||||
|
|
||||||
|
void updateBoundingRect();
|
||||||
|
%Docstring
|
||||||
|
Updates the bounding rect of this item. Call this function before doing any changes related to annotation out of the map rectangle
|
||||||
|
%End
|
||||||
|
|
||||||
|
virtual void refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties );
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/************************************************************************
|
/************************************************************************
|
||||||
|
@ -72,6 +72,12 @@ class QgsLayoutObject: QObject, QgsExpressionContextGenerator
|
|||||||
ScalebarLineWidth,
|
ScalebarLineWidth,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum PropertyValueType
|
||||||
|
{
|
||||||
|
EvaluatedValue,
|
||||||
|
OriginalValue
|
||||||
|
};
|
||||||
|
|
||||||
static const QgsPropertiesDefinition &propertyDefinitions();
|
static const QgsPropertiesDefinition &propertyDefinitions();
|
||||||
%Docstring
|
%Docstring
|
||||||
Returns the layout object property definitions.
|
Returns the layout object property definitions.
|
||||||
|
@ -21,6 +21,14 @@ class QgsLayoutUtils
|
|||||||
%End
|
%End
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
static void rotate( double angle, double &x, double &y );
|
||||||
|
%Docstring
|
||||||
|
Rotates a point / vector around the origin.
|
||||||
|
\param angle rotation angle in degrees, counterclockwise
|
||||||
|
\param x in/out: x coordinate before / after the rotation
|
||||||
|
\param y in/out: y coordinate before / after the rotation
|
||||||
|
%End
|
||||||
|
|
||||||
static double normalizedAngle( const double angle, const bool allowNegative = false );
|
static double normalizedAngle( const double angle, const bool allowNegative = false );
|
||||||
%Docstring
|
%Docstring
|
||||||
Ensures that an ``angle`` (in degrees) is in the range 0 <= angle < 360.
|
Ensures that an ``angle`` (in degrees) is in the range 0 <= angle < 360.
|
||||||
|
@ -203,6 +203,16 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
|
|||||||
*/
|
*/
|
||||||
void setParentGroup( QgsLayoutItemGroup *group );
|
void setParentGroup( QgsLayoutItemGroup *group );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of layers that this item requires for exporting during layered exports (e.g. SVG).
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Items which require multiply layers should check QgsLayoutContext::currentExportLayer() during
|
||||||
|
* their rendering to determine which layer should be drawn.
|
||||||
|
*/
|
||||||
|
virtual int numberExportLayers() const { return 0; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles preparing a paint surface for the layout item and painting the item's
|
* Handles preparing a paint surface for the layout item and painting the item's
|
||||||
* content. Derived classes must not override this method, but instead implement
|
* content. Derived classes must not override this method, but instead implement
|
||||||
@ -724,6 +734,11 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
|
|||||||
*/
|
*/
|
||||||
virtual bool readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context );
|
virtual bool readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the item should be drawn in the current context.
|
||||||
|
*/
|
||||||
|
bool shouldDrawItem() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// true if layout manages the z value for this item
|
// true if layout manages the z value for this item
|
||||||
@ -800,11 +815,6 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
|
|||||||
void setScenePos( const QPointF &destinationPos );
|
void setScenePos( const QPointF &destinationPos );
|
||||||
bool shouldBlockUndoCommands() const;
|
bool shouldBlockUndoCommands() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the item should be drawn in the current context.
|
|
||||||
*/
|
|
||||||
bool shouldDrawItem() const;
|
|
||||||
|
|
||||||
friend class TestQgsLayoutItem;
|
friend class TestQgsLayoutItem;
|
||||||
friend class TestQgsLayoutView;
|
friend class TestQgsLayoutView;
|
||||||
friend class QgsLayoutItemGroup;
|
friend class QgsLayoutItemGroup;
|
||||||
|
@ -69,6 +69,12 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
|
|
||||||
int type() const override;
|
int type() const override;
|
||||||
QString stringType() const override;
|
QString stringType() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the map id() to a number not yet used in the layout. The existing id() is kept if it is not in use.
|
||||||
|
*/
|
||||||
|
void assignFreeId();
|
||||||
|
|
||||||
//overridden to show "Map 1" type names
|
//overridden to show "Map 1" type names
|
||||||
virtual QString displayName() const override;
|
virtual QString displayName() const override;
|
||||||
|
|
||||||
@ -79,6 +85,11 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
*/
|
*/
|
||||||
static QgsLayoutItemMap *create( QgsLayout *layout ) SIP_FACTORY;
|
static QgsLayoutItemMap *create( QgsLayout *layout ) SIP_FACTORY;
|
||||||
|
|
||||||
|
// for now, map items behave a bit differently and don't implement draw. TODO - see if we can avoid this
|
||||||
|
void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override;
|
||||||
|
int numberExportLayers() const override;
|
||||||
|
void setFrameStrokeWidth( const QgsLayoutMeasurement &width ) override;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the map scale.
|
* Returns the map scale.
|
||||||
@ -351,26 +362,12 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
*/
|
*/
|
||||||
void setAtlasMargin( double margin ) { mAtlasMargin = margin; }
|
void setAtlasMargin( double margin ) { mAtlasMargin = margin; }
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = nullptr ) override;
|
void draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle = nullptr ) override;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Draw to paint device
|
* Return map settings that will be used for drawing of the map.
|
||||||
* \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;
|
QgsMapSettings mapSettings( const QgsRectangle &extent, QSizeF size, int dpi ) const;
|
||||||
|
|
||||||
@ -397,6 +394,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
void setOffset( double xOffset, double yOffset );
|
void setOffset( double xOffset, double yOffset );
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores state in Dom node
|
* Stores state in Dom node
|
||||||
* \param elem is Dom element corresponding to 'Composer' tag
|
* \param elem is Dom element corresponding to 'Composer' tag
|
||||||
@ -451,32 +449,15 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
#endif
|
#endif
|
||||||
QgsExpressionContext createExpressionContext() const override;
|
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
|
* Returns the conversion factor from map units to layout units.
|
||||||
* associated legend items know they should update
|
* This is calculated using the width of the map item and the width of the
|
||||||
|
* current visible map extent.
|
||||||
*/
|
*/
|
||||||
void layerStyleOverridesChanged();
|
double mapUnitsToLayoutUnits() const;
|
||||||
|
|
||||||
#if 0
|
#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
|
* 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,
|
* \returns 0 if this item is to be placed on the same layer as the previous item,
|
||||||
@ -494,7 +475,23 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
/**
|
/**
|
||||||
* Calculates the extent to request and the yShift of the top-left point in case of rotation.
|
* Calculates the extent to request and the yShift of the top-left point in case of rotation.
|
||||||
*/
|
*/
|
||||||
void requestedExtent( QgsRectangle &extent ) const;
|
QgsRectangle requestedExtent() const;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
@ -505,18 +502,19 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
|
|
||||||
//! Updates the bounding rect of this item. Call this function before doing any changes related to annotation out of the map rectangle
|
//! Updates the bounding rect of this item. Call this function before doing any changes related to annotation out of the map rectangle
|
||||||
void updateBoundingRect();
|
void updateBoundingRect();
|
||||||
#if 0
|
|
||||||
virtual void refreshDataDefinedProperty( const QgsComposerObject::DataDefinedProperty property = QgsComposerObject::AllProperties, const QgsExpressionContext *context = nullptr ) override;
|
void refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties ) override;
|
||||||
#endif
|
|
||||||
private slots:
|
private slots:
|
||||||
void layersAboutToBeRemoved( QList<QgsMapLayer *> layers );
|
void layersAboutToBeRemoved( const QList<QgsMapLayer *> &layers );
|
||||||
|
|
||||||
void painterJobFinished();
|
void painterJobFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
||||||
//! Unique identifier
|
//! Unique identifier
|
||||||
int mId = 0;
|
int mMapId = 1;
|
||||||
#if 0
|
#if 0
|
||||||
QgsComposerMapGridStack *mGridStack = nullptr;
|
QgsComposerMapGridStack *mGridStack = nullptr;
|
||||||
|
|
||||||
@ -544,6 +542,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
// background
|
// background
|
||||||
std::unique_ptr< QImage > mCacheFinalImage;
|
std::unique_ptr< QImage > mCacheFinalImage;
|
||||||
std::unique_ptr< QImage > mCacheRenderingImage;
|
std::unique_ptr< QImage > mCacheRenderingImage;
|
||||||
|
bool mUpdatesEnabled = true;
|
||||||
|
|
||||||
//! True if cached map image must be recreated
|
//! True if cached map image must be recreated
|
||||||
bool mCacheInvalidated = true;
|
bool mCacheInvalidated = true;
|
||||||
@ -591,8 +590,18 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
* is true. May be overridden by data-defined expression. */
|
* is true. May be overridden by data-defined expression. */
|
||||||
QString mFollowVisibilityPresetName;
|
QString mFollowVisibilityPresetName;
|
||||||
|
|
||||||
//! \brief Create cache image
|
/**
|
||||||
void recreateCachedImageInBackground();
|
* \brief Draw to paint device
|
||||||
|
* \param painter painter
|
||||||
|
* \param extent map extent
|
||||||
|
* \param size size in scene coordinates
|
||||||
|
* \param dpi scene dpi
|
||||||
|
*/
|
||||||
|
void drawMap( QPainter *painter, const QgsRectangle &extent, QSizeF size, double dpi );
|
||||||
|
|
||||||
|
|
||||||
|
//! Create cache image
|
||||||
|
void recreateCachedImageInBackground( double viewScaleFactor );
|
||||||
|
|
||||||
//! Establishes signal/slot connection for update in case of layer change
|
//! Establishes signal/slot connection for update in case of layer change
|
||||||
void connectUpdateSlot();
|
void connectUpdateSlot();
|
||||||
@ -647,14 +656,18 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
void mapPolygon( const QgsRectangle &extent, QPolygonF &poly ) const;
|
void mapPolygon( const QgsRectangle &extent, QPolygonF &poly ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scales a composer map shift (in MM) and rotates it by mRotation
|
* Scales a layout map shift (in layout units) and rotates it by mRotation
|
||||||
\param xShift in: shift in x direction (in item units), out: xShift in map units
|
* \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*/
|
* \param yShift in: shift in y direction (in item units), out: yShift in map units
|
||||||
|
*/
|
||||||
void transformShift( double &xShift, double &yShift ) const;
|
void transformShift( double &xShift, double &yShift ) const;
|
||||||
|
|
||||||
void drawAnnotations( QPainter *painter );
|
void drawAnnotations( QPainter *painter );
|
||||||
void drawAnnotation( const QgsAnnotation *item, QgsRenderContext &context );
|
void drawAnnotation( const QgsAnnotation *item, QgsRenderContext &context );
|
||||||
QPointF composerMapPosForItem( const QgsAnnotation *item ) const;
|
QPointF layoutMapPosForItem( const QgsAnnotation *item ) const;
|
||||||
|
|
||||||
|
void drawMapFrame( QPainter *p );
|
||||||
|
void drawMapBackground( QPainter *p );
|
||||||
|
|
||||||
enum PartType
|
enum PartType
|
||||||
{
|
{
|
||||||
@ -666,7 +679,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
SelectionBoxes
|
SelectionBoxes
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Test if a part of the copmosermap needs to be drawn, considering mCurrentExportLayer
|
//! Test if a part of the item needs to be drawn, considering the context's current export layer
|
||||||
bool shouldDrawPart( PartType part ) const;
|
bool shouldDrawPart( PartType part ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -675,8 +688,8 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
|||||||
*/
|
*/
|
||||||
void refreshMapExtents( const QgsExpressionContext *context = nullptr );
|
void refreshMapExtents( const QgsExpressionContext *context = nullptr );
|
||||||
|
|
||||||
friend class QgsComposerMapOverview; //to access mXOffset, mYOffset
|
friend class QgsLayoutMapOverview; //to access mXOffset, mYOffset
|
||||||
friend class TestQgsComposerMap;
|
friend class TestQgsLayoutMap;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,6 +22,16 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
void QgsLayoutUtils::rotate( double angle, double &x, double &y )
|
||||||
|
{
|
||||||
|
double rotToRad = angle * M_PI / 180.0;
|
||||||
|
double xRot, yRot;
|
||||||
|
xRot = x * std::cos( rotToRad ) - y * std::sin( rotToRad );
|
||||||
|
yRot = x * std::sin( rotToRad ) + y * std::cos( rotToRad );
|
||||||
|
x = xRot;
|
||||||
|
y = yRot;
|
||||||
|
}
|
||||||
|
|
||||||
double QgsLayoutUtils::normalizedAngle( const double angle, const bool allowNegative )
|
double QgsLayoutUtils::normalizedAngle( const double angle, const bool allowNegative )
|
||||||
{
|
{
|
||||||
double clippedAngle = angle;
|
double clippedAngle = angle;
|
||||||
|
@ -34,6 +34,14 @@ class CORE_EXPORT QgsLayoutUtils
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates a point / vector around the origin.
|
||||||
|
* \param angle rotation angle in degrees, counterclockwise
|
||||||
|
* \param x in/out: x coordinate before / after the rotation
|
||||||
|
* \param y in/out: y coordinate before / after the rotation
|
||||||
|
*/
|
||||||
|
static void rotate( double angle, double &x, double &y );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures that an \a angle (in degrees) is in the range 0 <= angle < 360.
|
* Ensures that an \a angle (in degrees) is in the range 0 <= angle < 360.
|
||||||
* If \a allowNegative is true then angles between (-360, 360) are allowed. If false,
|
* If \a allowNegative is true then angles between (-360, 360) are allowed. If false,
|
||||||
|
@ -135,6 +135,7 @@ SET(TESTS
|
|||||||
testqgslayoutcontext.cpp
|
testqgslayoutcontext.cpp
|
||||||
testqgslayoutitem.cpp
|
testqgslayoutitem.cpp
|
||||||
testqgslayoutitemgroup.cpp
|
testqgslayoutitemgroup.cpp
|
||||||
|
testqgslayoutmap.cpp
|
||||||
testqgslayoutmodel.cpp
|
testqgslayoutmodel.cpp
|
||||||
testqgslayoutobject.cpp
|
testqgslayoutobject.cpp
|
||||||
testqgslayoutpage.cpp
|
testqgslayoutpage.cpp
|
||||||
|
433
tests/src/core/testqgslayoutmap.cpp
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
testqgslayoutmap.cpp
|
||||||
|
----------------------
|
||||||
|
begin : October 2017
|
||||||
|
copyright : (C) 2017 by Nyall Dawson
|
||||||
|
email : nyall dot dawson at gmail dot com
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "qgsapplication.h"
|
||||||
|
#include "qgslayout.h"
|
||||||
|
#include "qgsmultirenderchecker.h"
|
||||||
|
#include "qgslayoutitemmap.h"
|
||||||
|
#include "qgsmultibandcolorrenderer.h"
|
||||||
|
#include "qgsrasterlayer.h"
|
||||||
|
#include "qgsrasterdataprovider.h"
|
||||||
|
#include "qgsvectorlayer.h"
|
||||||
|
#include "qgsvectordataprovider.h"
|
||||||
|
#include "qgsproject.h"
|
||||||
|
#include "qgsmapthemecollection.h"
|
||||||
|
#include "qgsproperty.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include "qgstest.h"
|
||||||
|
|
||||||
|
class TestQgsLayoutMap : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TestQgsLayoutMap() = default;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initTestCase();// will be called before the first testfunction is executed.
|
||||||
|
void cleanupTestCase();// will be called after the last testfunction was executed.
|
||||||
|
void init();// will be called before each testfunction is executed.
|
||||||
|
void cleanup();// will be called after every testfunction.
|
||||||
|
void id();
|
||||||
|
void render();
|
||||||
|
#if 0
|
||||||
|
void uniqueId(); //test if map id is adapted when doing copy paste
|
||||||
|
void worldFileGeneration(); // test world file generation
|
||||||
|
#endif
|
||||||
|
void mapPolygonVertices(); // test mapPolygon function with no map rotation
|
||||||
|
void dataDefinedLayers(); //test data defined layer string
|
||||||
|
void dataDefinedStyles(); //test data defined styles
|
||||||
|
|
||||||
|
private:
|
||||||
|
QgsRasterLayer *mRasterLayer = nullptr;
|
||||||
|
QgsVectorLayer *mPointsLayer = nullptr;
|
||||||
|
QgsVectorLayer *mPolysLayer = nullptr;
|
||||||
|
QgsVectorLayer *mLinesLayer = nullptr;
|
||||||
|
QString mReport;
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestQgsLayoutMap::initTestCase()
|
||||||
|
{
|
||||||
|
QgsApplication::init();
|
||||||
|
QgsApplication::initQgis();
|
||||||
|
|
||||||
|
//create maplayers from testdata and add to layer registry
|
||||||
|
QFileInfo rasterFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/landsat.tif" );
|
||||||
|
mRasterLayer = new QgsRasterLayer( rasterFileInfo.filePath(),
|
||||||
|
rasterFileInfo.completeBaseName() );
|
||||||
|
QgsMultiBandColorRenderer *rasterRenderer = new QgsMultiBandColorRenderer( mRasterLayer->dataProvider(), 2, 3, 4 );
|
||||||
|
mRasterLayer->setRenderer( rasterRenderer );
|
||||||
|
|
||||||
|
QFileInfo pointFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/points.shp" );
|
||||||
|
mPointsLayer = new QgsVectorLayer( pointFileInfo.filePath(),
|
||||||
|
pointFileInfo.completeBaseName(), QStringLiteral( "ogr" ) );
|
||||||
|
|
||||||
|
QFileInfo polyFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/polys.shp" );
|
||||||
|
mPolysLayer = new QgsVectorLayer( polyFileInfo.filePath(),
|
||||||
|
polyFileInfo.completeBaseName(), QStringLiteral( "ogr" ) );
|
||||||
|
|
||||||
|
QFileInfo lineFileInfo( QStringLiteral( TEST_DATA_DIR ) + "/lines.shp" );
|
||||||
|
mLinesLayer = new QgsVectorLayer( lineFileInfo.filePath(),
|
||||||
|
lineFileInfo.completeBaseName(), QStringLiteral( "ogr" ) );
|
||||||
|
|
||||||
|
// some layers need to be in project for data-defined layers functionality
|
||||||
|
QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << mRasterLayer << mPointsLayer << mPolysLayer << mLinesLayer );
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQgsLayoutMap::cleanupTestCase()
|
||||||
|
{
|
||||||
|
QString myReportFile = QDir::tempPath() + "/qgistest.html";
|
||||||
|
QFile myFile( myReportFile );
|
||||||
|
if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
|
||||||
|
{
|
||||||
|
QTextStream myQTextStream( &myFile );
|
||||||
|
myQTextStream << mReport;
|
||||||
|
myFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsApplication::exitQgis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQgsLayoutMap::init()
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
//create composition with composer map
|
||||||
|
mComposition = new QgsComposition( QgsProject::instance() );
|
||||||
|
mComposition->setPaperSize( 297, 210 ); //A4 landscape
|
||||||
|
mComposerMap = new QgsComposerMap( mComposition, 20, 20, 200, 100 );
|
||||||
|
mComposerMap->setFrameEnabled( true );
|
||||||
|
mComposerMap->setLayers( QList<QgsMapLayer *>() << mRasterLayer );
|
||||||
|
mComposition->addComposerMap( mComposerMap );
|
||||||
|
#endif
|
||||||
|
mReport = QStringLiteral( "<h1>Composer Map Tests</h1>\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQgsLayoutMap::cleanup()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQgsLayoutMap::id()
|
||||||
|
{
|
||||||
|
QgsLayout l( QgsProject::instance( ) );
|
||||||
|
QgsLayoutItemMap *map1 = new QgsLayoutItemMap( &l );
|
||||||
|
QCOMPARE( map1->displayName(), QStringLiteral( "Map 1" ) );
|
||||||
|
l.addLayoutItem( map1 );
|
||||||
|
|
||||||
|
QgsLayoutItemMap *map2 = new QgsLayoutItemMap( &l );
|
||||||
|
QCOMPARE( map2->displayName(), QStringLiteral( "Map 2" ) );
|
||||||
|
l.addLayoutItem( map2 );
|
||||||
|
|
||||||
|
map1->setId( "my map" );
|
||||||
|
QCOMPARE( map1->displayName(), QStringLiteral( "my map" ) );
|
||||||
|
|
||||||
|
// existing name should be recycled
|
||||||
|
l.removeLayoutItem( map1 );
|
||||||
|
QgsLayoutItemMap *map3 = new QgsLayoutItemMap( &l );
|
||||||
|
QCOMPARE( map3->displayName(), QStringLiteral( "Map 1" ) );
|
||||||
|
l.addLayoutItem( map3 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TestQgsLayoutMap::render()
|
||||||
|
{
|
||||||
|
QgsLayout l( QgsProject::instance() );
|
||||||
|
l.initializeDefaults();
|
||||||
|
QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
|
||||||
|
map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) );
|
||||||
|
map->setFrameEnabled( true );
|
||||||
|
map->setLayers( QList<QgsMapLayer *>() << mRasterLayer );
|
||||||
|
l.addLayoutItem( map );
|
||||||
|
|
||||||
|
map->setExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) );
|
||||||
|
QgsLayoutChecker checker( QStringLiteral( "composermap_render" ), &l );
|
||||||
|
checker.setControlPathPrefix( QStringLiteral( "composer_map" ) );
|
||||||
|
|
||||||
|
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
void TestQgsLayoutMap::uniqueId()
|
||||||
|
{
|
||||||
|
QDomDocument doc;
|
||||||
|
QDomElement documentElement = doc.createElement( QStringLiteral( "ComposerItemClipboard" ) );
|
||||||
|
mComposerMap->writeXml( documentElement, doc );
|
||||||
|
mComposition->addItemsFromXml( documentElement, doc, false );
|
||||||
|
|
||||||
|
//test if both composer maps have different ids
|
||||||
|
const QgsComposerMap *newMap = 0;
|
||||||
|
QList<const QgsComposerMap *> mapList = mComposition->composerMapItems();
|
||||||
|
QList<const QgsComposerMap *>::const_iterator mapIt = mapList.constBegin();
|
||||||
|
for ( ; mapIt != mapList.constEnd(); ++mapIt )
|
||||||
|
{
|
||||||
|
if ( *mapIt != mComposerMap )
|
||||||
|
{
|
||||||
|
newMap = *mapIt;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVERIFY( newMap );
|
||||||
|
|
||||||
|
int oldId = mComposerMap->id();
|
||||||
|
int newId = newMap->id();
|
||||||
|
|
||||||
|
mComposition->removeComposerItem( const_cast<QgsComposerMap *>( newMap ) );
|
||||||
|
|
||||||
|
QVERIFY( oldId != newId );
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQgsLayoutMap::worldFileGeneration()
|
||||||
|
{
|
||||||
|
mComposerMap->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) );
|
||||||
|
mComposerMap->setMapRotation( 30.0 );
|
||||||
|
|
||||||
|
mComposition->setGenerateWorldFile( true );
|
||||||
|
mComposition->setReferenceMap( mComposerMap );
|
||||||
|
|
||||||
|
double a, b, c, d, e, f;
|
||||||
|
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
|
||||||
|
|
||||||
|
QGSCOMPARENEAR( a, 4.18048, 0.001 );
|
||||||
|
QGSCOMPARENEAR( b, 2.41331, 0.001 );
|
||||||
|
QGSCOMPARENEAR( c, 779444, 1 );
|
||||||
|
QGSCOMPARENEAR( d, 2.4136, 0.001 );
|
||||||
|
QGSCOMPARENEAR( e, -4.17997, 0.001 );
|
||||||
|
QGSCOMPARENEAR( f, 3.34241e+06, 1e+03 );
|
||||||
|
|
||||||
|
//test with map on second page. Parameters should be the same
|
||||||
|
mComposerMap->setItemPosition( 20, 20, QgsComposerItem::UpperLeft, 2 );
|
||||||
|
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
|
||||||
|
|
||||||
|
QGSCOMPARENEAR( a, 4.18048, 0.001 );
|
||||||
|
QGSCOMPARENEAR( b, 2.41331, 0.001 );
|
||||||
|
QGSCOMPARENEAR( c, 779444, 1 );
|
||||||
|
QGSCOMPARENEAR( d, 2.4136, 0.001 );
|
||||||
|
QGSCOMPARENEAR( e, -4.17997, 0.001 );
|
||||||
|
QGSCOMPARENEAR( f, 3.34241e+06, 1e+03 );
|
||||||
|
|
||||||
|
//test computing parameters for specific region
|
||||||
|
mComposerMap->setItemPosition( 20, 20, QgsComposerItem::UpperLeft, 2 );
|
||||||
|
mComposition->computeWorldFileParameters( QRectF( 10, 5, 260, 200 ), a, b, c, d, e, f );
|
||||||
|
|
||||||
|
QGSCOMPARENEAR( a, 4.18061, 0.001 );
|
||||||
|
QGSCOMPARENEAR( b, 2.41321, 0.001 );
|
||||||
|
QGSCOMPARENEAR( c, 773810, 1 );
|
||||||
|
QGSCOMPARENEAR( d, 2.4137, 0.001 );
|
||||||
|
QGSCOMPARENEAR( e, -4.1798, 0.001 );
|
||||||
|
QGSCOMPARENEAR( f, 3.35331e+06, 1e+03 );
|
||||||
|
|
||||||
|
mComposition->setGenerateWorldFile( false );
|
||||||
|
mComposerMap->setMapRotation( 0.0 );
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void TestQgsLayoutMap::mapPolygonVertices()
|
||||||
|
{
|
||||||
|
QgsLayout l( QgsProject::instance() );
|
||||||
|
l.initializeDefaults();
|
||||||
|
QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
|
||||||
|
map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) );
|
||||||
|
map->setFrameEnabled( true );
|
||||||
|
map->setLayers( QList<QgsMapLayer *>() << mRasterLayer );
|
||||||
|
l.addLayoutItem( map );
|
||||||
|
|
||||||
|
map->setExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3345223.125 ) );
|
||||||
|
QPolygonF visibleExtent = map->visibleExtentPolygon();
|
||||||
|
|
||||||
|
//vertices should be returned in clockwise order starting at the top-left point
|
||||||
|
QVERIFY( std::fabs( visibleExtent[0].x() - 781662.375 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[0].y() - 3345223.125 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[1].x() - 793062.375 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[1].y() - 3345223.125 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[2].x() - 793062.375 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[2].y() - 3339523.125 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[3].x() - 781662.375 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[3].y() - 3339523.125 ) < 0.001 );
|
||||||
|
|
||||||
|
//polygon should be closed
|
||||||
|
QVERIFY( visibleExtent.isClosed() );
|
||||||
|
|
||||||
|
//now test with rotated map
|
||||||
|
map->setMapRotation( 10 );
|
||||||
|
visibleExtent = map->visibleExtentPolygon();
|
||||||
|
|
||||||
|
//vertices should be returned in clockwise order starting at the top-left point
|
||||||
|
QVERIFY( std::fabs( visibleExtent[0].x() - 781254.0735015 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[0].y() - 3344190.0324834 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[1].x() - 792480.881886 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[1].y() - 3346169.62171 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[2].x() - 793470.676499 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[2].y() - 3340556.21752 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[3].x() - 782243.868114 ) < 0.001 );
|
||||||
|
QVERIFY( std::fabs( visibleExtent[3].y() - 3338576.62829 ) < 0.001 );
|
||||||
|
|
||||||
|
//polygon should be closed
|
||||||
|
QVERIFY( visibleExtent.isClosed() );
|
||||||
|
|
||||||
|
map->setMapRotation( 0 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQgsLayoutMap::dataDefinedLayers()
|
||||||
|
{
|
||||||
|
QgsLayout l( QgsProject::instance() );
|
||||||
|
l.initializeDefaults();
|
||||||
|
QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
|
||||||
|
map->attemptMove( QgsLayoutPoint( 20, 20 ) );
|
||||||
|
map->attemptResize( QgsLayoutSize( 200, 100 ) );
|
||||||
|
|
||||||
|
map->setFrameEnabled( true );
|
||||||
|
l.addLayoutItem( map );
|
||||||
|
|
||||||
|
//test malformed layer set string
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapLayers, QgsProperty::fromExpression( QStringLiteral( "'x'" ) ) );
|
||||||
|
QList<QgsMapLayer *> result = map->layersToRender();
|
||||||
|
QVERIFY( result.isEmpty() );
|
||||||
|
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapLayers, QgsProperty::fromExpression( QStringLiteral( "'x|'" ) ) );
|
||||||
|
result = map->layersToRender();
|
||||||
|
QVERIFY( result.isEmpty() );
|
||||||
|
|
||||||
|
//test subset of valid layers
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapLayers, QgsProperty::fromExpression(
|
||||||
|
QStringLiteral( "'%1|%2'" ).arg( mPolysLayer->name(), mRasterLayer->name() ) ) );
|
||||||
|
result = map->layersToRender();
|
||||||
|
QCOMPARE( result.count(), 2 );
|
||||||
|
QVERIFY( result.contains( mPolysLayer ) );
|
||||||
|
QVERIFY( result.contains( mRasterLayer ) );
|
||||||
|
|
||||||
|
//test non-existent layer
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapLayers, QgsProperty::fromExpression(
|
||||||
|
QStringLiteral( "'x|%1|%2'" ).arg( mLinesLayer->name(), mPointsLayer->name() ) ) );
|
||||||
|
result = map->layersToRender();
|
||||||
|
QCOMPARE( result.count(), 2 );
|
||||||
|
QVERIFY( result.contains( mLinesLayer ) );
|
||||||
|
QVERIFY( result.contains( mPointsLayer ) );
|
||||||
|
|
||||||
|
//test no layers
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapLayers, QgsProperty::fromExpression(
|
||||||
|
QStringLiteral( "''" ) ) );
|
||||||
|
result = map->layersToRender();
|
||||||
|
QVERIFY( result.isEmpty() );
|
||||||
|
|
||||||
|
|
||||||
|
//test with atlas feature evaluation
|
||||||
|
QgsVectorLayer *atlasLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:string" ), QStringLiteral( "atlas" ), QStringLiteral( "memory" ) );
|
||||||
|
QVERIFY( atlasLayer->isValid() );
|
||||||
|
QgsFeature f1( atlasLayer->dataProvider()->fields(), 1 );
|
||||||
|
f1.setAttribute( QStringLiteral( "col1" ), mLinesLayer->name() );
|
||||||
|
QgsFeature f2( atlasLayer->dataProvider()->fields(), 1 );
|
||||||
|
f2.setAttribute( QStringLiteral( "col1" ), mPointsLayer->name() );
|
||||||
|
atlasLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 );
|
||||||
|
#if 0 //TODO
|
||||||
|
mComposition->atlasComposition().setCoverageLayer( atlasLayer );
|
||||||
|
mComposition->atlasComposition().setEnabled( true );
|
||||||
|
mComposition->setAtlasMode( QgsComposition::ExportAtlas );
|
||||||
|
mComposition->atlasComposition().beginRender();
|
||||||
|
mComposition->atlasComposition().prepareForFeature( 0 );
|
||||||
|
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapLayers, QgsProperty::fromField( QStringLiteral( "col1" ) ) );
|
||||||
|
result = map->layersToRender();
|
||||||
|
QCOMPARE( result.count(), 1 );
|
||||||
|
QCOMPARE( result.at( 0 ), mLinesLayer );
|
||||||
|
mComposition->atlasComposition().prepareForFeature( 1 );
|
||||||
|
result = map->layersToRender();
|
||||||
|
QCOMPARE( result.count(), 1 );
|
||||||
|
QCOMPARE( result.at( 0 ), mPointsLayer );
|
||||||
|
mComposition->atlasComposition().setEnabled( false );
|
||||||
|
delete atlasLayer;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//render test
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapLayers, QgsProperty::fromExpression(
|
||||||
|
QStringLiteral( "'%1|%2'" ).arg( mPolysLayer->name(), mPointsLayer->name() ) ) );
|
||||||
|
map->setExtent( QgsRectangle( -110.0, 25.0, -90, 40.0 ) );
|
||||||
|
|
||||||
|
QgsLayoutChecker checker( QStringLiteral( "composermap_ddlayers" ), &l );
|
||||||
|
checker.setControlPathPrefix( QStringLiteral( "composer_map" ) );
|
||||||
|
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQgsLayoutMap::dataDefinedStyles()
|
||||||
|
{
|
||||||
|
QList<QgsMapLayer *> layers = QList<QgsMapLayer *>() << mRasterLayer << mPolysLayer << mPointsLayer << mLinesLayer;
|
||||||
|
|
||||||
|
QgsLayout l( QgsProject::instance() );
|
||||||
|
l.initializeDefaults();
|
||||||
|
|
||||||
|
QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
|
||||||
|
map->attemptMove( QgsLayoutPoint( 20, 20 ) );
|
||||||
|
map->attemptResize( QgsLayoutSize( 200, 100 ) );
|
||||||
|
map->setFrameEnabled( true );
|
||||||
|
map->setLayers( layers );
|
||||||
|
l.addLayoutItem( map );
|
||||||
|
|
||||||
|
QgsMapThemeCollection::MapThemeRecord rec;
|
||||||
|
rec.setLayerRecords( QList<QgsMapThemeCollection::MapThemeLayerRecord>()
|
||||||
|
<< QgsMapThemeCollection::MapThemeLayerRecord( mPointsLayer )
|
||||||
|
<< QgsMapThemeCollection::MapThemeLayerRecord( mLinesLayer )
|
||||||
|
);
|
||||||
|
|
||||||
|
QgsProject::instance()->mapThemeCollection()->insert( QStringLiteral( "test preset" ), rec );
|
||||||
|
|
||||||
|
// test following of preset
|
||||||
|
map->setFollowVisibilityPreset( true );
|
||||||
|
map->setFollowVisibilityPresetName( QStringLiteral( "test preset" ) );
|
||||||
|
QSet<QgsMapLayer *> result = map->layersToRender().toSet();
|
||||||
|
QCOMPARE( result.count(), 2 );
|
||||||
|
map->setFollowVisibilityPresetName( QString() );
|
||||||
|
|
||||||
|
//test malformed style string
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapStylePreset, QgsProperty::fromExpression( QStringLiteral( "5" ) ) );
|
||||||
|
result = map->layersToRender().toSet();
|
||||||
|
QCOMPARE( result, layers.toSet() );
|
||||||
|
|
||||||
|
//test valid preset
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapStylePreset, QgsProperty::fromExpression( QStringLiteral( "'test preset'" ) ) );
|
||||||
|
result = map->layersToRender().toSet();
|
||||||
|
QCOMPARE( result.count(), 2 );
|
||||||
|
QVERIFY( result.contains( mLinesLayer ) );
|
||||||
|
QVERIFY( result.contains( mPointsLayer ) );
|
||||||
|
|
||||||
|
//test non-existent preset
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapStylePreset, QgsProperty::fromExpression( QStringLiteral( "'bad preset'" ) ) );
|
||||||
|
result = map->layersToRender().toSet();
|
||||||
|
QCOMPARE( result, layers.toSet() );
|
||||||
|
|
||||||
|
//test that dd layer set overrides style layers
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapStylePreset, QgsProperty::fromExpression( QStringLiteral( "'test preset'" ) ) );
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapLayers, QgsProperty::fromExpression(
|
||||||
|
QStringLiteral( "'%1'" ).arg( mPolysLayer->name() ) ) );
|
||||||
|
result = map->layersToRender().toSet();
|
||||||
|
QCOMPARE( result.count(), 1 );
|
||||||
|
QVERIFY( result.contains( mPolysLayer ) );
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapLayers, QgsProperty() );
|
||||||
|
|
||||||
|
//render test
|
||||||
|
map->dataDefinedProperties().setProperty( QgsLayoutObject::MapStylePreset, QgsProperty::fromExpression( QStringLiteral( "'test preset'" ) ) );
|
||||||
|
map->setExtent( QgsRectangle( -110.0, 25.0, -90, 40.0 ) );
|
||||||
|
|
||||||
|
QgsLayoutChecker checker( QStringLiteral( "composermap_ddstyles" ), &l );
|
||||||
|
checker.setControlPathPrefix( QStringLiteral( "composer_map" ) );
|
||||||
|
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
QGSTEST_MAIN( TestQgsLayoutMap )
|
||||||
|
#include "testqgslayoutmap.moc"
|
@ -30,6 +30,7 @@ class TestQgsLayoutUtils: public QObject
|
|||||||
void cleanupTestCase();// will be called after the last testfunction was executed.
|
void cleanupTestCase();// will be called after the last testfunction was executed.
|
||||||
void init();// will be called before each testfunction is executed.
|
void init();// will be called before each testfunction is executed.
|
||||||
void cleanup();// will be called after every testfunction.
|
void cleanup();// will be called after every testfunction.
|
||||||
|
void rotate();
|
||||||
void normalizedAngle(); //test normalised angle function
|
void normalizedAngle(); //test normalised angle function
|
||||||
void snappedAngle();
|
void snappedAngle();
|
||||||
void createRenderContextFromLayout();
|
void createRenderContextFromLayout();
|
||||||
@ -69,6 +70,28 @@ void TestQgsLayoutUtils::cleanup()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestQgsLayoutUtils::rotate()
|
||||||
|
{
|
||||||
|
// pairs of lines from before -> expected after position and angle to rotate
|
||||||
|
QList< QPair< QLineF, double > > testVals;
|
||||||
|
testVals << qMakePair( QLineF( 0, 1, 0, 1 ), 0.0 );
|
||||||
|
testVals << qMakePair( QLineF( 0, 1, -1, 0 ), 90.0 );
|
||||||
|
testVals << qMakePair( QLineF( 0, 1, 0, -1 ), 180.0 );
|
||||||
|
testVals << qMakePair( QLineF( 0, 1, 1, 0 ), 270.0 );
|
||||||
|
testVals << qMakePair( QLineF( 0, 1, 0, 1 ), 360.0 );
|
||||||
|
|
||||||
|
//test rotate helper function
|
||||||
|
QList< QPair< QLineF, double > >::const_iterator it = testVals.constBegin();
|
||||||
|
for ( ; it != testVals.constEnd(); ++it )
|
||||||
|
{
|
||||||
|
double x = ( *it ).first.x1();
|
||||||
|
double y = ( *it ).first.y1();
|
||||||
|
QgsLayoutUtils::rotate( ( *it ).second, x, y );
|
||||||
|
QGSCOMPARENEAR( x, ( *it ).first.x2(), 4 * DBL_EPSILON );
|
||||||
|
QGSCOMPARENEAR( y, ( *it ).first.y2(), 4 * DBL_EPSILON );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TestQgsLayoutUtils::normalizedAngle()
|
void TestQgsLayoutUtils::normalizedAngle()
|
||||||
{
|
{
|
||||||
QList< QPair< double, double > > testVals;
|
QList< QPair< double, double > > testVals;
|
||||||
|
@ -86,6 +86,7 @@ ADD_PYTHON_TEST(PyQgsLayoutGridSettings test_qgslayoutgridsettings.py)
|
|||||||
ADD_PYTHON_TEST(PyQgsLayoutGuide test_qgslayoutguides.py)
|
ADD_PYTHON_TEST(PyQgsLayoutGuide test_qgslayoutguides.py)
|
||||||
ADD_PYTHON_TEST(PyQgsLayoutItem test_qgslayoutitem.py)
|
ADD_PYTHON_TEST(PyQgsLayoutItem test_qgslayoutitem.py)
|
||||||
ADD_PYTHON_TEST(PyQgsLayoutItemPropertiesDialog test_qgslayoutitempropertiesdialog.py)
|
ADD_PYTHON_TEST(PyQgsLayoutItemPropertiesDialog test_qgslayoutitempropertiesdialog.py)
|
||||||
|
ADD_PYTHON_TEST(PyQgsLayoutMap test_qgslayoutmap.py)
|
||||||
ADD_PYTHON_TEST(PyQgsLayoutPolygon test_qgslayoutpolygon.py)
|
ADD_PYTHON_TEST(PyQgsLayoutPolygon test_qgslayoutpolygon.py)
|
||||||
ADD_PYTHON_TEST(PyQgsLayoutPolyline test_qgslayoutpolyline.py)
|
ADD_PYTHON_TEST(PyQgsLayoutPolyline test_qgslayoutpolyline.py)
|
||||||
ADD_PYTHON_TEST(PyQgsLayoutSnapper test_qgslayoutsnapper.py)
|
ADD_PYTHON_TEST(PyQgsLayoutSnapper test_qgslayoutsnapper.py)
|
||||||
|
237
tests/src/python/test_qgslayoutmap.py
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""QGIS Unit tests for QgsLayoutItemMap.
|
||||||
|
|
||||||
|
.. note:: This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
"""
|
||||||
|
__author__ = '(C) 2017 Nyall Dawson'
|
||||||
|
__date__ = '20/10/2017'
|
||||||
|
__copyright__ = 'Copyright 2017, The QGIS Project'
|
||||||
|
# This will get replaced with a git SHA1 when you do a git archive
|
||||||
|
__revision__ = '$Format:%H$'
|
||||||
|
|
||||||
|
import qgis # NOQA
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from qgis.PyQt.QtCore import QFileInfo, QRectF
|
||||||
|
from qgis.PyQt.QtXml import QDomDocument
|
||||||
|
from qgis.PyQt.QtGui import QPainter
|
||||||
|
|
||||||
|
from qgis.core import (QgsLayoutItemMap,
|
||||||
|
QgsRectangle,
|
||||||
|
QgsRasterLayer,
|
||||||
|
QgsVectorLayer,
|
||||||
|
QgsLayout,
|
||||||
|
QgsMapSettings,
|
||||||
|
QgsProject,
|
||||||
|
QgsMultiBandColorRenderer,
|
||||||
|
QgsCoordinateReferenceSystem
|
||||||
|
)
|
||||||
|
|
||||||
|
from qgis.testing import start_app, unittest
|
||||||
|
from utilities import unitTestDataPath
|
||||||
|
from qgslayoutchecker import QgsLayoutChecker
|
||||||
|
|
||||||
|
start_app()
|
||||||
|
TEST_DATA_DIR = unitTestDataPath()
|
||||||
|
|
||||||
|
|
||||||
|
class TestQgsComposerMap(unittest.TestCase):
|
||||||
|
|
||||||
|
def __init__(self, methodName):
|
||||||
|
"""Run once on class initialization."""
|
||||||
|
unittest.TestCase.__init__(self, methodName)
|
||||||
|
myPath = os.path.join(TEST_DATA_DIR, 'rgb256x256.png')
|
||||||
|
rasterFileInfo = QFileInfo(myPath)
|
||||||
|
self.raster_layer = QgsRasterLayer(rasterFileInfo.filePath(),
|
||||||
|
rasterFileInfo.completeBaseName())
|
||||||
|
rasterRenderer = QgsMultiBandColorRenderer(
|
||||||
|
self.raster_layer.dataProvider(), 1, 2, 3)
|
||||||
|
self.raster_layer.setRenderer(rasterRenderer)
|
||||||
|
|
||||||
|
myPath = os.path.join(TEST_DATA_DIR, 'points.shp')
|
||||||
|
vector_file_info = QFileInfo(myPath)
|
||||||
|
self.vector_layer = QgsVectorLayer(vector_file_info.filePath(),
|
||||||
|
vector_file_info.completeBaseName(), 'ogr')
|
||||||
|
assert self.vector_layer.isValid()
|
||||||
|
|
||||||
|
# pipe = mRasterLayer.pipe()
|
||||||
|
# assert pipe.set(rasterRenderer), 'Cannot set pipe renderer'
|
||||||
|
QgsProject.instance().addMapLayers([self.raster_layer, self.vector_layer])
|
||||||
|
|
||||||
|
# create composition with composer map
|
||||||
|
self.layout = QgsLayout(QgsProject.instance())
|
||||||
|
self.layout.initializeDefaults()
|
||||||
|
self.map = QgsLayoutItemMap(self.layout)
|
||||||
|
self.map.setFrameEnabled(True)
|
||||||
|
self.map.setLayers([self.raster_layer])
|
||||||
|
self.layout.addLayoutItem(self.map)
|
||||||
|
|
||||||
|
def testOverviewMap(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
overviewMap = QgsLayoutItemMap(self.layout) # , 20, 130, 70, 70)
|
||||||
|
overviewMap.setFrameEnabled(True)
|
||||||
|
overviewMap.setLayers([self.raster_layer])
|
||||||
|
self.layout.addComposerMap(overviewMap)
|
||||||
|
# zoom in
|
||||||
|
myRectangle = QgsRectangle(96, -152, 160, -120)
|
||||||
|
self.map.setNewExtent(myRectangle)
|
||||||
|
myRectangle2 = QgsRectangle(0, -256, 256, 0)
|
||||||
|
overviewMap.setNewExtent(myRectangle2)
|
||||||
|
overviewMap.overview().setFrameMap(self.map.id())
|
||||||
|
checker = QgsLayoutChecker('composermap_overview', self.layout)
|
||||||
|
checker.setControlPathPrefix("composer_mapoverview")
|
||||||
|
myTestResult, myMessage = checker.testComposition()
|
||||||
|
self.layout.removeComposerItem(overviewMap)
|
||||||
|
assert myTestResult, myMessage
|
||||||
|
|
||||||
|
def testOverviewMapBlend(self):
|
||||||
|
return
|
||||||
|
overviewMap = QgsComposerMap(self.layout, 20, 130, 70, 70)
|
||||||
|
overviewMap.setFrameEnabled(True)
|
||||||
|
overviewMap.setLayers([self.raster_layer])
|
||||||
|
self.layout.addComposerMap(overviewMap)
|
||||||
|
# zoom in
|
||||||
|
myRectangle = QgsRectangle(96, -152, 160, -120)
|
||||||
|
self.map.setNewExtent(myRectangle)
|
||||||
|
myRectangle2 = QgsRectangle(0, -256, 256, 0)
|
||||||
|
overviewMap.setNewExtent(myRectangle2)
|
||||||
|
overviewMap.overview().setFrameMap(self.map.id())
|
||||||
|
overviewMap.overview().setBlendMode(QPainter.CompositionMode_Multiply)
|
||||||
|
checker = QgsLayoutChecker('composermap_overview_blending', self.layout)
|
||||||
|
checker.setControlPathPrefix("composer_mapoverview")
|
||||||
|
myTestResult, myMessage = checker.testComposition()
|
||||||
|
self.layout.removeComposerItem(overviewMap)
|
||||||
|
assert myTestResult, myMessage
|
||||||
|
|
||||||
|
def testOverviewMapInvert(self):
|
||||||
|
return
|
||||||
|
overviewMap = QgsComposerMap(self.layout, 20, 130, 70, 70)
|
||||||
|
overviewMap.setFrameEnabled(True)
|
||||||
|
overviewMap.setLayers([self.raster_layer])
|
||||||
|
self.layout.addComposerMap(overviewMap)
|
||||||
|
# zoom in
|
||||||
|
myRectangle = QgsRectangle(96, -152, 160, -120)
|
||||||
|
self.map.setNewExtent(myRectangle)
|
||||||
|
myRectangle2 = QgsRectangle(0, -256, 256, 0)
|
||||||
|
overviewMap.setNewExtent(myRectangle2)
|
||||||
|
overviewMap.overview().setFrameMap(self.map.id())
|
||||||
|
overviewMap.overview().setInverted(True)
|
||||||
|
checker = QgsLayoutChecker('composermap_overview_invert', self.layout)
|
||||||
|
checker.setControlPathPrefix("composer_mapoverview")
|
||||||
|
myTestResult, myMessage = checker.testComposition()
|
||||||
|
self.layout.removeComposerItem(overviewMap)
|
||||||
|
assert myTestResult, myMessage
|
||||||
|
|
||||||
|
def testOverviewMapCenter(self):
|
||||||
|
return
|
||||||
|
overviewMap = QgsComposerMap(self.layout, 20, 130, 70, 70)
|
||||||
|
overviewMap.setFrameEnabled(True)
|
||||||
|
overviewMap.setLayers([self.raster_layer])
|
||||||
|
self.layout.addComposerMap(overviewMap)
|
||||||
|
# zoom in
|
||||||
|
myRectangle = QgsRectangle(192, -288, 320, -224)
|
||||||
|
self.map.setNewExtent(myRectangle)
|
||||||
|
myRectangle2 = QgsRectangle(0, -256, 256, 0)
|
||||||
|
overviewMap.setNewExtent(myRectangle2)
|
||||||
|
overviewMap.overview().setFrameMap(self.map.id())
|
||||||
|
overviewMap.overview().setInverted(False)
|
||||||
|
overviewMap.overview().setCentered(True)
|
||||||
|
checker = QgsLayoutChecker('composermap_overview_center', self.layout)
|
||||||
|
checker.setControlPathPrefix("composer_mapoverview")
|
||||||
|
myTestResult, myMessage = checker.testComposition()
|
||||||
|
self.layout.removeComposerItem(overviewMap)
|
||||||
|
assert myTestResult, myMessage
|
||||||
|
|
||||||
|
def testMapCrs(self):
|
||||||
|
# create composition with composer map
|
||||||
|
map_settings = QgsMapSettings()
|
||||||
|
map_settings.setLayers([self.vector_layer])
|
||||||
|
layout = QgsLayout(QgsProject.instance())
|
||||||
|
layout.initializeDefaults()
|
||||||
|
|
||||||
|
# check that new maps inherit project CRS
|
||||||
|
QgsProject.instance().setCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
|
||||||
|
map = QgsLayoutItemMap(layout)
|
||||||
|
map.attemptSetSceneRect(QRectF(20, 20, 200, 100))
|
||||||
|
map.setFrameEnabled(True)
|
||||||
|
rectangle = QgsRectangle(-13838977, 2369660, -8672298, 6250909)
|
||||||
|
map.setExtent(rectangle)
|
||||||
|
map.setLayers([self.vector_layer])
|
||||||
|
layout.addLayoutItem(map)
|
||||||
|
|
||||||
|
self.assertEqual(map.crs().authid(), 'EPSG:4326')
|
||||||
|
self.assertFalse(map.presetCrs().isValid())
|
||||||
|
|
||||||
|
# overwrite CRS
|
||||||
|
map.setCrs(QgsCoordinateReferenceSystem('EPSG:3857'))
|
||||||
|
self.assertEqual(map.crs().authid(), 'EPSG:3857')
|
||||||
|
self.assertEqual(map.presetCrs().authid(), 'EPSG:3857')
|
||||||
|
|
||||||
|
checker = QgsLayoutChecker('composermap_crs3857', layout)
|
||||||
|
checker.setControlPathPrefix("composer_map")
|
||||||
|
result, message = checker.testLayout()
|
||||||
|
self.assertTrue(result, message)
|
||||||
|
|
||||||
|
# overwrite CRS
|
||||||
|
map.setCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
|
||||||
|
self.assertEqual(map.presetCrs().authid(), 'EPSG:4326')
|
||||||
|
self.assertEqual(map.crs().authid(), 'EPSG:4326')
|
||||||
|
rectangle = QgsRectangle(-124, 17, -78, 52)
|
||||||
|
map.zoomToExtent(rectangle)
|
||||||
|
checker = QgsLayoutChecker('composermap_crs4326', layout)
|
||||||
|
checker.setControlPathPrefix("composer_map")
|
||||||
|
result, message = checker.testLayout()
|
||||||
|
self.assertTrue(result, message)
|
||||||
|
|
||||||
|
# change back to project CRS
|
||||||
|
map.setCrs(QgsCoordinateReferenceSystem())
|
||||||
|
self.assertEqual(map.crs().authid(), 'EPSG:4326')
|
||||||
|
self.assertFalse(map.presetCrs().isValid())
|
||||||
|
|
||||||
|
def testuniqueId(self):
|
||||||
|
return
|
||||||
|
doc = QDomDocument()
|
||||||
|
documentElement = doc.createElement('ComposerItemClipboard')
|
||||||
|
self.layout.writeXml(documentElement, doc)
|
||||||
|
self.layout.addItemsFromXml(documentElement, doc)
|
||||||
|
|
||||||
|
# test if both composer maps have different ids
|
||||||
|
newMap = QgsComposerMap(self.layout, 0, 0, 10, 10)
|
||||||
|
mapList = self.layout.composerMapItems()
|
||||||
|
|
||||||
|
for mapIt in mapList:
|
||||||
|
if mapIt != self.map:
|
||||||
|
newMap = mapIt
|
||||||
|
break
|
||||||
|
|
||||||
|
oldId = self.map.id()
|
||||||
|
newId = newMap.id()
|
||||||
|
|
||||||
|
self.layout.removeComposerItem(newMap)
|
||||||
|
myMessage = 'old: %s new: %s' % (oldId, newId)
|
||||||
|
assert oldId != newId, myMessage
|
||||||
|
|
||||||
|
def testWorldFileGeneration(self):
|
||||||
|
return
|
||||||
|
myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125)
|
||||||
|
self.map.setNewExtent(myRectangle)
|
||||||
|
self.map.setMapRotation(30.0)
|
||||||
|
|
||||||
|
self.layout.setGenerateWorldFile(True)
|
||||||
|
self.layout.setReferenceMap(self.map)
|
||||||
|
|
||||||
|
p = self.layout.computeWorldFileParameters()
|
||||||
|
pexpected = (4.180480199790922, 2.4133064516129026, 779443.7612381146,
|
||||||
|
2.4136013686911886, -4.179969388427311, 3342408.5663611)
|
||||||
|
ptolerance = (0.001, 0.001, 1, 0.001, 0.001, 1e+03)
|
||||||
|
for i in range(0, 6):
|
||||||
|
assert abs(p[i] - pexpected[i]) < ptolerance[i]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 61 KiB |