1 /***************************************************************************
2 qgsticksscalebarrenderer.cpp
3 ----------------------------
4 begin : June 2008
5 copyright : (C) 2008 by Marco Hugentobler
6 email : marco.hugentobler@karto.baug.ethz.ch
7 ***************************************************************************/
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "qgsticksscalebarrenderer.h"
18 #include "qgsscalebarsettings.h"
19 #include "qgslayoututils.h"
20 #include "qgssymbol.h"
21 #include "qgslinesymbol.h"
22 #include "qgstextrenderer.h"
23 #include <QPainter>
24
QgsTicksScaleBarRenderer(QgsTicksScaleBarRenderer::TickPosition position)25 QgsTicksScaleBarRenderer::QgsTicksScaleBarRenderer( QgsTicksScaleBarRenderer::TickPosition position )
26 : mTickPosition( position )
27 {
28
29 }
30
id() const31 QString QgsTicksScaleBarRenderer::id() const
32 {
33 switch ( mTickPosition )
34 {
35 case TicksUp:
36 return QStringLiteral( "Line Ticks Up" );
37 case TicksDown:
38 return QStringLiteral( "Line Ticks Down" );
39 case TicksMiddle:
40 return QStringLiteral( "Line Ticks Middle" );
41 }
42 return QString(); // to make gcc happy
43 }
44
visibleName() const45 QString QgsTicksScaleBarRenderer::visibleName() const
46 {
47 switch ( mTickPosition )
48 {
49 case TicksUp:
50 return QObject::tr( "Line Ticks Up" );
51 case TicksDown:
52 return QObject::tr( "Line Ticks Down" );
53 case TicksMiddle:
54 return QObject::tr( "Line Ticks Middle" );
55 }
56 return QString(); // to make gcc happy
57
58 }
59
sortKey() const60 int QgsTicksScaleBarRenderer::sortKey() const
61 {
62 switch ( mTickPosition )
63 {
64 case TicksUp:
65 return 5;
66 case TicksDown:
67 return 4;
68 case TicksMiddle:
69 return 3;
70 }
71 return 6;
72 }
73
flags() const74 QgsScaleBarRenderer::Flags QgsTicksScaleBarRenderer::flags() const
75 {
76 return Flag::FlagUsesLineSymbol |
77 Flag::FlagUsesDivisionSymbol |
78 Flag::FlagUsesSubdivisionSymbol |
79 Flag::FlagRespectsUnits |
80 Flag::FlagRespectsMapUnitsPerScaleBarUnit |
81 Flag::FlagUsesUnitLabel |
82 Flag::FlagUsesSegments |
83 Flag::FlagUsesLabelBarSpace |
84 Flag::FlagUsesLabelVerticalPlacement |
85 Flag::FlagUsesLabelHorizontalPlacement |
86 Flag::FlagUsesSubdivisions |
87 Flag::FlagUsesSubdivisionsHeight;
88 }
89
clone() const90 QgsTicksScaleBarRenderer *QgsTicksScaleBarRenderer::clone() const
91 {
92 return new QgsTicksScaleBarRenderer( * this );
93 }
94
draw(QgsRenderContext & context,const QgsScaleBarSettings & settings,const ScaleBarContext & scaleContext) const95 void QgsTicksScaleBarRenderer::draw( QgsRenderContext &context, const QgsScaleBarSettings &settings, const ScaleBarContext &scaleContext ) const
96 {
97 if ( !context.painter() )
98 return;
99
100 QPainter *painter = context.painter();
101
102 const double scaledLabelBarSpace = context.convertToPainterUnits( settings.labelBarSpace(), QgsUnitTypes::RenderMillimeters );
103 const double scaledBoxContentSpace = context.convertToPainterUnits( settings.boxContentSpace(), QgsUnitTypes::RenderMillimeters );
104 const QFontMetricsF fontMetrics = QgsTextRenderer::fontMetrics( context, settings.textFormat() );
105 const double barTopPosition = scaledBoxContentSpace + ( settings.labelVerticalPlacement() == QgsScaleBarSettings::LabelAboveSegment ? fontMetrics.ascent() + scaledLabelBarSpace : 0 );
106 const double scaledHeight = context.convertToPainterUnits( settings.height(), QgsUnitTypes::RenderMillimeters );
107 const double scaledSubdivisionsHeight = context.convertToPainterUnits( settings.subdivisionsHeight(), QgsUnitTypes::RenderMillimeters );
108 const double scaledMaxHeight = ( ( settings.numberOfSubdivisions() > 1 ) && ( scaledSubdivisionsHeight > scaledHeight ) ) ? scaledSubdivisionsHeight : scaledHeight;
109 const double middlePosition = barTopPosition + scaledMaxHeight / 2.0;
110 const double bottomPosition = barTopPosition + scaledMaxHeight;
111
112 const double xOffset = firstLabelXOffset( settings, context, scaleContext );
113
114 painter->save();
115 context.setPainterFlagsUsingContext( painter );
116
117 std::unique_ptr< QgsLineSymbol > symbol( settings.lineSymbol()->clone() );
118 symbol->startRender( context );
119
120 std::unique_ptr< QgsLineSymbol > divisionSymbol( settings.divisionLineSymbol()->clone() );
121 divisionSymbol->startRender( context );
122
123 std::unique_ptr< QgsLineSymbol > subdivisionSymbol( settings.subdivisionLineSymbol()->clone() );
124 subdivisionSymbol->startRender( context );
125
126 const QList<double> positions = segmentPositions( context, scaleContext, settings );
127
128 // vertical positions
129 double verticalPos = 0.0;
130 QList<double> subTickPositionsY;
131 QList<double> tickPositionsY;
132 switch ( mTickPosition )
133 {
134 case TicksDown:
135 verticalPos = barTopPosition;
136 subTickPositionsY << verticalPos;
137 subTickPositionsY << verticalPos + scaledSubdivisionsHeight;
138 tickPositionsY << verticalPos;
139 tickPositionsY << verticalPos + scaledHeight;
140 break;
141 case TicksMiddle:
142 verticalPos = middlePosition;
143 subTickPositionsY << verticalPos + scaledSubdivisionsHeight / 2.0;
144 subTickPositionsY << verticalPos - scaledSubdivisionsHeight / 2.0;
145 tickPositionsY << verticalPos + scaledHeight / 2.0;
146 tickPositionsY << verticalPos - scaledHeight / 2.0;
147 break;
148 case TicksUp:
149 verticalPos = bottomPosition;
150 subTickPositionsY << verticalPos;
151 subTickPositionsY << verticalPos - scaledSubdivisionsHeight;
152 tickPositionsY << verticalPos;
153 tickPositionsY << verticalPos - scaledHeight;
154 break;
155 }
156
157 int symbolLayerCount = symbol->symbolLayerCount();
158 symbolLayerCount = std::max( symbolLayerCount, divisionSymbol->symbolLayerCount() );
159 symbolLayerCount = std::max( symbolLayerCount, subdivisionSymbol->symbolLayerCount() );
160
161 // we render the bar symbol-layer-by-symbol-layer, to avoid ugliness where the lines overlap in multi-layer symbols
162 for ( int layer = 0; layer < symbolLayerCount; ++ layer )
163 {
164 const bool drawDivisionsForThisSymbolLayer = layer < divisionSymbol->symbolLayerCount();
165 const bool drawSubdivisionsForThisSymbolLayer = layer < subdivisionSymbol->symbolLayerCount();
166 const bool drawLineForThisSymbolLayer = layer < symbol->symbolLayerCount();
167
168 if ( drawDivisionsForThisSymbolLayer )
169 {
170 // first draw the vertical lines for segments
171 for ( int i = 0; i < positions.size(); ++i )
172 {
173 const double thisX = context.convertToPainterUnits( positions.at( i ), QgsUnitTypes::RenderMillimeters ) + xOffset;
174 divisionSymbol->renderPolyline( QPolygonF() << QPointF( thisX, tickPositionsY.at( 0 ) )
175 << QPointF( thisX, tickPositionsY.at( 1 ) ), nullptr, context, layer );
176 }
177 }
178
179 // draw the vertical lines for right subdivisions
180 if ( drawSubdivisionsForThisSymbolLayer )
181 {
182 for ( int i = settings.numberOfSegmentsLeft(); i < positions.size(); ++i )
183 {
184 for ( int j = 1; j < settings.numberOfSubdivisions(); ++j )
185 {
186 const double thisSubX = context.convertToPainterUnits( positions.at( i ) + j * scaleContext.segmentWidth / settings.numberOfSubdivisions(), QgsUnitTypes::RenderMillimeters ) + xOffset;
187 subdivisionSymbol->renderPolyline( QPolygonF() << QPointF( thisSubX, subTickPositionsY.at( 0 ) )
188 << QPointF( thisSubX, subTickPositionsY.at( 1 ) ), nullptr, context, layer );
189 }
190 }
191 }
192
193 //draw last tick and horizontal line
194 if ( !positions.isEmpty() )
195 {
196 const double lastTickPositionX = context.convertToPainterUnits( positions.at( positions.size() - 1 ) + scaleContext.segmentWidth, QgsUnitTypes::RenderMillimeters ) + xOffset;
197
198 //last vertical line
199 if ( drawDivisionsForThisSymbolLayer )
200 {
201 divisionSymbol->renderPolyline( QPolygonF() << QPointF( lastTickPositionX, tickPositionsY.at( 0 ) )
202 << QPointF( lastTickPositionX, tickPositionsY.at( 1 ) ),
203 nullptr, context, layer );
204 }
205
206 //horizontal line
207 if ( drawLineForThisSymbolLayer )
208 {
209 symbol->renderPolyline( QPolygonF() << QPointF( xOffset + context.convertToPainterUnits( positions.at( 0 ), QgsUnitTypes::RenderMillimeters ), verticalPos )
210 << QPointF( lastTickPositionX, verticalPos ), nullptr, context, layer );
211 }
212 }
213 }
214
215 symbol->stopRender( context );
216 divisionSymbol->stopRender( context );
217 subdivisionSymbol->stopRender( context );
218
219 painter->restore();
220
221 //draw labels using the default method
222 drawDefaultLabels( context, settings, scaleContext );
223 }
224
225
226