Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgslayoututils.cpp
3 : : ------------------
4 : : begin : July 2017
5 : : copyright : (C) 2017 by Nyall Dawson
6 : : email : nyall dot dawson at gmail dot com
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include "qgslayoututils.h"
19 : : #include "qgslayout.h"
20 : : #include "qgslayoutitemmap.h"
21 : : #include "qgsprojectviewsettings.h"
22 : : #include "qgsrendercontext.h"
23 : : #include "qgssettings.h"
24 : :
25 : : #include <QStyleOptionGraphicsItem>
26 : : #include <QPainter>
27 : : #include <cmath>
28 : :
29 : : #ifndef M_DEG2RAD
30 : : #define M_DEG2RAD 0.0174532925
31 : : #endif
32 : :
33 : 0 : void QgsLayoutUtils::rotate( double angle, double &x, double &y )
34 : : {
35 : 0 : double rotToRad = angle * M_PI / 180.0;
36 : : double xRot, yRot;
37 : 0 : xRot = x * std::cos( rotToRad ) - y * std::sin( rotToRad );
38 : 0 : yRot = x * std::sin( rotToRad ) + y * std::cos( rotToRad );
39 : 0 : x = xRot;
40 : 0 : y = yRot;
41 : 0 : }
42 : :
43 : 0 : double QgsLayoutUtils::normalizedAngle( const double angle, const bool allowNegative )
44 : : {
45 : 0 : double clippedAngle = angle;
46 : 0 : if ( clippedAngle >= 360.0 || clippedAngle <= -360.0 )
47 : : {
48 : 0 : clippedAngle = std::fmod( clippedAngle, 360.0 );
49 : 0 : }
50 : 0 : if ( !allowNegative && clippedAngle < 0.0 )
51 : : {
52 : 0 : clippedAngle += 360.0;
53 : 0 : }
54 : 0 : return clippedAngle;
55 : : }
56 : :
57 : 0 : double QgsLayoutUtils::snappedAngle( double angle )
58 : : {
59 : : //normalize angle to 0-360 degrees
60 : 0 : double clippedAngle = normalizedAngle( angle );
61 : :
62 : : //snap angle to 45 degree
63 : 0 : if ( clippedAngle >= 22.5 && clippedAngle < 67.5 )
64 : : {
65 : 0 : return 45.0;
66 : : }
67 : 0 : else if ( clippedAngle >= 67.5 && clippedAngle < 112.5 )
68 : : {
69 : 0 : return 90.0;
70 : : }
71 : 0 : else if ( clippedAngle >= 112.5 && clippedAngle < 157.5 )
72 : : {
73 : 0 : return 135.0;
74 : : }
75 : 0 : else if ( clippedAngle >= 157.5 && clippedAngle < 202.5 )
76 : : {
77 : 0 : return 180.0;
78 : : }
79 : 0 : else if ( clippedAngle >= 202.5 && clippedAngle < 247.5 )
80 : : {
81 : 0 : return 225.0;
82 : : }
83 : 0 : else if ( clippedAngle >= 247.5 && clippedAngle < 292.5 )
84 : : {
85 : 0 : return 270.0;
86 : : }
87 : 0 : else if ( clippedAngle >= 292.5 && clippedAngle < 337.5 )
88 : : {
89 : 0 : return 315.0;
90 : : }
91 : : else
92 : : {
93 : 0 : return 0.0;
94 : : }
95 : 0 : }
96 : :
97 : 0 : QgsRenderContext QgsLayoutUtils::createRenderContextForMap( QgsLayoutItemMap *map, QPainter *painter, double dpi )
98 : : {
99 : 0 : if ( !map )
100 : : {
101 : 0 : QgsRenderContext context;
102 : 0 : context.setPainter( painter );
103 : 0 : if ( dpi < 0 && painter && painter->device() )
104 : : {
105 : 0 : context.setScaleFactor( painter->device()->logicalDpiX() / 25.4 );
106 : 0 : }
107 : 0 : else if ( dpi > 0 )
108 : : {
109 : 0 : context.setScaleFactor( dpi / 25.4 );
110 : 0 : }
111 : : else
112 : : {
113 : 0 : context.setScaleFactor( 3.465 ); //assume 88 dpi as standard value
114 : : }
115 : 0 : return context;
116 : 0 : }
117 : : else
118 : : {
119 : : // default to 88 dpi if no painter specified
120 : 0 : if ( dpi < 0 )
121 : : {
122 : 0 : dpi = ( painter && painter->device() ) ? painter->device()->logicalDpiX() : 88;
123 : 0 : }
124 : 0 : double dotsPerMM = dpi / 25.4;
125 : :
126 : : // get map settings from reference map
127 : 0 : QgsRectangle extent = map->extent();
128 : 0 : QSizeF mapSizeLayoutUnits = map->rect().size();
129 : 0 : QSizeF mapSizeMM = map->layout()->convertFromLayoutUnits( mapSizeLayoutUnits, QgsUnitTypes::LayoutMillimeters ).toQSizeF();
130 : 0 : QgsMapSettings ms = map->mapSettings( extent, mapSizeMM * dotsPerMM, dpi, false );
131 : 0 : QgsRenderContext context = QgsRenderContext::fromMapSettings( ms );
132 : 0 : if ( painter )
133 : 0 : context.setPainter( painter );
134 : :
135 : 0 : context.setFlags( map->layout()->renderContext().renderContextFlags() );
136 : 0 : context.setTextRenderFormat( map->layout()->renderContext().textRenderFormat() );
137 : 0 : return context;
138 : 0 : }
139 : 0 : }
140 : :
141 : 0 : QgsRenderContext QgsLayoutUtils::createRenderContextForLayout( QgsLayout *layout, QPainter *painter, double dpi )
142 : : {
143 : 0 : QgsLayoutItemMap *referenceMap = layout ? layout->referenceMap() : nullptr;
144 : 0 : QgsRenderContext context = createRenderContextForMap( referenceMap, painter, dpi );
145 : 0 : if ( layout )
146 : : {
147 : 0 : context.setFlags( layout->renderContext().renderContextFlags() );
148 : 0 : context.setTextRenderFormat( layout->renderContext().textRenderFormat() );
149 : 0 : }
150 : :
151 : 0 : return context;
152 : 0 : }
153 : :
154 : 0 : void QgsLayoutUtils::relativeResizeRect( QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter )
155 : : {
156 : : //linearly scale rectToResize relative to the scaling from boundsBefore to boundsAfter
157 : 0 : double left = relativePosition( rectToResize.left(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
158 : 0 : double right = relativePosition( rectToResize.right(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
159 : 0 : double top = relativePosition( rectToResize.top(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
160 : 0 : double bottom = relativePosition( rectToResize.bottom(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
161 : :
162 : 0 : rectToResize.setRect( left, top, right - left, bottom - top );
163 : 0 : }
164 : :
165 : 0 : double QgsLayoutUtils::relativePosition( const double position, const double beforeMin, const double beforeMax, const double afterMin, const double afterMax )
166 : : {
167 : : //calculate parameters for linear scale between before and after ranges
168 : 0 : double m = ( afterMax - afterMin ) / ( beforeMax - beforeMin );
169 : 0 : double c = afterMin - ( beforeMin * m );
170 : :
171 : : //return linearly scaled position
172 : 0 : return m * position + c;
173 : : }
174 : 0 : QFont QgsLayoutUtils::scaledFontPixelSize( const QFont &font )
175 : : {
176 : : //upscale using FONT_WORKAROUND_SCALE
177 : : //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
178 : 0 : QFont scaledFont = font;
179 : 0 : double pixelSize = pointsToMM( scaledFont.pointSizeF() ) * FONT_WORKAROUND_SCALE + 0.5;
180 : 0 : scaledFont.setPixelSize( pixelSize );
181 : 0 : return scaledFont;
182 : 0 : }
183 : :
184 : 0 : double QgsLayoutUtils::fontAscentMM( const QFont &font )
185 : : {
186 : : //upscale using FONT_WORKAROUND_SCALE
187 : : //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
188 : 0 : QFont metricsFont = scaledFontPixelSize( font );
189 : 0 : QFontMetricsF fontMetrics( metricsFont );
190 : 0 : return ( fontMetrics.ascent() / FONT_WORKAROUND_SCALE );
191 : 0 : }
192 : :
193 : 0 : double QgsLayoutUtils::fontDescentMM( const QFont &font )
194 : : {
195 : : //upscale using FONT_WORKAROUND_SCALE
196 : : //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
197 : 0 : QFont metricsFont = scaledFontPixelSize( font );
198 : 0 : QFontMetricsF fontMetrics( metricsFont );
199 : 0 : return ( fontMetrics.descent() / FONT_WORKAROUND_SCALE );
200 : :
201 : 0 : }
202 : :
203 : 0 : double QgsLayoutUtils::fontHeightMM( const QFont &font )
204 : : {
205 : : //upscale using FONT_WORKAROUND_SCALE
206 : : //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
207 : 0 : QFont metricsFont = scaledFontPixelSize( font );
208 : 0 : QFontMetricsF fontMetrics( metricsFont );
209 : 0 : return ( fontMetrics.height() / FONT_WORKAROUND_SCALE );
210 : :
211 : 0 : }
212 : :
213 : 0 : double QgsLayoutUtils::fontHeightCharacterMM( const QFont &font, QChar character )
214 : : {
215 : : //upscale using FONT_WORKAROUND_SCALE
216 : : //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
217 : 0 : QFont metricsFont = scaledFontPixelSize( font );
218 : 0 : QFontMetricsF fontMetrics( metricsFont );
219 : 0 : return ( fontMetrics.boundingRect( character ).height() / FONT_WORKAROUND_SCALE );
220 : 0 : }
221 : :
222 : 0 : double QgsLayoutUtils::textWidthMM( const QFont &font, const QString &text )
223 : : {
224 : : //upscale using FONT_WORKAROUND_SCALE
225 : : //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
226 : :
227 : 0 : const QStringList multiLineSplit = text.split( '\n' );
228 : 0 : QFont metricsFont = scaledFontPixelSize( font );
229 : 0 : QFontMetricsF fontMetrics( metricsFont );
230 : :
231 : 0 : double maxWidth = 0;
232 : 0 : for ( const QString &line : multiLineSplit )
233 : : {
234 : 0 : maxWidth = std::max( maxWidth, ( fontMetrics.horizontalAdvance( line ) / FONT_WORKAROUND_SCALE ) );
235 : : }
236 : 0 : return maxWidth;
237 : 0 : }
238 : :
239 : 0 : double QgsLayoutUtils::textHeightMM( const QFont &font, const QString &text, double multiLineHeight )
240 : : {
241 : 0 : QStringList multiLineSplit = text.split( '\n' );
242 : 0 : int lines = multiLineSplit.size();
243 : :
244 : : //upscale using FONT_WORKAROUND_SCALE
245 : : //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
246 : 0 : QFont metricsFont = scaledFontPixelSize( font );
247 : 0 : QFontMetricsF fontMetrics( metricsFont );
248 : :
249 : 0 : double fontHeight = fontMetrics.ascent() + fontMetrics.descent(); // ignore +1 for baseline
250 : 0 : double textHeight = fontMetrics.ascent() + static_cast< double >( ( lines - 1 ) * fontHeight * multiLineHeight );
251 : :
252 : 0 : return textHeight / FONT_WORKAROUND_SCALE;
253 : 0 : }
254 : :
255 : 0 : void QgsLayoutUtils::drawText( QPainter *painter, QPointF position, const QString &text, const QFont &font, const QColor &color )
256 : : {
257 : 0 : if ( !painter )
258 : : {
259 : 0 : return;
260 : : }
261 : :
262 : : //upscale using FONT_WORKAROUND_SCALE
263 : : //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
264 : 0 : QFont textFont = scaledFontPixelSize( font );
265 : :
266 : 0 : QgsScopedQPainterState painterState( painter );
267 : 0 : painter->setFont( textFont );
268 : 0 : if ( color.isValid() )
269 : : {
270 : 0 : painter->setPen( color );
271 : 0 : }
272 : 0 : double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
273 : 0 : painter->scale( scaleFactor, scaleFactor );
274 : 0 : painter->drawText( position * FONT_WORKAROUND_SCALE, text );
275 : 0 : }
276 : :
277 : 0 : void QgsLayoutUtils::drawText( QPainter *painter, const QRectF &rect, const QString &text, const QFont &font, const QColor &color, const Qt::AlignmentFlag halignment, const Qt::AlignmentFlag valignment, const int flags )
278 : : {
279 : 0 : if ( !painter )
280 : : {
281 : 0 : return;
282 : : }
283 : :
284 : : //upscale using FONT_WORKAROUND_SCALE
285 : : //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
286 : 0 : QFont textFont = scaledFontPixelSize( font );
287 : :
288 : 0 : QRectF scaledRect( rect.x() * FONT_WORKAROUND_SCALE, rect.y() * FONT_WORKAROUND_SCALE,
289 : 0 : rect.width() * FONT_WORKAROUND_SCALE, rect.height() * FONT_WORKAROUND_SCALE );
290 : :
291 : 0 : QgsScopedQPainterState painterState( painter );
292 : 0 : painter->setFont( textFont );
293 : 0 : if ( color.isValid() )
294 : : {
295 : 0 : painter->setPen( color );
296 : 0 : }
297 : 0 : double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
298 : 0 : painter->scale( scaleFactor, scaleFactor );
299 : 0 : painter->drawText( scaledRect, halignment | valignment | flags, text );
300 : 0 : }
301 : :
302 : 0 : QRectF QgsLayoutUtils::largestRotatedRectWithinBounds( const QRectF &originalRect, const QRectF &boundsRect, const double rotation )
303 : : {
304 : 0 : double originalWidth = originalRect.width();
305 : 0 : double originalHeight = originalRect.height();
306 : 0 : double boundsWidth = boundsRect.width();
307 : 0 : double boundsHeight = boundsRect.height();
308 : 0 : double ratioBoundsRect = boundsWidth / boundsHeight;
309 : :
310 : 0 : double clippedRotation = normalizedAngle( rotation );
311 : :
312 : : //shortcut for some rotation values
313 : 0 : if ( qgsDoubleNear( clippedRotation, 0.0 ) || qgsDoubleNear( clippedRotation, 90.0 ) || qgsDoubleNear( clippedRotation, 180.0 ) || qgsDoubleNear( clippedRotation, 270.0 ) )
314 : : {
315 : : double rectScale;
316 : 0 : if ( qgsDoubleNear( clippedRotation, 0.0 ) || qgsDoubleNear( clippedRotation, 180.0 ) )
317 : : {
318 : 0 : rectScale = ( ( originalWidth / originalHeight ) > ratioBoundsRect ) ? boundsWidth / originalWidth : boundsHeight / originalHeight;
319 : 0 : }
320 : : else
321 : : {
322 : 0 : rectScale = ( ( originalHeight / originalWidth ) > ratioBoundsRect ) ? boundsWidth / originalHeight : boundsHeight / originalWidth;
323 : : }
324 : 0 : double rectScaledWidth = rectScale * originalWidth;
325 : 0 : double rectScaledHeight = rectScale * originalHeight;
326 : :
327 : 0 : if ( qgsDoubleNear( clippedRotation, 0.0 ) || qgsDoubleNear( clippedRotation, 180.0 ) )
328 : : {
329 : 0 : return QRectF( ( boundsWidth - rectScaledWidth ) / 2.0, ( boundsHeight - rectScaledHeight ) / 2.0, rectScaledWidth, rectScaledHeight );
330 : : }
331 : : else
332 : : {
333 : 0 : return QRectF( ( boundsWidth - rectScaledHeight ) / 2.0, ( boundsHeight - rectScaledWidth ) / 2.0, rectScaledWidth, rectScaledHeight );
334 : : }
335 : : }
336 : :
337 : : //convert angle to radians and flip
338 : 0 : double angleRad = -clippedRotation * M_DEG2RAD;
339 : 0 : double cosAngle = std::cos( angleRad );
340 : 0 : double sinAngle = std::sin( angleRad );
341 : :
342 : : //calculate size of bounds of rotated rectangle
343 : 0 : double widthBoundsRotatedRect = originalWidth * std::fabs( cosAngle ) + originalHeight * std::fabs( sinAngle );
344 : 0 : double heightBoundsRotatedRect = originalHeight * std::fabs( cosAngle ) + originalWidth * std::fabs( sinAngle );
345 : :
346 : : //compare ratio of rotated rect with bounds rect and calculate scaling of rotated
347 : : //rect to fit within bounds
348 : 0 : double ratioBoundsRotatedRect = widthBoundsRotatedRect / heightBoundsRotatedRect;
349 : 0 : double rectScale = ratioBoundsRotatedRect > ratioBoundsRect ? boundsWidth / widthBoundsRotatedRect : boundsHeight / heightBoundsRotatedRect;
350 : 0 : double rectScaledWidth = rectScale * originalWidth;
351 : 0 : double rectScaledHeight = rectScale * originalHeight;
352 : :
353 : : //now calculate offset so that rotated rectangle is centered within bounds
354 : : //first calculate min x and y coordinates
355 : 0 : double currentCornerX = 0;
356 : 0 : double minX = 0;
357 : 0 : currentCornerX += rectScaledWidth * cosAngle;
358 : 0 : minX = minX < currentCornerX ? minX : currentCornerX;
359 : 0 : currentCornerX += rectScaledHeight * sinAngle;
360 : 0 : minX = minX < currentCornerX ? minX : currentCornerX;
361 : 0 : currentCornerX -= rectScaledWidth * cosAngle;
362 : 0 : minX = minX < currentCornerX ? minX : currentCornerX;
363 : :
364 : 0 : double currentCornerY = 0;
365 : 0 : double minY = 0;
366 : 0 : currentCornerY -= rectScaledWidth * sinAngle;
367 : 0 : minY = minY < currentCornerY ? minY : currentCornerY;
368 : 0 : currentCornerY += rectScaledHeight * cosAngle;
369 : 0 : minY = minY < currentCornerY ? minY : currentCornerY;
370 : 0 : currentCornerY += rectScaledWidth * sinAngle;
371 : 0 : minY = minY < currentCornerY ? minY : currentCornerY;
372 : :
373 : : //now calculate offset position of rotated rectangle
374 : 0 : double offsetX = ratioBoundsRotatedRect > ratioBoundsRect ? 0 : ( boundsWidth - rectScale * widthBoundsRotatedRect ) / 2.0;
375 : 0 : offsetX += std::fabs( minX );
376 : 0 : double offsetY = ratioBoundsRotatedRect > ratioBoundsRect ? ( boundsHeight - rectScale * heightBoundsRotatedRect ) / 2.0 : 0;
377 : 0 : offsetY += std::fabs( minY );
378 : :
379 : 0 : return QRectF( offsetX, offsetY, rectScaledWidth, rectScaledHeight );
380 : 0 : }
381 : :
382 : 0 : QgsLayoutItemPage::Orientation QgsLayoutUtils::decodePaperOrientation( const QString &string, bool &ok )
383 : : {
384 : 0 : QString s = string.trimmed();
385 : 0 : if ( s.compare( QLatin1String( "Portrait" ), Qt::CaseInsensitive ) == 0 )
386 : : {
387 : 0 : ok = true;
388 : 0 : return QgsLayoutItemPage::Portrait;
389 : : }
390 : 0 : else if ( s.compare( QLatin1String( "Landscape" ), Qt::CaseInsensitive ) == 0 )
391 : : {
392 : 0 : ok = true;
393 : 0 : return QgsLayoutItemPage::Landscape;
394 : : }
395 : 0 : ok = false;
396 : 0 : return QgsLayoutItemPage::Landscape; // default to landscape
397 : 0 : }
398 : :
399 : 0 : double QgsLayoutUtils::scaleFactorFromItemStyle( const QStyleOptionGraphicsItem *style )
400 : : {
401 : : // workaround Qt bug 66185
402 : :
403 : : // Refs #18027 - if a QGraphicsItem is rotated by 90 or 270 degrees, then the item
404 : : // style given to QGraphicsItem::paint incorrectly uses the shear parameter of the matrix (m12)
405 : : // to store the current view scale, instead of the horizontal scale parameter (m11) which
406 : : // is used in all other cases
407 : :
408 : : // TODO - ifdef this out if Qt fixes upstream
409 : 0 : return !qgsDoubleNear( style->matrix.m11(), 0.0 ) ? style->matrix.m11() : style->matrix.m12();
410 : : }
411 : :
412 : 0 : QgsMapLayer *QgsLayoutUtils::mapLayerFromString( const QString &string, QgsProject *project )
413 : : {
414 : : // Maybe it's a layer id?
415 : 0 : if ( QgsMapLayer *ml = project->mapLayer( string ) )
416 : 0 : return ml;
417 : :
418 : : // Still nothing? Check for layer name
419 : 0 : if ( QgsMapLayer *ml = project->mapLayersByName( string ).value( 0 ) )
420 : 0 : return ml;
421 : :
422 : : // Still nothing? Check for layer name, case-insensitive
423 : 0 : const auto layers = project->mapLayers();
424 : 0 : for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
425 : : {
426 : 0 : if ( it.value()->name().compare( string, Qt::CaseInsensitive ) == 0 )
427 : 0 : return it.value();
428 : 0 : }
429 : :
430 : 0 : return nullptr;
431 : 0 : }
432 : :
433 : : // nextNiceNumber(4573.23, d) = 5000 (d=1) -> 4600 (d=10) -> 4580 (d=100) -> 4574 (d=1000) -> etc
434 : 0 : inline double nextNiceNumber( double a, double d = 1 )
435 : : {
436 : 0 : double s = std::pow( 10.0, std::floor( std::log10( a ) ) ) / d;
437 : 0 : return std::ceil( a / s ) * s;
438 : : }
439 : :
440 : : // prevNiceNumber(4573.23, d) = 4000 (d=1) -> 4500 (d=10) -> 4570 (d=100) -> 4573 (d=1000) -> etc
441 : 0 : inline double prevNiceNumber( double a, double d = 1 )
442 : : {
443 : 0 : double s = std::pow( 10.0, std::floor( std::log10( a ) ) ) / d;
444 : 0 : return std::floor( a / s ) * s;
445 : : }
446 : :
447 : 0 : double QgsLayoutUtils::calculatePrettySize( const double minimumSize, const double maximumSize )
448 : : {
449 : 0 : if ( maximumSize < minimumSize )
450 : : {
451 : 0 : return 0;
452 : : }
453 : : else
454 : : {
455 : : // Start with coarsest "nice" number closest to minimumSize resp
456 : : // maximumSize, then proceed to finer numbers as long as neither
457 : : // lowerNiceUnitsPerSeg nor upperNiceUnitsPerSeg are in
458 : : // [minimumSize, maximumSize]
459 : 0 : double lowerNiceUnitsPerSeg = nextNiceNumber( minimumSize );
460 : 0 : double upperNiceUnitsPerSeg = prevNiceNumber( maximumSize );
461 : :
462 : 0 : double d = 1;
463 : 0 : while ( lowerNiceUnitsPerSeg > maximumSize && upperNiceUnitsPerSeg < minimumSize )
464 : : {
465 : 0 : d *= 10;
466 : 0 : lowerNiceUnitsPerSeg = nextNiceNumber( minimumSize, d );
467 : 0 : upperNiceUnitsPerSeg = prevNiceNumber( maximumSize, d );
468 : : }
469 : :
470 : : // Pick size from {lowerNiceUnitsPerSeg, upperNiceUnitsPerSeg}, use the larger if possible
471 : 0 : return upperNiceUnitsPerSeg < minimumSize ? lowerNiceUnitsPerSeg : upperNiceUnitsPerSeg;
472 : : }
473 : 0 : }
474 : :
475 : 0 : bool QgsLayoutUtils::itemIsAClippingSource( const QgsLayoutItem *item )
476 : : {
477 : 0 : if ( !( item->itemFlags() & QgsLayoutItem::FlagProvidesClipPath ) )
478 : 0 : return false; // not a clipping provider, so shortcut out
479 : :
480 : : // current only maps can be clipped
481 : 0 : QList< QgsLayoutItemMap * > maps;
482 : 0 : item->layout()->layoutItems( maps );
483 : 0 : for ( QgsLayoutItemMap *map : std::as_const( maps ) )
484 : : {
485 : 0 : if ( map->itemClippingSettings()->isActive() && map->itemClippingSettings()->sourceItem() == item )
486 : 0 : return true;
487 : : }
488 : 0 : return false;
489 : 0 : }
490 : :
491 : 0 : double QgsLayoutUtils::pointsToMM( const double pointSize )
492 : : {
493 : : //conversion to mm based on 1 point = 1/72 inch
494 : 0 : return ( pointSize * 0.3527 );
495 : : }
496 : :
497 : 0 : double QgsLayoutUtils::mmToPoints( const double mmSize )
498 : : {
499 : : //conversion to points based on 1 point = 1/72 inch
500 : 0 : return ( mmSize / 0.3527 );
501 : : }
502 : :
503 : 0 : QVector< double > QgsLayoutUtils::predefinedScales( const QgsLayout *layout )
504 : : {
505 : 0 : QVector< double > mapScales;
506 : 0 : if ( layout->project() )
507 : 0 : mapScales = layout->project()->viewSettings()->mapScales();
508 : :
509 : 0 : bool hasProjectScales( layout->project()->viewSettings()->useProjectScales() );
510 : 0 : if ( !hasProjectScales || mapScales.isEmpty() )
511 : : {
512 : : // default to global map tool scales
513 : 0 : QgsSettings settings;
514 : 0 : QString scalesStr( settings.value( QStringLiteral( "Map/scales" ), Qgis::defaultProjectScales() ).toString() );
515 : 0 : const QStringList scales = scalesStr.split( ',' );
516 : 0 : for ( const QString &scale : scales )
517 : : {
518 : 0 : QStringList parts( scale.split( ':' ) );
519 : 0 : if ( parts.size() == 2 )
520 : : {
521 : 0 : mapScales.push_back( parts[1].toDouble() );
522 : 0 : }
523 : 0 : }
524 : 0 : }
525 : :
526 : 0 : return mapScales;
527 : 0 : }
|