QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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
40{
41 mStrokedPathsSegments = segments;
42}
43
45{
46 mSimplifyTolerance = tolerance;
47}
48
49bool QgsGeometryPaintEngine::begin( QPaintDevice * )
50{
51 return true;
52}
53
55{
56 return true;
57}
58
59QPaintEngine::Type QgsGeometryPaintEngine::type() const
60{
61 return QPaintEngine::User;
62}
63
64void QgsGeometryPaintEngine::updateState( const QPaintEngineState &state )
65{
66 if ( mUsePathStroker && state.state().testFlag( QPaintEngine::DirtyFlag::DirtyPen ) )
67 {
68 mPen = state.pen();
69 }
70}
71
72void QgsGeometryPaintEngine::drawImage( const QRectF &, const QImage &, const QRectF &, Qt::ImageConversionFlags )
73{
74 // ignore, we don't need to support raster drawing
75 QgsDebugError( u"QgsGeometryPaintEngine does not support drawImage method"_s );
76}
77
78void QgsGeometryPaintEngine::drawPixmap( const QRectF &, const QPixmap &, const QRectF & )
79{
80 // ignore, we don't need to support raster drawing
81 QgsDebugError( u"QgsGeometryPaintEngine does not support drawPixmap method"_s );
82}
83
84void QgsGeometryPaintEngine::drawTiledPixmap( const QRectF &, const QPixmap &, const QPointF & )
85{
86 // ignore, we don't need to support raster drawing
87 QgsDebugError( u"QgsGeometryPaintEngine does not support drawTiledPixmap method"_s );
88}
89
90template<typename T> void drawLinesImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *lines, int lineCount )
91{
92 geometry.reserve( geometry.numGeometries() + lineCount );
93 if ( transform.isIdentity() )
94 {
95 for ( int i = 0; i < lineCount; ++i, ++lines )
96 {
97 geometry.addGeometry( new QgsLineString(
98 QVector<double> { static_cast< double >( lines->x1() ), static_cast< double >( lines->x2() ) }, QVector<double> { static_cast< double >( lines->y1() ), static_cast< double >( lines->y2() ) }
99 ) );
100 }
101 }
102 else
103 {
104 for ( int i = 0; i < lineCount; ++i, ++lines )
105 {
106 double x1 = lines->x1();
107 double x2 = lines->x2();
108 double y1 = lines->y1();
109 double y2 = lines->y2();
110
111 double tx1, tx2, ty1, ty2;
112 transform.map( x1, y1, &tx1, &ty1 );
113 transform.map( x2, y2, &tx2, &ty2 );
114
115 geometry.addGeometry( new QgsLineString( QVector<double> { tx1, tx2 }, QVector<double> { ty1, ty2 } ) );
116 }
117 }
118}
119
120void QgsGeometryPaintEngine::drawLines( const QLineF *lines, int lineCount )
121{
122 if ( mUsePathStroker )
123 {
124 // if stroking we have no choice but to go via the QPainterPath route
125 QPaintEngine::drawLines( lines, lineCount );
126 }
127 else
128 {
129 const QTransform transform = painter()->combinedTransform();
130 drawLinesImp( transform, mGeometry, lines, lineCount );
131 }
132}
133
134void QgsGeometryPaintEngine::drawLines( const QLine *lines, int lineCount )
135{
136 if ( mUsePathStroker )
137 {
138 // if stroking we have no choice but to go via the QPainterPath route
139 QPaintEngine::drawLines( lines, lineCount );
140 }
141 else
142 {
143 const QTransform transform = painter()->combinedTransform();
144 drawLinesImp( transform, mGeometry, lines, lineCount );
145 }
146}
147
148template<typename T> void drawPointsImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount )
149{
150 geometry.reserve( geometry.numGeometries() + pointCount );
151 if ( transform.isIdentity() )
152 {
153 for ( int i = 0; i < pointCount; ++i, ++points )
154 {
155 geometry.addGeometry( new QgsPoint( static_cast< double >( points->x() ), static_cast< double >( points->y() ) ) );
156 }
157 }
158 else
159 {
160 for ( int i = 0; i < pointCount; ++i, ++points )
161 {
162 double x = points->x();
163 double y = points->y();
164
165 double tx, ty;
166 transform.map( x, y, &tx, &ty );
167
168 geometry.addGeometry( new QgsPoint( tx, ty ) );
169 }
170 }
171}
172
173void QgsGeometryPaintEngine::drawPoints( const QPointF *points, int pointCount )
174{
175 const QTransform transform = painter()->combinedTransform();
176 drawPointsImp( transform, mGeometry, points, pointCount );
177}
178
179void QgsGeometryPaintEngine::drawPoints( const QPoint *points, int pointCount )
180{
181 const QTransform transform = painter()->combinedTransform();
182 drawPointsImp( transform, mGeometry, points, pointCount );
183}
184
185template<typename T> void drawRectsImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *rects, int rectCount )
186{
187 geometry.reserve( geometry.numGeometries() + rectCount );
188 if ( transform.isIdentity() )
189 {
190 for ( int i = 0; i < rectCount; ++i, ++rects )
191 {
192 QgsLineString *exterior = new QgsLineString(
193 QVector<double> {
194 static_cast< double >( rects->left() ),
195 static_cast< double >( rects->right() ),
196 static_cast< double >( rects->right() ),
197 static_cast< double >( rects->left() ),
198 static_cast< double>( rects->left() )
199 },
200 QVector<double> {
201 static_cast< double >( rects->bottom() ),
202 static_cast< double >( rects->bottom() ),
203 static_cast< double >( rects->top() ),
204 static_cast< double >( rects->top() ),
205 static_cast< double >( rects->bottom() )
206 }
207 );
208 geometry.addGeometry( new QgsPolygon( exterior ) );
209 }
210 }
211 else
212 {
213 for ( int i = 0; i < rectCount; ++i, ++rects )
214 {
215 const double left = rects->left();
216 const double right = rects->right();
217 const double top = rects->top();
218 const double bottom = rects->bottom();
219
220 double bottomLeftX, bottomLeftY, bottomRightX, bottomRightY, topLeftX, topLeftY, topRightX, topRightY;
221 transform.map( left, bottom, &bottomLeftX, &bottomLeftY );
222 transform.map( right, bottom, &bottomRightX, &bottomRightY );
223 transform.map( left, top, &topLeftX, &topLeftY );
224 transform.map( right, top, &topRightX, &topRightY );
225
226 QgsLineString *exterior
227 = new QgsLineString( QVector<double> { bottomLeftX, bottomRightX, topRightX, topLeftX, bottomLeftX }, QVector<double> { bottomLeftY, bottomRightY, topRightY, topLeftY, bottomLeftY } );
228 geometry.addGeometry( new QgsPolygon( exterior ) );
229 }
230 }
231}
232
233void QgsGeometryPaintEngine::drawRects( const QRectF *rects, int rectCount )
234{
235 const QTransform transform = painter()->combinedTransform();
236 drawRectsImp( transform, mGeometry, rects, rectCount );
237}
238
239void QgsGeometryPaintEngine::drawRects( const QRect *rects, int rectCount )
240{
241 const QTransform transform = painter()->combinedTransform();
242 drawRectsImp( transform, mGeometry, rects, rectCount );
243}
244
245template<typename T> void drawPolygonImp( const QTransform &transform, QgsGeometryCollection &geometry, const T *points, int pointCount, QPaintEngine::PolygonDrawMode mode, double simplifyTolerance )
246{
247 QVector< double > x;
248 QVector< double > y;
249 x.resize( pointCount );
250 y.resize( pointCount );
251 double *xData = x.data();
252 double *yData = y.data();
253
254 if ( transform.isIdentity() )
255 {
256 for ( int i = 0; i < pointCount; ++i, ++points )
257 {
258 *xData++ = points->x();
259 *yData++ = points->y();
260 }
261 }
262 else
263 {
264 for ( int i = 0; i < pointCount; ++i, ++points )
265 {
266 const double x = points->x();
267 const double y = points->y();
268 double tx, ty;
269 transform.map( x, y, &tx, &ty );
270
271 *xData++ = tx;
272 *yData++ = ty;
273 }
274 }
275
276 switch ( mode )
277 {
278 case QPaintEngine::PolylineMode:
279 if ( simplifyTolerance > 0 )
280 geometry.addGeometry( QgsLineString( x, y ).simplifyByDistance( simplifyTolerance ) );
281 else
282 geometry.addGeometry( new QgsLineString( x, y ) );
283 break;
284
285 case QPaintEngine::OddEvenMode:
286 case QPaintEngine::WindingMode:
287 case QPaintEngine::ConvexMode:
288 if ( simplifyTolerance > 0 )
289 geometry.addGeometry( new QgsPolygon( QgsLineString( x, y ).simplifyByDistance( simplifyTolerance ) ) );
290 else
291 geometry.addGeometry( new QgsPolygon( new QgsLineString( x, y ) ) );
292 break;
293 }
294}
295
296void QgsGeometryPaintEngine::drawPolygon( const QPoint *points, int pointCount, QPaintEngine::PolygonDrawMode mode )
297{
298 if ( mUsePathStroker && mode == PolygonDrawMode::PolylineMode )
299 {
300 // if stroking we have no choice but to go via the QPainterPath route
301 if ( pointCount > 0 )
302 {
303 QPainterPath path;
304 path.moveTo( *points++ );
305 for ( int i = 1; i < pointCount; ++i )
306 {
307 path.lineTo( *points++ );
308 }
309 drawPath( path );
310 }
311 }
312 else
313 {
314 const QTransform transform = painter()->combinedTransform();
315 drawPolygonImp( transform, mGeometry, points, pointCount, mode, mSimplifyTolerance );
316 }
317}
318
319void QgsGeometryPaintEngine::drawPolygon( const QPointF *points, int pointCount, QPaintEngine::PolygonDrawMode mode )
320{
321 if ( mUsePathStroker )
322 {
323 // if stroking we have no choice but to go via the QPainterPath route
324 if ( pointCount > 0 )
325 {
326 QPainterPath path;
327 path.moveTo( *points++ );
328 for ( int i = 1; i < pointCount; ++i )
329 {
330 path.lineTo( *points++ );
331 }
332 drawPath( path );
333 }
334 }
335 else
336 {
337 const QTransform transform = painter()->combinedTransform();
338 drawPolygonImp( transform, mGeometry, points, pointCount, mode, mSimplifyTolerance );
339 }
340}
341
342void QgsGeometryPaintEngine::addStrokedLine( const QgsLineString *line, double penWidth, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit, const QTransform *matrix )
343{
344 std::unique_ptr< QgsAbstractGeometry > buffered;
345 if ( mSimplifyTolerance > 0 )
346 {
347 // For performance, we apply a lower level of simplification to the line BEFORE doing the buffer.
348 // This avoids making the call to GEOS buffer function too expensive, as we'd otherwise be doing it
349 // on the unsimplified line and then immediately discarding most of the detail when we simplify
350 // the resultant buffer.
351 // The 0.75 factor here is just a guess! This could likely be made smarter, eg by considering the pen width?
352 const double preBufferedSimplificationFactor = mSimplifyTolerance * 0.75;
353 std::unique_ptr< QgsLineString > simplified( line->simplifyByDistance( preBufferedSimplificationFactor ) );
354 QgsGeos geos( simplified.get() );
355 buffered.reset( geos.buffer( penWidth / 2, mStrokedPathsSegments, endCapStyle, joinStyle, miterLimit ) );
356 }
357 else
358 {
359 QgsGeos geos( line );
360 buffered.reset( geos.buffer( penWidth / 2, mStrokedPathsSegments, endCapStyle, joinStyle, miterLimit ) );
361 }
362
363 if ( !buffered )
364 return;
365
366 if ( matrix )
367 buffered->transform( *matrix );
368
369 if ( QgsGeometryCollection *bufferedCollection = qgsgeometry_cast< QgsGeometryCollection * >( buffered.get() ) )
370 {
371 if ( mSimplifyTolerance > 0 )
372 {
373 for ( auto it = bufferedCollection->const_parts_begin(); it != bufferedCollection->const_parts_end(); ++it )
374 {
375 mGeometry.addGeometry( ( *it )->simplifyByDistance( mSimplifyTolerance ) );
376 }
377 }
378 else
379 {
380 mGeometry.addGeometries( bufferedCollection->takeGeometries() );
381 }
382 }
383 else if ( buffered )
384 {
385 if ( mSimplifyTolerance > 0 )
386 {
387 mGeometry.addGeometry( buffered->simplifyByDistance( mSimplifyTolerance ) );
388 }
389 else
390 {
391 mGeometry.addGeometry( buffered.release() );
392 }
393 }
394}
395
396// based on QPainterPath::toSubpathPolygons()
397void QgsGeometryPaintEngine::addSubpathGeometries( const QPainterPath &path, const QTransform &matrix )
398{
399 if ( path.isEmpty() )
400 return;
401
402 const bool transformIsIdentity = matrix.isIdentity();
403
404 const Qgis::EndCapStyle endCapStyle = QgsSymbolLayerUtils::penCapStyleToEndCapStyle( mPen.capStyle() );
405 const Qgis::JoinStyle joinStyle = QgsSymbolLayerUtils::penJoinStyleToJoinStyle( mPen.joinStyle() );
406 const double penWidth = mPen.widthF() <= 0 ? 1 : mPen.widthF();
407 const double miterLimit = mPen.miterLimit();
408
409 QVector< double > currentX;
410 QVector< double > currentY;
411 const int count = path.elementCount();
412
413 // polygon parts get queued and post-processed before adding them to the collection
414 std::vector< std::unique_ptr< QgsPolygon > > queuedPolygons;
415
416 for ( int i = 0; i < count; ++i )
417 {
418 const QPainterPath::Element &e = path.elementAt( i );
419 switch ( e.type )
420 {
421 case QPainterPath::MoveToElement:
422 {
423 if ( currentX.size() > 1 )
424 {
425 auto line = std::make_unique< QgsLineString >( currentX, currentY );
426 if ( mUsePathStroker )
427 {
428 addStrokedLine( line.get(), penWidth, endCapStyle, joinStyle, miterLimit, transformIsIdentity ? nullptr : &matrix );
429 }
430 else if ( line->isClosed() )
431 {
432 if ( !transformIsIdentity )
433 line->transform( matrix );
434
435 if ( mSimplifyTolerance > 0 )
436 {
437 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line->simplifyByDistance( mSimplifyTolerance ) ) );
438 line.reset();
439 }
440 else
441 {
442 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line.release() ) );
443 }
444 }
445 else
446 {
447 if ( !transformIsIdentity )
448 line->transform( matrix );
449 if ( mSimplifyTolerance > 0 )
450 {
451 mGeometry.addGeometry( line->simplifyByDistance( mSimplifyTolerance ) );
452 line.reset();
453 }
454 else
455 {
456 mGeometry.addGeometry( line.release() );
457 }
458 }
459 }
460 currentX.resize( 0 );
461 currentY.resize( 0 );
462
463 currentX.reserve( 16 );
464 currentY.reserve( 16 );
465 currentX << e.x;
466 currentY << e.y;
467 break;
468 }
469
470 case QPainterPath::LineToElement:
471 {
472 currentX << e.x;
473 currentY << e.y;
474 break;
475 }
476
477 case QPainterPath::CurveToElement:
478 {
479 Q_ASSERT( path.elementAt( i + 1 ).type == QPainterPath::CurveToDataElement );
480 Q_ASSERT( path.elementAt( i + 2 ).type == QPainterPath::CurveToDataElement );
481
482 const double x1 = path.elementAt( i - 1 ).x;
483 const double y1 = path.elementAt( i - 1 ).y;
484
485 const double x3 = path.elementAt( i + 1 ).x;
486 const double y3 = path.elementAt( i + 1 ).y;
487
488 const double x4 = path.elementAt( i + 2 ).x;
489 const double y4 = path.elementAt( i + 2 ).y;
490
491 // TODO -- we could likely reduce the number of segmented points here!
492 std::unique_ptr< QgsLineString> bezier( QgsLineString::fromBezierCurve( QgsPoint( x1, y1 ), QgsPoint( e.x, e.y ), QgsPoint( x3, y3 ), QgsPoint( x4, y4 ) ) );
493
494 currentX << bezier->xVector();
495 currentY << bezier->yVector();
496
497 i += 2;
498 break;
499 }
500 case QPainterPath::CurveToDataElement:
501 Q_ASSERT( !"addSubpathGeometries(), bad element type" );
502 break;
503 }
504 }
505
506 if ( currentX.size() > 1 )
507 {
508 auto line = std::make_unique< QgsLineString >( currentX, currentY );
509 if ( mUsePathStroker )
510 {
511 addStrokedLine( line.get(), penWidth, endCapStyle, joinStyle, miterLimit, transformIsIdentity ? nullptr : &matrix );
512 }
513 else if ( line->isClosed() )
514 {
515 if ( !transformIsIdentity )
516 line->transform( matrix );
517 if ( mSimplifyTolerance > 0 )
518 {
519 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line->simplifyByDistance( mSimplifyTolerance ) ) );
520 line.reset();
521 }
522 else
523 {
524 queuedPolygons.emplace_back( std::make_unique< QgsPolygon >( line.release() ) );
525 }
526 }
527 else
528 {
529 if ( !transformIsIdentity )
530 line->transform( matrix );
531 if ( mSimplifyTolerance > 0 )
532 {
533 mGeometry.addGeometry( line->simplifyByDistance( mSimplifyTolerance ) );
534 line.reset();
535 }
536 else
537 {
538 mGeometry.addGeometry( line.release() );
539 }
540 }
541 }
542
543 if ( queuedPolygons.empty() )
544 return;
545
546 mGeometry.reserve( static_cast< int >( mGeometry.numGeometries() + queuedPolygons.size() ) );
547
548 QgsMultiPolygon tempMultiPolygon;
549 tempMultiPolygon.reserve( static_cast< int >( queuedPolygons.size() ) );
550 for ( auto &part : queuedPolygons )
551 {
552 tempMultiPolygon.addGeometry( part.release() );
553 }
554
555 // ensure holes are holes, not overlapping polygons
556 QgsGeos geosCollection( &tempMultiPolygon );
557 std::unique_ptr< QgsAbstractGeometry > g = geosCollection.makeValid( Qgis::MakeValidMethod::Linework );
558 if ( !g )
559 return;
560
561 for ( auto it = g->const_parts_begin(); it != g->const_parts_end(); ++it )
562 {
563 mGeometry.addGeometry( ( *it )->clone() );
564 }
565}
566
567void QgsGeometryPaintEngine::drawPath( const QPainterPath &path )
568{
569 const QTransform transform = painter()->combinedTransform();
570 addSubpathGeometries( path, transform );
571}
572
573
574//
575// QgsGeometryPaintDevice
576//
577
579{
580 mPaintEngine = std::make_unique<QgsGeometryPaintEngine>( usePathStroker );
581}
582
584{
585 if ( mPaintEngine )
586 mPaintEngine->setStrokedPathSegments( segments );
587}
588
590{
591 if ( mPaintEngine )
592 mPaintEngine->setSimplificationTolerance( tolerance );
593}
594
596{
597 return mPaintEngine.get();
598}
599
600int QgsGeometryPaintDevice::metric( PaintDeviceMetric m ) const
601{
602 // copy/paste from qpicture.cpp
603 int val;
604
605 switch ( m )
606 {
607 case PdmWidth:
608 val = static_cast< int >( mPaintEngine->geometry().boundingBox().width() );
609 break;
610 case PdmHeight:
611 val = static_cast< int >( mPaintEngine->geometry().boundingBox().height() );
612 break;
613 case PdmWidthMM:
614 val = static_cast< int >( 25.4 / QgsPainting::qtDefaultDpiX() * mPaintEngine->geometry().boundingBox().width() );
615 break;
616 case PdmHeightMM:
617 val = static_cast< int >( 25.4 / QgsPainting::qtDefaultDpiY() * mPaintEngine->geometry().boundingBox().height() );
618 break;
619 case PdmDpiX:
620 case PdmPhysicalDpiX:
622 break;
623 case PdmDpiY:
624 case PdmPhysicalDpiY:
626 break;
627 case PdmNumColors:
628 val = 16777216;
629 break;
630 case PdmDepth:
631 val = 24;
632 break;
633 case PdmDevicePixelRatio:
634 val = 1;
635 break;
636 case PdmDevicePixelRatioScaled:
637 val = static_cast< int >( 1 * QPaintDevice::devicePixelRatioFScale() );
638 break;
639 default:
640 val = 0;
641 qWarning( "QPicture::metric: Invalid metric command" );
642 }
643 return val;
644}
645
647{
648 return mPaintEngine->geometry();
649}
650
652{
654 QPainter painter( &device );
655 painter.drawPath( path );
656 painter.end();
657 return QgsGeometry( device.geometry().clone() );
658}
JoinStyle
Join styles for buffers.
Definition qgis.h:2201
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2188
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2261
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:139
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:76
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