Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgslegendpatchshape.cpp 3 : : ------------------- 4 : : begin : April 2020 5 : : copyright : (C) 2020 by Nyall Dawson 6 : : email : nyall dot dawson at gmail dot com 7 : : ***************************************************************************/ 8 : : 9 : : /*************************************************************************** 10 : : * * 11 : : * This program is free software; you can redistribute it and/or modify * 12 : : * it under the terms of the GNU General Public License as published by * 13 : : * the Free Software Foundation; either version 2 of the License, or * 14 : : * (at your option) any later version. * 15 : : * * 16 : : ***************************************************************************/ 17 : : 18 : : #include "qgslegendpatchshape.h" 19 : : #include "qgsmultilinestring.h" 20 : : #include "qgslinestring.h" 21 : : #include "qgspolygon.h" 22 : : #include "qgsstyle.h" 23 : : 24 : 0 : QgsLegendPatchShape::QgsLegendPatchShape( QgsSymbol::SymbolType type, const QgsGeometry &geometry, bool preserveAspectRatio ) 25 : 0 : : mSymbolType( type ) 26 : 0 : , mGeometry( geometry ) 27 : 0 : , mPreserveAspectRatio( preserveAspectRatio ) 28 : : { 29 : : 30 : 0 : } 31 : : 32 : 0 : bool QgsLegendPatchShape::isNull() const 33 : : { 34 : 0 : return mGeometry.isNull() || mGeometry.isEmpty(); 35 : : } 36 : : 37 : 0 : QgsGeometry QgsLegendPatchShape::geometry() const 38 : : { 39 : 0 : return mGeometry; 40 : : } 41 : : 42 : 0 : void QgsLegendPatchShape::setGeometry( const QgsGeometry &geometry ) 43 : : { 44 : 0 : mGeometry = geometry; 45 : 0 : } 46 : : 47 : 0 : bool QgsLegendPatchShape::preserveAspectRatio() const 48 : : { 49 : 0 : return mPreserveAspectRatio; 50 : : } 51 : : 52 : 0 : void QgsLegendPatchShape::setPreserveAspectRatio( bool preserveAspectRatio ) 53 : : { 54 : 0 : mPreserveAspectRatio = preserveAspectRatio; 55 : 0 : } 56 : : 57 : 0 : QPolygonF lineStringToQPolygonF( const QgsLineString *line ) 58 : : { 59 : 0 : const double *srcX = line->xData(); 60 : 0 : const double *srcY = line->yData(); 61 : 0 : const int count = line->numPoints(); 62 : 0 : QPolygonF thisRes( count ); 63 : 0 : QPointF *dest = thisRes.data(); 64 : 0 : for ( int i = 0; i < count; ++i ) 65 : : { 66 : 0 : *dest++ = QPointF( *srcX++, *srcY++ ); 67 : 0 : } 68 : 0 : return thisRes; 69 : 0 : } 70 : : 71 : 0 : QPolygonF curveToPolygonF( const QgsCurve *curve ) 72 : : { 73 : 0 : if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( curve ) ) 74 : : { 75 : 0 : return lineStringToQPolygonF( line ); 76 : : } 77 : : else 78 : : { 79 : 0 : std::unique_ptr< QgsLineString > straightened( curve->curveToLine() ); 80 : 0 : return lineStringToQPolygonF( straightened.get() ); 81 : 0 : } 82 : 0 : } 83 : : 84 : 0 : QList<QList<QPolygonF> > QgsLegendPatchShape::toQPolygonF( QgsSymbol::SymbolType type, QSizeF size ) const 85 : : { 86 : 0 : if ( isNull() || type != mSymbolType ) 87 : 0 : return QgsStyle::defaultStyle()->defaultPatchAsQPolygonF( type, size ); 88 : : 89 : : // scale and translate to desired size 90 : : 91 : 0 : const QRectF bounds = mGeometry.boundingBox().toRectF(); 92 : : 93 : 0 : double dx = 0; 94 : 0 : double dy = 0; 95 : 0 : if ( mPreserveAspectRatio && bounds.height() > 0 && bounds.width() > 0 ) 96 : : { 97 : 0 : const double scaling = std::min( size.width() / bounds.width(), size.height() / bounds.height() ); 98 : 0 : const QSizeF scaledSize = bounds.size() * scaling; 99 : 0 : dx = ( size.width() - scaledSize.width() ) / 2.0; 100 : 0 : dy = ( size.height() - scaledSize.height() ) / 2.0; 101 : 0 : size = scaledSize; 102 : 0 : } 103 : : 104 : : // important -- the transform needs to flip from north-up to painter style "increasing y down" coordinates 105 : 0 : QPolygonF targetRectPoly = QPolygonF() << QPointF( dx, dy + size.height() ) 106 : 0 : << QPointF( dx + size.width(), dy + size.height() ) 107 : 0 : << QPointF( dx + size.width(), dy ) 108 : 0 : << QPointF( dx, dy ); 109 : 0 : QTransform t; 110 : : 111 : 0 : if ( bounds.width() > 0 && bounds.height() > 0 ) 112 : : { 113 : 0 : QPolygonF patchRectPoly = QPolygonF( bounds ); 114 : : //workaround QT Bug #21329 115 : 0 : patchRectPoly.pop_back(); 116 : : 117 : 0 : QTransform::quadToQuad( patchRectPoly, targetRectPoly, t ); 118 : 0 : } 119 : 0 : else if ( bounds.width() > 0 ) 120 : : { 121 : 0 : t = QTransform::fromScale( size.width() / bounds.width(), 1 ).translate( -bounds.left(), size.height() / 2 - bounds.y() ); 122 : 0 : } 123 : 0 : else if ( bounds.height() > 0 ) 124 : : { 125 : 0 : t = QTransform::fromScale( 1, size.height() / bounds.height() ).translate( size.width() / 2 - bounds.x(), -bounds.top() ); 126 : 0 : } 127 : : 128 : 0 : QgsGeometry geom = mGeometry; 129 : 0 : geom.transform( t ); 130 : : 131 : 0 : switch ( mSymbolType ) 132 : : { 133 : : case QgsSymbol::Marker: 134 : : { 135 : 0 : QPolygonF points; 136 : : 137 : 0 : if ( QgsWkbTypes::flatType( mGeometry.wkbType() ) == QgsWkbTypes::MultiPoint ) 138 : : { 139 : 0 : const QgsGeometry patch = geom; 140 : 0 : for ( auto it = patch.vertices_begin(); it != patch.vertices_end(); ++it ) 141 : 0 : points << QPointF( ( *it ).x(), ( *it ).y() ); 142 : 0 : } 143 : : else 144 : : { 145 : 0 : points << QPointF( static_cast< int >( size.width() ) / 2, static_cast< int >( size.height() ) / 2 ); 146 : : } 147 : 0 : return QList< QList<QPolygonF> >() << ( QList< QPolygonF >() << points ); 148 : 0 : } 149 : : 150 : : case QgsSymbol::Line: 151 : : { 152 : 0 : QList< QList<QPolygonF> > res; 153 : 0 : const QgsGeometry patch = geom; 154 : 0 : if ( QgsWkbTypes::geometryType( mGeometry.wkbType() ) == QgsWkbTypes::LineGeometry ) 155 : : { 156 : 0 : for ( auto it = patch.const_parts_begin(); it != patch.const_parts_end(); ++it ) 157 : : { 158 : 0 : res << ( QList< QPolygonF >() << curveToPolygonF( qgsgeometry_cast< const QgsCurve * >( *it ) ) ); 159 : 0 : } 160 : 0 : } 161 : 0 : return res; 162 : 0 : } 163 : : 164 : : case QgsSymbol::Fill: 165 : : { 166 : 0 : QList< QList<QPolygonF> > res; 167 : : 168 : 0 : const QgsGeometry patch = geom; 169 : 0 : for ( auto it = patch.const_parts_begin(); it != patch.const_parts_end(); ++it ) 170 : : { 171 : 0 : QList<QPolygonF> thisPart; 172 : 0 : const QgsCurvePolygon *surface = qgsgeometry_cast< const QgsCurvePolygon * >( *it ); 173 : 0 : if ( !surface ) 174 : 0 : continue; 175 : : 176 : 0 : if ( !surface->exteriorRing() ) 177 : 0 : continue; 178 : : 179 : 0 : thisPart << curveToPolygonF( surface->exteriorRing() ); 180 : : 181 : 0 : for ( int i = 0; i < surface->numInteriorRings(); ++i ) 182 : 0 : thisPart << curveToPolygonF( surface->interiorRing( i ) ); 183 : 0 : res << thisPart; 184 : 0 : } 185 : : 186 : 0 : return res; 187 : 0 : } 188 : : 189 : : case QgsSymbol::Hybrid: 190 : 0 : return QList< QList<QPolygonF> >(); 191 : : } 192 : : 193 : 0 : return QList< QList<QPolygonF> >(); 194 : 0 : } 195 : : 196 : 0 : void QgsLegendPatchShape::readXml( const QDomElement &element, const QgsReadWriteContext & ) 197 : : { 198 : 0 : mGeometry = QgsGeometry::fromWkt( element.attribute( QStringLiteral( "wkt" ) ) ); 199 : 0 : mPreserveAspectRatio = element.attribute( QStringLiteral( "preserveAspect" ) ).toInt(); 200 : 0 : mSymbolType = static_cast< QgsSymbol::SymbolType >( element.attribute( QStringLiteral( "type" ) ).toInt() ); 201 : 0 : } 202 : : 203 : 0 : void QgsLegendPatchShape::writeXml( QDomElement &element, QDomDocument &, const QgsReadWriteContext & ) const 204 : : { 205 : 0 : element.setAttribute( QStringLiteral( "wkt" ), mGeometry.isNull() ? QString() : mGeometry.asWkt( ) ); 206 : 0 : element.setAttribute( QStringLiteral( "preserveAspect" ), mPreserveAspectRatio ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ); 207 : 0 : element.setAttribute( QStringLiteral( "type" ), QString::number( mSymbolType ) ); 208 : 0 : } 209 : : 210 : 0 : QgsSymbol::SymbolType QgsLegendPatchShape::symbolType() const 211 : : { 212 : 0 : return mSymbolType; 213 : : } 214 : : 215 : 0 : void QgsLegendPatchShape::setSymbolType( QgsSymbol::SymbolType type ) 216 : : { 217 : 0 : mSymbolType = type; 218 : 0 : }