Skip to content

Commit 166b183

Browse files
author
wonder
committed
Another round of refactoring.
Moved most of the candidate cost handling code to separate CostCalculator source. Some more "friend class" removals. git-svn-id: http://svn.osgeo.org/qgis/branches/symbology-ng-branch@11045 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent 5cfe487 commit 166b183

File tree

12 files changed

+575
-566
lines changed

12 files changed

+575
-566
lines changed

src/core/pal/costcalculator.cpp

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
2+
#include <iostream>
3+
#include <fstream>
4+
#include <cmath>
5+
#include <cstring>
6+
#include <cfloat>
7+
8+
#include <pal/layer.h>
9+
#include <pal/pal.h>
10+
#include <pal/label.h>
11+
12+
#include "feature.h"
13+
#include "geomfunction.h"
14+
#include "labelposition.h"
15+
#include "util.h"
16+
17+
#include "costcalculator.h"
18+
19+
namespace pal
20+
{
21+
22+
void CostCalculator::addObstacleCostPenalty(LabelPosition* lp, PointSet* feat)
23+
{
24+
int n = 0;
25+
double dist;
26+
double distlabel = lp->feature->getLabelDistance();
27+
/*unit_convert( double( lp->feature->distlabel ),
28+
pal::PIXEL,
29+
pal->map_unit,
30+
pal->dpi, scale, 1 );*/
31+
32+
switch ( feat->getGeosType() )
33+
{
34+
case GEOS_POINT:
35+
36+
dist = lp->getDistanceToPoint( feat->x[0], feat->y[0] );
37+
if ( dist < 0 )
38+
n = 2;
39+
else if ( dist < distlabel )
40+
n = 1;
41+
else
42+
n = 0;
43+
break;
44+
45+
case GEOS_LINESTRING:
46+
47+
// Is one of label's borders crossing the line ?
48+
n = ( lp->isBorderCrossingLine( feat ) ? 1 : 0 );
49+
break;
50+
51+
case GEOS_POLYGON:
52+
n = lp->getNumPointsInPolygon( feat->getNumPoints(), feat->x, feat->y );
53+
break;
54+
}
55+
56+
// label cost is penalized
57+
lp->setCost( lp->getCost() + double( n ) );
58+
}
59+
60+
61+
////////
62+
63+
void CostCalculator::setPolygonCandidatesCost( int nblp, LabelPosition **lPos, int max_p, RTree<PointSet*, double, 2, double> *obstacles, double bbx[4], double bby[4] )
64+
{
65+
int i;
66+
67+
double normalizer;
68+
// compute raw cost
69+
#ifdef _DEBUG_
70+
std::cout << "LabelPosition for feat: " << lPos[0]->feature->uid << std::endl;
71+
#endif
72+
73+
for ( i = 0;i < nblp;i++ )
74+
setCandidateCostFromPolygon( lPos[i], obstacles, bbx, bby );
75+
76+
// lPos with big values came fisrts (value = min distance from label to Polygon's Perimeter)
77+
//sort ( (void**) lPos, nblp, costGrow);
78+
sort(( void** ) lPos, nblp, LabelPosition::costShrink );
79+
80+
81+
// define the value's range
82+
double cost_max = lPos[0]->getCost();
83+
double cost_min = lPos[max_p-1]->getCost();
84+
85+
cost_max -= cost_min;
86+
87+
if ( cost_max > EPSILON )
88+
{
89+
normalizer = 0.0020 / cost_max;
90+
}
91+
else
92+
{
93+
normalizer = 1;
94+
}
95+
96+
// adjust cost => the best is 0.0001, the worst is 0.0021
97+
// others are set proportionally between best and worst
98+
for ( i = 0;i < max_p;i++ )
99+
{
100+
#ifdef _DEBUG_
101+
std::cout << " lpos[" << i << "] = " << lPos[i]->cost;
102+
#endif
103+
//if (cost_max - cost_min < EPSILON)
104+
if ( cost_max > EPSILON )
105+
{
106+
lPos[i]->cost = 0.0021 - ( lPos[i]->getCost() - cost_min ) * normalizer;
107+
}
108+
else
109+
{
110+
//lPos[i]->cost = 0.0001 + (lPos[i]->cost - cost_min) * normalizer;
111+
lPos[i]->cost = 0.0001;
112+
}
113+
114+
#ifdef _DEBUG_
115+
std::cout << " ==> " << lPos[i]->cost << std::endl;
116+
#endif
117+
}
118+
}
119+
120+
121+
void CostCalculator::setCandidateCostFromPolygon( LabelPosition* lp, RTree <PointSet*, double, 2, double> *obstacles, double bbx[4], double bby[4] )
122+
{
123+
124+
double amin[2];
125+
double amax[2];
126+
127+
PolygonCostCalculator *pCost = new PolygonCostCalculator( lp );
128+
129+
// center
130+
//cost = feat->getDistInside((this->x[0] + this->x[2])/2.0, (this->y[0] + this->y[2])/2.0 );
131+
132+
lp->feature->fetchCoordinates();
133+
pCost->update( lp->feature );
134+
135+
PointSet *extent = new PointSet( 4, bbx, bby );
136+
137+
pCost->update( extent );
138+
139+
delete extent;
140+
141+
lp->feature->getBoundingBox(amin, amax);
142+
143+
obstacles->Search( amin, amax, LabelPosition::polygonObstacleCallback, pCost );
144+
145+
lp->setCost( pCost->getCost() );
146+
147+
lp->feature->releaseCoordinates();
148+
delete pCost;
149+
}
150+
151+
152+
int CostCalculator::finalizeCandidatesCosts( Feats* feat, int max_p, RTree <PointSet*, double, 2, double> *obstacles, double bbx[4], double bby[4] )
153+
{
154+
// If candidates list is smaller than expected
155+
if ( max_p > feat->nblp )
156+
max_p = feat->nblp;
157+
//
158+
// sort candidates list, best label to worst
159+
sort(( void** ) feat->lPos, feat->nblp, LabelPosition::costGrow );
160+
161+
// try to exclude all conflitual labels (good ones have cost < 1 by pruning)
162+
double discrim = 0.0;
163+
int stop;
164+
do
165+
{
166+
discrim += 1.0;
167+
for ( stop = 0;stop < feat->nblp && feat->lPos[stop]->getCost() < discrim;stop++ );
168+
}
169+
while ( stop == 0 && discrim < feat->lPos[feat->nblp-1]->getCost() + 2.0 );
170+
171+
if ( discrim > 1.5 )
172+
{
173+
int k;
174+
for ( k = 0;k < stop;k++ )
175+
feat->lPos[k]->setCost( 0.0021 );
176+
}
177+
178+
if ( max_p > stop )
179+
max_p = stop;
180+
181+
#ifdef _DEBUG_FULL_
182+
std::cout << "Nblabel kept for feat " << feat->feature->uid << "/" << feat->feature->layer->name << ": " << max_p << "/" << feat->nblp << std::endl;
183+
#endif
184+
185+
// Sets costs for candidates of polygon
186+
187+
if ( feat->feature->getGeosType() == GEOS_POLYGON )
188+
{
189+
int arrangement = feat->feature->getLayer()->getArrangement();
190+
if ( arrangement == P_FREE || arrangement == P_HORIZ )
191+
setPolygonCandidatesCost( stop, feat->lPos, max_p, obstacles, bbx, bby );
192+
}
193+
194+
return max_p;
195+
}
196+
197+
198+
199+
//////////
200+
201+
PolygonCostCalculator::PolygonCostCalculator( LabelPosition *lp ) : lp( lp )
202+
{
203+
int i;
204+
double hyp = max( lp->feature->xmax - lp->feature->xmin, lp->feature->ymax - lp->feature->ymin );
205+
hyp *= 10;
206+
207+
px = ( lp->x[0] + lp->x[2] ) / 2.0;
208+
py = ( lp->y[0] + lp->y[2] ) / 2.0;
209+
dLp[0] = lp->w / 2;
210+
dLp[1] = lp->h / 2;
211+
dLp[2] = dLp[0] / cos( M_PI / 4 );
212+
213+
/*
214+
3 2 1
215+
\ | /
216+
4 --x -- 0
217+
/ | \
218+
5 6 7
219+
*/
220+
221+
double alpha = lp->getAlpha();
222+
for ( i = 0;i < 8;i++, alpha += M_PI / 4 )
223+
{
224+
dist[i] = DBL_MAX;
225+
ok[i] = false;
226+
rpx[i] = px + cos( alpha ) * hyp;
227+
rpy[i] = py + sin( alpha ) * hyp;
228+
}
229+
}
230+
231+
void PolygonCostCalculator::update( PointSet *pset )
232+
{
233+
if ( pset->type == GEOS_POINT )
234+
{
235+
updatePoint( pset );
236+
}
237+
else
238+
{
239+
double rx, ry;
240+
if ( pset->getDist( px, py, &rx, &ry ) < updateLinePoly( pset ) )
241+
{
242+
PointSet *point = new PointSet( ry, ry );
243+
update( point );
244+
delete point;
245+
}
246+
}
247+
}
248+
249+
void PolygonCostCalculator::updatePoint( PointSet *pset )
250+
{
251+
double beta = atan2( pset->y[0] - py, pset->x[0] - px ) - lp->getAlpha();
252+
253+
while ( beta < 0 )
254+
{
255+
beta += 2 * M_PI;
256+
}
257+
258+
double a45 = M_PI / 4;
259+
260+
int i = ( int )( beta / a45 );
261+
262+
for ( int j = 0;j < 2;j++, i = ( i + 1 ) % 8 )
263+
{
264+
double rx, ry;
265+
rx = px - rpy[i] + py;
266+
ry = py + rpx[i] - px;
267+
double ix, iy; // the point that we look for
268+
if ( computeLineIntersection( px, py, rpx[i], rpy[i], pset->x[0], pset->y[0], rx, ry, &ix, &iy ) )
269+
{
270+
double d = dist_euc2d_sq( px, py, ix, iy );
271+
if ( d < dist[i] )
272+
{
273+
dist[i] = d;
274+
ok[i] = true;
275+
}
276+
}
277+
else
278+
{
279+
std::cout << "this shouldn't occurs !!!" << std::endl;
280+
}
281+
}
282+
}
283+
284+
double PolygonCostCalculator::updateLinePoly( PointSet *pset )
285+
{
286+
int i, j, k;
287+
int nbP = ( pset->type == GEOS_POLYGON ? pset->nbPoints : pset->nbPoints - 1 );
288+
double min_dist = DBL_MAX;
289+
290+
for ( i = 0;i < nbP;i++ )
291+
{
292+
j = ( i + 1 ) % pset->nbPoints;
293+
294+
for ( k = 0;k < 8;k++ )
295+
{
296+
double ix, iy;
297+
if ( computeSegIntersection( px, py, rpx[k], rpy[k], pset->x[i], pset->y[i], pset->x[j], pset->y[j], &ix, &iy ) )
298+
{
299+
double d = dist_euc2d_sq( px, py, ix, iy );
300+
if ( d < dist[k] )
301+
{
302+
dist[k] = d;
303+
ok[k] = true;
304+
}
305+
if ( d < min_dist )
306+
{
307+
min_dist = d;
308+
}
309+
}
310+
}
311+
}
312+
return min_dist;
313+
}
314+
315+
LabelPosition* PolygonCostCalculator::getLabel()
316+
{
317+
return lp;
318+
}
319+
320+
double PolygonCostCalculator::getCost()
321+
{
322+
int i;
323+
324+
for ( i = 0;i < 8;i++ )
325+
{
326+
dist[i] -= (( i % 2 ) ? dLp[2] : (( i == 0 || i == 4 ) ? dLp[0] : dLp[1] ) );
327+
if ( !ok[i] || dist[i] < 0.1 )
328+
{
329+
dist[i] = 0.1;
330+
}
331+
}
332+
333+
double a, b, c, d;
334+
335+
a = min( dist[0], dist[4] );
336+
b = min( dist[1], dist[5] );
337+
c = min( dist[2], dist[6] );
338+
d = min( dist[3], dist[7] );
339+
340+
//return (a+b+c+d);
341+
return ( a*b*c*d );
342+
}
343+
344+
}

0 commit comments

Comments
 (0)