QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgsgeometrypaintdevice.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometrypaintdevice.cpp
3 --------------------------------------
4 Date : May 2024
5 Copyright : (C) 2024 by Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsgeos.h"
19#include "qgslinestring.h"
20#include "qgsmultilinestring.h"
21#include "qgsmultipolygon.h"
22#include "qgspainting.h"
23#include "qgspolygon.h"
24#include "qgssymbollayerutils.h"
25
26#include <QString>
27
28using namespace Qt::StringLiterals;
29
30//
31// QgsGeometryPaintEngine
32//
33
35 : QPaintEngine( QPaintEngine::AllFeatures ) // we lie and say we support all paint features, as we don't want Qt trying to be helpful and rasterizing shapes
36 , mUsePathStroker( usePathStroker )
37{
38}
39
41{
42 mStrokedPathsSegments = segments;
43}
44
46{
47 mSimplifyTolerance = tolerance;
48}
49
50bool QgsGeometryPaintEngine::begin( QPaintDevice * )
51{
52 return true;
53}
54
56{
57 return true;
58}
59
60QPaintEngine::Type QgsGeometryPaintEngine::type() const
61{
62 return QPaintEngine::User;
63}
64
65void QgsGeometryPaintEngine::updateState( const QPaintEngineState &state )
66{
67 if ( mUsePathStroker && state.state().testFlag( QPaintEngine::DirtyFlag::DirtyPen ) )
68 {
69 mPen = state.pen();
70 }
71}
72
73void QgsGeometryPaintEngine::drawImage( const QRectF &, const QImage &, const QRectF &, Qt::ImageConversionFlags )
74{
75 // ignore, we don't need to support raster drawing
76 QgsDebugError( u"QgsGeometryPaintEngine does not support drawImage method"_s );
77}
78
79void QgsGeometryPaintEngine::drawPixmap( const QRectF &, const QPixmap &, const QRectF & )
80{
81 // ignore, we don't need to support raster drawing
82 QgsDebugError( u"QgsGeometryPaintEngine does not support drawPixmap method"_s );
83}
84
85void QgsGeometryPaintEngine::drawTiledPixmap( const QRectF &, const QPixmap &, const QPointF & )
86{
87 // ignore, we don't need to support raster drawing
88 QgsDebugError( u"QgsGeometryPaintEngine does not support drawTiledPixmap method"_s );
89}
90
91template <typename T>
92void drawLinesImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *lines, int lineCount )
93{
94 geometry.reserve( geometry.numGeometries() + lineCount );
95 if ( transform.isIdentity() )
96 {
97 for ( int i = 0; i < lineCount; ++i, ++lines )
98 {
99 geometry.addGeometry( new QgsLineString(
100 QVector<double> { static_cast< double >( lines->x1() ), static_cast< double >( lines->x2() ) },
101 QVector<double> { static_cast< double >( lines->y1() ), static_cast< double >( lines->y2() ) } )
102 );
103 }
104 }
105 else
106 {
107 for ( int i = 0; i < lineCount; ++i, ++lines )
108 {
109 double x1 = lines->x1();
110 double x2 = lines->x2();
111 double y1 = lines->y1();
112 double y2 = lines->y2();
113
114 double tx1, tx2, ty1, ty2;
115 transform.map( x1, y1, &tx1, &ty1 );
116 transform.map( x2, y2, &tx2, &ty2 );
117
118 geometry.addGeometry( new QgsLineString(
119 QVector<double> { tx1, tx2 },
120 QVector<double> { ty1, ty2 } )
121 );
122 }
123 }
124}
125
126void QgsGeometryPaintEngine::drawLines( const QLineF *lines, int lineCount )
127{
128 if ( mUsePathStroker )
129 {
130 // if stroking we have no choice but to go via the QPainterPath route
131 QPaintEngine::drawLines( lines, lineCount );
132 }
133 else
134 {
135 const QTransform transform = painter()->combinedTransform();
136 drawLinesImp( transform, mGeometry, lines, lineCount );
137 }
138}
139
140void QgsGeometryPaintEngine::drawLines( const QLine *lines, int lineCount )
141{
142 if ( mUsePathStroker )
143 {
144 // if stroking we have no choice but to go via the QPainterPath route
145 QPaintEngine::drawLines( lines, lineCount );
146 }
147 else
148 {
149 const QTransform transform = painter()->combinedTransform();
150 drawLinesImp( transform, mGeometry, lines, lineCount );
151 }
152}
153
154template <typename T>
155void drawPointsImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount )
156{
157 geometry.reserve( geometry.numGeometries() + pointCount );
158 if ( transform.isIdentity() )
159 {
160 for ( int i = 0; i < pointCount; ++i, ++points )
161 {
162 geometry.addGeometry( new QgsPoint( static_cast< double >( points->x() ),
163 static_cast< double >( points->y() ) ) );
164 }
165 }
166 else
167 {
168 for ( int i = 0; i < pointCount; ++i, ++points )
169 {
170 double x = points->x();
171 double y = points->y();
172
173 double tx, ty;
174 transform.map( x, y, &tx, &ty );
175
176 geometry.addGeometry( new QgsPoint( tx, ty ) );
177 }
178 }
179}
180
181void QgsGeometryPaintEngine::drawPoints( const QPointF *points, int pointCount )
182{
183 const QTransform transform = painter()->combinedTransform();
184 drawPointsImp( transform, mGeometry, points, pointCount );
185}
186
187void QgsGeometryPaintEngine::drawPoints( const QPoint *points, int pointCount )
188{
189 const QTransform transform = painter()->combinedTransform();
190 drawPointsImp( transform, mGeometry, points, pointCount );
191}
192
193template <typename T>
194void drawRectsImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *rects, int rectCount )
195{
196 geometry.reserve( geometry.numGeometries() + rectCount );
197 if ( transform.isIdentity() )
198 {
199 for ( int i = 0; i < rectCount; ++i, ++rects )
200 {
201 QgsLineString *exterior = new QgsLineString(
202 QVector<double> { static_cast< double >( rects->left() ),
203 static_cast< double >( rects->right() ),
204 static_cast< double >( rects->right() ),
205 static_cast< double >( rects->left() ),
206 static_cast< double>( rects->left() )
207 },
208 QVector<double> { static_cast< double >( rects->bottom() ),
209 static_cast< double >( rects->bottom() ),
210 static_cast< double >( rects->top() ),
211 static_cast< double >( rects->top() ),
212 static_cast< double >( rects->bottom() )
213 } );
214 geometry.addGeometry( new QgsPolygon( exterior ) );
215 }
216 }
217 else
218 {
219 for ( int i = 0; i < rectCount; ++i, ++rects )
220 {
221 const double left = rects->left();
222 const double right = rects->right();
223 const double top = rects->top();
224 const double bottom = rects->bottom();
225
226 double bottomLeftX, bottomLeftY, bottomRightX, bottomRightY, topLeftX, topLeftY, topRightX, topRightY;
227 transform.map( left, bottom, &bottomLeftX, &bottomLeftY );
228 transform.map( right, bottom, &bottomRightX, &bottomRightY );
229 transform.map( left, top, &topLeftX, &topLeftY );
230 transform.map( right, top, &topRightX, &topRightY );
231
232 QgsLineString *exterior = new QgsLineString(
233 QVector<double> { bottomLeftX, bottomRightX, topRightX, topLeftX, bottomLeftX },
234 QVector<double> { bottomLeftY, bottomRightY, topRightY, topLeftY, bottomLeftY } );
235 geometry.addGeometry( new QgsPolygon( exterior ) );
236 }
237 }
238}
239
240void QgsGeometryPaintEngine::drawRects( const QRectF *rects, int rectCount )
241{
242 const QTransform transform = painter()->combinedTransform();
243 drawRectsImp( transform, mGeometry, rects, rectCount );
244}
245
246void QgsGeometryPaintEngine::drawRects( const QRect *rects, int rectCount )
247{
248 const QTransform transform = painter()->combinedTransform();
249 drawRectsImp( transform, mGeometry, rects, rectCount );
250}
251
252template <typename T>
253void drawPolygonImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount, QPaintEngine::PolygonDrawMode mode, double simplifyTolerance )
254{
255 QVector< double > x;
256 QVector< double > y;
257 x.resize( pointCount );
258 y.resize( pointCount );
259 double *xData = x.data();
260 double *yData = y.data();
261
262 if ( transform.isIdentity() )
263 {
264 for ( int i = 0; i < pointCount; ++i, ++points )
265 {
266 *xData++ = points->x();
267 *yData++ = points->y();
268 }
269 }
270 else
271 {
272 for ( int i = 0; i < pointCount; ++i, ++points )
273 {
274 const double x = points->x();
275 const double y = points->y();
276 double tx, ty;
277 transform.map( x, y, &tx, &ty );
278
279 *xData++ = tx;
280 *yData++ = ty;
281 }
282 }
283
284 switch ( mode )
285 {
286 case QPaintEngine::PolylineMode:
287 if ( simplifyTolerance > 0 )
288 geometry.addGeometry( QgsLineString( x, y ).simplifyByDistance( simplifyTolerance ) );
289 else
290 geometry.addGeometry( new QgsLineString( x, y ) );
291 break;
292
293 case QPaintEngine::OddEvenMode:
294 case QPaintEngine::WindingMode:
295 case QPaintEngine::ConvexMode:
296 if ( simplifyTolerance > 0 )
297 geometry.addGeometry( new QgsPolygon( QgsLineString( x, y ).simplifyByDistance( simplifyTolerance ) ) );
298 else
299 geometry.addGeometry( new QgsPolygon( new QgsLineString( x, y ) ) );
300 break;
301 }
302}
303
304void QgsGeometryPaintEngine::drawPolygon( const QPoint *points, int pointCount, QPaintEngine::PolygonDrawMode mode )
305{
306 if ( mUsePathStroker && mode == PolygonDrawMode::PolylineMode )
307 {
308 // if stroking we have no choice but to go via the QPainterPath route
309 if ( pointCount > 0 )
310 {
311 QPainterPath path;
312 path.moveTo( *points++ );
313 for ( int i = 1; i < pointCount; ++i )
314 {
315 path.lineTo( *points++ );
316 }
317 drawPath( path );
318 }
319 }
320 else
321 {
322 const QTransform transform = painter()->combinedTransform();
323 drawPolygonImp( transform, mGeometry, points, pointCount, mode, mSimplifyTolerance );
324 }
325}
326
327void QgsGeometryPaintEngine::drawPolygon( const QPointF *points, int pointCount, QPaintEngine::PolygonDrawMode mode )
328{
329 if ( mUsePathStroker )
330 {
331 // if stroking we have no choice but to go via the QPainterPath route
332 if ( pointCount > 0 )
333 {
334 QPainterPath path;
335 path.moveTo( *points++ );
336 for ( int i = 1; i < pointCount; ++i )
337 {
338 path.lineTo( *points++ );
339 }
340 drawPath( path );
341 }
342 }
343 else
344 {
345 const QTransform transform = painter()->combinedTransform();
346 drawPolygonImp( transform, mGeometry, points, pointCount, mode, mSimplifyTolerance );
347 }
348}
349
350void QgsGeometryPaintEngine::addStrokedLine( const QgsLineString *line, double penWidth, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit, const QTransform *matrix )
351{
352 std::unique_ptr< QgsAbstractGeometry > buffered;
353 if ( mSimplifyTolerance > 0 )
354 {
355 // For performance, we apply a lower level of simplification to the line BEFORE doing the buffer.
356 // This avoids making the call to GEOS buffer function too expensive, as we'd otherwise be doing it
357 // on the unsimplified line and then immediately discarding most of the detail when we simplify
358 // the resultant buffer.
359 // The 0.75 factor here is just a guess! This could likely be made smarter, eg by considering the pen width?
360 const double preBufferedSimplificationFactor = mSimplifyTolerance * 0.75;
361 std::unique_ptr< QgsLineString > simplified( line->simplifyByDistance( preBufferedSimplificationFactor ) );
362 QgsGeos geos( simplified.get() );
363 buffered.reset( geos.buffer( penWidth / 2, mStrokedPathsSegments, endCapStyle, joinStyle, miterLimit ) );
364 }
365 else
366 {
367 QgsGeos geos( line );
368 buffered.reset( geos.buffer( penWidth / 2, mStrokedPathsSegments, endCapStyle, joinStyle, miterLimit ) );
369 }
370
371 if ( !buffered )
372 return;
373
374 if ( matrix )
375 buffered->transform( *matrix );
376
377 if ( QgsGeometryCollection *bufferedCollection = qgsgeometry_cast< QgsGeometryCollection * >( buffered.get() ) )
378 {
379 if ( mSimplifyTolerance > 0 )
380 {
381 for ( auto it = bufferedCollection->const_parts_begin(); it != bufferedCollection->const_parts_end(); ++it )
382 {
383 mGeometry.addGeometry( ( *it )->simplifyByDistance( mSimplifyTolerance ) );
384 }
385 }
386 else
387 {
388 mGeometry.addGeometries( bufferedCollection->takeGeometries() );
389 }
390 }
391 else if ( buffered )
392 {
393 if ( mSimplifyTolerance > 0 )
394 {
395 mGeometry.addGeometry( buffered->simplifyByDistance( mSimplifyTolerance ) );
396 }
397 else
398 {
399 mGeometry.addGeometry( buffered.release() );
400 }
401 }
402}
403
404// based on QPainterPath::toSubpathPolygons()
405void QgsGeometryPaintEngine::addSubpathGeometries( const QPainterPath &path, const QTransform &matrix )
406{
407 if ( path.isEmpty() )
408 return;
409
410 const bool transformIsIdentity = matrix.isIdentity();
411
412 const Qgis::EndCapStyle endCapStyle = QgsSymbolLayerUtils::penCapStyleToEndCapStyle( mPen.capStyle() );
413 const Qgis::JoinStyle joinStyle = QgsSymbolLayerUtils::penJoinStyleToJoinStyle( mPen.joinStyle() );
414 const double penWidth = mPen.widthF() <= 0 ? 1 : mPen.widthF();
415 const double miterLimit = mPen.miterLimit();
416
417 QVector< double > currentX;
418 QVector< double > currentY;
419 const int count = path.elementCount();
420
421 // polygon parts get queued and post-processed before adding them to the collection
422 std::vector< std::unique_ptr< QgsPolygon > > queuedPolygons;
423
424 for ( int i = 0; i < count; ++i )
425 {
426 const QPainterPath::Element &e = path.elementAt( i );
427 switch ( e.type )
428 {
429 case QPainterPath::MoveToElement:
430 {
431 if ( currentX.size() > 1 )
432 {
433 auto line = std::make_unique< QgsLineString >( currentX, currentY );
434 if ( mUsePathStroker )
435 {
436 addStrokedLine( line.get(), penWidth, endCapStyle, joinStyle, miterLimit, transformIsIdentity ? nullptr : &matrix );
437 }
438 else if ( line->isClosed() )
439 {
440 if ( !transformIsIdentity )
441 line->transform( matrix );
442
443 if ( mSimplifyTolerance > 0 )
444 {
445 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line->simplifyByDistance( mSimplifyTolerance ) ) );
446 line.reset();
447 }
448 else
449 {
450 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line.release() ) );
451 }
452 }
453 else
454 {
455 if ( !transformIsIdentity )
456 line->transform( matrix );
457 if ( mSimplifyTolerance > 0 )
458 {
459 mGeometry.addGeometry( line->simplifyByDistance( mSimplifyTolerance ) );
460 line.reset();
461 }
462 else
463 {
464 mGeometry.addGeometry( line.release() );
465 }
466 }
467 }
468 currentX.resize( 0 );
469 currentY.resize( 0 );
470
471 currentX.reserve( 16 );
472 currentY.reserve( 16 );
473 currentX << e.x;
474 currentY << e.y;
475 break;
476 }
477
478 case QPainterPath::LineToElement:
479 {
480 currentX << e.x;
481 currentY << e.y;
482 break;
483 }
484
485 case QPainterPath::CurveToElement:
486 {
487 Q_ASSERT( path.elementAt( i + 1 ).type == QPainterPath::CurveToDataElement );
488 Q_ASSERT( path.elementAt( i + 2 ).type == QPainterPath::CurveToDataElement );
489
490 const double x1 = path.elementAt( i - 1 ).x;
491 const double y1 = path.elementAt( i - 1 ).y;
492
493 const double x3 = path.elementAt( i + 1 ).x;
494 const double y3 = path.elementAt( i + 1 ).y;
495
496 const double x4 = path.elementAt( i + 2 ).x;
497 const double y4 = path.elementAt( i + 2 ).y;
498
499 // TODO -- we could likely reduce the number of segmented points here!
500 std::unique_ptr< QgsLineString> bezier( QgsLineString::fromBezierCurve(
501 QgsPoint( x1, y1 ),
502 QgsPoint( e.x, e.y ),
503 QgsPoint( x3, y3 ),
504 QgsPoint( x4, y4 ) ) );
505
506 currentX << bezier->xVector();
507 currentY << bezier->yVector();
508
509 i += 2;
510 break;
511 }
512 case QPainterPath::CurveToDataElement:
513 Q_ASSERT( !"addSubpathGeometries(), bad element type" );
514 break;
515 }
516 }
517
518 if ( currentX.size() > 1 )
519 {
520 auto line = std::make_unique< QgsLineString >( currentX, currentY );
521 if ( mUsePathStroker )
522 {
523 addStrokedLine( line.get(), penWidth, endCapStyle, joinStyle, miterLimit, transformIsIdentity ? nullptr : &matrix );
524 }
525 else if ( line->isClosed() )
526 {
527 if ( !transformIsIdentity )
528 line->transform( matrix );
529 if ( mSimplifyTolerance > 0 )
530 {
531 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line->simplifyByDistance( mSimplifyTolerance ) ) );
532 line.reset();
533 }
534 else
535 {
536 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line.release() ) );
537 }
538 }
539 else
540 {
541 if ( !transformIsIdentity )
542 line->transform( matrix );
543 if ( mSimplifyTolerance > 0 )
544 {
545 mGeometry.addGeometry( line->simplifyByDistance( mSimplifyTolerance ) );
546 line.reset();
547 }
548 else
549 {
550 mGeometry.addGeometry( line.release() );
551 }
552 }
553 }
554
555 if ( queuedPolygons.empty() )
556 return;
557
558 mGeometry.reserve( static_cast< int >( mGeometry.numGeometries() + queuedPolygons.size() ) );
559
560 QgsMultiPolygon tempMultiPolygon;
561 tempMultiPolygon.reserve( static_cast< int >( queuedPolygons.size() ) );
562 for ( auto &part : queuedPolygons )
563 {
564 tempMultiPolygon.addGeometry( part.release() );
565 }
566
567 // ensure holes are holes, not overlapping polygons
568 QgsGeos geosCollection( &tempMultiPolygon );
569 std::unique_ptr< QgsAbstractGeometry > g = geosCollection.makeValid( Qgis::MakeValidMethod::Linework );
570 if ( !g )
571 return;
572
573 for ( auto it = g->const_parts_begin(); it != g->const_parts_end(); ++it )
574 {
575 mGeometry.addGeometry( ( *it )->clone() );
576 }
577}
578
579void QgsGeometryPaintEngine::drawPath( const QPainterPath &path )
580{
581 const QTransform transform = painter()->combinedTransform();
582 addSubpathGeometries( path, transform );
583}
584
585
586//
587// QgsGeometryPaintDevice
588//
589
591{
592 mPaintEngine = std::make_unique<QgsGeometryPaintEngine>( usePathStroker );
593}
594
596{
597 if ( mPaintEngine )
598 mPaintEngine->setStrokedPathSegments( segments );
599}
600
602{
603 if ( mPaintEngine )
604 mPaintEngine->setSimplificationTolerance( tolerance );
605}
606
608{
609 return mPaintEngine.get();
610}
611
612int QgsGeometryPaintDevice::metric( PaintDeviceMetric m ) const
613{
614 // copy/paste from qpicture.cpp
615 int val;
616
617 switch ( m )
618 {
619 case PdmWidth:
620 val = static_cast< int >( mPaintEngine->geometry().boundingBox().width() );
621 break;
622 case PdmHeight:
623 val = static_cast< int >( mPaintEngine->geometry().boundingBox().height() );
624 break;
625 case PdmWidthMM:
626 val = static_cast< int >( 25.4 / QgsPainting::qtDefaultDpiX() * mPaintEngine->geometry().boundingBox().width() );
627 break;
628 case PdmHeightMM:
629 val = static_cast< int >( 25.4 / QgsPainting::qtDefaultDpiY() * mPaintEngine->geometry().boundingBox().height() );
630 break;
631 case PdmDpiX:
632 case PdmPhysicalDpiX:
634 break;
635 case PdmDpiY:
636 case PdmPhysicalDpiY:
638 break;
639 case PdmNumColors:
640 val = 16777216;
641 break;
642 case PdmDepth:
643 val = 24;
644 break;
645 case PdmDevicePixelRatio:
646 val = 1;
647 break;
648 case PdmDevicePixelRatioScaled:
649 val = static_cast< int >( 1 * QPaintDevice::devicePixelRatioFScale() );
650 break;
651 default:
652 val = 0;
653 qWarning( "QPicture::metric: Invalid metric command" );
654 }
655 return val;
656}
657
659{
660 return mPaintEngine->geometry();
661}
662
664{
666 QPainter painter( &device );
667 painter.drawPath( path );
668 painter.end();
669 return QgsGeometry( device.geometry().clone() );
670}
671
JoinStyle
Join styles for buffers.
Definition qgis.h:2179
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2166
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2239
Abstract base class for all geometries.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
void reserve(int size)
Attempts to allocate memory for at least size geometries.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int numGeometries() const
Returns the number of geometries within the collection.
QgsGeometryPaintDevice(bool usePathStroker=false)
Constructor for QgsGeometryPaintDevice.
void setStrokedPathSegments(int segments)
Sets the number of segments to use when drawing stroked paths with a rounded pen.
int metric(PaintDeviceMetric metric) const override
void setSimplificationTolerance(double tolerance)
Sets a simplification tolerance (in painter units) to use for on-the-fly simplification of geometries...
QPaintEngine * paintEngine() const override
static QgsGeometry painterPathToGeometry(const QPainterPath &path)
Converts a painter path to a QgsGeometry.
const QgsAbstractGeometry & geometry() const
Returns the rendered geometry.
void setSimplificationTolerance(double tolerance)
Sets a simplification tolerance (in painter units) to use for on-the-fly simplification of geometries...
void drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags=Qt::AutoColor) final
void drawRects(const QRectF *rects, int rectCount) final
void drawPolygon(const QPointF *points, int pointCount, QPaintEngine::PolygonDrawMode mode) final
void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) final
void drawPoints(const QPointF *points, int pointCount) final
bool begin(QPaintDevice *) final
QgsGeometryPaintEngine(bool usePathStroker=false)
Constructor for QgsGeometryPaintEngine.
void drawLines(const QLineF *lines, int lineCount) final
QPaintEngine::Type type() const final
void drawTiledPixmap(const QRectF &rect, const QPixmap &pixmap, const QPointF &p) final
void updateState(const QPaintEngineState &) final
void drawPath(const QPainterPath &path) final
void setStrokedPathSegments(int segments)
Sets the number of segments to use when drawing stroked paths with a rounded pen.
A geometry is the spatial representation of a feature.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:141
Line string geometry type, with support for z-dimension and m-values.
static std::unique_ptr< QgsLineString > fromBezierCurve(const QgsPoint &start, const QgsPoint &controlPoint1, const QgsPoint &controlPoint2, const QgsPoint &end, int segments=30)
Returns a new linestring created by segmentizing the bezier curve between start and end,...
bool isClosed() const override
Returns true if the curve is closed.
QgsLineString * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
static int qtDefaultDpiY()
Returns the default Qt vertical DPI.
static int qtDefaultDpiX()
Returns the default Qt horizontal DPI.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
Polygon geometry type.
Definition qgspolygon.h:37
static Qgis::EndCapStyle penCapStyleToEndCapStyle(Qt::PenCapStyle style)
Converts a Qt pen cap style to a QGIS end cap style.
static Qgis::JoinStyle penJoinStyleToJoinStyle(Qt::PenJoinStyle style)
Converts a Qt pen joinstyle to a QGIS join style.
Contains geos related utilities and functions.
Definition qgsgeos.h:77
T qgsgeometry_cast(QgsAbstractGeometry *geom)
void drawPointsImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount)
void drawRectsImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *rects, int rectCount)
void drawLinesImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *lines, int lineCount)
void drawPolygonImp(const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount, QPaintEngine::PolygonDrawMode mode, double simplifyTolerance)
#define QgsDebugError(str)
Definition qgslogger.h:59