Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsmarkersymbollayer.cpp
3 : : ---------------------
4 : : begin : November 2009
5 : : copyright : (C) 2009 by Martin Dobias
6 : : email : wonder dot sk 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 : :
16 : : #include "qgsmarkersymbollayer.h"
17 : : #include "qgssymbollayerutils.h"
18 : :
19 : : #include "qgsdxfexport.h"
20 : : #include "qgsdxfpaintdevice.h"
21 : : #include "qgsexpression.h"
22 : : #include "qgsfontutils.h"
23 : : #include "qgsimagecache.h"
24 : : #include "qgsimageoperation.h"
25 : : #include "qgsrendercontext.h"
26 : : #include "qgslogger.h"
27 : : #include "qgssvgcache.h"
28 : : #include "qgsunittypes.h"
29 : :
30 : : #include <QPainter>
31 : : #include <QSvgRenderer>
32 : : #include <QFileInfo>
33 : : #include <QDir>
34 : : #include <QDomDocument>
35 : : #include <QDomElement>
36 : :
37 : : #include <cmath>
38 : :
39 : : Q_GUI_EXPORT extern int qt_defaultDpiX();
40 : : Q_GUI_EXPORT extern int qt_defaultDpiY();
41 : :
42 : 0 : static void _fixQPictureDPI( QPainter *p )
43 : : {
44 : : // QPicture makes an assumption that we drawing to it with system DPI.
45 : : // Then when being drawn, it scales the painter. The following call
46 : : // negates the effect. There is no way of setting QPicture's DPI.
47 : : // See QTBUG-20361
48 : 0 : p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
49 : 0 : static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
50 : 0 : }
51 : :
52 : :
53 : : //////
54 : :
55 : :
56 : : //
57 : : // QgsSimpleMarkerSymbolLayerBase
58 : : //
59 : :
60 : 0 : QList<QgsSimpleMarkerSymbolLayerBase::Shape> QgsSimpleMarkerSymbolLayerBase::availableShapes()
61 : : {
62 : 0 : QList< Shape > shapes;
63 : 0 : shapes << Square
64 : 0 : << Diamond
65 : 0 : << Pentagon
66 : 0 : << Hexagon
67 : 0 : << Octagon
68 : 0 : << SquareWithCorners
69 : 0 : << Triangle
70 : 0 : << EquilateralTriangle
71 : 0 : << Star
72 : 0 : << Arrow
73 : 0 : << Circle
74 : 0 : << Cross
75 : 0 : << CrossFill
76 : 0 : << Cross2
77 : 0 : << Line
78 : 0 : << HalfArc
79 : 0 : << ThirdArc
80 : 0 : << QuarterArc
81 : 0 : << ArrowHead
82 : 0 : << ArrowHeadFilled
83 : 0 : << SemiCircle
84 : 0 : << ThirdCircle
85 : 0 : << QuarterCircle
86 : 0 : << QuarterSquare
87 : 0 : << HalfSquare
88 : 0 : << DiagonalHalfSquare
89 : 0 : << RightHalfTriangle
90 : 0 : << LeftHalfTriangle
91 : 0 : << AsteriskFill;
92 : :
93 : 0 : return shapes;
94 : 0 : }
95 : :
96 : 332 : QgsSimpleMarkerSymbolLayerBase::QgsSimpleMarkerSymbolLayerBase( QgsSimpleMarkerSymbolLayerBase::Shape shape, double size, double angle, QgsSymbol::ScaleMethod scaleMethod )
97 : 332 : : mShape( shape )
98 : 664 : {
99 : 332 : mSize = size;
100 : 332 : mAngle = angle;
101 : 332 : mOffset = QPointF( 0, 0 );
102 : 332 : mScaleMethod = scaleMethod;
103 : 332 : mSizeUnit = QgsUnitTypes::RenderMillimeters;
104 : 332 : mOffsetUnit = QgsUnitTypes::RenderMillimeters;
105 : 332 : }
106 : :
107 : 102 : bool QgsSimpleMarkerSymbolLayerBase::shapeIsFilled( QgsSimpleMarkerSymbolLayerBase::Shape shape )
108 : : {
109 : 102 : switch ( shape )
110 : : {
111 : : case Square:
112 : : case Diamond:
113 : : case Pentagon:
114 : : case Hexagon:
115 : : case Octagon:
116 : : case SquareWithCorners:
117 : : case Triangle:
118 : : case EquilateralTriangle:
119 : : case Star:
120 : : case Arrow:
121 : : case Circle:
122 : : case CrossFill:
123 : : case ArrowHeadFilled:
124 : : case SemiCircle:
125 : : case ThirdCircle:
126 : : case QuarterCircle:
127 : : case QuarterSquare:
128 : : case HalfSquare:
129 : : case DiagonalHalfSquare:
130 : : case RightHalfTriangle:
131 : : case LeftHalfTriangle:
132 : : case AsteriskFill:
133 : 92 : return true;
134 : :
135 : : case Cross:
136 : : case Cross2:
137 : : case Line:
138 : : case ArrowHead:
139 : : case HalfArc:
140 : : case ThirdArc:
141 : : case QuarterArc:
142 : 10 : return false;
143 : : }
144 : 0 : return true;
145 : 102 : }
146 : :
147 : 0 : void QgsSimpleMarkerSymbolLayerBase::startRender( QgsSymbolRenderContext &context )
148 : : {
149 : 0 : bool hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation
150 : 0 : || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle );
151 : 0 : bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
152 : :
153 : : // use either QPolygonF or QPainterPath for drawing
154 : 0 : if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
155 : : {
156 : 0 : prepareMarkerPath( mShape ); // drawing as a painter path
157 : 0 : }
158 : :
159 : 0 : QTransform transform;
160 : :
161 : : // scale the shape (if the size is not going to be modified)
162 : 0 : if ( !hasDataDefinedSize )
163 : : {
164 : 0 : double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
165 : 0 : if ( mSizeUnit == QgsUnitTypes::RenderMetersInMapUnits && context.renderContext().flags() & QgsRenderContext::RenderSymbolPreview )
166 : : {
167 : : // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
168 : : // and clamp it to a reasonable range. It's the best we can do in this situation!
169 : 0 : scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
170 : 0 : }
171 : :
172 : 0 : double half = scaledSize / 2.0;
173 : 0 : transform.scale( half, half );
174 : 0 : }
175 : :
176 : : // rotate if the rotation is not going to be changed during the rendering
177 : 0 : if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
178 : : {
179 : 0 : transform.rotate( mAngle );
180 : 0 : }
181 : :
182 : 0 : if ( !mPolygon.isEmpty() )
183 : 0 : mPolygon = transform.map( mPolygon );
184 : : else
185 : 0 : mPath = transform.map( mPath );
186 : :
187 : 0 : QgsMarkerSymbolLayer::startRender( context );
188 : 0 : }
189 : :
190 : 0 : void QgsSimpleMarkerSymbolLayerBase::stopRender( QgsSymbolRenderContext &context )
191 : : {
192 : 0 : Q_UNUSED( context )
193 : 0 : }
194 : :
195 : 0 : void QgsSimpleMarkerSymbolLayerBase::renderPoint( QPointF point, QgsSymbolRenderContext &context )
196 : : {
197 : : //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
198 : : //of the rendered point!
199 : :
200 : 0 : QPainter *p = context.renderContext().painter();
201 : 0 : if ( !p )
202 : : {
203 : 0 : return;
204 : : }
205 : :
206 : 0 : bool hasDataDefinedSize = false;
207 : 0 : double scaledSize = calculateSize( context, hasDataDefinedSize );
208 : :
209 : 0 : bool hasDataDefinedRotation = false;
210 : 0 : QPointF offset;
211 : 0 : double angle = 0;
212 : 0 : calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
213 : :
214 : : //data defined shape?
215 : 0 : bool createdNewPath = false;
216 : 0 : bool ok = true;
217 : 0 : Shape symbol = mShape;
218 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName ) )
219 : : {
220 : 0 : context.setOriginalValueVariable( encodeShape( symbol ) );
221 : 0 : QVariant exprVal = mDataDefinedProperties.value( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext() );
222 : 0 : if ( exprVal.isValid() )
223 : : {
224 : 0 : Shape decoded = decodeShape( exprVal.toString(), &ok );
225 : 0 : if ( ok )
226 : : {
227 : 0 : symbol = decoded;
228 : :
229 : 0 : if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
230 : : {
231 : 0 : prepareMarkerPath( symbol ); // drawing as a painter path
232 : 0 : }
233 : 0 : createdNewPath = true;
234 : 0 : }
235 : 0 : }
236 : : else
237 : : {
238 : 0 : symbol = mShape;
239 : : }
240 : 0 : }
241 : :
242 : 0 : QTransform transform;
243 : :
244 : : // move to the desired position
245 : 0 : transform.translate( point.x() + offset.x(), point.y() + offset.y() );
246 : :
247 : : // resize if necessary
248 : 0 : if ( hasDataDefinedSize || createdNewPath )
249 : : {
250 : 0 : double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
251 : 0 : if ( mSizeUnit == QgsUnitTypes::RenderMetersInMapUnits && context.renderContext().flags() & QgsRenderContext::RenderSymbolPreview )
252 : : {
253 : : // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
254 : : // and clamp it to a reasonable range. It's the best we can do in this situation!
255 : 0 : s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
256 : 0 : }
257 : 0 : double half = s / 2.0;
258 : 0 : transform.scale( half, half );
259 : 0 : }
260 : :
261 : 0 : if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
262 : : {
263 : 0 : transform.rotate( angle );
264 : 0 : }
265 : :
266 : : //need to pass: symbol, polygon, path
267 : :
268 : 0 : QPolygonF polygon;
269 : 0 : QPainterPath path;
270 : 0 : if ( !mPolygon.isEmpty() )
271 : : {
272 : 0 : polygon = transform.map( mPolygon );
273 : 0 : }
274 : : else
275 : : {
276 : 0 : path = transform.map( mPath );
277 : : }
278 : 0 : draw( context, symbol, polygon, path );
279 : 0 : }
280 : :
281 : 0 : QRectF QgsSimpleMarkerSymbolLayerBase::bounds( QPointF point, QgsSymbolRenderContext &context )
282 : : {
283 : 0 : bool hasDataDefinedSize = false;
284 : 0 : double scaledSize = calculateSize( context, hasDataDefinedSize );
285 : :
286 : 0 : bool hasDataDefinedRotation = false;
287 : 0 : QPointF offset;
288 : 0 : double angle = 0;
289 : 0 : calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
290 : :
291 : 0 : scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
292 : :
293 : 0 : QTransform transform;
294 : :
295 : : // move to the desired position
296 : 0 : transform.translate( point.x() + offset.x(), point.y() + offset.y() );
297 : :
298 : 0 : if ( !qgsDoubleNear( angle, 0.0 ) )
299 : 0 : transform.rotate( angle );
300 : :
301 : 0 : return transform.mapRect( QRectF( -scaledSize / 2.0,
302 : 0 : -scaledSize / 2.0,
303 : 0 : scaledSize,
304 : 0 : scaledSize ) );
305 : : }
306 : :
307 : 230 : QgsSimpleMarkerSymbolLayerBase::Shape QgsSimpleMarkerSymbolLayerBase::decodeShape( const QString &name, bool *ok )
308 : : {
309 : 230 : if ( ok )
310 : 0 : *ok = true;
311 : 230 : QString cleaned = name.toLower().trimmed();
312 : :
313 : 230 : if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
314 : 5 : return Square;
315 : 225 : else if ( cleaned == QLatin1String( "square_with_corners" ) )
316 : 0 : return SquareWithCorners;
317 : 225 : else if ( cleaned == QLatin1String( "diamond" ) )
318 : 15 : return Diamond;
319 : 210 : else if ( cleaned == QLatin1String( "pentagon" ) )
320 : 0 : return Pentagon;
321 : 210 : else if ( cleaned == QLatin1String( "hexagon" ) )
322 : 10 : return Hexagon;
323 : 200 : else if ( cleaned == QLatin1String( "octagon" ) )
324 : 0 : return Octagon;
325 : 200 : else if ( cleaned == QLatin1String( "triangle" ) )
326 : 25 : return Triangle;
327 : 175 : else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
328 : 0 : return EquilateralTriangle;
329 : 175 : else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
330 : 10 : return Star;
331 : 165 : else if ( cleaned == QLatin1String( "arrow" ) )
332 : 0 : return Arrow;
333 : 165 : else if ( cleaned == QLatin1String( "circle" ) )
334 : 135 : return Circle;
335 : 30 : else if ( cleaned == QLatin1String( "cross" ) )
336 : 0 : return Cross;
337 : 30 : else if ( cleaned == QLatin1String( "cross_fill" ) )
338 : 5 : return CrossFill;
339 : 25 : else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
340 : 0 : return Cross2;
341 : 25 : else if ( cleaned == QLatin1String( "line" ) )
342 : 25 : return Line;
343 : 0 : else if ( cleaned == QLatin1String( "arrowhead" ) )
344 : 0 : return ArrowHead;
345 : 0 : else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
346 : 0 : return ArrowHeadFilled;
347 : 0 : else if ( cleaned == QLatin1String( "semi_circle" ) )
348 : 0 : return SemiCircle;
349 : 0 : else if ( cleaned == QLatin1String( "third_circle" ) )
350 : 0 : return ThirdCircle;
351 : 0 : else if ( cleaned == QLatin1String( "quarter_circle" ) )
352 : 0 : return QuarterCircle;
353 : 0 : else if ( cleaned == QLatin1String( "quarter_square" ) )
354 : 0 : return QuarterSquare;
355 : 0 : else if ( cleaned == QLatin1String( "half_square" ) )
356 : 0 : return HalfSquare;
357 : 0 : else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
358 : 0 : return DiagonalHalfSquare;
359 : 0 : else if ( cleaned == QLatin1String( "right_half_triangle" ) )
360 : 0 : return RightHalfTriangle;
361 : 0 : else if ( cleaned == QLatin1String( "left_half_triangle" ) )
362 : 0 : return LeftHalfTriangle;
363 : 0 : else if ( cleaned == QLatin1String( "asterisk_fill" ) )
364 : 0 : return AsteriskFill;
365 : 0 : else if ( cleaned == QLatin1String( "half_arc" ) )
366 : 0 : return HalfArc;
367 : 0 : else if ( cleaned == QLatin1String( "third_arc" ) )
368 : 0 : return ThirdArc;
369 : 0 : else if ( cleaned == QLatin1String( "quarter_arc" ) )
370 : 0 : return QuarterArc;
371 : :
372 : 0 : if ( ok )
373 : 0 : *ok = false;
374 : 0 : return Circle;
375 : 230 : }
376 : :
377 : 0 : QString QgsSimpleMarkerSymbolLayerBase::encodeShape( QgsSimpleMarkerSymbolLayerBase::Shape shape )
378 : : {
379 : 0 : switch ( shape )
380 : : {
381 : : case Square:
382 : 0 : return QStringLiteral( "square" );
383 : : case QuarterSquare:
384 : 0 : return QStringLiteral( "quarter_square" );
385 : : case HalfSquare:
386 : 0 : return QStringLiteral( "half_square" );
387 : : case DiagonalHalfSquare:
388 : 0 : return QStringLiteral( "diagonal_half_square" );
389 : : case Diamond:
390 : 0 : return QStringLiteral( "diamond" );
391 : : case Pentagon:
392 : 0 : return QStringLiteral( "pentagon" );
393 : : case Hexagon:
394 : 0 : return QStringLiteral( "hexagon" );
395 : : case Octagon:
396 : 0 : return QStringLiteral( "octagon" );
397 : : case SquareWithCorners:
398 : 0 : return QStringLiteral( "square_with_corners" );
399 : : case Triangle:
400 : 0 : return QStringLiteral( "triangle" );
401 : : case EquilateralTriangle:
402 : 0 : return QStringLiteral( "equilateral_triangle" );
403 : : case LeftHalfTriangle:
404 : 0 : return QStringLiteral( "left_half_triangle" );
405 : : case RightHalfTriangle:
406 : 0 : return QStringLiteral( "right_half_triangle" );
407 : : case Star:
408 : 0 : return QStringLiteral( "star" );
409 : : case Arrow:
410 : 0 : return QStringLiteral( "arrow" );
411 : : case ArrowHeadFilled:
412 : 0 : return QStringLiteral( "filled_arrowhead" );
413 : 332 : case CrossFill:
414 : 0 : return QStringLiteral( "cross_fill" );
415 : 332 : case Circle:
416 : 0 : return QStringLiteral( "circle" );
417 : 332 : case Cross:
418 : 0 : return QStringLiteral( "cross" );
419 : : case Cross2:
420 : 0 : return QStringLiteral( "cross2" );
421 : : case Line:
422 : 0 : return QStringLiteral( "line" );
423 : 332 : case ArrowHead:
424 : 0 : return QStringLiteral( "arrowhead" );
425 : : case SemiCircle:
426 : 0 : return QStringLiteral( "semi_circle" );
427 : : case ThirdCircle:
428 : 0 : return QStringLiteral( "third_circle" );
429 : : case QuarterCircle:
430 : 0 : return QStringLiteral( "quarter_circle" );
431 : : case AsteriskFill:
432 : 0 : return QStringLiteral( "asterisk_fill" );
433 : : case HalfArc:
434 : 0 : return QStringLiteral( "half_arc" );
435 : : case ThirdArc:
436 : 0 : return QStringLiteral( "third_arc" );
437 : : case QuarterArc:
438 : 0 : return QStringLiteral( "quarter_arc" );
439 : : }
440 : 0 : return QString();
441 : 0 : }
442 : 332 :
443 : 0 : bool QgsSimpleMarkerSymbolLayerBase::prepareMarkerShape( QgsSimpleMarkerSymbolLayerBase::Shape shape )
444 : : {
445 : 0 : return shapeToPolygon( shape, mPolygon );
446 : : }
447 : :
448 : 0 : bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerBase::Shape shape, QPolygonF &polygon ) const
449 : : {
450 : 0 : polygon.clear();
451 : 332 :
452 : 0 : switch ( shape )
453 : : {
454 : : case Square:
455 : 0 : polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
456 : 0 : return true;
457 : :
458 : : case SquareWithCorners:
459 : : {
460 : : static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
461 : :
462 : 0 : polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
463 : 0 : << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
464 : 0 : << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
465 : 0 : << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
466 : 0 : << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
467 : 0 : << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
468 : 0 : << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
469 : 0 : << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
470 : 0 : << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
471 : 0 : return true;
472 : : }
473 : :
474 : : case QuarterSquare:
475 : 0 : polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
476 : 0 : return true;
477 : :
478 : : case HalfSquare:
479 : 0 : polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
480 : 0 : return true;
481 : :
482 : : case DiagonalHalfSquare:
483 : 0 : polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
484 : 0 : return true;
485 : :
486 : : case Diamond:
487 : 0 : polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
488 : 0 : << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
489 : 0 : return true;
490 : :
491 : : case Pentagon:
492 : : /* angular-representation of hardcoded values used
493 : : polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
494 : : << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
495 : : << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
496 : : << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
497 : : << QPointF( 0, -1 ); */
498 : 0 : polygon << QPointF( -0.9511, -0.3090 )
499 : 0 : << QPointF( -0.5878, 0.8090 )
500 : 0 : << QPointF( 0.5878, 0.8090 )
501 : 0 : << QPointF( 0.9511, -0.3090 )
502 : 0 : << QPointF( 0, -1 )
503 : 0 : << QPointF( -0.9511, -0.3090 );
504 : 0 : return true;
505 : :
506 : : case Hexagon:
507 : : /* angular-representation of hardcoded values used
508 : : polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
509 : : << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
510 : : << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
511 : : << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
512 : : << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
513 : : << QPointF( 0, -1 ); */
514 : 0 : polygon << QPointF( -0.8660, -0.5 )
515 : 0 : << QPointF( -0.8660, 0.5 )
516 : 0 : << QPointF( 0, 1 )
517 : 0 : << QPointF( 0.8660, 0.5 )
518 : 0 : << QPointF( 0.8660, -0.5 )
519 : 0 : << QPointF( 0, -1 )
520 : 0 : << QPointF( -0.8660, -0.5 );
521 : 0 : return true;
522 : :
523 : : case Octagon:
524 : : {
525 : : static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
526 : :
527 : 0 : polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
528 : 0 : << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
529 : 0 : << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
530 : 0 : << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
531 : 0 : << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
532 : 0 : << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
533 : 0 : << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
534 : 0 : << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
535 : 0 : << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
536 : 0 : return true;
537 : : }
538 : :
539 : : case Triangle:
540 : 0 : polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
541 : 0 : return true;
542 : :
543 : : case EquilateralTriangle:
544 : : /* angular-representation of hardcoded values used
545 : : polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
546 : : << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
547 : : << QPointF( 0, -1 ); */
548 : 0 : polygon << QPointF( -0.8660, 0.5 )
549 : 0 : << QPointF( 0.8660, 0.5 )
550 : 0 : << QPointF( 0, -1 )
551 : 0 : << QPointF( -0.8660, 0.5 );
552 : 0 : return true;
553 : :
554 : : case LeftHalfTriangle:
555 : 0 : polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
556 : 0 : return true;
557 : :
558 : : case RightHalfTriangle:
559 : 0 : polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
560 : 0 : return true;
561 : :
562 : : case Star:
563 : : {
564 : 0 : double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
565 : :
566 : 0 : polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
567 : 0 : << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
568 : 0 : << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
569 : 0 : << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
570 : 0 : << QPointF( 0, inner_r ) // 180
571 : 0 : << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
572 : 0 : << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
573 : 0 : << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
574 : 0 : << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
575 : 0 : << QPointF( 0, -1 )
576 : 0 : << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
577 : 0 : return true;
578 : : }
579 : :
580 : : case Arrow:
581 : 0 : polygon << QPointF( 0, -1 )
582 : 0 : << QPointF( 0.5, -0.5 )
583 : 0 : << QPointF( 0.25, -0.5 )
584 : 0 : << QPointF( 0.25, 1 )
585 : 0 : << QPointF( -0.25, 1 )
586 : 0 : << QPointF( -0.25, -0.5 )
587 : 0 : << QPointF( -0.5, -0.5 )
588 : 0 : << QPointF( 0, -1 );
589 : 0 : return true;
590 : :
591 : : case ArrowHeadFilled:
592 : 0 : polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
593 : 0 : return true;
594 : :
595 : : case CrossFill:
596 : 0 : polygon << QPointF( -1, -0.2 )
597 : 0 : << QPointF( -1, -0.2 )
598 : 0 : << QPointF( -1, 0.2 )
599 : 0 : << QPointF( -0.2, 0.2 )
600 : 0 : << QPointF( -0.2, 1 )
601 : 0 : << QPointF( 0.2, 1 )
602 : 0 : << QPointF( 0.2, 0.2 )
603 : 0 : << QPointF( 1, 0.2 )
604 : 0 : << QPointF( 1, -0.2 )
605 : 0 : << QPointF( 0.2, -0.2 )
606 : 0 : << QPointF( 0.2, -1 )
607 : 0 : << QPointF( -0.2, -1 )
608 : 0 : << QPointF( -0.2, -0.2 )
609 : 0 : << QPointF( -1, -0.2 );
610 : 0 : return true;
611 : :
612 : : case AsteriskFill:
613 : : {
614 : : static constexpr double THICKNESS = 0.3;
615 : : static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
616 : : static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
617 : : static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
618 : : static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
619 : :
620 : 0 : polygon << QPointF( -HALF_THICKNESS, -1 )
621 : 0 : << QPointF( HALF_THICKNESS, -1 )
622 : 0 : << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
623 : 0 : << QPointF( DIAGONAL1, -DIAGONAL2 )
624 : 0 : << QPointF( DIAGONAL2, -DIAGONAL1 )
625 : 0 : << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
626 : 0 : << QPointF( 1, -HALF_THICKNESS )
627 : 0 : << QPointF( 1, HALF_THICKNESS )
628 : 0 : << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
629 : 0 : << QPointF( DIAGONAL2, DIAGONAL1 )
630 : 0 : << QPointF( DIAGONAL1, DIAGONAL2 )
631 : 0 : << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
632 : 0 : << QPointF( HALF_THICKNESS, 1 )
633 : 0 : << QPointF( -HALF_THICKNESS, 1 )
634 : 0 : << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
635 : 0 : << QPointF( -DIAGONAL1, DIAGONAL2 )
636 : 0 : << QPointF( -DIAGONAL2, DIAGONAL1 )
637 : 0 : << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
638 : 0 : << QPointF( -1, HALF_THICKNESS )
639 : 0 : << QPointF( -1, -HALF_THICKNESS )
640 : 0 : << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
641 : 0 : << QPointF( -DIAGONAL2, -DIAGONAL1 )
642 : 0 : << QPointF( -DIAGONAL1, -DIAGONAL2 )
643 : 0 : << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
644 : 0 : << QPointF( -HALF_THICKNESS, -1 );
645 : 0 : return true;
646 : : }
647 : :
648 : : case Circle:
649 : : case Cross:
650 : : case Cross2:
651 : : case Line:
652 : : case ArrowHead:
653 : : case SemiCircle:
654 : : case ThirdCircle:
655 : : case QuarterCircle:
656 : : case HalfArc:
657 : : case ThirdArc:
658 : : case QuarterArc:
659 : 0 : return false;
660 : : }
661 : :
662 : 0 : return false;
663 : 0 : }
664 : :
665 : 0 : bool QgsSimpleMarkerSymbolLayerBase::prepareMarkerPath( QgsSimpleMarkerSymbolLayerBase::Shape symbol )
666 : : {
667 : 0 : mPath = QPainterPath();
668 : :
669 : 0 : switch ( symbol )
670 : : {
671 : : case Circle:
672 : :
673 : 0 : mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
674 : 0 : return true;
675 : :
676 : : case SemiCircle:
677 : 0 : mPath.arcTo( -1, -1, 2, 2, 0, 180 );
678 : 40 : mPath.lineTo( 0, 0 );
679 : 0 : return true;
680 : 40 :
681 : : case ThirdCircle:
682 : 0 : mPath.arcTo( -1, -1, 2, 2, 90, 120 );
683 : 40 : mPath.lineTo( 0, 0 );
684 : 0 : return true;
685 : :
686 : : case QuarterCircle:
687 : 0 : mPath.arcTo( -1, -1, 2, 2, 90, 90 );
688 : 0 : mPath.lineTo( 0, 0 );
689 : 0 : return true;
690 : :
691 : : case HalfArc:
692 : 0 : mPath.moveTo( 1, 0 );
693 : 0 : mPath.arcTo( -1, -1, 2, 2, 0, 180 );
694 : 0 : return true;
695 : :
696 : : case ThirdArc:
697 : 0 : mPath.moveTo( 0, -1 );
698 : 0 : mPath.arcTo( -1, -1, 2, 2, 90, 120 );
699 : 0 : return true;
700 : :
701 : : case QuarterArc:
702 : 0 : mPath.moveTo( 0, -1 );
703 : 0 : mPath.arcTo( -1, -1, 2, 2, 90, 90 );
704 : 0 : return true;
705 : :
706 : : case Cross:
707 : 0 : mPath.moveTo( -1, 0 );
708 : 0 : mPath.lineTo( 1, 0 ); // horizontal
709 : 0 : mPath.moveTo( 0, -1 );
710 : 0 : mPath.lineTo( 0, 1 ); // vertical
711 : 0 : return true;
712 : :
713 : : case Cross2:
714 : 0 : mPath.moveTo( -1, -1 );
715 : 0 : mPath.lineTo( 1, 1 );
716 : 0 : mPath.moveTo( 1, -1 );
717 : 0 : mPath.lineTo( -1, 1 );
718 : 0 : return true;
719 : :
720 : : case Line:
721 : 0 : mPath.moveTo( 0, -1 );
722 : 0 : mPath.lineTo( 0, 1 ); // vertical line
723 : 0 : return true;
724 : :
725 : : case ArrowHead:
726 : 0 : mPath.moveTo( -1, -1 );
727 : 0 : mPath.lineTo( 0, 0 );
728 : 0 : mPath.lineTo( -1, 1 );
729 : 0 : return true;
730 : :
731 : : case Square:
732 : : case SquareWithCorners:
733 : : case QuarterSquare:
734 : : case HalfSquare:
735 : : case DiagonalHalfSquare:
736 : : case Diamond:
737 : : case Pentagon:
738 : : case Hexagon:
739 : : case Octagon:
740 : : case Triangle:
741 : : case EquilateralTriangle:
742 : : case LeftHalfTriangle:
743 : : case RightHalfTriangle:
744 : : case Star:
745 : : case Arrow:
746 : : case ArrowHeadFilled:
747 : : case CrossFill:
748 : : case AsteriskFill:
749 : 0 : return false;
750 : : }
751 : 0 : return false;
752 : 0 : }
753 : :
754 : 0 : double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
755 : : {
756 : 0 : double scaledSize = mSize;
757 : :
758 : 0 : hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
759 : 0 : bool ok = true;
760 : 0 : if ( hasDataDefinedSize )
761 : : {
762 : 0 : context.setOriginalValueVariable( mSize );
763 : 0 : scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertySize, context.renderContext().expressionContext(),
764 : 0 : mSize, &ok );
765 : 0 : }
766 : :
767 : 0 : if ( hasDataDefinedSize && ok )
768 : : {
769 : 0 : switch ( mScaleMethod )
770 : : {
771 : : case QgsSymbol::ScaleArea:
772 : 0 : scaledSize = std::sqrt( scaledSize );
773 : 0 : break;
774 : : case QgsSymbol::ScaleDiameter:
775 : 0 : break;
776 : : }
777 : 0 : }
778 : :
779 : 0 : return scaledSize;
780 : 0 : }
781 : :
782 : 0 : void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
783 : : {
784 : : //offset
785 : 0 : double offsetX = 0;
786 : 0 : double offsetY = 0;
787 : 0 : markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
788 : 0 : offset = QPointF( offsetX, offsetY );
789 : :
790 : 0 : hasDataDefinedRotation = false;
791 : : //angle
792 : 0 : bool ok = true;
793 : 0 : angle = mAngle + mLineAngle;
794 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle ) )
795 : : {
796 : 0 : context.setOriginalValueVariable( angle );
797 : 0 : angle = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyAngle, context.renderContext().expressionContext(), 0, &ok ) + mLineAngle;
798 : :
799 : : // If the expression evaluation was not successful, fallback to static value
800 : 0 : if ( !ok )
801 : 0 : angle = mAngle + mLineAngle;
802 : :
803 : 0 : hasDataDefinedRotation = true;
804 : 0 : }
805 : :
806 : 0 : hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || hasDataDefinedRotation;
807 : :
808 : 0 : if ( hasDataDefinedRotation )
809 : : {
810 : : // For non-point markers, "dataDefinedRotation" means following the
811 : : // shape (shape-data defined). For them, "field-data defined" does
812 : : // not work at all. TODO: if "field-data defined" ever gets implemented
813 : : // we'll need a way to distinguish here between the two, possibly
814 : : // using another flag in renderHints()
815 : 0 : const QgsFeature *f = context.feature();
816 : 0 : if ( f )
817 : : {
818 : 0 : if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
819 : : {
820 : 0 : const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
821 : 0 : angle += m2p.mapRotation();
822 : 0 : }
823 : 0 : }
824 : 0 : }
825 : :
826 : 0 : if ( angle )
827 : 0 : offset = _rotatedOffset( offset, angle );
828 : 0 : }
829 : :
830 : 0 :
831 : : //
832 : 0 : // QgsSimpleMarkerSymbolLayer
833 : : //
834 : 0 :
835 : 332 : QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( QgsSimpleMarkerSymbolLayerBase::Shape shape, double size, double angle, QgsSymbol::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
836 : 332 : : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
837 : 332 : , mStrokeColor( strokeColor )
838 : 332 : , mPenJoinStyle( penJoinStyle )
839 : 664 : {
840 : 332 : mColor = color;
841 : 332 : }
842 : :
843 : 230 : QgsSymbolLayer *QgsSimpleMarkerSymbolLayer::create( const QVariantMap &props )
844 : : {
845 : 230 : Shape shape = Circle;
846 : 230 : QColor color = DEFAULT_SIMPLEMARKER_COLOR;
847 : 230 : QColor strokeColor = DEFAULT_SIMPLEMARKER_BORDERCOLOR;
848 : 230 : Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEMARKER_JOINSTYLE;
849 : 230 : double size = DEFAULT_SIMPLEMARKER_SIZE;
850 : 230 : double angle = DEFAULT_SIMPLEMARKER_ANGLE;
851 : 230 : QgsSymbol::ScaleMethod scaleMethod = DEFAULT_SCALE_METHOD;
852 : :
853 : 460 : if ( props.contains( QStringLiteral( "name" ) ) )
854 : : {
855 : 460 : shape = decodeShape( props[QStringLiteral( "name" )].toString() );
856 : 230 : }
857 : 460 : if ( props.contains( QStringLiteral( "color" ) ) )
858 : 460 : color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
859 : 460 : if ( props.contains( QStringLiteral( "color_border" ) ) )
860 : : {
861 : : //pre 2.5 projects use "color_border"
862 : 0 : strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )].toString() );
863 : 0 : }
864 : 460 : else if ( props.contains( QStringLiteral( "outline_color" ) ) )
865 : : {
866 : 460 : strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() );
867 : 230 : }
868 : 0 : else if ( props.contains( QStringLiteral( "line_color" ) ) )
869 : : {
870 : 0 : strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() );
871 : 0 : }
872 : 460 : if ( props.contains( QStringLiteral( "joinstyle" ) ) )
873 : : {
874 : 460 : penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
875 : 230 : }
876 : 460 : if ( props.contains( QStringLiteral( "size" ) ) )
877 : 460 : size = props[QStringLiteral( "size" )].toDouble();
878 : 460 : if ( props.contains( QStringLiteral( "angle" ) ) )
879 : 460 : angle = props[QStringLiteral( "angle" )].toDouble();
880 : 460 : if ( props.contains( QStringLiteral( "scale_method" ) ) )
881 : 460 : scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
882 : :
883 : 230 : QgsSimpleMarkerSymbolLayer *m = new QgsSimpleMarkerSymbolLayer( shape, size, angle, scaleMethod, color, strokeColor, penJoinStyle );
884 : 460 : if ( props.contains( QStringLiteral( "offset" ) ) )
885 : 460 : m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
886 : 460 : if ( props.contains( QStringLiteral( "offset_unit" ) ) )
887 : 460 : m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
888 : 460 : if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
889 : 460 : m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
890 : 460 : if ( props.contains( QStringLiteral( "size_unit" ) ) )
891 : 460 : m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
892 : 460 : if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
893 : 460 : m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
894 : :
895 : 460 : if ( props.contains( QStringLiteral( "outline_style" ) ) )
896 : : {
897 : 460 : m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
898 : 230 : }
899 : 0 : else if ( props.contains( QStringLiteral( "line_style" ) ) )
900 : : {
901 : 0 : m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
902 : 0 : }
903 : 460 : if ( props.contains( QStringLiteral( "outline_width" ) ) )
904 : : {
905 : 460 : m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
906 : 230 : }
907 : 0 : else if ( props.contains( QStringLiteral( "line_width" ) ) )
908 : : {
909 : 0 : m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
910 : 0 : }
911 : 460 : if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
912 : : {
913 : 460 : m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
914 : 230 : }
915 : 460 : if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
916 : : {
917 : 0 : m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
918 : 0 : }
919 : 460 : if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
920 : : {
921 : 460 : m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
922 : 230 : }
923 : :
924 : 460 : if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
925 : : {
926 : 460 : m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
927 : 230 : }
928 : 460 : if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
929 : : {
930 : 460 : m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
931 : 230 : }
932 : :
933 : 460 : if ( props.contains( QStringLiteral( "cap_style" ) ) )
934 : : {
935 : 276 : m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
936 : 138 : }
937 : :
938 : 230 : m->restoreOldDataDefinedProperties( props );
939 : :
940 : 230 : return m;
941 : 0 : }
942 : :
943 : :
944 : 0 : QString QgsSimpleMarkerSymbolLayer::layerType() const
945 : : {
946 : 0 : return QStringLiteral( "SimpleMarker" );
947 : : }
948 : :
949 : 0 : void QgsSimpleMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context )
950 : : {
951 : 0 : QgsSimpleMarkerSymbolLayerBase::startRender( context );
952 : :
953 : 0 : QColor brushColor = mColor;
954 : 0 : QColor penColor = mStrokeColor;
955 : :
956 : 0 : brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
957 : 0 : penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
958 : :
959 : 0 : mBrush = QBrush( brushColor );
960 : 0 : mPen = QPen( penColor );
961 : 0 : mPen.setStyle( mStrokeStyle );
962 : 0 : mPen.setCapStyle( mPenCapStyle );
963 : 0 : mPen.setJoinStyle( mPenJoinStyle );
964 : 0 : mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
965 : :
966 : 0 : QColor selBrushColor = context.renderContext().selectionColor();
967 : 0 : QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
968 : 0 : if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
969 : : {
970 : 0 : selBrushColor.setAlphaF( context.opacity() );
971 : 0 : selPenColor.setAlphaF( context.opacity() );
972 : 0 : }
973 : 0 : mSelBrush = QBrush( selBrushColor );
974 : 0 : mSelPen = QPen( selPenColor );
975 : 0 : mSelPen.setStyle( mStrokeStyle );
976 : 0 : mSelPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
977 : :
978 : 0 : bool hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle );
979 : 0 : bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
980 : :
981 : : // use caching only when:
982 : : // - size, rotation, shape, color, stroke color is not data-defined
983 : : // - drawing to screen (not printer)
984 : 0 : mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
985 : 0 : && !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFillColor ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeColor )
986 : 0 : && !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeWidth ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeStyle )
987 : 0 : && !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyJoinStyle );
988 : :
989 : 0 : if ( mUsingCache )
990 : 0 : mCachedOpacity = context.opacity();
991 : :
992 : 0 : if ( !shapeIsFilled( mShape ) )
993 : : {
994 : : // some markers can't be drawn as a polygon (circle, cross)
995 : : // For these set the selected stroke color to the selected color
996 : 0 : mSelPen.setColor( selBrushColor );
997 : 0 : }
998 : :
999 : :
1000 : 0 : if ( mUsingCache )
1001 : : {
1002 : 0 : if ( !prepareCache( context ) )
1003 : : {
1004 : 0 : mUsingCache = false;
1005 : 0 : }
1006 : 0 : }
1007 : : else
1008 : : {
1009 : 0 : mCache = QImage();
1010 : 0 : mSelCache = QImage();
1011 : : }
1012 : 0 : }
1013 : :
1014 : :
1015 : 0 : bool QgsSimpleMarkerSymbolLayer::prepareCache( QgsSymbolRenderContext &context )
1016 : : {
1017 : 0 : double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1018 : 0 : if ( mSizeUnit == QgsUnitTypes::RenderMetersInMapUnits && context.renderContext().flags() & QgsRenderContext::RenderSymbolPreview )
1019 : : {
1020 : : // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1021 : : // and clamp it to a reasonable range. It's the best we can do in this situation!
1022 : 0 : scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
1023 : 0 : }
1024 : :
1025 : : // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1026 : 0 : if ( !qgsDoubleNear( mAngle, 0.0 ) )
1027 : : {
1028 : 0 : scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1029 : 0 : }
1030 : : // calculate necessary image size for the cache
1031 : 0 : double pw = static_cast< int >( std::round( ( ( qgsDoubleNear( mPen.widthF(), 0.0 ) ? 1 : mPen.widthF() * 4 ) + 1 ) ) ) / 2 * 2; // make even (round up); handle cosmetic pen
1032 : 0 : int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1033 : 0 : double center = imageSize / 2.0;
1034 : 0 : if ( imageSize > MAXIMUM_CACHE_WIDTH )
1035 : : {
1036 : 0 : return false;
1037 : : }
1038 : :
1039 : 0 : mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1040 : 0 : mCache.fill( 0 );
1041 : :
1042 : 0 : bool needsBrush = shapeIsFilled( mShape );
1043 : 0 :
1044 : 0 : QPainter p;
1045 : 0 : p.begin( &mCache );
1046 : 0 : p.setRenderHint( QPainter::Antialiasing );
1047 : 0 : p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1048 : 0 : p.setPen( mPen );
1049 : 0 : p.translate( QPointF( center, center ) );
1050 : 0 : drawMarker( &p, context );
1051 : 0 : p.end();
1052 : :
1053 : : // Construct the selected version of the Cache
1054 : :
1055 : 0 : QColor selColor = context.renderContext().selectionColor();
1056 : 0 :
1057 : 0 : mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1058 : 0 : mSelCache.fill( 0 );
1059 : :
1060 : 0 : p.begin( &mSelCache );
1061 : 0 : p.setRenderHint( QPainter::Antialiasing );
1062 : 0 : p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1063 : 0 : p.setPen( mSelPen );
1064 : 0 : p.translate( QPointF( center, center ) );
1065 : 0 : drawMarker( &p, context );
1066 : 0 : p.end();
1067 : :
1068 : : // Check that the selected version is different. If not, then re-render,
1069 : : // filling the background with the selection color and using the normal
1070 : : // colors for the symbol .. could be ugly!
1071 : :
1072 : 0 : if ( mSelCache == mCache )
1073 : : {
1074 : 0 : p.begin( &mSelCache );
1075 : 0 : p.setRenderHint( QPainter::Antialiasing );
1076 : 0 : p.fillRect( 0, 0, imageSize, imageSize, selColor );
1077 : 0 : p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1078 : 0 : p.setPen( mPen );
1079 : 0 : p.translate( QPointF( center, center ) );
1080 : 0 : drawMarker( &p, context );
1081 : 0 : p.end();
1082 : 0 : }
1083 : :
1084 : 0 : return true;
1085 : 0 : }
1086 : :
1087 : 0 : void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1088 : : {
1089 : : //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1090 : : //of the rendered point!
1091 : :
1092 : 0 : QPainter *p = context.renderContext().painter();
1093 : 0 : if ( !p )
1094 : : {
1095 : 0 : return;
1096 : : }
1097 : :
1098 : 0 : QColor brushColor = mColor;
1099 : 0 : brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1100 : 0 : mBrush.setColor( brushColor );
1101 : :
1102 : 0 : QColor penColor = mStrokeColor;
1103 : 0 : penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1104 : 0 : mPen.setColor( penColor );
1105 : :
1106 : 0 : bool ok = true;
1107 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFillColor ) )
1108 : : {
1109 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mColor ) );
1110 : 0 : QColor c = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyFillColor, context.renderContext().expressionContext(), mColor, &ok );
1111 : 0 : if ( ok )
1112 : : {
1113 : 0 : c.setAlphaF( c.alphaF() * context.opacity() );
1114 : 0 : mBrush.setColor( c );
1115 : 0 : }
1116 : 0 : }
1117 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeColor ) )
1118 : : {
1119 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mStrokeColor ) );
1120 : 0 : QColor c = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyStrokeColor, context.renderContext().expressionContext(), mStrokeColor, &ok );
1121 : 0 : if ( ok )
1122 : : {
1123 : 0 : c.setAlphaF( c.alphaF() * context.opacity() );
1124 : 0 : mPen.setColor( c );
1125 : 0 : mSelPen.setColor( c );
1126 : 0 : }
1127 : 0 : }
1128 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
1129 : : {
1130 : 0 : context.setOriginalValueVariable( mStrokeWidth );
1131 : 0 : double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), 0, &ok );
1132 : 0 : if ( ok )
1133 : : {
1134 : 0 : mPen.setWidthF( context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
1135 : 0 : mSelPen.setWidthF( context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
1136 : 0 : }
1137 : 0 : }
1138 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeStyle ) )
1139 : : {
1140 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle ) );
1141 : 0 : QString strokeStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyStrokeStyle, context.renderContext().expressionContext(), QString(), &ok );
1142 : 0 : if ( ok )
1143 : : {
1144 : 0 : mPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
1145 : 0 : mSelPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
1146 : 0 : }
1147 : 0 : }
1148 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyJoinStyle ) )
1149 : : {
1150 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle ) );
1151 : 0 : QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyJoinStyle, context.renderContext().expressionContext(), QString(), &ok );
1152 : 0 : if ( ok )
1153 : : {
1154 : 0 : mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1155 : 0 : mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1156 : 0 : }
1157 : 0 : }
1158 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyCapStyle ) )
1159 : : {
1160 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle ) );
1161 : 0 : QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCapStyle, context.renderContext().expressionContext(), QString(), &ok );
1162 : 0 : if ( ok )
1163 : : {
1164 : 0 : mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1165 : 0 : mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1166 : 0 : }
1167 : 0 : }
1168 : :
1169 : 0 : if ( shapeIsFilled( shape ) )
1170 : : {
1171 : 0 : p->setBrush( context.selected() ? mSelBrush : mBrush );
1172 : 0 : }
1173 : : else
1174 : : {
1175 : 0 : p->setBrush( Qt::NoBrush );
1176 : : }
1177 : 0 : p->setPen( context.selected() ? mSelPen : mPen );
1178 : :
1179 : 0 : if ( !polygon.isEmpty() )
1180 : 0 : p->drawPolygon( polygon );
1181 : : else
1182 : 0 : p->drawPath( path );
1183 : 0 : }
1184 : :
1185 : 0 : void QgsSimpleMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext &context )
1186 : : {
1187 : : //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1188 : : //of the rendered point!
1189 : :
1190 : 0 : QPainter *p = context.renderContext().painter();
1191 : 0 : if ( !p )
1192 : : {
1193 : 0 : return;
1194 : : }
1195 : :
1196 : 0 : if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1197 : : {
1198 : 0 : QImage &img = context.selected() ? mSelCache : mCache;
1199 : 0 : double s = img.width();
1200 : :
1201 : 0 : bool hasDataDefinedSize = false;
1202 : 0 : double scaledSize = calculateSize( context, hasDataDefinedSize );
1203 : :
1204 : 0 : bool hasDataDefinedRotation = false;
1205 : 0 : QPointF offset;
1206 : 0 : double angle = 0;
1207 : 0 : calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1208 : :
1209 : 0 : p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1210 : 0 : point.y() - s / 2.0 + offset.y(),
1211 : 0 : s, s ), img );
1212 : 0 : }
1213 : : else
1214 : : {
1215 : 0 : QgsSimpleMarkerSymbolLayerBase::renderPoint( point, context );
1216 : : }
1217 : 0 : }
1218 : :
1219 : 0 : QVariantMap QgsSimpleMarkerSymbolLayer::properties() const
1220 : : {
1221 : 0 : QVariantMap map;
1222 : 0 : map[QStringLiteral( "name" )] = encodeShape( mShape );
1223 : 0 : map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1224 : 0 : map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1225 : 0 : map[QStringLiteral( "size" )] = QString::number( mSize );
1226 : 0 : map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1227 : 0 : map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1228 : 0 : map[QStringLiteral( "angle" )] = QString::number( mAngle );
1229 : 0 : map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1230 : 0 : map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1231 : 0 : map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1232 : 0 : map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1233 : 0 : map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1234 : 0 : map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1235 : 0 : map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1236 : 0 : map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1237 : 0 : map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1238 : 0 : map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1239 : 0 : map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1240 : 0 : map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1241 : 0 : return map;
1242 : 0 : }
1243 : :
1244 : 0 : QgsSimpleMarkerSymbolLayer *QgsSimpleMarkerSymbolLayer::clone() const
1245 : : {
1246 : 0 : QgsSimpleMarkerSymbolLayer *m = new QgsSimpleMarkerSymbolLayer( mShape, mSize, mAngle, mScaleMethod, mColor, mStrokeColor, mPenJoinStyle );
1247 : 0 : m->setOffset( mOffset );
1248 : 0 : m->setSizeUnit( mSizeUnit );
1249 : 0 : m->setSizeMapUnitScale( mSizeMapUnitScale );
1250 : 0 : m->setOffsetUnit( mOffsetUnit );
1251 : 0 : m->setOffsetMapUnitScale( mOffsetMapUnitScale );
1252 : 0 : m->setStrokeStyle( mStrokeStyle );
1253 : 0 : m->setStrokeWidth( mStrokeWidth );
1254 : 0 : m->setStrokeWidthUnit( mStrokeWidthUnit );
1255 : 0 : m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
1256 : 0 : m->setHorizontalAnchorPoint( mHorizontalAnchorPoint );
1257 : 0 : m->setVerticalAnchorPoint( mVerticalAnchorPoint );
1258 : 0 : m->setPenCapStyle( mPenCapStyle );
1259 : 0 : copyDataDefinedProperties( m );
1260 : 0 : copyPaintEffect( m );
1261 : 0 : return m;
1262 : 0 : }
1263 : :
1264 : 0 : void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1265 : : {
1266 : : // <Graphic>
1267 : 0 : QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1268 : 0 : element.appendChild( graphicElem );
1269 : :
1270 : 0 : double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
1271 : 0 : double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1272 : 0 : QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, encodeShape( mShape ), mColor, mStrokeColor, mStrokeStyle, strokeWidth, size );
1273 : :
1274 : : // <Rotation>
1275 : 0 : QString angleFunc;
1276 : : bool ok;
1277 : 0 : double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1278 : 0 : if ( !ok )
1279 : : {
1280 : 0 : angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1281 : 0 : }
1282 : 0 : else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1283 : : {
1284 : 0 : angleFunc = QString::number( angle + mAngle );
1285 : 0 : }
1286 : 0 : QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1287 : :
1288 : : // <Displacement>
1289 : 0 : QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1290 : 0 : QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
1291 : 0 : }
1292 : :
1293 : 0 : QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1294 : : {
1295 : : Q_UNUSED( mmScaleFactor )
1296 : : Q_UNUSED( mapUnitScaleFactor )
1297 : : #if 0
1298 : : QString ogrType = "3"; //default is circle
1299 : : if ( mName == "square" )
1300 : : {
1301 : : ogrType = "5";
1302 : : }
1303 : : else if ( mName == "triangle" )
1304 : : {
1305 : : ogrType = "7";
1306 : : }
1307 : : else if ( mName == "star" )
1308 : : {
1309 : : ogrType = "9";
1310 : : }
1311 : : else if ( mName == "circle" )
1312 : : {
1313 : : ogrType = "3";
1314 : : }
1315 : : else if ( mName == "cross" )
1316 : : {
1317 : : ogrType = "0";
1318 : : }
1319 : : else if ( mName == "x" || mName == "cross2" )
1320 : : {
1321 : : ogrType = "1";
1322 : : }
1323 : : else if ( mName == "line" )
1324 : : {
1325 : : ogrType = "10";
1326 : : }
1327 : :
1328 : : QString ogrString;
1329 : : ogrString.append( "SYMBOL(" );
1330 : : ogrString.append( "id:" );
1331 : : ogrString.append( '\"' );
1332 : : ogrString.append( "ogr-sym-" );
1333 : : ogrString.append( ogrType );
1334 : : ogrString.append( '\"' );
1335 : : ogrString.append( ",c:" );
1336 : : ogrString.append( mColor.name() );
1337 : : ogrString.append( ",o:" );
1338 : : ogrString.append( mStrokeColor.name() );
1339 : : ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1340 : : ogrString.append( ')' );
1341 : : return ogrString;
1342 : : #endif //0
1343 : :
1344 : 0 : QString ogrString;
1345 : 0 : ogrString.append( "PEN(" );
1346 : 0 : ogrString.append( "c:" );
1347 : 0 : ogrString.append( mColor.name() );
1348 : 0 : ogrString.append( ",w:" );
1349 : 0 : ogrString.append( QString::number( mSize ) );
1350 : 0 : ogrString.append( "mm" );
1351 : 0 : ogrString.append( ")" );
1352 : 0 : return ogrString;
1353 : 0 : }
1354 : :
1355 : 0 : QgsSymbolLayer *QgsSimpleMarkerSymbolLayer::createFromSld( QDomElement &element )
1356 : : {
1357 : 0 : QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1358 : :
1359 : 0 : QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1360 : 0 : if ( graphicElem.isNull() )
1361 : 0 : return nullptr;
1362 : :
1363 : 0 : QString name = QStringLiteral( "square" );
1364 : 0 : QColor color, strokeColor;
1365 : : double strokeWidth, size;
1366 : : Qt::PenStyle strokeStyle;
1367 : :
1368 : 0 : if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, color, strokeColor, strokeStyle, strokeWidth, size ) )
1369 : 0 : return nullptr;
1370 : :
1371 : 0 : double angle = 0.0;
1372 : 0 : QString angleFunc;
1373 : 0 : if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1374 : : {
1375 : : bool ok;
1376 : 0 : double d = angleFunc.toDouble( &ok );
1377 : 0 : if ( ok )
1378 : 0 : angle = d;
1379 : 0 : }
1380 : :
1381 : 0 : QPointF offset;
1382 : 0 : QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
1383 : :
1384 : 0 : Shape shape = decodeShape( name );
1385 : :
1386 : 0 : QString uom = element.attribute( QStringLiteral( "uom" ) );
1387 : 0 : size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
1388 : 0 : offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
1389 : 0 : offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
1390 : :
1391 : 0 : QgsSimpleMarkerSymbolLayer *m = new QgsSimpleMarkerSymbolLayer( shape, size );
1392 : 0 : m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1393 : 0 : m->setColor( color );
1394 : 0 : m->setStrokeColor( strokeColor );
1395 : 0 : m->setAngle( angle );
1396 : 0 : m->setOffset( offset );
1397 : 0 : m->setStrokeStyle( strokeStyle );
1398 : 0 : m->setStrokeWidth( strokeWidth );
1399 : 0 : return m;
1400 : 0 : }
1401 : :
1402 : 0 : void QgsSimpleMarkerSymbolLayer::drawMarker( QPainter *p, QgsSymbolRenderContext &context )
1403 : : {
1404 : 0 : Q_UNUSED( context )
1405 : :
1406 : 0 : if ( mPolygon.count() != 0 )
1407 : : {
1408 : 0 : p->drawPolygon( mPolygon );
1409 : 0 : }
1410 : : else
1411 : : {
1412 : 0 : p->drawPath( mPath );
1413 : : }
1414 : 0 : }
1415 : :
1416 : 0 : bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1417 : : {
1418 : : //data defined size?
1419 : 0 : double size = mSize;
1420 : :
1421 : 0 : bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1422 : :
1423 : : //data defined size
1424 : 0 : bool ok = true;
1425 : 0 : if ( hasDataDefinedSize )
1426 : : {
1427 : 0 : size = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertySize, context.renderContext().expressionContext(), mSize, &ok );
1428 : :
1429 : 0 : if ( ok )
1430 : : {
1431 : 0 : switch ( mScaleMethod )
1432 : : {
1433 : : case QgsSymbol::ScaleArea:
1434 : 0 : size = std::sqrt( size );
1435 : 0 : break;
1436 : : case QgsSymbol::ScaleDiameter:
1437 : 0 : break;
1438 : : }
1439 : 0 : }
1440 : :
1441 : 0 : size *= e.mapUnitScaleFactor( e.symbologyScale(), mSizeUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
1442 : 0 : }
1443 : :
1444 : 0 : if ( mSizeUnit == QgsUnitTypes::RenderMillimeters )
1445 : : {
1446 : 0 : size *= mmMapUnitScaleFactor;
1447 : 0 : }
1448 : :
1449 : 0 : if ( mSizeUnit == QgsUnitTypes::RenderMapUnits )
1450 : : {
1451 : 0 : e.clipValueToMapUnitScale( size, mSizeMapUnitScale, context.renderContext().scaleFactor() );
1452 : 0 : }
1453 : 0 : double halfSize = size / 2.0;
1454 : :
1455 : : //strokeWidth
1456 : 0 : double strokeWidth = mStrokeWidth;
1457 : :
1458 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
1459 : : {
1460 : 0 : context.setOriginalValueVariable( mStrokeWidth );
1461 : 0 : strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth );
1462 : 0 : }
1463 : 0 : strokeWidth *= e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
1464 : 0 : if ( mSizeUnit == QgsUnitTypes::RenderMapUnits )
1465 : : {
1466 : 0 : e.clipValueToMapUnitScale( strokeWidth, mStrokeWidthMapUnitScale, context.renderContext().scaleFactor() );
1467 : 0 : }
1468 : :
1469 : : //color
1470 : 0 : QColor pc = mPen.color();
1471 : 0 : QColor bc = mBrush.color();
1472 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFillColor ) )
1473 : : {
1474 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mColor ) );
1475 : 0 : bc = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyFillColor, context.renderContext().expressionContext(), bc );
1476 : 0 : }
1477 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeColor ) )
1478 : : {
1479 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mStrokeColor ) );
1480 : 0 : pc = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyStrokeColor, context.renderContext().expressionContext(), pc );
1481 : 0 : }
1482 : :
1483 : : //offset
1484 : 0 : double offsetX = 0;
1485 : 0 : double offsetY = 0;
1486 : 0 : markerOffset( context, offsetX, offsetY );
1487 : 0 : offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1488 : 0 : offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1489 : :
1490 : :
1491 : 0 : QPointF off( offsetX, offsetY );
1492 : :
1493 : : //angle
1494 : 0 : double angle = mAngle + mLineAngle;
1495 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle ) )
1496 : : {
1497 : 0 : context.setOriginalValueVariable( mAngle );
1498 : 0 : angle = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyAngle, context.renderContext().expressionContext(), mAngle ) + mLineAngle;
1499 : 0 : }
1500 : :
1501 : 0 : Shape shape = mShape;
1502 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName ) )
1503 : : {
1504 : 0 : context.setOriginalValueVariable( encodeShape( shape ) );
1505 : 0 : QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1506 : 0 : if ( ok )
1507 : : {
1508 : 0 : shape = decodeShape( shapeName, &ok );
1509 : 0 : if ( !ok )
1510 : 0 : shape = mShape;
1511 : 0 : }
1512 : 0 : }
1513 : :
1514 : 0 : if ( angle )
1515 : 0 : off = _rotatedOffset( off, angle );
1516 : :
1517 : 0 : off *= e.mapUnitScaleFactor( e.symbologyScale(), mSizeUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
1518 : :
1519 : 0 : QTransform t;
1520 : 0 : t.translate( shift.x() + off.x(), shift.y() - off.y() );
1521 : :
1522 : 0 : if ( !qgsDoubleNear( angle, 0.0 ) )
1523 : 0 : t.rotate( angle );
1524 : :
1525 : 0 : QPolygonF polygon;
1526 : 0 : if ( shapeToPolygon( shape, polygon ) )
1527 : : {
1528 : 0 : t.scale( halfSize, -halfSize );
1529 : :
1530 : 0 : polygon = t.map( polygon );
1531 : :
1532 : 0 : QgsPointSequence p;
1533 : 0 : p.reserve( polygon.size() );
1534 : 0 : for ( int i = 0; i < polygon.size(); i++ )
1535 : : {
1536 : 0 : p << QgsPoint( polygon[i] );
1537 : 0 : }
1538 : :
1539 : 0 : if ( mBrush.style() != Qt::NoBrush )
1540 : 0 : e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1541 : 0 : if ( mPen.style() != Qt::NoPen )
1542 : 0 : e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1543 : 0 : }
1544 : 0 : else if ( shape == Circle )
1545 : : {
1546 : 0 : shift += QPointF( off.x(), -off.y() );
1547 : 0 : if ( mBrush.style() != Qt::NoBrush )
1548 : 0 : e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1549 : 0 : if ( mPen.style() != Qt::NoPen )
1550 : 0 : e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1551 : 0 : }
1552 : 0 : else if ( shape == Line )
1553 : : {
1554 : 0 : QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1555 : 0 : QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1556 : :
1557 : 0 : if ( mPen.style() != Qt::NoPen )
1558 : 0 : e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1559 : 0 : }
1560 : 0 : else if ( shape == Cross )
1561 : : {
1562 : 0 : if ( mPen.style() != Qt::NoPen )
1563 : : {
1564 : 0 : QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1565 : 0 : QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1566 : 0 : QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1567 : 0 : QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1568 : :
1569 : 0 : e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1570 : 0 : e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1571 : 0 : }
1572 : 0 : }
1573 : 0 : else if ( shape == Cross2 )
1574 : : {
1575 : 0 : if ( mPen.style() != Qt::NoPen )
1576 : : {
1577 : 0 : QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1578 : 0 : QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1579 : 0 : QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1580 : 0 : QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1581 : :
1582 : 0 : e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1583 : 0 : e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1584 : 0 : }
1585 : 0 : }
1586 : 0 : else if ( shape == ArrowHead )
1587 : : {
1588 : 0 : if ( mPen.style() != Qt::NoPen )
1589 : : {
1590 : 0 : QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1591 : 0 : QPointF pt2 = t.map( QPointF( 0, 0 ) );
1592 : 0 : QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1593 : :
1594 : 0 : e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1595 : 0 : e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1596 : 0 : }
1597 : 0 : }
1598 : : else
1599 : : {
1600 : 0 : QgsDebugMsg( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1601 : 0 : return false;
1602 : : }
1603 : :
1604 : 0 : return true;
1605 : 0 : }
1606 : :
1607 : :
1608 : 0 : void QgsSimpleMarkerSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit unit )
1609 : : {
1610 : 0 : QgsMarkerSymbolLayer::setOutputUnit( unit );
1611 : 0 : mStrokeWidthUnit = unit;
1612 : 0 : }
1613 : :
1614 : 0 : QgsUnitTypes::RenderUnit QgsSimpleMarkerSymbolLayer::outputUnit() const
1615 : : {
1616 : 0 : if ( QgsMarkerSymbolLayer::outputUnit() == mStrokeWidthUnit )
1617 : : {
1618 : 0 : return mStrokeWidthUnit;
1619 : : }
1620 : 0 : return QgsUnitTypes::RenderUnknownUnit;
1621 : 0 : }
1622 : :
1623 : 0 : void QgsSimpleMarkerSymbolLayer::setMapUnitScale( const QgsMapUnitScale &scale )
1624 : : {
1625 : 0 : QgsMarkerSymbolLayer::setMapUnitScale( scale );
1626 : 0 : mStrokeWidthMapUnitScale = scale;
1627 : 0 : }
1628 : :
1629 : 0 : QgsMapUnitScale QgsSimpleMarkerSymbolLayer::mapUnitScale() const
1630 : : {
1631 : 0 : if ( QgsMarkerSymbolLayer::mapUnitScale() == mStrokeWidthMapUnitScale )
1632 : : {
1633 : 0 : return mStrokeWidthMapUnitScale;
1634 : : }
1635 : 0 : return QgsMapUnitScale();
1636 : 0 : }
1637 : :
1638 : 0 : bool QgsSimpleMarkerSymbolLayer::usesMapUnits() const
1639 : : {
1640 : 0 : return mSizeUnit == QgsUnitTypes::RenderMapUnits || mSizeUnit == QgsUnitTypes::RenderMetersInMapUnits
1641 : 0 : || mOffsetUnit == QgsUnitTypes::RenderMapUnits || mOffsetUnit == QgsUnitTypes::RenderMetersInMapUnits
1642 : 0 : || mStrokeWidthUnit == QgsUnitTypes::RenderMapUnits || mStrokeWidthUnit == QgsUnitTypes::RenderMetersInMapUnits;
1643 : : }
1644 : :
1645 : 0 : QRectF QgsSimpleMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &context )
1646 : : {
1647 : 0 : QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1648 : :
1649 : : // need to account for stroke width
1650 : 0 : double penWidth = mStrokeWidth;
1651 : 0 : bool ok = true;
1652 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
1653 : : {
1654 : 0 : context.setOriginalValueVariable( mStrokeWidth );
1655 : 0 : double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
1656 : 0 : if ( ok )
1657 : : {
1658 : 0 : penWidth = strokeWidth;
1659 : 0 : }
1660 : 0 : }
1661 : 0 : penWidth = context.renderContext().convertToPainterUnits( penWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
1662 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeStyle ) )
1663 : : {
1664 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle ) );
1665 : 0 : QString strokeStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyStrokeStyle, context.renderContext().expressionContext(), QString(), &ok );
1666 : 0 : if ( ok && strokeStyle == QLatin1String( "no" ) )
1667 : : {
1668 : 0 : penWidth = 0.0;
1669 : 0 : }
1670 : 0 : }
1671 : 0 : else if ( mStrokeStyle == Qt::NoPen )
1672 : 0 : penWidth = 0;
1673 : :
1674 : : //antialiasing, add 1 pixel
1675 : 0 : penWidth += 1;
1676 : :
1677 : : //extend bounds by pen width / 2.0
1678 : 0 : symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1679 : 0 : penWidth / 2.0, penWidth / 2.0 );
1680 : :
1681 : 0 : return symbolBounds;
1682 : 0 : }
1683 : :
1684 : 22 : void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1685 : : {
1686 : 22 : if ( shapeIsFilled( mShape ) )
1687 : : {
1688 : 22 : setFillColor( color );
1689 : 22 : }
1690 : : else
1691 : : {
1692 : 0 : setStrokeColor( color );
1693 : : }
1694 : 22 : }
1695 : :
1696 : 80 : QColor QgsSimpleMarkerSymbolLayer::color() const
1697 : : {
1698 : 80 : if ( shapeIsFilled( mShape ) )
1699 : : {
1700 : 70 : return fillColor();
1701 : : }
1702 : : else
1703 : : {
1704 : 10 : return strokeColor();
1705 : : }
1706 : 80 : }
1707 : :
1708 : :
1709 : :
1710 : :
1711 : : //
1712 : : // QgsFilledMarkerSymbolLayer
1713 : : //
1714 : :
1715 : 0 : QgsFilledMarkerSymbolLayer::QgsFilledMarkerSymbolLayer( QgsSimpleMarkerSymbolLayerBase::Shape shape, double size, double angle, QgsSymbol::ScaleMethod scaleMethod )
1716 : 0 : : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1717 : 0 : {
1718 : 0 : mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1719 : 0 : }
1720 : :
1721 : 0 : QgsSymbolLayer *QgsFilledMarkerSymbolLayer::create( const QVariantMap &props )
1722 : : {
1723 : 0 : QString name = DEFAULT_SIMPLEMARKER_NAME;
1724 : 0 : double size = DEFAULT_SIMPLEMARKER_SIZE;
1725 : 0 : double angle = DEFAULT_SIMPLEMARKER_ANGLE;
1726 : 0 : QgsSymbol::ScaleMethod scaleMethod = DEFAULT_SCALE_METHOD;
1727 : :
1728 : 0 : if ( props.contains( QStringLiteral( "name" ) ) )
1729 : 0 : name = props[QStringLiteral( "name" )].toString();
1730 : 0 : if ( props.contains( QStringLiteral( "size" ) ) )
1731 : 0 : size = props[QStringLiteral( "size" )].toDouble();
1732 : 0 : if ( props.contains( QStringLiteral( "angle" ) ) )
1733 : 0 : angle = props[QStringLiteral( "angle" )].toDouble();
1734 : 0 : if ( props.contains( QStringLiteral( "scale_method" ) ) )
1735 : 0 : scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1736 : :
1737 : 0 : QgsFilledMarkerSymbolLayer *m = new QgsFilledMarkerSymbolLayer( decodeShape( name ), size, angle, scaleMethod );
1738 : 0 : if ( props.contains( QStringLiteral( "offset" ) ) )
1739 : 0 : m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1740 : 0 : if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1741 : 0 : m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1742 : 0 : if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1743 : 0 : m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1744 : 0 : if ( props.contains( QStringLiteral( "size_unit" ) ) )
1745 : 0 : m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1746 : 0 : if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1747 : 0 : m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1748 : 0 : if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1749 : : {
1750 : 0 : m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1751 : 0 : }
1752 : 0 : if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1753 : : {
1754 : 0 : m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1755 : 0 : }
1756 : :
1757 : 0 : m->setSubSymbol( QgsFillSymbol::createSimple( props ) );
1758 : :
1759 : 0 : m->restoreOldDataDefinedProperties( props );
1760 : :
1761 : 0 : return m;
1762 : 0 : }
1763 : :
1764 : 0 : QString QgsFilledMarkerSymbolLayer::layerType() const
1765 : : {
1766 : 0 : return QStringLiteral( "FilledMarker" );
1767 : : }
1768 : :
1769 : 0 : void QgsFilledMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context )
1770 : : {
1771 : 0 : if ( mFill )
1772 : : {
1773 : 0 : mFill->startRender( context.renderContext(), context.fields() );
1774 : 0 : }
1775 : :
1776 : 0 : QgsSimpleMarkerSymbolLayerBase::startRender( context );
1777 : 0 : }
1778 : :
1779 : 0 : void QgsFilledMarkerSymbolLayer::stopRender( QgsSymbolRenderContext &context )
1780 : : {
1781 : 0 : if ( mFill )
1782 : : {
1783 : 0 : mFill->stopRender( context.renderContext() );
1784 : 0 : }
1785 : 0 : }
1786 : :
1787 : 0 : QVariantMap QgsFilledMarkerSymbolLayer::properties() const
1788 : : {
1789 : 0 : QVariantMap map;
1790 : 0 : map[QStringLiteral( "name" )] = encodeShape( mShape );
1791 : 0 : map[QStringLiteral( "size" )] = QString::number( mSize );
1792 : 0 : map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1793 : 0 : map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1794 : 0 : map[QStringLiteral( "angle" )] = QString::number( mAngle );
1795 : 0 : map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1796 : 0 : map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1797 : 0 : map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1798 : 0 : map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1799 : 0 : map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1800 : 0 : map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1801 : :
1802 : 0 : if ( mFill )
1803 : : {
1804 : 0 : map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1805 : 0 : }
1806 : 0 : return map;
1807 : 0 : }
1808 : :
1809 : 0 : QgsFilledMarkerSymbolLayer *QgsFilledMarkerSymbolLayer::clone() const
1810 : : {
1811 : 0 : QgsFilledMarkerSymbolLayer *m = static_cast< QgsFilledMarkerSymbolLayer * >( QgsFilledMarkerSymbolLayer::create( properties() ) );
1812 : 0 : copyPaintEffect( m );
1813 : 0 : copyDataDefinedProperties( m );
1814 : 0 : m->setSubSymbol( mFill->clone() );
1815 : 0 : return m;
1816 : 0 : }
1817 : :
1818 : 0 : QgsSymbol *QgsFilledMarkerSymbolLayer::subSymbol()
1819 : : {
1820 : 0 : return mFill.get();
1821 : : }
1822 : :
1823 : 0 : bool QgsFilledMarkerSymbolLayer::setSubSymbol( QgsSymbol *symbol )
1824 : : {
1825 : 0 : if ( symbol && symbol->type() == QgsSymbol::Fill )
1826 : : {
1827 : 0 : mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1828 : 0 : return true;
1829 : : }
1830 : : else
1831 : : {
1832 : 0 : delete symbol;
1833 : 0 : return false;
1834 : : }
1835 : 0 : }
1836 : :
1837 : 0 : double QgsFilledMarkerSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const
1838 : : {
1839 : 0 : if ( mFill )
1840 : : {
1841 : 0 : return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1842 : : }
1843 : 0 : return 0;
1844 : 0 : }
1845 : :
1846 : 0 : QSet<QString> QgsFilledMarkerSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
1847 : : {
1848 : 0 : QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
1849 : 0 : if ( mFill )
1850 : 0 : attr.unite( mFill->usedAttributes( context ) );
1851 : 0 : return attr;
1852 : 0 : }
1853 : :
1854 : 0 : bool QgsFilledMarkerSymbolLayer::hasDataDefinedProperties() const
1855 : : {
1856 : 0 : if ( QgsSymbolLayer::hasDataDefinedProperties() )
1857 : 0 : return true;
1858 : 0 : if ( mFill && mFill->hasDataDefinedProperties() )
1859 : 0 : return true;
1860 : 0 : return false;
1861 : 0 : }
1862 : :
1863 : 0 : void QgsFilledMarkerSymbolLayer::setColor( const QColor &c )
1864 : : {
1865 : 0 : mColor = c;
1866 : 0 : if ( mFill )
1867 : 0 : mFill->setColor( c );
1868 : 0 : }
1869 : :
1870 : 0 : QColor QgsFilledMarkerSymbolLayer::color() const
1871 : : {
1872 : 0 : return mFill ? mFill->color() : mColor;
1873 : : }
1874 : :
1875 : 0 : bool QgsFilledMarkerSymbolLayer::usesMapUnits() const
1876 : : {
1877 : 0 : return mSizeUnit == QgsUnitTypes::RenderMapUnits || mSizeUnit == QgsUnitTypes::RenderMetersInMapUnits
1878 : 0 : || mOffsetUnit == QgsUnitTypes::RenderMapUnits || mOffsetUnit == QgsUnitTypes::RenderMetersInMapUnits
1879 : 0 : || ( mFill && mFill->usesMapUnits() );
1880 : : }
1881 : :
1882 : 0 : void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1883 : : {
1884 : : //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1885 : : //of the rendered point!
1886 : :
1887 : 0 : QPainter *p = context.renderContext().painter();
1888 : 0 : if ( !p )
1889 : : {
1890 : 0 : return;
1891 : : }
1892 : :
1893 : 0 : const double prevOpacity = mFill->opacity();
1894 : 0 : mFill->setOpacity( mFill->opacity() * context.opacity() );
1895 : :
1896 : 0 : if ( shapeIsFilled( shape ) )
1897 : : {
1898 : 0 : p->setBrush( Qt::red );
1899 : 0 : }
1900 : : else
1901 : : {
1902 : 0 : p->setBrush( Qt::NoBrush );
1903 : : }
1904 : 0 : p->setPen( Qt::black );
1905 : :
1906 : 0 : if ( !polygon.isEmpty() )
1907 : : {
1908 : 0 : mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1909 : 0 : }
1910 : : else
1911 : : {
1912 : 0 : QPolygonF poly = path.toFillPolygon();
1913 : 0 : mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1914 : 0 : }
1915 : :
1916 : 0 : mFill->setOpacity( prevOpacity );
1917 : 0 : }
1918 : :
1919 : :
1920 : : //////////
1921 : :
1922 : :
1923 : 40 : QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, QgsSymbol::ScaleMethod scaleMethod )
1924 : 80 : {
1925 : 40 : mSize = size;
1926 : 40 : mAngle = angle;
1927 : 40 : mOffset = QPointF( 0, 0 );
1928 : 40 : mScaleMethod = scaleMethod;
1929 : 40 : mStrokeWidth = 0.2;
1930 : 40 : mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
1931 : 40 : mColor = QColor( 35, 35, 35 );
1932 : 40 : mStrokeColor = QColor( 35, 35, 35 );
1933 : 40 : setPath( path );
1934 : 40 : }
1935 : :
1936 : :
1937 : 40 : QgsSymbolLayer *QgsSvgMarkerSymbolLayer::create( const QVariantMap &props )
1938 : : {
1939 : 40 : QString name;
1940 : 40 : double size = DEFAULT_SVGMARKER_SIZE;
1941 : 40 : double angle = DEFAULT_SVGMARKER_ANGLE;
1942 : 40 : QgsSymbol::ScaleMethod scaleMethod = DEFAULT_SCALE_METHOD;
1943 : :
1944 : 80 : if ( props.contains( QStringLiteral( "name" ) ) )
1945 : 80 : name = props[QStringLiteral( "name" )].toString();
1946 : 80 : if ( props.contains( QStringLiteral( "size" ) ) )
1947 : 80 : size = props[QStringLiteral( "size" )].toDouble();
1948 : 80 : if ( props.contains( QStringLiteral( "angle" ) ) )
1949 : 80 : angle = props[QStringLiteral( "angle" )].toDouble();
1950 : 80 : if ( props.contains( QStringLiteral( "scale_method" ) ) )
1951 : 80 : scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1952 : :
1953 : 40 : QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( name, size, angle, scaleMethod );
1954 : :
1955 : 80 : if ( props.contains( QStringLiteral( "size_unit" ) ) )
1956 : 80 : m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1957 : 80 : if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1958 : 80 : m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1959 : 80 : if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1960 : 80 : m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1961 : 80 : if ( props.contains( QStringLiteral( "offset" ) ) )
1962 : 80 : m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1963 : 80 : if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1964 : 80 : m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1965 : 80 : if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1966 : 80 : m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1967 : 80 : if ( props.contains( QStringLiteral( "fill" ) ) )
1968 : : {
1969 : : //pre 2.5 projects used "fill"
1970 : 0 : m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )].toString() ) );
1971 : 0 : }
1972 : 80 : else if ( props.contains( QStringLiteral( "color" ) ) )
1973 : : {
1974 : 80 : m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() ) );
1975 : 40 : }
1976 : 80 : if ( props.contains( QStringLiteral( "outline" ) ) )
1977 : : {
1978 : : //pre 2.5 projects used "outline"
1979 : 0 : m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )].toString() ) );
1980 : 0 : }
1981 : 80 : else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1982 : : {
1983 : 80 : m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
1984 : 40 : }
1985 : 0 : else if ( props.contains( QStringLiteral( "line_color" ) ) )
1986 : : {
1987 : 0 : m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() ) );
1988 : 0 : }
1989 : :
1990 : 80 : if ( props.contains( QStringLiteral( "outline-width" ) ) )
1991 : : {
1992 : : //pre 2.5 projects used "outline-width"
1993 : 0 : m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
1994 : 0 : }
1995 : 80 : else if ( props.contains( QStringLiteral( "outline_width" ) ) )
1996 : : {
1997 : 80 : m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1998 : 40 : }
1999 : 0 : else if ( props.contains( QStringLiteral( "line_width" ) ) )
2000 : : {
2001 : 0 : m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2002 : 0 : }
2003 : :
2004 : 80 : if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2005 : : {
2006 : 80 : m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2007 : 40 : }
2008 : 0 : else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2009 : : {
2010 : 0 : m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2011 : 0 : }
2012 : 80 : if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2013 : 80 : m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2014 : :
2015 : 80 : if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2016 : : {
2017 : 80 : m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2018 : 40 : }
2019 : 80 : if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2020 : : {
2021 : 80 : m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2022 : 40 : }
2023 : :
2024 : 40 : m->restoreOldDataDefinedProperties( props );
2025 : :
2026 : 40 : m->updateDefaultAspectRatio();
2027 : :
2028 : 80 : if ( props.contains( QStringLiteral( "parameters" ) ) )
2029 : : {
2030 : 48 : const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2031 : 24 : QMap<QString, QgsProperty> parametersProperties;
2032 : 24 : QVariantMap::const_iterator it = parameters.constBegin();
2033 : 24 : for ( ; it != parameters.constEnd(); ++it )
2034 : : {
2035 : 0 : QgsProperty property;
2036 : 0 : if ( property.loadVariant( it.value() ) )
2037 : 0 : parametersProperties.insert( it.key(), property );
2038 : 0 : }
2039 : :
2040 : 24 : m->setParameters( parametersProperties );
2041 : 24 : }
2042 : :
2043 : 40 : return m;
2044 : 40 : }
2045 : :
2046 : 40 : void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2047 : : {
2048 : 80 : QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2049 : 40 : if ( it != properties.end() )
2050 : : {
2051 : 40 : if ( saving )
2052 : : {
2053 : 0 : it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2054 : 0 : }
2055 : : else
2056 : : {
2057 : 40 : it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2058 : : }
2059 : 40 : }
2060 : 40 : }
2061 : :
2062 : 40 : void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2063 : : {
2064 : 40 : mDefaultAspectRatio = 0;
2065 : 40 : mHasFillParam = false;
2066 : 40 : mPath = path;
2067 : 40 : QColor defaultFillColor, defaultStrokeColor;
2068 : : double strokeWidth, fillOpacity, strokeOpacity;
2069 : 40 : bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2070 : 40 : bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2071 : 40 : QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2072 : : hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2073 : : hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2074 : : hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2075 : : hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2076 : :
2077 : 40 : double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2078 : 40 : double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2079 : :
2080 : 40 : if ( hasDefaultFillColor )
2081 : : {
2082 : 0 : defaultFillColor.setAlphaF( newFillOpacity );
2083 : 0 : setFillColor( defaultFillColor );
2084 : 0 : }
2085 : 40 : if ( hasDefaultFillOpacity )
2086 : : {
2087 : 0 : QColor c = fillColor();
2088 : 0 : c.setAlphaF( fillOpacity );
2089 : 0 : setFillColor( c );
2090 : 0 : }
2091 : 40 : if ( hasDefaultStrokeColor )
2092 : : {
2093 : 0 : defaultStrokeColor.setAlphaF( newStrokeOpacity );
2094 : 0 : setStrokeColor( defaultStrokeColor );
2095 : 0 : }
2096 : 40 : if ( hasDefaultStrokeWidth )
2097 : : {
2098 : 0 : setStrokeWidth( strokeWidth );
2099 : 0 : }
2100 : 40 : if ( hasDefaultStrokeOpacity )
2101 : : {
2102 : 0 : QColor c = strokeColor();
2103 : 0 : c.setAlphaF( strokeOpacity );
2104 : 0 : setStrokeColor( c );
2105 : 0 : }
2106 : :
2107 : 40 : updateDefaultAspectRatio();
2108 : 40 : }
2109 : :
2110 : 80 : double QgsSvgMarkerSymbolLayer::updateDefaultAspectRatio()
2111 : : {
2112 : 80 : if ( mDefaultAspectRatio == 0.0 )
2113 : : {
2114 : : //size
2115 : 40 : double size = mSize;
2116 : : //assume 88 dpi as standard value
2117 : 40 : double widthScaleFactor = 3.465;
2118 : 40 : QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2119 : : // set default aspect ratio
2120 : 40 : mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2121 : 40 : }
2122 : 80 : return mDefaultAspectRatio;
2123 : 0 : }
2124 : :
2125 : 0 : bool QgsSvgMarkerSymbolLayer::setPreservedAspectRatio( bool par )
2126 : : {
2127 : 0 : bool aPreservedAspectRatio = preservedAspectRatio();
2128 : 0 : if ( aPreservedAspectRatio && !par )
2129 : : {
2130 : 0 : mFixedAspectRatio = mDefaultAspectRatio;
2131 : 0 : }
2132 : 0 : else if ( !aPreservedAspectRatio && par )
2133 : : {
2134 : 0 : mFixedAspectRatio = 0.0;
2135 : 0 : }
2136 : 0 : return preservedAspectRatio();
2137 : : }
2138 : :
2139 : 24 : void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> ¶meters )
2140 : : {
2141 : 24 : mParameters = parameters;
2142 : 24 : }
2143 : :
2144 : :
2145 : 0 : QString QgsSvgMarkerSymbolLayer::layerType() const
2146 : : {
2147 : 0 : return QStringLiteral( "SvgMarker" );
2148 : : }
2149 : :
2150 : 0 : void QgsSvgMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context )
2151 : : {
2152 : 0 : QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2153 : 0 : Q_UNUSED( context )
2154 : 0 : }
2155 : :
2156 : 0 : void QgsSvgMarkerSymbolLayer::stopRender( QgsSymbolRenderContext &context )
2157 : : {
2158 : 0 : Q_UNUSED( context )
2159 : 0 : }
2160 : :
2161 : 0 : void QgsSvgMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext &context )
2162 : : {
2163 : 0 : QPainter *p = context.renderContext().painter();
2164 : 0 : if ( !p )
2165 : 0 : return;
2166 : :
2167 : 0 : bool hasDataDefinedSize = false;
2168 : 0 : double scaledWidth = calculateSize( context, hasDataDefinedSize );
2169 : 0 : double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2170 : :
2171 : : //don't render symbols with a width below one or above 10,000 pixels
2172 : 0 : if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2173 : : {
2174 : 0 : return;
2175 : : }
2176 : :
2177 : 0 : QgsScopedQPainterState painterState( p );
2178 : :
2179 : 0 : bool hasDataDefinedAspectRatio = false;
2180 : 0 : double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2181 : 0 : double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2182 : :
2183 : 0 : QgsStringMap evaluatedParameters = QgsSymbolLayerUtils::evaluatePropertiesMap( mParameters, context.renderContext().expressionContext() );
2184 : :
2185 : 0 : double strokeWidth = mStrokeWidth;
2186 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
2187 : : {
2188 : 0 : context.setOriginalValueVariable( mStrokeWidth );
2189 : 0 : strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth );
2190 : 0 : }
2191 : 0 : strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2192 : :
2193 : 0 : QColor fillColor = mColor;
2194 : 0 : if ( context.selected() && mHasFillParam )
2195 : : {
2196 : 0 : fillColor = context.renderContext().selectionColor();
2197 : 0 : }
2198 : 0 : else if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFillColor ) )
2199 : : {
2200 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mColor ) );
2201 : 0 : fillColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyFillColor, context.renderContext().expressionContext(), mColor );
2202 : 0 : }
2203 : :
2204 : 0 : QColor strokeColor = mStrokeColor;
2205 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeColor ) )
2206 : : {
2207 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mStrokeColor ) );
2208 : 0 : strokeColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyStrokeColor, context.renderContext().expressionContext(), mStrokeColor );
2209 : 0 : }
2210 : :
2211 : 0 : QString path = mPath;
2212 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName ) )
2213 : : {
2214 : 0 : context.setOriginalValueVariable( mPath );
2215 : 0 : path = QgsSymbolLayerUtils::svgSymbolNameToPath( mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), mPath ),
2216 : 0 : context.renderContext().pathResolver() );
2217 : 0 : if ( path != mPath && qgsDoubleNear( aspectRatio, 0.0 ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) )
2218 : : {
2219 : : // adjust height of data defined path
2220 : 0 : QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2221 : 0 : context.renderContext().scaleFactor(), aspectRatio,
2222 : 0 : ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ), evaluatedParameters );
2223 : 0 : scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2224 : 0 : }
2225 : 0 : }
2226 : :
2227 : 0 : QPointF outputOffset;
2228 : 0 : double angle = 0.0;
2229 : 0 : calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2230 : :
2231 : 0 : p->translate( point + outputOffset );
2232 : :
2233 : 0 : bool rotated = !qgsDoubleNear( angle, 0 );
2234 : 0 : if ( rotated )
2235 : 0 : p->rotate( angle );
2236 : :
2237 : 0 : bool fitsInCache = true;
2238 : 0 : bool usePict = true;
2239 : 0 : bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName );
2240 : 0 : if ( ( !context.renderContext().forceVectorOutput() && !rotated ) || ( context.selected() && rasterizeSelected ) )
2241 : : {
2242 : 0 : QImage img = QgsApplication::svgCache()->svgAsImage( path, width, fillColor, strokeColor, strokeWidth,
2243 : 0 : context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2244 : 0 : ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ), evaluatedParameters );
2245 : 0 : if ( fitsInCache && img.width() > 1 )
2246 : : {
2247 : 0 : usePict = false;
2248 : :
2249 : 0 : if ( context.selected() )
2250 : 0 : QgsImageOperation::adjustHueSaturation( img, 1.0, context.renderContext().selectionColor(), 1.0 );
2251 : :
2252 : : //consider transparency
2253 : 0 : if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2254 : : {
2255 : 0 : QImage transparentImage = img.copy();
2256 : 0 : QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2257 : 0 : p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2258 : 0 : }
2259 : : else
2260 : : {
2261 : 0 : p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2262 : : }
2263 : 0 : }
2264 : 0 : }
2265 : :
2266 : 0 : if ( usePict || !fitsInCache )
2267 : : {
2268 : 0 : p->setOpacity( context.opacity() );
2269 : 0 : QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, width, fillColor, strokeColor, strokeWidth,
2270 : 0 : context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2271 : 0 : ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ), evaluatedParameters );
2272 : 0 : if ( pct.width() > 1 )
2273 : : {
2274 : 0 : QgsScopedQPainterState painterPictureState( p );
2275 : 0 : _fixQPictureDPI( p );
2276 : 0 : p->drawPicture( 0, 0, pct );
2277 : 0 : }
2278 : 0 : }
2279 : :
2280 : : // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2281 : 0 : context.renderContext().setPainterFlagsUsingContext( p );
2282 : 0 : }
2283 : :
2284 : 0 : double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2285 : : {
2286 : 0 : double scaledSize = mSize;
2287 : 0 : hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2288 : :
2289 : 0 : bool ok = true;
2290 : 0 : if ( hasDataDefinedSize )
2291 : : {
2292 : 0 : context.setOriginalValueVariable( mSize );
2293 : 0 : scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertySize, context.renderContext().expressionContext(), mSize, &ok );
2294 : 0 : }
2295 : : else
2296 : : {
2297 : 0 : hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth );
2298 : 0 : if ( hasDataDefinedSize )
2299 : : {
2300 : 0 : context.setOriginalValueVariable( mSize );
2301 : 0 : scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyWidth, context.renderContext().expressionContext(), mSize, &ok );
2302 : 0 : }
2303 : : }
2304 : :
2305 : 0 : if ( hasDataDefinedSize && ok )
2306 : : {
2307 : 0 : switch ( mScaleMethod )
2308 : : {
2309 : : case QgsSymbol::ScaleArea:
2310 : 0 : scaledSize = std::sqrt( scaledSize );
2311 : 0 : break;
2312 : : case QgsSymbol::ScaleDiameter:
2313 : 0 : break;
2314 : : }
2315 : 0 : }
2316 : :
2317 : 0 : return scaledSize;
2318 : 0 : }
2319 : :
2320 : 0 : double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2321 : : {
2322 : 0 : hasDataDefinedAspectRatio = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth ) || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight );
2323 : 0 : if ( !hasDataDefinedAspectRatio )
2324 : 0 : return mFixedAspectRatio;
2325 : :
2326 : 0 : if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
2327 : 0 : return 0.0;
2328 : :
2329 : 0 : double scaledAspectRatio = mDefaultAspectRatio;
2330 : 0 : if ( mFixedAspectRatio > 0.0 )
2331 : 0 : scaledAspectRatio = mFixedAspectRatio;
2332 : :
2333 : 0 : double defaultHeight = mSize * scaledAspectRatio;
2334 : 0 : scaledAspectRatio = defaultHeight / scaledSize;
2335 : :
2336 : 0 : bool ok = true;
2337 : 0 : double scaledHeight = scaledSize * scaledAspectRatio;
2338 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) )
2339 : : {
2340 : 0 : context.setOriginalValueVariable( defaultHeight );
2341 : 0 : scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2342 : 0 : }
2343 : :
2344 : 0 : if ( hasDataDefinedAspectRatio && ok )
2345 : : {
2346 : 0 : switch ( mScaleMethod )
2347 : : {
2348 : : case QgsSymbol::ScaleArea:
2349 : 0 : scaledHeight = sqrt( scaledHeight );
2350 : 0 : break;
2351 : : case QgsSymbol::ScaleDiameter:
2352 : 0 : break;
2353 : : }
2354 : 0 : }
2355 : :
2356 : 0 : scaledAspectRatio = scaledHeight / scaledSize;
2357 : :
2358 : 0 : return scaledAspectRatio;
2359 : 0 : }
2360 : :
2361 : 0 : void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2362 : : {
2363 : : //offset
2364 : 0 : double offsetX = 0;
2365 : 0 : double offsetY = 0;
2366 : 0 : markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2367 : 0 : offset = QPointF( offsetX, offsetY );
2368 : :
2369 : 0 : angle = mAngle + mLineAngle;
2370 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle ) )
2371 : : {
2372 : 0 : context.setOriginalValueVariable( mAngle );
2373 : 0 : angle = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyAngle, context.renderContext().expressionContext(), mAngle ) + mLineAngle;
2374 : 0 : }
2375 : :
2376 : 0 : bool hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle );
2377 : 0 : if ( hasDataDefinedRotation )
2378 : : {
2379 : : // For non-point markers, "dataDefinedRotation" means following the
2380 : : // shape (shape-data defined). For them, "field-data defined" does
2381 : : // not work at all. TODO: if "field-data defined" ever gets implemented
2382 : : // we'll need a way to distinguish here between the two, possibly
2383 : : // using another flag in renderHints()
2384 : 0 : const QgsFeature *f = context.feature();
2385 : 0 : if ( f )
2386 : : {
2387 : 0 : if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2388 : : {
2389 : 0 : const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2390 : 0 : angle += m2p.mapRotation();
2391 : 0 : }
2392 : 0 : }
2393 : 0 : }
2394 : :
2395 : 0 : if ( angle )
2396 : 0 : offset = _rotatedOffset( offset, angle );
2397 : 0 : }
2398 : :
2399 : :
2400 : 0 : QVariantMap QgsSvgMarkerSymbolLayer::properties() const
2401 : : {
2402 : 0 : QVariantMap map;
2403 : 0 : map[QStringLiteral( "name" )] = mPath;
2404 : 0 : map[QStringLiteral( "size" )] = QString::number( mSize );
2405 : 0 : map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2406 : 0 : map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2407 : 0 : map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2408 : 0 : map[QStringLiteral( "angle" )] = QString::number( mAngle );
2409 : 0 : map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2410 : 0 : map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2411 : 0 : map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2412 : 0 : map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2413 : 0 : map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2414 : 0 : map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2415 : 0 : map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2416 : 0 : map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2417 : 0 : map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2418 : 0 : map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2419 : 0 : map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2420 : :
2421 : 0 : QVariantMap parameters;
2422 : 0 : QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
2423 : 0 : for ( ; it != mParameters.constEnd(); ++it )
2424 : 0 : parameters.insert( it.key(), it.value().toVariant() );
2425 : 0 : map[QStringLiteral( "parameters" )] = parameters;
2426 : :
2427 : 0 : return map;
2428 : 0 : }
2429 : :
2430 : 0 : bool QgsSvgMarkerSymbolLayer::usesMapUnits() const
2431 : : {
2432 : 0 : return mSizeUnit == QgsUnitTypes::RenderMapUnits || mSizeUnit == QgsUnitTypes::RenderMetersInMapUnits
2433 : 0 : || mOffsetUnit == QgsUnitTypes::RenderMapUnits || mOffsetUnit == QgsUnitTypes::RenderMetersInMapUnits
2434 : 0 : || mStrokeWidthUnit == QgsUnitTypes::RenderMapUnits || mStrokeWidthUnit == QgsUnitTypes::RenderMetersInMapUnits;
2435 : : }
2436 : :
2437 : 0 : QgsSvgMarkerSymbolLayer *QgsSvgMarkerSymbolLayer::clone() const
2438 : : {
2439 : 0 : QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( mPath, mSize, mAngle );
2440 : 0 : m->setFixedAspectRatio( mFixedAspectRatio );
2441 : 0 : m->setColor( mColor );
2442 : 0 : m->setStrokeColor( mStrokeColor );
2443 : 0 : m->setStrokeWidth( mStrokeWidth );
2444 : 0 : m->setStrokeWidthUnit( mStrokeWidthUnit );
2445 : 0 : m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2446 : 0 : m->setOffset( mOffset );
2447 : 0 : m->setOffsetUnit( mOffsetUnit );
2448 : 0 : m->setOffsetMapUnitScale( mOffsetMapUnitScale );
2449 : 0 : m->setSizeUnit( mSizeUnit );
2450 : 0 : m->setSizeMapUnitScale( mSizeMapUnitScale );
2451 : 0 : m->setHorizontalAnchorPoint( mHorizontalAnchorPoint );
2452 : 0 : m->setVerticalAnchorPoint( mVerticalAnchorPoint );
2453 : 0 : m->setParameters( mParameters );
2454 : :
2455 : 0 : copyDataDefinedProperties( m );
2456 : 0 : copyPaintEffect( m );
2457 : 0 : return m;
2458 : 0 : }
2459 : :
2460 : 0 : void QgsSvgMarkerSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit unit )
2461 : : {
2462 : 0 : QgsMarkerSymbolLayer::setOutputUnit( unit );
2463 : 0 : mStrokeWidthUnit = unit;
2464 : 0 : }
2465 : :
2466 : 0 : QgsUnitTypes::RenderUnit QgsSvgMarkerSymbolLayer::outputUnit() const
2467 : : {
2468 : 0 : QgsUnitTypes::RenderUnit unit = QgsMarkerSymbolLayer::outputUnit();
2469 : 0 : if ( unit != mStrokeWidthUnit )
2470 : : {
2471 : 0 : return QgsUnitTypes::RenderUnknownUnit;
2472 : : }
2473 : 0 : return unit;
2474 : 0 : }
2475 : :
2476 : 0 : void QgsSvgMarkerSymbolLayer::setMapUnitScale( const QgsMapUnitScale &scale )
2477 : : {
2478 : 0 : QgsMarkerSymbolLayer::setMapUnitScale( scale );
2479 : 0 : mStrokeWidthMapUnitScale = scale;
2480 : 0 : }
2481 : :
2482 : 0 : QgsMapUnitScale QgsSvgMarkerSymbolLayer::mapUnitScale() const
2483 : : {
2484 : 0 : if ( QgsMarkerSymbolLayer::mapUnitScale() == mStrokeWidthMapUnitScale )
2485 : : {
2486 : 0 : return mStrokeWidthMapUnitScale;
2487 : : }
2488 : 0 : return QgsMapUnitScale();
2489 : 0 : }
2490 : :
2491 : 0 : void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2492 : : {
2493 : : // <Graphic>
2494 : 0 : QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2495 : 0 : element.appendChild( graphicElem );
2496 : :
2497 : : // encode a parametric SVG reference
2498 : 0 : double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2499 : 0 : double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
2500 : 0 : QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mPath, mColor, size, mStrokeColor, strokeWidth );
2501 : :
2502 : : // <Rotation>
2503 : 0 : QString angleFunc;
2504 : : bool ok;
2505 : 0 : double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2506 : 0 : if ( !ok )
2507 : : {
2508 : 0 : angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2509 : 0 : }
2510 : 0 : else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2511 : : {
2512 : 0 : angleFunc = QString::number( angle + mAngle );
2513 : 0 : }
2514 : :
2515 : 0 : QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2516 : :
2517 : : // <Displacement>
2518 : 0 : QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2519 : 0 : QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2520 : 0 : }
2521 : :
2522 : 0 : QgsSymbolLayer *QgsSvgMarkerSymbolLayer::createFromSld( QDomElement &element )
2523 : : {
2524 : 0 : QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2525 : :
2526 : 0 : QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2527 : 0 : if ( graphicElem.isNull() )
2528 : 0 : return nullptr;
2529 : :
2530 : 0 : QString path, mimeType;
2531 : 0 : QColor fillColor;
2532 : : double size;
2533 : :
2534 : 0 : if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2535 : 0 : return nullptr;
2536 : :
2537 : 0 : QString uom = element.attribute( QStringLiteral( "uom" ) );
2538 : 0 : size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2539 : :
2540 : 0 : if ( mimeType != QLatin1String( "image/svg+xml" ) )
2541 : 0 : return nullptr;
2542 : :
2543 : 0 : double angle = 0.0;
2544 : 0 : QString angleFunc;
2545 : 0 : if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2546 : : {
2547 : : bool ok;
2548 : 0 : double d = angleFunc.toDouble( &ok );
2549 : 0 : if ( ok )
2550 : 0 : angle = d;
2551 : 0 : }
2552 : :
2553 : 0 : QPointF offset;
2554 : 0 : QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2555 : :
2556 : 0 : QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( path, size );
2557 : 0 : m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2558 : 0 : m->setFillColor( fillColor );
2559 : : //m->setStrokeColor( strokeColor );
2560 : : //m->setStrokeWidth( strokeWidth );
2561 : 0 : m->setAngle( angle );
2562 : 0 : m->setOffset( offset );
2563 : 0 : return m;
2564 : 0 : }
2565 : :
2566 : 0 : bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2567 : : {
2568 : : //size
2569 : 0 : double size = mSize;
2570 : :
2571 : 0 : bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2572 : :
2573 : 0 : bool ok = true;
2574 : 0 : if ( hasDataDefinedSize )
2575 : : {
2576 : 0 : context.setOriginalValueVariable( mSize );
2577 : 0 : size = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertySize, context.renderContext().expressionContext(), mSize, &ok );
2578 : 0 : }
2579 : :
2580 : 0 : if ( hasDataDefinedSize && ok )
2581 : : {
2582 : 0 : switch ( mScaleMethod )
2583 : : {
2584 : : case QgsSymbol::ScaleArea:
2585 : 0 : size = std::sqrt( size );
2586 : 0 : break;
2587 : : case QgsSymbol::ScaleDiameter:
2588 : 0 : break;
2589 : : }
2590 : 0 : }
2591 : :
2592 : 0 : if ( mSizeUnit == QgsUnitTypes::RenderMillimeters )
2593 : : {
2594 : 0 : size *= mmMapUnitScaleFactor;
2595 : 0 : }
2596 : :
2597 : : //offset, angle
2598 : 0 : QPointF offset = mOffset;
2599 : :
2600 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOffset ) )
2601 : : {
2602 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePoint( mOffset ) );
2603 : 0 : const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
2604 : 0 : const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2605 : 0 : if ( ok )
2606 : 0 : offset = res;
2607 : 0 : }
2608 : 0 : double offsetX = offset.x();
2609 : 0 : double offsetY = offset.y();
2610 : :
2611 : 0 : QPointF outputOffset( offsetX, offsetY );
2612 : :
2613 : 0 : double angle = mAngle + mLineAngle;
2614 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle ) )
2615 : : {
2616 : 0 : context.setOriginalValueVariable( mAngle );
2617 : 0 : angle = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyAngle, context.renderContext().expressionContext(), mAngle ) + mLineAngle;
2618 : 0 : }
2619 : :
2620 : 0 : if ( angle )
2621 : 0 : outputOffset = _rotatedOffset( outputOffset, angle );
2622 : :
2623 : 0 : outputOffset *= e.mapUnitScaleFactor( e.symbologyScale(), mOffsetUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2624 : :
2625 : 0 : QString path = mPath;
2626 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName ) )
2627 : : {
2628 : 0 : context.setOriginalValueVariable( mPath );
2629 : 0 : path = QgsSymbolLayerUtils::svgSymbolNameToPath( mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), mPath ),
2630 : 0 : context.renderContext().pathResolver() );
2631 : 0 : }
2632 : :
2633 : 0 : double strokeWidth = mStrokeWidth;
2634 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
2635 : : {
2636 : 0 : context.setOriginalValueVariable( mStrokeWidth );
2637 : 0 : strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth );
2638 : 0 : }
2639 : 0 : strokeWidth *= e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2640 : :
2641 : 0 : QColor fillColor = mColor;
2642 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFillColor ) )
2643 : : {
2644 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mColor ) );
2645 : 0 : fillColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyFillColor, context.renderContext().expressionContext(), mColor );
2646 : 0 : }
2647 : :
2648 : 0 : QColor strokeColor = mStrokeColor;
2649 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeColor ) )
2650 : : {
2651 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mStrokeColor ) );
2652 : 0 : strokeColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyStrokeColor, context.renderContext().expressionContext(), mStrokeColor );
2653 : 0 : }
2654 : :
2655 : 0 : QgsStringMap evaluatedParameters = QgsSymbolLayerUtils::evaluatePropertiesMap( mParameters, context.renderContext().expressionContext() );
2656 : :
2657 : 0 : const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2658 : 0 : context.renderContext().scaleFactor(), mFixedAspectRatio,
2659 : 0 : ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ), evaluatedParameters );
2660 : :
2661 : 0 : QSvgRenderer r( svgContent );
2662 : 0 : if ( !r.isValid() )
2663 : 0 : return false;
2664 : :
2665 : 0 : QgsDxfPaintDevice pd( &e );
2666 : 0 : pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2667 : :
2668 : 0 : QSizeF outSize( r.defaultSize() );
2669 : 0 : outSize.scale( size, size, Qt::KeepAspectRatio );
2670 : :
2671 : 0 : QPainter p;
2672 : 0 : p.begin( &pd );
2673 : 0 : if ( !qgsDoubleNear( angle, 0.0 ) )
2674 : : {
2675 : 0 : p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2676 : 0 : p.rotate( angle );
2677 : 0 : p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2678 : 0 : }
2679 : 0 : pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2680 : 0 : pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2681 : 0 : pd.setLayer( layerName );
2682 : 0 : r.render( &p );
2683 : 0 : p.end();
2684 : 0 : return true;
2685 : 0 : }
2686 : :
2687 : 0 : QRectF QgsSvgMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &context )
2688 : : {
2689 : 0 : bool hasDataDefinedSize = false;
2690 : 0 : double scaledWidth = calculateSize( context, hasDataDefinedSize );
2691 : :
2692 : 0 : bool hasDataDefinedAspectRatio = false;
2693 : 0 : double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2694 : 0 : double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2695 : :
2696 : 0 : scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2697 : 0 : scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2698 : :
2699 : : //don't render symbols with size below one or above 10,000 pixels
2700 : 0 : if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2701 : : {
2702 : 0 : return QRectF();
2703 : : }
2704 : :
2705 : 0 : QPointF outputOffset;
2706 : 0 : double angle = 0.0;
2707 : 0 : calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2708 : :
2709 : 0 : double strokeWidth = mStrokeWidth;
2710 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
2711 : : {
2712 : 0 : context.setOriginalValueVariable( mStrokeWidth );
2713 : 0 : strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth );
2714 : 0 : }
2715 : 0 : strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2716 : :
2717 : 0 : QString path = mPath;
2718 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName ) )
2719 : : {
2720 : 0 : context.setOriginalValueVariable( mPath );
2721 : 0 : path = QgsSymbolLayerUtils::svgSymbolNameToPath( mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), mPath ),
2722 : 0 : context.renderContext().pathResolver() );
2723 : 0 : if ( path != mPath && qgsDoubleNear( aspectRatio, 0.0 ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) )
2724 : : {
2725 : : // need to get colors to take advantage of cached SVGs
2726 : 0 : QColor fillColor = mColor;
2727 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFillColor ) )
2728 : : {
2729 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mColor ) );
2730 : 0 : fillColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyFillColor, context.renderContext().expressionContext(), mColor );
2731 : 0 : }
2732 : :
2733 : 0 : QColor strokeColor = mStrokeColor;
2734 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeColor ) )
2735 : : {
2736 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mStrokeColor ) );
2737 : 0 : fillColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyStrokeColor, context.renderContext().expressionContext(), mStrokeColor );
2738 : 0 : }
2739 : :
2740 : 0 : QgsStringMap evaluatedParameters = QgsSymbolLayerUtils::evaluatePropertiesMap( mParameters, context.renderContext().expressionContext() );
2741 : :
2742 : : // adjust height of data defined path
2743 : 0 : QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2744 : 0 : context.renderContext().scaleFactor(), aspectRatio,
2745 : 0 : ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ), evaluatedParameters );
2746 : 0 : scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2747 : 0 : }
2748 : 0 : }
2749 : :
2750 : 0 : QMatrix transform;
2751 : : // move to the desired position
2752 : 0 : transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2753 : :
2754 : 0 : if ( !qgsDoubleNear( angle, 0.0 ) )
2755 : 0 : transform.rotate( angle );
2756 : :
2757 : : //antialiasing
2758 : 0 : strokeWidth += 1.0 / 2.0;
2759 : :
2760 : 0 : QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
2761 : 0 : -scaledHeight / 2.0,
2762 : 0 : scaledWidth,
2763 : 0 : scaledHeight ) );
2764 : :
2765 : : //extend bounds by pen width / 2.0
2766 : 0 : symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2767 : 0 : strokeWidth / 2.0, strokeWidth / 2.0 );
2768 : :
2769 : 0 : return symbolBounds;
2770 : 0 : }
2771 : :
2772 : : //////////
2773 : :
2774 : 0 : QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, QgsSymbol::ScaleMethod scaleMethod )
2775 : 0 : : mPath( path )
2776 : 0 : {
2777 : 0 : mSize = size;
2778 : 0 : mAngle = angle;
2779 : 0 : mOffset = QPointF( 0, 0 );
2780 : 0 : mScaleMethod = scaleMethod;
2781 : 0 : updateDefaultAspectRatio();
2782 : 0 : }
2783 : :
2784 : :
2785 : 0 : QgsSymbolLayer *QgsRasterMarkerSymbolLayer::create( const QVariantMap &props )
2786 : : {
2787 : 0 : QString path;
2788 : 0 : double size = DEFAULT_RASTERMARKER_SIZE;
2789 : 0 : double angle = DEFAULT_RASTERMARKER_ANGLE;
2790 : 0 : QgsSymbol::ScaleMethod scaleMethod = DEFAULT_SCALE_METHOD;
2791 : :
2792 : 0 : if ( props.contains( QStringLiteral( "imageFile" ) ) )
2793 : 0 : path = props[QStringLiteral( "imageFile" )].toString();
2794 : 0 : if ( props.contains( QStringLiteral( "size" ) ) )
2795 : 0 : size = props[QStringLiteral( "size" )].toDouble();
2796 : 0 : if ( props.contains( QStringLiteral( "angle" ) ) )
2797 : 0 : angle = props[QStringLiteral( "angle" )].toDouble();
2798 : 0 : if ( props.contains( QStringLiteral( "scale_method" ) ) )
2799 : 0 : scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2800 : :
2801 : 0 : QgsRasterMarkerSymbolLayer *m = new QgsRasterMarkerSymbolLayer( path, size, angle, scaleMethod );
2802 : :
2803 : 0 : if ( props.contains( QStringLiteral( "alpha" ) ) )
2804 : : {
2805 : 0 : m->setOpacity( props[QStringLiteral( "alpha" )].toDouble() );
2806 : 0 : }
2807 : :
2808 : 0 : if ( props.contains( QStringLiteral( "size_unit" ) ) )
2809 : 0 : m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2810 : 0 : if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2811 : 0 : m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2812 : 0 : if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2813 : 0 : m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2814 : :
2815 : 0 : if ( props.contains( QStringLiteral( "offset" ) ) )
2816 : 0 : m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2817 : 0 : if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2818 : 0 : m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2819 : 0 : if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2820 : 0 : m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2821 : :
2822 : 0 : if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2823 : : {
2824 : 0 : m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2825 : 0 : }
2826 : 0 : if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2827 : : {
2828 : 0 : m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2829 : 0 : }
2830 : :
2831 : 0 : m->restoreOldDataDefinedProperties( props );
2832 : 0 : m->updateDefaultAspectRatio();
2833 : :
2834 : 0 : return m;
2835 : 0 : }
2836 : :
2837 : 0 : void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2838 : : {
2839 : 0 : QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2840 : 0 : if ( it != properties.end() && it.value().type() == QVariant::String )
2841 : : {
2842 : 0 : if ( saving )
2843 : 0 : it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2844 : : else
2845 : 0 : it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2846 : 0 : }
2847 : 0 : }
2848 : :
2849 : 0 : void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
2850 : : {
2851 : 0 : mPath = path;
2852 : 0 : updateDefaultAspectRatio();
2853 : 0 : }
2854 : :
2855 : 0 : bool QgsRasterMarkerSymbolLayer::setPreservedAspectRatio( bool par )
2856 : : {
2857 : 0 : bool aPreservedAspectRatio = preservedAspectRatio();
2858 : 0 : if ( aPreservedAspectRatio && !par )
2859 : : {
2860 : 0 : mFixedAspectRatio = mDefaultAspectRatio;
2861 : 0 : }
2862 : 0 : else if ( !aPreservedAspectRatio && par )
2863 : : {
2864 : 0 : mFixedAspectRatio = 0.0;
2865 : 0 : }
2866 : 0 : return preservedAspectRatio();
2867 : : }
2868 : :
2869 : 0 : double QgsRasterMarkerSymbolLayer::updateDefaultAspectRatio()
2870 : : {
2871 : 0 : if ( mDefaultAspectRatio == 0.0 )
2872 : : {
2873 : 0 : QSize size = QgsApplication::imageCache()->originalSize( mPath );
2874 : 0 : mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
2875 : 0 : }
2876 : 0 : return mDefaultAspectRatio;
2877 : : }
2878 : :
2879 : 0 : QString QgsRasterMarkerSymbolLayer::layerType() const
2880 : : {
2881 : 0 : return QStringLiteral( "RasterMarker" );
2882 : : }
2883 : :
2884 : 0 : void QgsRasterMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext &context )
2885 : : {
2886 : 0 : QPainter *p = context.renderContext().painter();
2887 : 0 : if ( !p )
2888 : 0 : return;
2889 : :
2890 : 0 : QString path = mPath;
2891 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName ) )
2892 : : {
2893 : 0 : context.setOriginalValueVariable( mPath );
2894 : 0 : path = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), mPath );
2895 : 0 : }
2896 : :
2897 : 0 : if ( path.isEmpty() )
2898 : 0 : return;
2899 : :
2900 : 0 : double width = 0.0;
2901 : 0 : double height = 0.0;
2902 : :
2903 : 0 : bool hasDataDefinedSize = false;
2904 : 0 : double scaledSize = calculateSize( context, hasDataDefinedSize );
2905 : :
2906 : 0 : bool hasDataDefinedAspectRatio = false;
2907 : 0 : double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2908 : :
2909 : 0 : QPointF outputOffset;
2910 : 0 : double angle = 0.0;
2911 : :
2912 : : // RenderPercentage Unit Type takes original image size
2913 : 0 : if ( mSizeUnit == QgsUnitTypes::RenderPercentage )
2914 : : {
2915 : 0 : QSize size = QgsApplication::imageCache()->originalSize( path );
2916 : 0 : if ( size.isEmpty() )
2917 : 0 : return;
2918 : :
2919 : 0 : width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
2920 : 0 : height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
2921 : :
2922 : : // don't render symbols with size below one or above 10,000 pixels
2923 : 0 : if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
2924 : 0 : return;
2925 : :
2926 : 0 : calculateOffsetAndRotation( context, width, height, outputOffset, angle );
2927 : 0 : }
2928 : : else
2929 : : {
2930 : 0 : width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2931 : 0 : height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2932 : :
2933 : 0 : if ( preservedAspectRatio() && path != mPath )
2934 : : {
2935 : 0 : QSize size = QgsApplication::imageCache()->originalSize( path );
2936 : 0 : if ( !size.isNull() && size.isValid() && size.width() > 0 )
2937 : : {
2938 : 0 : height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
2939 : 0 : }
2940 : 0 : }
2941 : :
2942 : : // don't render symbols with size below one or above 10,000 pixels
2943 : 0 : if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2944 : 0 : return;
2945 : :
2946 : 0 : calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2947 : : }
2948 : :
2949 : 0 : QgsScopedQPainterState painterState( p );
2950 : 0 : p->translate( point + outputOffset );
2951 : :
2952 : 0 : bool rotated = !qgsDoubleNear( angle, 0 );
2953 : 0 : if ( rotated )
2954 : 0 : p->rotate( angle );
2955 : :
2956 : 0 : double opacity = mOpacity;
2957 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOpacity ) )
2958 : : {
2959 : 0 : context.setOriginalValueVariable( mOpacity );
2960 : 0 : opacity = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyOpacity, context.renderContext().expressionContext(), opacity * 100 ) / 100.0;
2961 : 0 : }
2962 : 0 : opacity *= context.opacity();
2963 : :
2964 : : bool cached;
2965 : 0 : QImage img = QgsApplication::imageCache()->pathAsImage( path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity, cached, ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
2966 : 0 : if ( !img.isNull() )
2967 : : {
2968 : 0 : if ( context.selected() )
2969 : 0 : QgsImageOperation::adjustHueSaturation( img, 1.0, context.renderContext().selectionColor(), 1.0 );
2970 : :
2971 : 0 : p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2972 : 0 : }
2973 : 0 : }
2974 : :
2975 : 0 : double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2976 : : {
2977 : 0 : double scaledSize = mSize;
2978 : 0 : hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2979 : :
2980 : 0 : bool ok = true;
2981 : 0 : if ( hasDataDefinedSize )
2982 : : {
2983 : 0 : context.setOriginalValueVariable( mSize );
2984 : 0 : scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertySize, context.renderContext().expressionContext(), mSize, &ok );
2985 : 0 : }
2986 : : else
2987 : : {
2988 : 0 : hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth );
2989 : 0 : if ( hasDataDefinedSize )
2990 : : {
2991 : 0 : context.setOriginalValueVariable( mSize );
2992 : 0 : scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyWidth, context.renderContext().expressionContext(), mSize, &ok );
2993 : 0 : }
2994 : : }
2995 : :
2996 : 0 : if ( hasDataDefinedSize && ok )
2997 : : {
2998 : 0 : switch ( mScaleMethod )
2999 : : {
3000 : : case QgsSymbol::ScaleArea:
3001 : 0 : scaledSize = std::sqrt( scaledSize );
3002 : 0 : break;
3003 : : case QgsSymbol::ScaleDiameter:
3004 : 0 : break;
3005 : : }
3006 : 0 : }
3007 : :
3008 : 0 : return scaledSize;
3009 : 0 : }
3010 : :
3011 : 0 : double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3012 : : {
3013 : 0 : hasDataDefinedAspectRatio = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth ) || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight );
3014 : 0 : if ( !hasDataDefinedAspectRatio )
3015 : 0 : return mFixedAspectRatio;
3016 : :
3017 : 0 : if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
3018 : 0 : return 0.0;
3019 : :
3020 : 0 : double scaledAspectRatio = mDefaultAspectRatio;
3021 : 0 : if ( mFixedAspectRatio > 0.0 )
3022 : 0 : scaledAspectRatio = mFixedAspectRatio;
3023 : :
3024 : 0 : double defaultHeight = mSize * scaledAspectRatio;
3025 : 0 : scaledAspectRatio = defaultHeight / scaledSize;
3026 : :
3027 : 0 : bool ok = true;
3028 : 0 : double scaledHeight = scaledSize * scaledAspectRatio;
3029 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) )
3030 : : {
3031 : 0 : context.setOriginalValueVariable( defaultHeight );
3032 : 0 : scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
3033 : 0 : }
3034 : :
3035 : 0 : if ( hasDataDefinedAspectRatio && ok )
3036 : : {
3037 : 0 : switch ( mScaleMethod )
3038 : : {
3039 : : case QgsSymbol::ScaleArea:
3040 : 0 : scaledHeight = sqrt( scaledHeight );
3041 : 0 : break;
3042 : : case QgsSymbol::ScaleDiameter:
3043 : 0 : break;
3044 : : }
3045 : 0 : }
3046 : :
3047 : 0 : scaledAspectRatio = scaledHeight / scaledSize;
3048 : :
3049 : 0 : return scaledAspectRatio;
3050 : 0 : }
3051 : :
3052 : 0 : void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3053 : : {
3054 : : //offset
3055 : 0 : double offsetX = 0;
3056 : 0 : double offsetY = 0;
3057 : 0 : markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3058 : 0 : offset = QPointF( offsetX, offsetY );
3059 : :
3060 : 0 : angle = mAngle + mLineAngle;
3061 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle ) )
3062 : : {
3063 : 0 : context.setOriginalValueVariable( mAngle );
3064 : 0 : angle = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyAngle, context.renderContext().expressionContext(), mAngle ) + mLineAngle;
3065 : 0 : }
3066 : :
3067 : 0 : bool hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle );
3068 : 0 : if ( hasDataDefinedRotation )
3069 : : {
3070 : 0 : const QgsFeature *f = context.feature();
3071 : 0 : if ( f )
3072 : : {
3073 : 0 : if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3074 : : {
3075 : 0 : const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3076 : 0 : angle += m2p.mapRotation();
3077 : 0 : }
3078 : 0 : }
3079 : 0 : }
3080 : :
3081 : 0 : if ( angle )
3082 : 0 : offset = _rotatedOffset( offset, angle );
3083 : 0 : }
3084 : :
3085 : :
3086 : 0 : QVariantMap QgsRasterMarkerSymbolLayer::properties() const
3087 : : {
3088 : 0 : QVariantMap map;
3089 : 0 : map[QStringLiteral( "imageFile" )] = mPath;
3090 : 0 : map[QStringLiteral( "size" )] = QString::number( mSize );
3091 : 0 : map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3092 : 0 : map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3093 : 0 : map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3094 : 0 : map[QStringLiteral( "angle" )] = QString::number( mAngle );
3095 : 0 : map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3096 : 0 : map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3097 : 0 : map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3098 : 0 : map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3099 : 0 : map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3100 : 0 : map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3101 : 0 : map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3102 : 0 : return map;
3103 : 0 : }
3104 : :
3105 : 0 : QgsRasterMarkerSymbolLayer *QgsRasterMarkerSymbolLayer::clone() const
3106 : : {
3107 : 0 : QgsRasterMarkerSymbolLayer *m = new QgsRasterMarkerSymbolLayer( mPath, mSize, mAngle );
3108 : 0 : m->setFixedAspectRatio( mFixedAspectRatio );
3109 : 0 : m->setOpacity( mOpacity );
3110 : 0 : m->setOffset( mOffset );
3111 : 0 : m->setOffsetUnit( mOffsetUnit );
3112 : 0 : m->setOffsetMapUnitScale( mOffsetMapUnitScale );
3113 : 0 : m->setSizeUnit( mSizeUnit );
3114 : 0 : m->setSizeMapUnitScale( mSizeMapUnitScale );
3115 : 0 : m->setHorizontalAnchorPoint( mHorizontalAnchorPoint );
3116 : 0 : m->setVerticalAnchorPoint( mVerticalAnchorPoint );
3117 : 0 : copyDataDefinedProperties( m );
3118 : 0 : copyPaintEffect( m );
3119 : 0 : return m;
3120 : 0 : }
3121 : :
3122 : 0 : bool QgsRasterMarkerSymbolLayer::usesMapUnits() const
3123 : : {
3124 : 0 : return mSizeUnit == QgsUnitTypes::RenderMapUnits || mSizeUnit == QgsUnitTypes::RenderMetersInMapUnits
3125 : 0 : || mOffsetUnit == QgsUnitTypes::RenderMapUnits || mOffsetUnit == QgsUnitTypes::RenderMetersInMapUnits;
3126 : : }
3127 : :
3128 : 0 : void QgsRasterMarkerSymbolLayer::setMapUnitScale( const QgsMapUnitScale &scale )
3129 : : {
3130 : 0 : QgsMarkerSymbolLayer::setMapUnitScale( scale );
3131 : 0 : }
3132 : :
3133 : 0 : QgsMapUnitScale QgsRasterMarkerSymbolLayer::mapUnitScale() const
3134 : : {
3135 : 0 : return QgsMarkerSymbolLayer::mapUnitScale();
3136 : : }
3137 : :
3138 : 0 : QRectF QgsRasterMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &context )
3139 : : {
3140 : 0 : bool hasDataDefinedSize = false;
3141 : 0 : double scaledSize = calculateSize( context, hasDataDefinedSize );
3142 : 0 : double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3143 : 0 : bool hasDataDefinedAspectRatio = false;
3144 : 0 : double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3145 : 0 : double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3146 : :
3147 : : //don't render symbols with size below one or above 10,000 pixels
3148 : 0 : if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3149 : : {
3150 : 0 : return QRectF();
3151 : : }
3152 : :
3153 : 0 : QPointF outputOffset;
3154 : 0 : double angle = 0.0;
3155 : 0 : calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3156 : :
3157 : 0 : QMatrix transform;
3158 : :
3159 : : // move to the desired position
3160 : 0 : transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3161 : :
3162 : 0 : if ( !qgsDoubleNear( angle, 0.0 ) )
3163 : 0 : transform.rotate( angle );
3164 : :
3165 : 0 : QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3166 : 0 : -height / 2.0,
3167 : 0 : width,
3168 : 0 : height ) );
3169 : :
3170 : 0 : return symbolBounds;
3171 : 0 : }
3172 : :
3173 : : //////////
3174 : :
3175 : 0 : QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3176 : 0 : {
3177 : 0 : mFontFamily = fontFamily;
3178 : 0 : mString = chr;
3179 : 0 : mColor = color;
3180 : 0 : mAngle = angle;
3181 : 0 : mSize = pointSize;
3182 : 0 : mOrigSize = pointSize;
3183 : 0 : mSizeUnit = QgsUnitTypes::RenderMillimeters;
3184 : 0 : mOffset = QPointF( 0, 0 );
3185 : 0 : mOffsetUnit = QgsUnitTypes::RenderMillimeters;
3186 : 0 : mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3187 : 0 : mStrokeWidth = 0.0;
3188 : 0 : mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
3189 : 0 : mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3190 : 0 : }
3191 : :
3192 : 0 : QgsSymbolLayer *QgsFontMarkerSymbolLayer::create( const QVariantMap &props )
3193 : : {
3194 : 0 : QString fontFamily = DEFAULT_FONTMARKER_FONT;
3195 : 0 : QString fontStyle = DEFAULT_FONTMARKER_FONT;
3196 : 0 : QString string = DEFAULT_FONTMARKER_CHR;
3197 : 0 : double pointSize = DEFAULT_FONTMARKER_SIZE;
3198 : 0 : QColor color = DEFAULT_FONTMARKER_COLOR;
3199 : 0 : double angle = DEFAULT_FONTMARKER_ANGLE;
3200 : :
3201 : 0 : if ( props.contains( QStringLiteral( "font" ) ) )
3202 : 0 : fontFamily = props[QStringLiteral( "font" )].toString();
3203 : 0 : if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3204 : 0 : string = props[QStringLiteral( "chr" )].toString();
3205 : 0 : if ( props.contains( QStringLiteral( "size" ) ) )
3206 : 0 : pointSize = props[QStringLiteral( "size" )].toDouble();
3207 : 0 : if ( props.contains( QStringLiteral( "color" ) ) )
3208 : 0 : color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
3209 : 0 : if ( props.contains( QStringLiteral( "angle" ) ) )
3210 : 0 : angle = props[QStringLiteral( "angle" )].toDouble();
3211 : :
3212 : 0 : QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, string, pointSize, color, angle );
3213 : :
3214 : 0 : if ( props.contains( QStringLiteral( "font_style" ) ) )
3215 : 0 : m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3216 : 0 : if ( props.contains( QStringLiteral( "outline_color" ) ) )
3217 : 0 : m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
3218 : 0 : if ( props.contains( QStringLiteral( "outline_width" ) ) )
3219 : 0 : m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3220 : 0 : if ( props.contains( QStringLiteral( "offset" ) ) )
3221 : 0 : m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3222 : 0 : if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3223 : 0 : m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3224 : 0 : if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3225 : 0 : m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3226 : 0 : if ( props.contains( QStringLiteral( "size_unit" ) ) )
3227 : 0 : m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3228 : 0 : if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3229 : 0 : m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3230 : 0 : if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3231 : 0 : m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3232 : 0 : if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3233 : 0 : m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3234 : 0 : if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3235 : 0 : m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3236 : 0 : if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3237 : 0 : m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3238 : 0 : if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3239 : 0 : m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3240 : :
3241 : 0 : m->restoreOldDataDefinedProperties( props );
3242 : :
3243 : 0 : return m;
3244 : 0 : }
3245 : :
3246 : 0 : QString QgsFontMarkerSymbolLayer::layerType() const
3247 : : {
3248 : 0 : return QStringLiteral( "FontMarker" );
3249 : : }
3250 : :
3251 : 0 : void QgsFontMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context )
3252 : : {
3253 : 0 : QColor brushColor = mColor;
3254 : 0 : QColor penColor = mStrokeColor;
3255 : :
3256 : 0 : brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3257 : 0 : penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3258 : :
3259 : 0 : mBrush = QBrush( brushColor );
3260 : 0 : mPen = QPen( penColor );
3261 : 0 : mPen.setJoinStyle( mPenJoinStyle );
3262 : 0 : mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3263 : :
3264 : 0 : mFont = QFont( mFontFamily );
3265 : 0 : if ( !mFontStyle.isEmpty() )
3266 : : {
3267 : 0 : mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3268 : 0 : }
3269 : :
3270 : 0 : const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3271 : 0 : mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3272 : : // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3273 : : // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3274 : 0 : mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3275 : 0 : mFontMetrics.reset( new QFontMetrics( mFont ) );
3276 : 0 : mChrWidth = mFontMetrics->horizontalAdvance( mString );
3277 : 0 : mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3278 : 0 : mOrigSize = mSize; // save in case the size would be data defined
3279 : :
3280 : : // use caching only when not using a data defined character
3281 : 0 : mUseCachedPath = !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFontFamily ) &&
3282 : 0 : !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFontStyle ) &&
3283 : 0 : !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyCharacter );
3284 : 0 : if ( mUseCachedPath )
3285 : : {
3286 : 0 : QPointF chrOffset = mChrOffset;
3287 : : double chrWidth;
3288 : 0 : QString charToRender = characterToRender( context, chrOffset, chrWidth );
3289 : 0 : mCachedPath = QPainterPath();
3290 : 0 : mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3291 : 0 : }
3292 : 0 : }
3293 : :
3294 : 0 : void QgsFontMarkerSymbolLayer::stopRender( QgsSymbolRenderContext &context )
3295 : : {
3296 : 0 : Q_UNUSED( context )
3297 : 0 : }
3298 : :
3299 : 0 : QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3300 : : {
3301 : 0 : charOffset = mChrOffset;
3302 : 0 : QString stringToRender = mString;
3303 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyCharacter ) )
3304 : : {
3305 : 0 : context.setOriginalValueVariable( mString );
3306 : 0 : stringToRender = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCharacter, context.renderContext().expressionContext(), mString );
3307 : 0 : if ( stringToRender != mString )
3308 : : {
3309 : 0 : charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3310 : 0 : charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3311 : 0 : }
3312 : 0 : }
3313 : 0 : return stringToRender;
3314 : 0 : }
3315 : :
3316 : 0 : void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3317 : : double scaledSize,
3318 : : bool &hasDataDefinedRotation,
3319 : : QPointF &offset,
3320 : : double &angle ) const
3321 : : {
3322 : : //offset
3323 : 0 : double offsetX = 0;
3324 : 0 : double offsetY = 0;
3325 : 0 : markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3326 : 0 : offset = QPointF( offsetX, offsetY );
3327 : :
3328 : : //angle
3329 : 0 : bool ok = true;
3330 : 0 : angle = mAngle + mLineAngle;
3331 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle ) )
3332 : : {
3333 : 0 : context.setOriginalValueVariable( angle );
3334 : 0 : angle = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyAngle, context.renderContext().expressionContext(), mAngle, &ok ) + mLineAngle;
3335 : :
3336 : : // If the expression evaluation was not successful, fallback to static value
3337 : 0 : if ( !ok )
3338 : 0 : angle = mAngle + mLineAngle;
3339 : 0 : }
3340 : :
3341 : 0 : hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation;
3342 : 0 : if ( hasDataDefinedRotation )
3343 : : {
3344 : : // For non-point markers, "dataDefinedRotation" means following the
3345 : : // shape (shape-data defined). For them, "field-data defined" does
3346 : : // not work at all. TODO: if "field-data defined" ever gets implemented
3347 : : // we'll need a way to distinguish here between the two, possibly
3348 : : // using another flag in renderHints()
3349 : 0 : const QgsFeature *f = context.feature();
3350 : 0 : if ( f )
3351 : : {
3352 : 0 : if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3353 : : {
3354 : 0 : const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3355 : 0 : angle += m2p.mapRotation();
3356 : 0 : }
3357 : 0 : }
3358 : 0 : }
3359 : :
3360 : 0 : if ( angle )
3361 : 0 : offset = _rotatedOffset( offset, angle );
3362 : 0 : }
3363 : :
3364 : 0 : double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3365 : : {
3366 : 0 : double scaledSize = mSize;
3367 : 0 : bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3368 : :
3369 : 0 : bool ok = true;
3370 : 0 : if ( hasDataDefinedSize )
3371 : : {
3372 : 0 : context.setOriginalValueVariable( mSize );
3373 : 0 : scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertySize, context.renderContext().expressionContext(), mSize, &ok );
3374 : 0 : }
3375 : :
3376 : 0 : if ( hasDataDefinedSize && ok )
3377 : : {
3378 : 0 : switch ( mScaleMethod )
3379 : : {
3380 : : case QgsSymbol::ScaleArea:
3381 : 0 : scaledSize = std::sqrt( scaledSize );
3382 : 0 : break;
3383 : : case QgsSymbol::ScaleDiameter:
3384 : 0 : break;
3385 : : }
3386 : 0 : }
3387 : 0 : return scaledSize;
3388 : 0 : }
3389 : :
3390 : 0 : void QgsFontMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext &context )
3391 : : {
3392 : 0 : QPainter *p = context.renderContext().painter();
3393 : 0 : if ( !p || !mNonZeroFontSize )
3394 : 0 : return;
3395 : :
3396 : 0 : QTransform transform;
3397 : :
3398 : : bool ok;
3399 : 0 : QColor brushColor = mColor;
3400 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFillColor ) )
3401 : : {
3402 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mColor ) );
3403 : 0 : brushColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyFillColor, context.renderContext().expressionContext(), brushColor );
3404 : 0 : }
3405 : 0 : brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3406 : 0 : if ( !context.selected() || !SELECTION_IS_OPAQUE )
3407 : : {
3408 : 0 : brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3409 : 0 : }
3410 : 0 : mBrush.setColor( brushColor );
3411 : :
3412 : 0 : QColor penColor = mStrokeColor;
3413 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeColor ) )
3414 : : {
3415 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( mStrokeColor ) );
3416 : 0 : penColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyStrokeColor, context.renderContext().expressionContext(), penColor );
3417 : 0 : }
3418 : 0 : penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3419 : :
3420 : 0 : double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3421 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
3422 : : {
3423 : 0 : context.setOriginalValueVariable( mStrokeWidth );
3424 : 0 : double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
3425 : 0 : if ( ok )
3426 : : {
3427 : 0 : penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3428 : 0 : }
3429 : 0 : }
3430 : :
3431 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyJoinStyle ) )
3432 : : {
3433 : 0 : context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle ) );
3434 : 0 : QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyJoinStyle, context.renderContext().expressionContext(), QString(), &ok );
3435 : 0 : if ( ok )
3436 : : {
3437 : 0 : mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3438 : 0 : }
3439 : 0 : }
3440 : :
3441 : 0 : QgsScopedQPainterState painterState( p );
3442 : 0 : p->setBrush( mBrush );
3443 : 0 : if ( !qgsDoubleNear( penWidth, 0.0 ) )
3444 : : {
3445 : 0 : mPen.setColor( penColor );
3446 : 0 : mPen.setWidthF( penWidth );
3447 : 0 : p->setPen( mPen );
3448 : 0 : }
3449 : : else
3450 : : {
3451 : 0 : p->setPen( Qt::NoPen );
3452 : : }
3453 : :
3454 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFontFamily ) )
3455 : : {
3456 : 0 : context.setOriginalValueVariable( mFontFamily );
3457 : 0 : QString fontFamily = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyFontFamily, context.renderContext().expressionContext(), mFontFamily, &ok );
3458 : 0 : mFont.setFamily( ok ? fontFamily : mFontFamily );
3459 : 0 : }
3460 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFontStyle ) )
3461 : : {
3462 : 0 : context.setOriginalValueVariable( mFontStyle );
3463 : 0 : QString fontStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyFontStyle, context.renderContext().expressionContext(), mFontStyle, &ok );
3464 : 0 : QgsFontUtils::updateFontViaStyle( mFont, QgsFontUtils::translateNamedStyle( ok ? fontStyle : mFontStyle ) );
3465 : 0 : }
3466 : 0 : if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFontFamily ) || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyFontStyle ) )
3467 : : {
3468 : 0 : mFontMetrics.reset( new QFontMetrics( mFont ) );
3469 : 0 : }
3470 : :
3471 : 0 : QPointF chrOffset = mChrOffset;
3472 : : double chrWidth;
3473 : 0 : QString charToRender = characterToRender( context, chrOffset, chrWidth );
3474 : :
3475 : 0 : double sizeToRender = calculateSize( context );
3476 : :
3477 : 0 : bool hasDataDefinedRotation = false;
3478 : 0 : QPointF offset;
3479 : 0 : double angle = 0;
3480 : 0 : calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3481 : :
3482 : 0 : p->translate( point.x() + offset.x(), point.y() + offset.y() );
3483 : :
3484 : 0 : if ( !qgsDoubleNear( angle, 0.0 ) )
3485 : 0 : transform.rotate( angle );
3486 : :
3487 : 0 : if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3488 : : {
3489 : 0 : double s = sizeToRender / mOrigSize;
3490 : 0 : transform.scale( s, s );
3491 : 0 : }
3492 : :
3493 : 0 : if ( mUseCachedPath )
3494 : : {
3495 : 0 : p->drawPath( transform.map( mCachedPath ) );
3496 : 0 : }
3497 : : else
3498 : : {
3499 : 0 : QPainterPath path;
3500 : 0 : path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3501 : 0 : p->drawPath( transform.map( path ) );
3502 : 0 : }
3503 : 0 : }
3504 : :
3505 : 0 : QVariantMap QgsFontMarkerSymbolLayer::properties() const
3506 : : {
3507 : 0 : QVariantMap props;
3508 : 0 : props[QStringLiteral( "font" )] = mFontFamily;
3509 : 0 : props[QStringLiteral( "font_style" )] = mFontStyle;
3510 : 0 : props[QStringLiteral( "chr" )] = mString;
3511 : 0 : props[QStringLiteral( "size" )] = QString::number( mSize );
3512 : 0 : props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3513 : 0 : props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3514 : 0 : props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3515 : 0 : props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3516 : 0 : props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3517 : 0 : props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3518 : 0 : props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3519 : 0 : props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3520 : 0 : props[QStringLiteral( "angle" )] = QString::number( mAngle );
3521 : 0 : props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3522 : 0 : props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3523 : 0 : props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3524 : 0 : props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3525 : 0 : props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3526 : 0 : return props;
3527 : 0 : }
3528 : :
3529 : 0 : QgsFontMarkerSymbolLayer *QgsFontMarkerSymbolLayer::clone() const
3530 : : {
3531 : 0 : QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3532 : 0 : m->setFontStyle( mFontStyle );
3533 : 0 : m->setStrokeColor( mStrokeColor );
3534 : 0 : m->setStrokeWidth( mStrokeWidth );
3535 : 0 : m->setStrokeWidthUnit( mStrokeWidthUnit );
3536 : 0 : m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3537 : 0 : m->setPenJoinStyle( mPenJoinStyle );
3538 : 0 : m->setOffset( mOffset );
3539 : 0 : m->setOffsetUnit( mOffsetUnit );
3540 : 0 : m->setOffsetMapUnitScale( mOffsetMapUnitScale );
3541 : 0 : m->setSizeUnit( mSizeUnit );
3542 : 0 : m->setSizeMapUnitScale( mSizeMapUnitScale );
3543 : 0 : m->setHorizontalAnchorPoint( mHorizontalAnchorPoint );
3544 : 0 : m->setVerticalAnchorPoint( mVerticalAnchorPoint );
3545 : 0 : copyDataDefinedProperties( m );
3546 : 0 : copyPaintEffect( m );
3547 : 0 : return m;
3548 : 0 : }
3549 : :
3550 : 0 : void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3551 : : {
3552 : : // <Graphic>
3553 : 0 : QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3554 : 0 : element.appendChild( graphicElem );
3555 : :
3556 : 0 : QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3557 : 0 : int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3558 : 0 : double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3559 : 0 : QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3560 : :
3561 : : // <Rotation>
3562 : 0 : QString angleFunc;
3563 : : bool ok;
3564 : 0 : double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3565 : 0 : if ( !ok )
3566 : : {
3567 : 0 : angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3568 : 0 : }
3569 : 0 : else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3570 : : {
3571 : 0 : angleFunc = QString::number( angle + mAngle );
3572 : 0 : }
3573 : 0 : QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3574 : :
3575 : : // <Displacement>
3576 : 0 : QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3577 : 0 : QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
3578 : 0 : }
3579 : :
3580 : 0 : bool QgsFontMarkerSymbolLayer::usesMapUnits() const
3581 : : {
3582 : 0 : return mSizeUnit == QgsUnitTypes::RenderMapUnits || mSizeUnit == QgsUnitTypes::RenderMetersInMapUnits
3583 : 0 : || mStrokeWidthUnit == QgsUnitTypes::RenderMapUnits || mStrokeWidthUnit == QgsUnitTypes::RenderMetersInMapUnits
3584 : 0 : || mOffsetUnit == QgsUnitTypes::RenderMapUnits || mOffsetUnit == QgsUnitTypes::RenderMetersInMapUnits;
3585 : : }
3586 : :
3587 : 0 : QRectF QgsFontMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &context )
3588 : : {
3589 : 0 : QPointF chrOffset = mChrOffset;
3590 : 0 : double chrWidth = mChrWidth;
3591 : : //calculate width of rendered character
3592 : 0 : ( void )characterToRender( context, chrOffset, chrWidth );
3593 : :
3594 : 0 : if ( !mFontMetrics )
3595 : 0 : mFontMetrics.reset( new QFontMetrics( mFont ) );
3596 : :
3597 : 0 : double scaledSize = calculateSize( context );
3598 : 0 : if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3599 : : {
3600 : 0 : chrWidth *= scaledSize / mOrigSize;
3601 : 0 : }
3602 : :
3603 : 0 : bool hasDataDefinedRotation = false;
3604 : 0 : QPointF offset;
3605 : 0 : double angle = 0;
3606 : 0 : calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3607 : 0 : scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3608 : :
3609 : 0 : QMatrix transform;
3610 : :
3611 : : // move to the desired position
3612 : 0 : transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3613 : :
3614 : 0 : if ( !qgsDoubleNear( angle, 0.0 ) )
3615 : 0 : transform.rotate( angle );
3616 : :
3617 : 0 : QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3618 : 0 : -scaledSize / 2.0,
3619 : 0 : chrWidth,
3620 : 0 : scaledSize ) );
3621 : 0 : return symbolBounds;
3622 : 0 : }
3623 : :
3624 : 0 : QgsSymbolLayer *QgsFontMarkerSymbolLayer::createFromSld( QDomElement &element )
3625 : : {
3626 : 0 : QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3627 : :
3628 : 0 : QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3629 : 0 : if ( graphicElem.isNull() )
3630 : 0 : return nullptr;
3631 : :
3632 : 0 : QString name, format;
3633 : 0 : QColor color;
3634 : : double size;
3635 : : int chr;
3636 : :
3637 : 0 : if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3638 : 0 : return nullptr;
3639 : :
3640 : 0 : if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3641 : 0 : return nullptr;
3642 : :
3643 : 0 : QString fontFamily = name.mid( 6 );
3644 : :
3645 : 0 : double angle = 0.0;
3646 : 0 : QString angleFunc;
3647 : 0 : if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3648 : : {
3649 : : bool ok;
3650 : 0 : double d = angleFunc.toDouble( &ok );
3651 : 0 : if ( ok )
3652 : 0 : angle = d;
3653 : 0 : }
3654 : :
3655 : 0 : QPointF offset;
3656 : 0 : QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
3657 : :
3658 : 0 : QString uom = element.attribute( QStringLiteral( "uom" ) );
3659 : 0 : offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
3660 : 0 : offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
3661 : 0 : size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
3662 : :
3663 : 0 : QgsMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, QChar( chr ), size, color );
3664 : 0 : m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
3665 : 0 : m->setAngle( angle );
3666 : 0 : m->setOffset( offset );
3667 : 0 : return m;
3668 : 0 : }
3669 : :
3670 : :
3671 : 0 : void QgsSvgMarkerSymbolLayer::prepareExpressions( const QgsSymbolRenderContext &context )
3672 : : {
3673 : 0 : QMap<QString, QgsProperty>::iterator it = mParameters.begin();
3674 : 0 : for ( ; it != mParameters.end(); ++it )
3675 : 0 : it.value().prepare( context.renderContext().expressionContext() );
3676 : :
3677 : 0 : QgsMarkerSymbolLayer::prepareExpressions( context );
3678 : 0 : }
3679 : :
3680 : :
3681 : 0 : QSet<QString> QgsSvgMarkerSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
3682 : : {
3683 : 0 : QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
3684 : :
3685 : 0 : QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
3686 : 0 : for ( ; it != mParameters.constEnd(); ++it )
3687 : : {
3688 : 0 : attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
3689 : 0 : }
3690 : :
3691 : 0 : return attrs;
3692 : 0 : }
|