Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgslabelsearchtree.cpp
3 : : ---------------------
4 : : begin : November 2010
5 : : copyright : (C) 2010 by Marco Hugentobler
6 : : email : marco dot hugentobler at sourcepole dot ch
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 : : #include "qgslabelsearchtree.h"
16 : : #include "labelposition.h"
17 : :
18 : 0 : QgsLabelSearchTree::QgsLabelSearchTree() = default;
19 : :
20 : 0 : QgsLabelSearchTree::~QgsLabelSearchTree() = default;
21 : :
22 : 0 : void QgsLabelSearchTree::label( const QgsPointXY &point, QList<QgsLabelPosition *> &posList ) const
23 : : {
24 : 0 : QgsPointXY p( point );
25 : :
26 : 0 : QList<QgsLabelPosition *> searchResults;
27 : 0 : mSpatialIndex.intersects( QgsRectangle( p.x() - 0.1, p.y() - 0.1, p.x() + 0.1, p.y() + 0.1 ), [&searchResults]( const QgsLabelPosition * pos ) -> bool
28 : : {
29 : 0 : searchResults.push_back( const_cast< QgsLabelPosition * >( pos ) );
30 : 0 : return true;
31 : : } );
32 : :
33 : : //tolerance +-0.1 could be high in case of degree crs, so check if p is really contained in the results
34 : 0 : posList.clear();
35 : 0 : QList<QgsLabelPosition *>::const_iterator resultIt = searchResults.constBegin();
36 : 0 : for ( ; resultIt != searchResults.constEnd(); ++resultIt )
37 : : {
38 : 0 : if ( ( *resultIt )->labelGeometry.contains( &p ) )
39 : : {
40 : 0 : posList.push_back( *resultIt );
41 : 0 : }
42 : 0 : }
43 : 0 : }
44 : :
45 : 0 : void QgsLabelSearchTree::labelsInRect( const QgsRectangle &r, QList<QgsLabelPosition *> &posList ) const
46 : : {
47 : 0 : QList<QgsLabelPosition *> searchResults;
48 : 0 : mSpatialIndex.intersects( r, [&searchResults]( const QgsLabelPosition * pos )->bool
49 : : {
50 : 0 : searchResults.push_back( const_cast< QgsLabelPosition * >( pos ) );
51 : 0 : return true;
52 : : } );
53 : :
54 : 0 : posList.clear();
55 : 0 : QList<QgsLabelPosition *>::const_iterator resultIt = searchResults.constBegin();
56 : 0 : for ( ; resultIt != searchResults.constEnd(); ++resultIt )
57 : : {
58 : 0 : if ( ( *resultIt )->labelGeometry.intersects( r ) )
59 : : {
60 : 0 : posList.push_back( *resultIt );
61 : 0 : }
62 : 0 : }
63 : 0 : }
64 : :
65 : 0 : bool QgsLabelSearchTree::insertLabel( pal::LabelPosition *labelPos, QgsFeatureId featureId, const QString &layerName, const QString &labeltext, const QFont &labelfont, bool diagram, bool pinned, const QString &providerId, bool isUnplaced )
66 : : {
67 : 0 : if ( !labelPos )
68 : : {
69 : 0 : return false;
70 : : }
71 : :
72 : 0 : QVector<QgsPointXY> cornerPoints;
73 : 0 : cornerPoints.reserve( 4 );
74 : 0 : double xMin = std::numeric_limits< double >::max();
75 : 0 : double yMin = std::numeric_limits< double >::max();
76 : 0 : double xMax = std::numeric_limits< double >::lowest();
77 : 0 : double yMax = std::numeric_limits< double >::lowest();
78 : 0 : for ( int i = 0; i < 4; ++i )
79 : : {
80 : : // we have to transform the bounding box to convert pre-rotated label positions back to real world locations
81 : 0 : QPointF res = mTransform.map( QPointF( labelPos->getX( i ), labelPos->getY( i ) ) );
82 : 0 : cornerPoints.push_back( QgsPointXY( res ) );
83 : 0 : xMin = std::min( xMin, res.x() );
84 : 0 : xMax = std::max( xMax, res.x() );
85 : 0 : yMin = std::min( yMin, res.y() );
86 : 0 : yMax = std::max( yMax, res.y() );
87 : 0 : }
88 : :
89 : 0 : const QgsRectangle bounds( xMin, yMin, xMax, yMax );
90 : 0 : QgsGeometry labelGeometry( QgsGeometry::fromPolygonXY( QVector<QgsPolylineXY>() << cornerPoints ) );
91 : 0 : std::unique_ptr< QgsLabelPosition > newEntry = std::make_unique< QgsLabelPosition >( featureId, labelPos->getAlpha() + mMapSettings.rotation(), cornerPoints, bounds,
92 : 0 : labelPos->getWidth(), labelPos->getHeight(), layerName, labeltext, labelfont, labelPos->getUpsideDown(), diagram, pinned, providerId, labelGeometry, isUnplaced );
93 : 0 : mSpatialIndex.insert( newEntry.get(), bounds );
94 : 0 : mOwnedPositions.emplace_back( std::move( newEntry ) );
95 : :
96 : 0 : if ( pal::LabelPosition *next = labelPos->nextPart() )
97 : : {
98 : 0 : return insertLabel( next, featureId, layerName, labeltext, labelfont, diagram, pinned, providerId, isUnplaced );
99 : : }
100 : 0 : return true;
101 : 0 : }
102 : :
103 : 0 : bool QgsLabelSearchTree::insertCallout( const QgsCalloutPosition &position )
104 : : {
105 : 0 : const QPointF origin = position.origin();
106 : 0 : const QPointF destination = position.destination();
107 : :
108 : 0 : std::unique_ptr< QgsCalloutPosition > newEntry = std::make_unique< QgsCalloutPosition >( position );
109 : :
110 : 0 : mCalloutIndex.insert( newEntry.get(), QgsRectangle( origin.x(), origin.y(), origin.x(), origin.y() ) );
111 : 0 : mCalloutIndex.insert( newEntry.get(), QgsRectangle( destination.x(), destination.y(), destination.x(), destination.y() ) );
112 : :
113 : 0 : mOwnedCalloutPositions.emplace_back( std::move( newEntry ) );
114 : :
115 : : return true;
116 : 0 : }
117 : :
118 : 0 : QList<const QgsCalloutPosition *> QgsLabelSearchTree::calloutsInRectangle( const QgsRectangle &rectangle ) const
119 : : {
120 : 0 : QList<const QgsCalloutPosition *> searchResults;
121 : 0 : mCalloutIndex.intersects( rectangle, [&searchResults]( const QgsCalloutPosition * pos )->bool
122 : : {
123 : 0 : searchResults.push_back( pos );
124 : 0 : return true;
125 : : } );
126 : :
127 : 0 : std::sort( searchResults.begin(), searchResults.end() );
128 : 0 : searchResults.erase( std::unique( searchResults.begin(), searchResults.end() ), searchResults.end() );
129 : :
130 : 0 : return searchResults;
131 : 0 : }
132 : :
133 : 0 : void QgsLabelSearchTree::setMapSettings( const QgsMapSettings &settings )
134 : : {
135 : 0 : mMapSettings = settings;
136 : :
137 : 0 : if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
138 : : {
139 : : // build a transform to convert points from real world to pre-rotated label positions
140 : 0 : const QgsPointXY center = mMapSettings.visibleExtent().center();
141 : 0 : mTransform = QTransform::fromTranslate( center.x(), center.y() );
142 : 0 : mTransform.rotate( mMapSettings.rotation() );
143 : 0 : mTransform.translate( -center.x(), -center.y() );
144 : 0 : }
145 : : else
146 : : {
147 : 0 : mTransform = QTransform();
148 : : }
149 : 0 : }
150 : :
151 : :
152 : 0 : void QgsLabelSearchTree::clear()
153 : : {
154 : :
155 : 0 : }
|