@@ -59,12 +59,48 @@ class MyLabel : public PalGeometry
59
59
int mId ;
60
60
};
61
61
62
+ // -------------
63
+
64
+ LayerSettings::LayerSettings ()
65
+ : palLayer(NULL ), fontMetrics(NULL )
66
+ {
67
+ }
68
+
69
+ LayerSettings::~LayerSettings ()
70
+ {
71
+ // pal layer is deleted internally in PAL
72
+ delete fontMetrics;
73
+ }
74
+
75
+ void LayerSettings::calculateLabelSize (QString text, double & labelX, double & labelY)
76
+ {
77
+ // QFontMetrics fontMetrics(textFont);
78
+ QRect labelRect = /* QRect(0,0,20,20);*/ fontMetrics->boundingRect (text);
79
+
80
+ // 2px border...
81
+ QgsPoint ptSize = xform->toMapCoordinates ( labelRect.width ()+2 ,labelRect.height ()+2 );
82
+ labelX = fabs (ptSize.x ()-ptZero.x ());
83
+ labelY = fabs (ptSize.y ()-ptZero.y ());
84
+ }
85
+
86
+ void LayerSettings::registerFeature (QgsFeature& f)
87
+ {
88
+ QString labelText = f.attributeMap ()[fieldIndex].toString ();
89
+ double labelX, labelY; // will receive label size
90
+ calculateLabelSize (labelText, labelX, labelY);
91
+
92
+ // std::cout << labelX << " " << labelY << std::endl;
93
+ MyLabel* lbl = new MyLabel (f.id (), labelText, GEOSGeom_clone ( f.geometry ()->asGeos () ) );
94
+
95
+ // register feature to the layer
96
+ palLayer->registerFeature (lbl->strId (), lbl, labelX, labelY);
97
+ }
62
98
63
99
64
100
// -------------
65
101
66
102
PalLabeling::PalLabeling (QgsMapCanvas* mapCanvas)
67
- : mMapCanvas(mapCanvas)
103
+ : mMapCanvas(mapCanvas), mPal( NULL )
68
104
{
69
105
// find out engine defaults
70
106
Pal p;
@@ -79,11 +115,31 @@ PalLabeling::PalLabeling(QgsMapCanvas* mapCanvas)
79
115
case POPMUSIC_CHAIN: mSearch = Popmusic_Chain; break ;
80
116
case POPMUSIC_TABU_CHAIN: mSearch = Popmusic_Tabu_Chain; break ;
81
117
}
118
+
119
+ initPal ();
120
+ }
121
+
122
+
123
+ PalLabeling::~PalLabeling ()
124
+ {
125
+ delete mPal ;
126
+
127
+ // make sure to remove hooks from all layers
128
+ while (mLayers .count ())
129
+ {
130
+ removeLayer (mLayers [0 ].layerId );
131
+ }
82
132
}
83
133
134
+
84
135
void PalLabeling::addLayer (LayerSettings layerSettings)
85
136
{
86
137
mLayers .append (layerSettings);
138
+
139
+ QgsVectorLayer* vlayer = (QgsVectorLayer*) QgsMapLayerRegistry::instance ()->mapLayer (layerSettings.layerId );
140
+
141
+ LayerSettings& lyr = mLayers [ mLayers .count ()-1 ]; // make sure we have the right pointer
142
+ vlayer->setLabelingHooks (PalLabeling::prepareLayerHook, PalLabeling::registerFeatureHook, this , &lyr);
87
143
}
88
144
89
145
void PalLabeling::removeLayer (QString layerId)
@@ -92,13 +148,16 @@ void PalLabeling::removeLayer(QString layerId)
92
148
{
93
149
if (mLayers .at (i).layerId == layerId)
94
150
{
151
+ QgsVectorLayer* vlayer = (QgsVectorLayer*) QgsMapLayerRegistry::instance ()->mapLayer (mLayers .at (i).layerId );
152
+ if (vlayer) { vlayer->setLabelingHooks (NULL , NULL , NULL , NULL ); }
153
+
95
154
mLayers .removeAt (i);
96
155
return ;
97
156
}
98
157
}
99
158
}
100
159
101
- PalLabeling:: LayerSettings PalLabeling::layer (QString layerId)
160
+ LayerSettings PalLabeling::layer (QString layerId)
102
161
{
103
162
for (int i = 0 ; i < mLayers .count (); i++)
104
163
{
@@ -111,71 +170,63 @@ PalLabeling::LayerSettings PalLabeling::layer(QString layerId)
111
170
}
112
171
113
172
114
- int PalLabeling::prepareLayer (Pal& pal, const LayerSettings& lyr)
173
+
174
+ int PalLabeling::prepareLayerHook (void * context, void * layerContext, int & attrIndex)
115
175
{
116
- if (!lyr. enabled )
117
- return 0 ;
176
+ PalLabeling* thisClass = (PalLabeling*) context;
177
+ LayerSettings* lyr = (LayerSettings*) layerContext ;
118
178
119
- QgsVectorLayer* vlayer = (QgsVectorLayer*) QgsMapLayerRegistry::instance ()->mapLayer (lyr. layerId );
179
+ QgsVectorLayer* vlayer = (QgsVectorLayer*) QgsMapLayerRegistry::instance ()->mapLayer (lyr-> layerId );
120
180
if (vlayer == NULL )
121
181
return 0 ;
122
182
123
- QgsAttributeList attrs;
124
-
125
- int fldName = vlayer->dataProvider ()->fieldNameIndex (lyr.fieldName );
126
- if (fldName == -1 )
183
+ // find out which field will be needed
184
+ int fldIndex = vlayer->dataProvider ()->fieldNameIndex (lyr->fieldName );
185
+ if (fldIndex == -1 )
127
186
return 0 ;
128
- attrs << fldName;
129
- vlayer->select (attrs, mMapCanvas ->extent ());
187
+ attrIndex = fldIndex;
130
188
131
189
132
190
// how to place the labels
133
191
Arrangement arrangement;
134
- switch (lyr. placement )
192
+ switch (lyr-> placement )
135
193
{
136
- case AroundPoint: arrangement = P_POINT; break ;
137
- case OnLine: arrangement = P_LINE; break ;
138
- case AroundLine: arrangement = P_LINE_AROUND; break ;
139
- case Horizontal: arrangement = P_HORIZ; break ;
140
- case Free: arrangement = P_FREE; break ;
194
+ case LayerSettings:: AroundPoint: arrangement = P_POINT; break ;
195
+ case LayerSettings:: OnLine: arrangement = P_LINE; break ;
196
+ case LayerSettings:: AroundLine: arrangement = P_LINE_AROUND; break ;
197
+ case LayerSettings:: Horizontal: arrangement = P_HORIZ; break ;
198
+ case LayerSettings:: Free: arrangement = P_FREE; break ;
141
199
}
142
200
143
201
// create the pal layer
144
- double priority = 1 - lyr.priority /10.0 ; // convert 0..10 --> 1..0
145
- Layer* l = pal.addLayer (lyr.layerId .toLocal8Bit ().data (), -1 , -1 , arrangement, METER, priority, lyr.obstacle , true , true );
146
-
147
- QFontMetrics fm (lyr.textFont );
148
-
149
- QgsFeature f;
150
- int feats = 0 ;
151
- const QgsMapToPixel* xform = mMapCanvas ->mapRenderer ()->coordinateTransform ();
152
- QgsPoint ptZero = xform->toMapCoordinates ( 0 ,0 );
153
-
154
- while (vlayer->nextFeature (f))
155
- {
156
- QString labelText = f.attributeMap ()[fldName].toString ();
157
- QRect labelRect = fm.boundingRect (labelText);
158
- // std::cout << "bound: " << labelRect.width() << "x" << labelRect.height() << std::endl;
159
- // 2px border...
160
- QgsPoint ptSize = xform->toMapCoordinates ( labelRect.width ()+2 ,labelRect.height ()+2 );
161
- double labelX = fabs (ptSize.x ()-ptZero.x ());
162
- double labelY = fabs (ptSize.y ()-ptZero.y ());
163
- // std::cout << "L " << labelX << " " << labelY << std::endl;
164
-
165
- MyLabel* lbl = new MyLabel (f.id (), labelText, GEOSGeom_clone ( f.geometry ()->asGeos () ) );
166
-
167
- // TODO: owner of the id?
168
- l->registerFeature (lbl->strId (), lbl, labelX, labelY);
169
- feats++;
170
- }
202
+ double priority = 1 - lyr->priority /10.0 ; // convert 0..10 --> 1..0
203
+ Layer* l = thisClass->mPal ->addLayer (lyr->layerId .toLocal8Bit ().data (), -1 , -1 , arrangement, METER, priority, lyr->obstacle , true , true );
204
+
205
+ // save the pal layer to our layer context (with some additional info)
206
+ lyr->palLayer = l;
207
+ lyr->fieldIndex = fldIndex;
208
+ lyr->fontMetrics = new QFontMetrics (lyr->textFont );
209
+ lyr->fontBaseline = lyr->fontMetrics ->boundingRect (" X" ).bottom (); // dummy text to find out how many pixels of the text are below the baseline
210
+ lyr->xform = thisClass->mMapCanvas ->mapRenderer ()->coordinateTransform ();
211
+ lyr->ptZero = lyr->xform ->toMapCoordinates ( 0 ,0 );
212
+
213
+ return 1 ; // init successful
214
+ }
171
215
172
- return feats;
216
+ void PalLabeling::registerFeatureHook (QgsFeature& f, void * layerContext)
217
+ {
218
+ LayerSettings* lyr = (LayerSettings*) layerContext;
219
+ lyr->registerFeature (f);
173
220
}
174
221
175
222
176
- void PalLabeling::doLabeling (QPainter* painter )
223
+ void PalLabeling::initPal ( )
177
224
{
178
- Pal p;
225
+ // delete if exists already
226
+ if (mPal )
227
+ delete mPal ;
228
+
229
+ mPal = new Pal;
179
230
180
231
SearchMethod s;
181
232
switch (mSearch )
@@ -185,32 +236,31 @@ void PalLabeling::doLabeling(QPainter* painter)
185
236
case Popmusic_Chain: s = POPMUSIC_CHAIN; break ;
186
237
case Popmusic_Tabu_Chain: s = POPMUSIC_TABU_CHAIN; break ;
187
238
}
188
- p. setSearch (s);
239
+ mPal -> setSearch (s);
189
240
190
241
// set number of candidates generated per feature
191
- p.setPointP (mCandPoint );
192
- p.setLineP (mCandLine );
193
- p.setPolyP (mCandPolygon );
242
+ mPal ->setPointP (mCandPoint );
243
+ mPal ->setLineP (mCandLine );
244
+ mPal ->setPolyP (mCandPolygon );
245
+ }
246
+
247
+
194
248
195
- // p.setSearch(POPMUSIC_TABU_CHAIN);// this is really slow! // default is CHAIN (worst, fastest)
196
- // TODO: API 0.2 - no mention about changing map units!
197
- // pal map units = METER by default ... change setMapUnit
198
- // p.setMapUnit(METER);
199
- // pal label units ... to be chosen
200
- // pal dist label - pixels?
249
+ void PalLabeling::doLabeling (QPainter* painter)
250
+ {
201
251
202
252
QTime t;
203
253
t.start ();
204
254
205
- int feats = 0 ;
255
+ // make sure to delete fontmetrics otherwise it crashes inside Qt when drawing... :-(
256
+ // probably gets invalid when setting fonts in the label drawing loop
206
257
for (int i = 0 ; i < mLayers .count (); i++)
207
258
{
208
- feats += prepareLayer (p, mLayers .at (i));
259
+ LayerSettings& lyr = mLayers [i];
260
+ delete lyr.fontMetrics ;
261
+ lyr.fontMetrics = NULL ;
209
262
}
210
263
211
- std::cout << " LABELING prepare: " << t.elapsed () << " ms" << std::endl;
212
- t.restart ();
213
-
214
264
// do the labeling itself
215
265
double scale = 1 ; // scale denominator
216
266
QgsRectangle r = mMapCanvas ->extent ();
@@ -219,22 +269,17 @@ void PalLabeling::doLabeling(QPainter* painter)
219
269
std::list<Label*>* labels;
220
270
try
221
271
{
222
- labels = p. labeller (scale, bbox, NULL , false );
272
+ labels = mPal -> labeller (scale, bbox, NULL , false );
223
273
}
224
274
catch ( std::exception e )
225
275
{
226
276
std::cerr << " PAL EXCEPTION :-( " << e.what () << std::endl;
227
277
return ;
228
278
}
229
279
230
- std::cout << " LABELING work: " << t.elapsed () << " ms" << std::endl;
231
- std::cout << " -->> " << labels->size () << " /" << feats << std::endl;
280
+ std::cout << " LABELING work: " << t.elapsed () << " ms ... labels# " << labels->size () << std::endl;
232
281
t.restart ();
233
282
234
- QFontMetrics fm = painter->fontMetrics ();
235
- QRect labelRect = fm.boundingRect (" X" ); // dummy text to find out height
236
- int baseline = labelRect.bottom (); // how many pixels of the text are below the baseline
237
-
238
283
// draw the labels
239
284
const QgsMapToPixel* xform = mMapCanvas ->mapRenderer ()->coordinateTransform ();
240
285
std::list<Label*>::iterator it = labels->begin ();
@@ -251,7 +296,7 @@ void PalLabeling::doLabeling(QPainter* painter)
251
296
painter->save ();
252
297
painter->setPen ( lyr.textColor );
253
298
painter->setFont ( lyr.textFont );
254
- painter->translate ( QPointF (outPt.x ()+1 , outPt.y ()-1 -baseline ) );
299
+ painter->translate ( QPointF (outPt.x ()+1 , outPt.y ()-1 -lyr. fontBaseline ) );
255
300
painter->rotate (-label->getRotation () * 180 / M_PI );
256
301
painter->drawText (0 ,0 , ((MyLabel*)label->getGeometry ())->text ());
257
302
painter->restore ();
@@ -263,6 +308,9 @@ void PalLabeling::doLabeling(QPainter* painter)
263
308
std::cout << " LABELING draw: " << t.elapsed () << " ms" << std::endl;
264
309
265
310
delete labels;
311
+
312
+ // re-create PAL
313
+ initPal ();
266
314
}
267
315
268
316
void PalLabeling::numCandidatePositions (int & candPoint, int & candLine, int & candPolygon)
0 commit comments