QGIS API Documentation  2.12.0-Lyon
feature.cpp
Go to the documentation of this file.
1 /*
2  * libpal - Automated Placement of Labels Library
3  *
4  * Copyright (C) 2008 Maxence Laurent, MIS-TIC, HEIG-VD
5  * University of Applied Sciences, Western Switzerland
6  * http://www.hes-so.ch
7  *
8  * Contact:
9  * maxence.laurent <at> heig-vd <dot> ch
10  * or
11  * eric.taillard <at> heig-vd <dot> ch
12  *
13  * This file is part of libpal.
14  *
15  * libpal is free software: you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation, either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * libpal is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with libpal. If not, see <http://www.gnu.org/licenses/>.
27  *
28  */
29 
30 #define _CRT_SECURE_NO_DEPRECATE
31 
32 
33 #if defined(_VERBOSE_) || (_DEBUG_)
34 #include <iostream>
35 #endif
36 
37 #include "qgsgeometry.h"
38 #include "pal.h"
39 #include "layer.h"
40 #include "feature.h"
41 #include "geomfunction.h"
42 #include "labelposition.h"
43 #include "pointset.h"
44 #include "util.h"
45 #include "qgis.h"
46 #include "qgsgeos.h"
47 #include "qgsmessagelog.h"
48 #include "costcalculator.h"
49 #include <QLinkedList>
50 #include <cmath>
51 #include <cfloat>
52 
53 #ifndef M_PI
54 #define M_PI 3.14159265358979323846
55 #endif
56 
57 namespace pal
58 {
59 
60  FeaturePart::FeaturePart( QgsLabelFeature* feat, const GEOSGeometry* geom )
61  : mLF( feat )
62  {
63  // we'll remove const, but we won't modify that geometry
64  mGeos = const_cast<GEOSGeometry*>( geom );
65  mOwnsGeom = false; // geometry is owned by Feature class
66 
67  extractCoords( geom );
68 
69  holeOf = NULL;
70  for ( int i = 0; i < mHoles.count(); i++ )
71  {
72  mHoles.at( i )->holeOf = this;
73  }
74 
75  }
76 
77 
79  {
80  // X and Y are deleted in PointSet
81 
82  qDeleteAll( mHoles );
83  mHoles.clear();
84  }
85 
86  void FeaturePart::extractCoords( const GEOSGeometry* geom )
87  {
88  const GEOSCoordSequence *coordSeq;
89  GEOSContextHandle_t geosctxt = geosContext();
90 
91  type = GEOSGeomTypeId_r( geosctxt, geom );
92 
93  if ( type == GEOS_POLYGON )
94  {
95  if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
96  {
97  int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
98 
99  for ( int i = 0; i < numHoles; ++i )
100  {
101  const GEOSGeometry* interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
102  FeaturePart* hole = new FeaturePart( mLF, interior );
103  hole->holeOf = NULL;
104  // possibly not needed. it's not done for the exterior ring, so I'm not sure
105  // why it's just done here...
106  reorderPolygon( hole->nbPoints, hole->x, hole->y );
107 
108  mHoles << hole;
109  }
110  }
111 
112  // use exterior ring for the extraction of coordinates that follows
113  geom = GEOSGetExteriorRing_r( geosctxt, geom );
114  }
115  else
116  {
117  qDeleteAll( mHoles );
118  mHoles.clear();
119  }
120 
121  // find out number of points
122  nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
123  coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
124 
125  // initialize bounding box
126  xmin = ymin = DBL_MAX;
127  xmax = ymax = -DBL_MAX;
128 
129  // initialize coordinate arrays
130  deleteCoords();
131  x = new double[nbPoints];
132  y = new double[nbPoints];
133 
134  for ( int i = 0; i < nbPoints; ++i )
135  {
136  GEOSCoordSeq_getX_r( geosctxt, coordSeq, i, &x[i] );
137  GEOSCoordSeq_getY_r( geosctxt, coordSeq, i, &y[i] );
138 
139  xmax = x[i] > xmax ? x[i] : xmax;
140  xmin = x[i] < xmin ? x[i] : xmin;
141 
142  ymax = y[i] > ymax ? y[i] : ymax;
143  ymin = y[i] < ymin ? y[i] : ymin;
144  }
145  }
146 
148  {
149  return mLF->layer();
150  }
151 
153  {
154  return mLF->id();
155  }
156 
157  LabelPosition::Quadrant FeaturePart::quadrantFromOffset() const
158  {
159  QPointF quadOffset = mLF->quadOffset();
160  qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
161 
162  if ( quadOffsetX < 0 )
163  {
164  if ( quadOffsetY < 0 )
165  {
167  }
168  else if ( quadOffsetY > 0 )
169  {
171  }
172  else
173  {
175  }
176  }
177  else if ( quadOffsetX > 0 )
178  {
179  if ( quadOffsetY < 0 )
180  {
182  }
183  else if ( quadOffsetY > 0 )
184  {
186  }
187  else
188  {
190  }
191  }
192  else
193  {
194  if ( quadOffsetY < 0 )
195  {
197  }
198  else if ( quadOffsetY > 0 )
199  {
201  }
202  else
203  {
205  }
206  }
207  }
208 
209  int FeaturePart::setPositionOverPoint( double x, double y, QList< LabelPosition*>& lPos, double angle, PointSet *mapShape )
210  {
211  int nbp = 1;
212 
213  // get from feature
214  double labelW = getLabelWidth();
215  double labelH = getLabelHeight();
216 
217  double cost = 0.0001;
218  int id = 0;
219 
220  double xdiff = -labelW / 2.0;
221  double ydiff = -labelH / 2.0;
222 
223  if ( mLF->quadOffset().x() != 0 )
224  {
225  xdiff += labelW / 2.0 * mLF->quadOffset().x();
226  }
227  if ( mLF->quadOffset().y() != 0 )
228  {
229  ydiff += labelH / 2.0 * mLF->quadOffset().y();
230  }
231 
232  if ( ! mLF->hasFixedPosition() )
233  {
234  if ( angle != 0 )
235  {
236  double xd = xdiff * cos( angle ) - ydiff * sin( angle );
237  double yd = xdiff * sin( angle ) + ydiff * cos( angle );
238  xdiff = xd;
239  ydiff = yd;
240  }
241  }
242 
243  if ( mLF->layer()->arrangement() == P_POINT )
244  {
245  //if in "around point" placement mode, then we use the label distance to determine
246  //the label's offset
247  if ( qgsDoubleNear( mLF->quadOffset().x(), 0.0 ) )
248  {
249  ydiff += mLF->quadOffset().y() * mLF->distLabel();
250  }
251  else if ( qgsDoubleNear( mLF->quadOffset().y(), 0.0 ) )
252  {
253  xdiff += mLF->quadOffset().x() * mLF->distLabel();
254  }
255  else
256  {
257  xdiff += mLF->quadOffset().x() * M_SQRT1_2 * mLF->distLabel();
258  ydiff += mLF->quadOffset().y() * M_SQRT1_2 * mLF->distLabel();
259  }
260  }
261  else
262  {
263  if ( mLF->positionOffset().x() != 0 )
264  {
265  xdiff += mLF->positionOffset().x();
266  }
267  if ( mLF->positionOffset().y() != 0 )
268  {
269  ydiff += mLF->positionOffset().y();
270  }
271  }
272 
273  double lx = x + xdiff;
274  double ly = y + ydiff;
275 
276  if ( mapShape && type == GEOS_POLYGON && mLF->layer()->fitInPolygonOnly() )
277  {
278  if ( !mapShape->containsLabelCandidate( lx, ly, labelW, labelH, angle ) )
279  {
280  return 0;
281  }
282  }
283 
284  lPos << new LabelPosition( id, lx, ly, labelW, labelH, angle, cost, this, false, quadrantFromOffset() );
285  return nbp;
286  }
287 
288  int FeaturePart::setPositionForPoint( double x, double y, QList< LabelPosition* >& lPos, double angle, PointSet *mapShape )
289  {
290 
291 #ifdef _DEBUG_
292  std::cout << "SetPosition (point) : " << layer->name << "/" << uid << std::endl;
293 #endif
294 
295  double labelWidth = getLabelWidth();
296  double labelHeight = getLabelHeight();
297  double distanceToLabel = getLabelDistance();
298 
299  int numberCandidates = mLF->layer()->pal->point_p;
300 
301  //std::cout << "Nbp : " << nbp << std::endl;
302  int icost = 0;
303  int inc = 2;
304 
305  double candidateAngleIncrement = 2 * M_PI / numberCandidates; /* angle bw 2 pos */
306 
307  /* various angles */
308  double a90 = M_PI / 2;
309  double a180 = M_PI;
310  double a270 = a180 + a90;
311  double a360 = 2 * M_PI;
312 
313  double gamma1, gamma2;
314 
315  if ( distanceToLabel > 0 )
316  {
317  gamma1 = atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
318  gamma2 = atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
319  }
320  else
321  {
322  gamma1 = gamma2 = a90 / 3.0;
323  }
324 
325  if ( gamma1 > a90 / 3.0 )
326  gamma1 = a90 / 3.0;
327 
328  if ( gamma2 > a90 / 3.0 )
329  gamma2 = a90 / 3.0;
330 
331 
332  if ( gamma1 == 0 || gamma2 == 0 )
333  {
334  std::cout << "Oups... label size error..." << std::endl;
335  }
336 
337  QList< LabelPosition* > candidates;
338 
339  int i;
340  double angleToCandidate;
341  for ( i = 0, angleToCandidate = M_PI / 4; i < numberCandidates; i++, angleToCandidate += candidateAngleIncrement )
342  {
343  double labelX = x;
344  double labelY = y;
345 
346  if ( angleToCandidate > a360 )
347  angleToCandidate -= a360;
348 
350 
351  if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 ) // on the right
352  {
353  labelX += distanceToLabel;
354  double iota = ( angleToCandidate + gamma1 );
355  if ( iota > a360 - gamma1 )
356  iota -= a360;
357 
358  //ly += -yrm/2.0 + tan(alpha)*(distlabel + xrm/2);
359  labelY += -labelHeight + labelHeight * iota / ( 2 * gamma1 );
360 
361  quadrant = LabelPosition::QuadrantRight;
362  }
363  else if ( angleToCandidate < a90 - gamma2 ) // top-right
364  {
365  labelX += distanceToLabel * cos( angleToCandidate );
366  labelY += distanceToLabel * sin( angleToCandidate );
368  }
369  else if ( angleToCandidate < a90 + gamma2 ) // top
370  {
371  //lx += -xrm/2.0 - tan(alpha+a90)*(distlabel + yrm/2);
372  labelX += -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
373  labelY += distanceToLabel;
374  quadrant = LabelPosition::QuadrantAbove;
375  }
376  else if ( angleToCandidate < a180 - gamma1 ) // top left
377  {
378  labelX += distanceToLabel * cos( angleToCandidate ) - labelWidth;
379  labelY += distanceToLabel * sin( angleToCandidate );
381  }
382  else if ( angleToCandidate < a180 + gamma1 ) // left
383  {
384  labelX += -distanceToLabel - labelWidth;
385  //ly += -yrm/2.0 - tan(alpha)*(distlabel + xrm/2);
386  labelY += - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
387  quadrant = LabelPosition::QuadrantLeft;
388  }
389  else if ( angleToCandidate < a270 - gamma2 ) // down - left
390  {
391  labelX += distanceToLabel * cos( angleToCandidate ) - labelWidth;
392  labelY += distanceToLabel * sin( angleToCandidate ) - labelHeight;
394  }
395  else if ( angleToCandidate < a270 + gamma2 ) // down
396  {
397  labelY += -distanceToLabel - labelHeight;
398  //lx += -xrm/2.0 + tan(alpha+a90)*(distlabel + yrm/2);
399  labelX += -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
400  quadrant = LabelPosition::QuadrantBelow;
401  }
402  else if ( angleToCandidate < a360 ) // down - right
403  {
404  labelX += distanceToLabel * cos( angleToCandidate );
405  labelY += distanceToLabel * sin( angleToCandidate ) - labelHeight;
407  }
408 
409  double cost;
410 
411  if ( numberCandidates == 1 )
412  cost = 0.0001;
413  else
414  cost = 0.0001 + 0.0020 * double( icost ) / double( numberCandidates - 1 );
415 
416 
417  if ( mapShape && type == GEOS_POLYGON && mLF->layer()->fitInPolygonOnly() )
418  {
419  if ( !mapShape->containsLabelCandidate( labelX, labelY, labelWidth, labelHeight, angle ) )
420  {
421  continue;
422  }
423  }
424 
425  candidates << new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost, this, false, quadrant );
426 
427  icost += inc;
428 
429  if ( icost == numberCandidates )
430  {
431  icost = numberCandidates - 1;
432  inc = -2;
433  }
434  else if ( icost > numberCandidates )
435  {
436  icost = numberCandidates - 2;
437  inc = -2;
438  }
439 
440  }
441 
442  if ( !candidates.isEmpty() )
443  {
444  for ( int i = 0; i < candidates.count(); ++i )
445  {
446  lPos << candidates.at( i );
447  }
448  }
449 
450  return candidates.count();
451  }
452 
453 // TODO work with squared distance by removing call to sqrt or dist_euc2d
455  {
456 #ifdef _DEBUG_
457  std::cout << "SetPosition (line) : " << layer->name << "/" << uid << std::endl;
458 #endif
459  int i;
460  double distlabel = getLabelDistance();
461 
462  double xrm = getLabelWidth();
463  double yrm = getLabelHeight();
464 
465  double *d; // segments lengths distance bw pt[i] && pt[i+1]
466  double *ad; // absolute distance bw pt[0] and pt[i] along the line
467  double ll; // line length
468  double dist;
469  double bx, by, ex, ey;
470  int nbls;
471  double alpha;
472  double cost;
473 
474  LineArrangementFlags flags = mLF->layer()->arrangementFlags();
475  if ( flags == 0 )
476  flags = FLAG_ON_LINE; // default flag
477 
478  QLinkedList<LabelPosition*> positions;
479 
480  int nbPoints;
481  double *x;
482  double *y;
483 
484  PointSet * line = mapShape;
485 #ifdef _DEBUG_FULL_
486  std::cout << "New line of " << line->nbPoints << " points with label " << xrm << "x" << yrm << std::endl;
487 #endif
488 
489  nbPoints = line->nbPoints;
490  x = line->x;
491  y = line->y;
492 
493  d = new double[nbPoints-1];
494  ad = new double[nbPoints];
495 
496  ll = 0.0; // line length
497  for ( i = 0; i < line->nbPoints - 1; i++ )
498  {
499  if ( i == 0 )
500  ad[i] = 0;
501  else
502  ad[i] = ad[i-1] + d[i-1];
503 
504  d[i] = dist_euc2d( x[i], y[i], x[i+1], y[i+1] );
505  ll += d[i];
506  }
507 
508  ad[line->nbPoints-1] = ll;
509 
510 
511  nbls = ( int )( ll / xrm ); // ratio bw line length and label width
512 
513 #ifdef _DEBUG_FULL_
514  std::cout << "line length :" << ll << std::endl;
515  std::cout << "nblp :" << nbls << std::endl;
516 #endif
517 
518  dist = ( ll - xrm );
519 
520  double l;
521 
522  if ( nbls > 0 )
523  {
524  //dist /= nbls;
525  l = 0;
526  dist = qMin( yrm, xrm );
527  }
528  else // line length < label with => centering label position
529  {
530  l = - ( xrm - ll ) / 2.0;
531  dist = xrm;
532  ll = xrm;
533  }
534 
535  double birdfly;
536  double beta;
537  i = 0;
538  //for (i=0;i<nbp;i++){
539 #ifdef _DEBUG_FULL_
540  std::cout << l << " / " << ll - xrm << std::endl;
541 #endif
542  while ( l < ll - xrm )
543  {
544  // => bx, by
545  line->getPointByDistance( d, ad, l, &bx, &by );
546  // same but l = l+xrm
547  line->getPointByDistance( d, ad, l + xrm, &ex, &ey );
548 
549  // Label is bigger than line ...
550  if ( l < 0 )
551  birdfly = sqrt(( x[nbPoints-1] - x[0] ) * ( x[nbPoints-1] - x[0] )
552  + ( y[nbPoints-1] - y[0] ) * ( y[nbPoints-1] - y[0] ) );
553  else
554  birdfly = sqrt(( ex - bx ) * ( ex - bx ) + ( ey - by ) * ( ey - by ) );
555 
556  cost = birdfly / xrm;
557  if ( cost > 0.98 )
558  cost = 0.0001;
559  else
560  cost = ( 1 - cost ) / 100; // < 0.0001, 0.01 > (but 0.005 is already pretty much)
561 
562  // penalize positions which are further from the line's midpoint
563  double costCenter = qAbs( ll / 2 - ( l + xrm / 2 ) ) / ll; // <0, 0.5>
564  cost += costCenter / 1000; // < 0, 0.0005 >
565 
566  if ( qgsDoubleNear( ey, by ) && qgsDoubleNear( ex, bx ) )
567  {
568  std::cout << "b: " << bx << ";" << by << std::endl;
569  std::cout << "e: " << ex << ";" << ey << std::endl;
570  alpha = 0.0;
571  }
572  else
573  alpha = atan2( ey - by, ex - bx );
574 
575  beta = alpha + M_PI / 2;
576 
577 #ifdef _DEBUG_FULL_
578  std::cout << " Create new label" << std::endl;
579 #endif
580  if ( mLF->layer()->arrangement() == P_LINE )
581  {
582  // find out whether the line direction for this candidate is from right to left
583  bool isRightToLeft = ( alpha > M_PI / 2 || alpha <= -M_PI / 2 );
584  // meaning of above/below may be reversed if using line position dependent orientation
585  // and the line has right-to-left direction
586  bool reversed = (( flags & FLAG_MAP_ORIENTATION ) ? isRightToLeft : false );
587  bool aboveLine = ( !reversed && ( flags & FLAG_ABOVE_LINE ) ) || ( reversed && ( flags & FLAG_BELOW_LINE ) );
588  bool belowLine = ( !reversed && ( flags & FLAG_BELOW_LINE ) ) || ( reversed && ( flags & FLAG_ABOVE_LINE ) );
589 
590  if ( aboveLine )
591  {
592  if ( !mLF->layer()->fitInPolygonOnly() || mapShape->containsLabelCandidate( bx + cos( beta ) *distlabel, by + sin( beta ) *distlabel, xrm, yrm, alpha ) )
593  positions.append( new LabelPosition( i, bx + cos( beta ) *distlabel, by + sin( beta ) *distlabel, xrm, yrm, alpha, cost, this, isRightToLeft ) ); // Line
594  }
595  if ( belowLine )
596  {
597  if ( !mLF->layer()->fitInPolygonOnly() || mapShape->containsLabelCandidate( bx - cos( beta ) *( distlabel + yrm ), by - sin( beta ) *( distlabel + yrm ), xrm, yrm, alpha ) )
598  positions.append( new LabelPosition( i, bx - cos( beta ) *( distlabel + yrm ), by - sin( beta ) *( distlabel + yrm ), xrm, yrm, alpha, cost, this, isRightToLeft ) ); // Line
599  }
600  if ( flags & FLAG_ON_LINE )
601  {
602  if ( !mLF->layer()->fitInPolygonOnly() || mapShape->containsLabelCandidate( bx - yrm*cos( beta ) / 2, by - yrm*sin( beta ) / 2, xrm, yrm, alpha ) )
603  positions.append( new LabelPosition( i, bx - yrm*cos( beta ) / 2, by - yrm*sin( beta ) / 2, xrm, yrm, alpha, cost, this, isRightToLeft ) ); // Line
604  }
605  }
606  else if ( mLF->layer()->arrangement() == P_HORIZ )
607  {
608  positions.append( new LabelPosition( i, bx - xrm / 2, by - yrm / 2, xrm, yrm, 0, cost, this ) ); // Line
609  }
610  else
611  {
612  // an invalid arrangement?
613  }
614 
615  l += dist;
616 
617  i++;
618 
619  if ( nbls == 0 )
620  break;
621  }
622 
623  //delete line;
624 
625  delete[] d;
626  delete[] ad;
627 
628  int nbp = positions.size();
629  while ( positions.size() > 0 )
630  {
631  lPos << positions.takeFirst();
632  }
633 
634  return nbp;
635  }
636 
637 
638  LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, double* path_distances, int orientation, int index, double distance )
639  {
640  // Check that the given distance is on the given index and find the correct index and distance if not
641  while ( distance < 0 && index > 1 )
642  {
643  index--;
644  distance += path_distances[index];
645  }
646 
647  if ( index <= 1 && distance < 0 ) // We've gone off the start, fail out
648  {
649  return NULL;
650  }
651 
652  // Same thing, checking if we go off the end
653  while ( index < path_positions->nbPoints && distance > path_distances[index] )
654  {
655  distance -= path_distances[index];
656  index += 1;
657  }
658  if ( index >= path_positions->nbPoints )
659  {
660  return NULL;
661  }
662 
663  LabelInfo* li = mLF->curvedLabelInfo();
664 
665  // Keep track of the initial index,distance incase we need to re-call get_placement_offset
666  int initial_index = index;
667  double initial_distance = distance;
668 
669  double string_height = li->label_height;
670  double old_x = path_positions->x[index-1];
671  double old_y = path_positions->y[index-1];
672 
673  double new_x = path_positions->x[index];
674  double new_y = path_positions->y[index];
675 
676  double dx = new_x - old_x;
677  double dy = new_y - old_y;
678 
679  double segment_length = path_distances[index];
680  if ( segment_length == 0 )
681  {
682  // Not allowed to place across on 0 length segments or discontinuities
683  return NULL;
684  }
685 
686  LabelPosition* slp = NULL;
687  LabelPosition* slp_tmp = NULL;
688  // current_placement = placement_result()
689  double angle = atan2( -dy, dx );
690 
691  bool orientation_forced = ( orientation != 0 ); // Whether the orientation was set by the caller
692  if ( !orientation_forced )
693  orientation = ( angle > 0.55 * M_PI || angle < -0.45 * M_PI ? -1 : 1 );
694 
695  int upside_down_char_count = 0; // Count of characters that are placed upside down.
696 
697  for ( int i = 0; i < li->char_num; i++ )
698  {
699  double last_character_angle = angle;
700 
701  // grab the next character according to the orientation
702  LabelInfo::CharacterInfo& ci = ( orientation > 0 ? li->char_info[i] : li->char_info[li->char_num-i-1] );
703 
704  // Coordinates this character will start at
705  if ( segment_length == 0 )
706  {
707  // Not allowed to place across on 0 length segments or discontinuities
708  delete slp;
709  return NULL;
710  }
711 
712  double start_x = old_x + dx * distance / segment_length;
713  double start_y = old_y + dy * distance / segment_length;
714  // Coordinates this character ends at, calculated below
715  double end_x = 0;
716  double end_y = 0;
717 
718  //std::cerr << "segment len " << segment_length << " distance " << distance << std::endl;
719  if ( segment_length - distance >= ci.width )
720  {
721  // if the distance remaining in this segment is enough, we just go further along the segment
722  distance += ci.width;
723  end_x = old_x + dx * distance / segment_length;
724  end_y = old_y + dy * distance / segment_length;
725  }
726  else
727  {
728  // If there isn't enough distance left on this segment
729  // then we need to search until we find the line segment that ends further than ci.width away
730  do
731  {
732  old_x = new_x;
733  old_y = new_y;
734  index++;
735  if ( index >= path_positions->nbPoints ) // Bail out if we run off the end of the shape
736  {
737  delete slp;
738  return NULL;
739  }
740  new_x = path_positions->x[index];
741  new_y = path_positions->y[index];
742  dx = new_x - old_x;
743  dy = new_y - old_y;
744  segment_length = path_distances[index];
745 
746  //std::cerr << "-> " << sqrt(pow(start_x - new_x,2) + pow(start_y - new_y,2)) << " vs " << ci.width << std::endl;
747 
748  }
749  while ( sqrt( pow( start_x - new_x, 2 ) + pow( start_y - new_y, 2 ) ) < ci.width ); // Distance from start_ to new_
750 
751  // Calculate the position to place the end of the character on
752  findLineCircleIntersection( start_x, start_y, ci.width, old_x, old_y, new_x, new_y, end_x, end_y );
753 
754  // Need to calculate distance on the new segment
755  distance = sqrt( pow( old_x - end_x, 2 ) + pow( old_y - end_y, 2 ) );
756  }
757 
758  // Calculate angle from the start of the character to the end based on start_/end_ position
759  angle = atan2( start_y - end_y, end_x - start_x );
760  //angle = atan2(end_y-start_y, end_x-start_x);
761 
762  // Test last_character_angle vs angle
763  // since our rendering angle has changed then check against our
764  // max allowable angle change.
765  double angle_delta = last_character_angle - angle;
766  // normalise between -180 and 180
767  while ( angle_delta > M_PI ) angle_delta -= 2 * M_PI;
768  while ( angle_delta < -M_PI ) angle_delta += 2 * M_PI;
769  if (( li->max_char_angle_inside > 0 && angle_delta > 0
770  && angle_delta > li->max_char_angle_inside*( M_PI / 180 ) )
771  || ( li->max_char_angle_outside < 0 && angle_delta < 0
772  && angle_delta < li->max_char_angle_outside*( M_PI / 180 ) ) )
773  {
774  delete slp;
775  return NULL;
776  }
777 
778  double render_angle = angle;
779 
780  double render_x = start_x;
781  double render_y = start_y;
782 
783  // Center the text on the line
784  //render_x -= ((string_height/2.0) - 1.0)*math.cos(render_angle+math.pi/2)
785  //render_y += ((string_height/2.0) - 1.0)*math.sin(render_angle+math.pi/2)
786 
787  if ( orientation < 0 )
788  {
789  // rotate in place
790  render_x += ci.width * cos( render_angle ); //- (string_height-2)*sin(render_angle);
791  render_y -= ci.width * sin( render_angle ); //+ (string_height-2)*cos(render_angle);
792  render_angle += M_PI;
793  }
794 
795  //std::cerr << "adding part: " << render_x << " " << render_y << std::endl;
796  LabelPosition* tmp = new LabelPosition( 0, render_x /*- xBase*/, render_y /*- yBase*/, ci.width, string_height, -render_angle, 0.0001, this );
797  tmp->setPartId( orientation > 0 ? i : li->char_num - i - 1 );
798  if ( slp == NULL )
799  slp = tmp;
800  else
801  slp_tmp->setNextPart( tmp );
802  slp_tmp = tmp;
803 
804  //current_placement.add_node(ci.character,render_x, -render_y, render_angle);
805  //current_placement.add_node(ci.character,render_x - current_placement.starting_x, render_y - current_placement.starting_y, render_angle)
806 
807  // Normalise to 0 <= angle < 2PI
808  while ( render_angle >= 2*M_PI ) render_angle -= 2 * M_PI;
809  while ( render_angle < 0 ) render_angle += 2 * M_PI;
810 
811  if ( render_angle > M_PI / 2 && render_angle < 1.5*M_PI )
812  upside_down_char_count++;
813  }
814  // END FOR
815 
816  // If we placed too many characters upside down
817  if ( upside_down_char_count >= li->char_num / 2.0 )
818  {
819  // if we auto-detected the orientation then retry with the opposite orientation
820  if ( !orientation_forced )
821  {
822  orientation = -orientation;
823  delete slp;
824  slp = curvedPlacementAtOffset( path_positions, path_distances, orientation, initial_index, initial_distance );
825  }
826  else
827  {
828  // Otherwise we have failed to find a placement
829  delete slp;
830  return NULL;
831  }
832  }
833 
834  return slp;
835  }
836 
837  static LabelPosition* _createCurvedCandidate( LabelPosition* lp, double angle, double dist )
838  {
839  LabelPosition* newLp = new LabelPosition( *lp );
840  newLp->offsetPosition( dist*cos( angle + M_PI / 2 ), dist*sin( angle + M_PI / 2 ) );
841  return newLp;
842  }
843 
845  {
846  LabelInfo* li = mLF->curvedLabelInfo();
847 
848  // label info must be present
849  if ( li == NULL || li->char_num == 0 )
850  return 0;
851 
852  // distance calculation
853  double* path_distances = new double[mapShape->nbPoints];
854  double total_distance = 0;
855  double old_x = -1.0, old_y = -1.0;
856  for ( int i = 0; i < mapShape->nbPoints; i++ )
857  {
858  if ( i == 0 )
859  path_distances[i] = 0;
860  else
861  path_distances[i] = sqrt( pow( old_x - mapShape->x[i], 2 ) + pow( old_y - mapShape->y[i], 2 ) );
862  old_x = mapShape->x[i];
863  old_y = mapShape->y[i];
864 
865  total_distance += path_distances[i];
866  }
867 
868  if ( total_distance == 0 )
869  {
870  delete[] path_distances;
871  return 0;
872  }
873 
874  //calculate overall angle of line
875  double lineAngle;
876  double bx = mapShape->x[0];
877  double by = mapShape->y[0];
878  double ex = mapShape->x[ mapShape->nbPoints - 1 ];
879  double ey = mapShape->y[ mapShape->nbPoints - 1 ];
880  if ( qgsDoubleNear( ey, by ) && qgsDoubleNear( ex, bx ) )
881  {
882  lineAngle = 0.0;
883  }
884  else
885  lineAngle = atan2( ey - by, ex - bx );
886 
887  // find out whether the line direction for this candidate is from right to left
888  bool isRightToLeft = ( lineAngle > M_PI / 2 || lineAngle <= -M_PI / 2 );
889 
890  QLinkedList<LabelPosition*> positions;
891  double delta = qMax( li->label_height, total_distance / 10.0 );
892 
893  unsigned long flags = mLF->layer()->arrangementFlags();
894  if ( flags == 0 )
895  flags = FLAG_ON_LINE; // default flag
896  // placements may need to be reversed if using line position dependent orientation
897  // and the line has right-to-left direction
898  bool reversed = ( !( flags & FLAG_MAP_ORIENTATION ) ? isRightToLeft : false );
899 
900  // generate curved labels
901  for ( int i = 0; i*delta < total_distance; i++ )
902  {
903  LabelPosition* slp = curvedPlacementAtOffset( mapShape, path_distances, 0, 1, i * delta );
904 
905  if ( slp )
906  {
907  // evaluate cost
908  double angle_diff = 0.0, angle_last = 0.0, diff;
909  LabelPosition* tmp = slp;
910  double sin_avg = 0, cos_avg = 0;
911  while ( tmp )
912  {
913  if ( tmp != slp ) // not first?
914  {
915  diff = fabs( tmp->getAlpha() - angle_last );
916  if ( diff > 2*M_PI ) diff -= 2 * M_PI;
917  diff = qMin( diff, 2 * M_PI - diff ); // difference 350 deg is actually just 10 deg...
918  angle_diff += diff;
919  }
920 
921  sin_avg += sin( tmp->getAlpha() );
922  cos_avg += cos( tmp->getAlpha() );
923  angle_last = tmp->getAlpha();
924  tmp = tmp->getNextPart();
925  }
926 
927  double angle_diff_avg = li->char_num > 1 ? ( angle_diff / ( li->char_num - 1 ) ) : 0; // <0, pi> but pi/8 is much already
928  double cost = angle_diff_avg / 100; // <0, 0.031 > but usually <0, 0.003 >
929  if ( cost < 0.0001 ) cost = 0.0001;
930 
931  // penalize positions which are further from the line's midpoint
932  double labelCenter = ( i * delta ) + getLabelWidth() / 2;
933  double costCenter = qAbs( total_distance / 2 - labelCenter ) / total_distance; // <0, 0.5>
934  cost += costCenter / 1000; // < 0, 0.0005 >
935  //std::cerr << "cost " << angle_diff << " vs " << costCenter << std::endl;
936  slp->setCost( cost );
937 
938  // average angle is calculated with respect to periodicity of angles
939  double angle_avg = atan2( sin_avg / li->char_num, cos_avg / li->char_num );
940  // displacement
941  if (( !reversed && ( flags & FLAG_ABOVE_LINE ) ) || ( reversed && ( flags & FLAG_BELOW_LINE ) ) )
942  positions.append( _createCurvedCandidate( slp, angle_avg, mLF->distLabel() ) );
943  if ( flags & FLAG_ON_LINE )
944  positions.append( _createCurvedCandidate( slp, angle_avg, -li->label_height / 2 ) );
945  if (( !reversed && ( flags & FLAG_BELOW_LINE ) ) || ( reversed && ( flags & FLAG_ABOVE_LINE ) ) )
946  positions.append( _createCurvedCandidate( slp, angle_avg, -li->label_height - mLF->distLabel() ) );
947 
948  // delete original candidate
949  delete slp;
950  }
951  }
952 
953 
954  int nbp = positions.size();
955  for ( int i = 0; i < nbp; i++ )
956  {
957  lPos << positions.takeFirst();
958  }
959 
960  delete[] path_distances;
961 
962  return nbp;
963  }
964 
965 
966 
967 
968  /*
969  * seg 2
970  * pt3 ____________pt2
971  * ¦ ¦
972  * ¦ ¦
973  * seg 3 ¦ BBOX ¦ seg 1
974  * ¦ ¦
975  * ¦____________¦
976  * pt0 seg 0 pt1
977  *
978  */
979 
981  {
982 
983 #ifdef _DEBUG_
984  std::cout << "SetPosition (polygon) : " << layer->name << "/" << uid << std::endl;
985 #endif
986 
987  int i;
988  int j;
989 
990  double labelWidth = getLabelWidth();
991  double labelHeight = getLabelHeight();
992 
993  //print();
994 
995  QLinkedList<PointSet*> shapes_toProcess;
996  QLinkedList<PointSet*> shapes_final;
997 
998  mapShape->parent = NULL;
999 
1000  shapes_toProcess.append( mapShape );
1001 
1002  splitPolygons( shapes_toProcess, shapes_final, labelWidth, labelHeight, mLF->id() );
1003 
1004  int nbp;
1005 
1006  if ( shapes_final.size() > 0 )
1007  {
1008  QLinkedList<LabelPosition*> positions;
1009 
1010  int id = 0; // ids for candidates
1011  double dlx, dly; // delta from label center and bottom-left corner
1012  double alpha = 0.0; // rotation for the label
1013  double px, py;
1014  double dx;
1015  double dy;
1016  int bbid;
1017  double beta;
1018  double diago = sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1019  double rx, ry;
1020  CHullBox **boxes = new CHullBox*[shapes_final.size()];
1021  j = 0;
1022 
1023  // Compute bounding box foreach finalShape
1024  while ( shapes_final.size() > 0 )
1025  {
1026  PointSet *shape = shapes_final.takeFirst();
1027  boxes[j] = shape->compute_chull_bbox();
1028 
1029  if ( shape->parent )
1030  delete shape;
1031 
1032  j++;
1033  }
1034 
1035  //dx = dy = min( yrm, xrm ) / 2;
1036  dx = labelWidth / 2.0;
1037  dy = labelHeight / 2.0;
1038 
1039 
1040  int numTry = 0;
1041 
1042  //fit in polygon only mode slows down calculation a lot, so if it's enabled
1043  //then use a smaller limit for number of iterations
1044  int maxTry = mLF->layer()->fitInPolygonOnly() ? 7 : 10;
1045 
1046  do
1047  {
1048  for ( bbid = 0; bbid < j; bbid++ )
1049  {
1050  CHullBox *box = boxes[bbid];
1051 
1052  if (( box->length * box->width ) > ( xmax - xmin ) *( ymax - ymin ) *5 )
1053  {
1054  std::cout << "Very Large BBOX (should never occur : bug-report please)" << std::endl;
1055  std::cout << " Box size: " << box->length << "/" << box->width << std::endl;
1056  std::cout << " Alpha: " << alpha << " " << alpha * 180 / M_PI << std::endl;
1057  std::cout << " Dx;Dy: " << dx << " " << dy << std::endl;
1058  std::cout << " LabelSizerm: " << labelWidth << " " << labelHeight << std::endl;
1059  std::cout << " LabelSizeUn: " << getLabelWidth() << " " << getLabelHeight() << std::endl;
1060  continue;
1061  }
1062 
1063  if ( mLF->layer()->arrangement() == P_HORIZ && mLF->layer()->fitInPolygonOnly() )
1064  {
1065  //check width/height of bbox is sufficient for label
1066  if ( box->length < labelWidth || box->width < labelHeight )
1067  {
1068  //no way label can fit in this box, skip it
1069  continue;
1070  }
1071  }
1072 
1073 #ifdef _DEBUG_FULL_
1074  std::cout << "New BBox : " << bbid << std::endl;
1075  for ( i = 0; i < 4; i++ )
1076  {
1077  std::cout << box->x[i] << "\t" << box->y[i] << std::endl;
1078  }
1079 #endif
1080 
1081  bool enoughPlace = false;
1082  if ( mLF->layer()->arrangement() == P_FREE )
1083  {
1084  enoughPlace = true;
1085  px = ( box->x[0] + box->x[2] ) / 2 - labelWidth;
1086  py = ( box->y[0] + box->y[2] ) / 2 - labelHeight;
1087  int i, j;
1088 
1089  // Virtual label: center on bbox center, label size = 2x original size
1090  // alpha = 0.
1091  // If all corner are in bbox then place candidates horizontaly
1092  for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1093  {
1094  for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1095  {
1096  if ( !mapShape->containsPoint( rx, ry ) )
1097  {
1098  enoughPlace = false;
1099  break;
1100  }
1101  }
1102  if ( !enoughPlace )
1103  {
1104  break;
1105  }
1106  }
1107 
1108  } // arrangement== FREE ?
1109 
1110  if ( mLF->layer()->arrangement() == P_HORIZ || enoughPlace )
1111  {
1112  alpha = 0.0; // HORIZ
1113  }
1114  else if ( box->length > 1.5*labelWidth && box->width > 1.5*labelWidth )
1115  {
1116  if ( box->alpha <= M_PI / 4 )
1117  {
1118  alpha = box->alpha;
1119  }
1120  else
1121  {
1122  alpha = box->alpha - M_PI / 2;
1123  }
1124  }
1125  else if ( box->length > box->width )
1126  {
1127  alpha = box->alpha - M_PI / 2;
1128  }
1129  else
1130  {
1131  alpha = box->alpha;
1132  }
1133 
1134  beta = atan2( labelHeight, labelWidth ) + alpha;
1135 
1136 
1137  //alpha = box->alpha;
1138 
1139  // delta from label center and down-left corner
1140  dlx = cos( beta ) * diago;
1141  dly = sin( beta ) * diago;
1142 
1143  double px0, py0;
1144 
1145  px0 = box->width / 2.0;
1146  py0 = box->length / 2.0;
1147 
1148  px0 -= ceil( px0 / dx ) * dx;
1149  py0 -= ceil( py0 / dy ) * dy;
1150 
1151  for ( px = px0; px <= box->width; px += dx )
1152  {
1153  for ( py = py0; py <= box->length; py += dy )
1154  {
1155 
1156  rx = cos( box->alpha ) * px + cos( box->alpha - M_PI / 2 ) * py;
1157  ry = sin( box->alpha ) * px + sin( box->alpha - M_PI / 2 ) * py;
1158 
1159  rx += box->x[0];
1160  ry += box->y[0];
1161 
1162  bool candidateAcceptable = ( mLF->layer()->fitInPolygonOnly()
1163  ? mapShape->containsLabelCandidate( rx - dlx, ry - dly, labelWidth, labelHeight, alpha )
1164  : mapShape->containsPoint( rx, ry ) );
1165  if ( candidateAcceptable )
1166  {
1167  // cost is set to minimal value, evaluated later
1168  positions.append( new LabelPosition( id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001, this ) ); // Polygon
1169  }
1170  }
1171  }
1172  } // forall box
1173 
1174  nbp = positions.size();
1175  if ( nbp == 0 )
1176  {
1177  dx /= 2;
1178  dy /= 2;
1179  numTry++;
1180  }
1181  }
1182  while ( nbp == 0 && numTry < maxTry );
1183 
1184  nbp = positions.size();
1185 
1186  for ( i = 0; i < nbp; i++ )
1187  {
1188  lPos << positions.takeFirst();
1189  }
1190 
1191  for ( bbid = 0; bbid < j; bbid++ )
1192  {
1193  delete boxes[bbid];
1194  }
1195 
1196  delete[] boxes;
1197  }
1198  else
1199  {
1200  nbp = 0;
1201  }
1202 
1203 #ifdef _DEBUG_FULL_
1204  std::cout << "NbLabelPosition: " << nbp << std::endl;
1205 #endif
1206  return nbp;
1207  }
1208 
1209 #if 0
1210  void FeaturePart::print()
1211  {
1212  int i, j;
1213  std::cout << "Geometry id : " << f->uid << std::endl;
1214  std::cout << "Type: " << type << std::endl;
1215  if ( x && y )
1216  {
1217  for ( i = 0; i < nbPoints; i++ )
1218  std::cout << x[i] << ", " << y[i] << std::endl;
1219  std::cout << "Obstacle: " << nbHoles << std::endl;
1220  for ( i = 0; i < nbHoles; i++ )
1221  {
1222  std::cout << " obs " << i << std::endl;
1223  for ( j = 0; j < holes[i]->nbPoints; j++ )
1224  {
1225  std::cout << holes[i]->x[j] << ";" << holes[i]->y[j] << std::endl;
1226  }
1227  }
1228  }
1229 
1230  std::cout << std::endl;
1231  }
1232 #endif
1233 
1235  double bbox_min[2], double bbox_max[2],
1236  PointSet *mapShape, RTree<LabelPosition*, double, 2, double> *candidates )
1237  {
1238  double bbox[4];
1239 
1240  bbox[0] = bbox_min[0];
1241  bbox[1] = bbox_min[1];
1242  bbox[2] = bbox_max[0];
1243  bbox[3] = bbox_max[1];
1244 
1245  double angle = mLF->hasFixedAngle() ? mLF->fixedAngle() : 0.0;
1246 
1247  if ( mLF->hasFixedPosition() )
1248  {
1249  lPos << new LabelPosition( 0, mLF->fixedPosition().x(), mLF->fixedPosition().y(), getLabelWidth(), getLabelHeight(), angle, 0.0, this );
1250  }
1251  else
1252  {
1253  switch ( type )
1254  {
1255  case GEOS_POINT:
1256  if ( mLF->layer()->arrangement() == P_POINT_OVER || mLF->hasFixedQuadrant() )
1257  setPositionOverPoint( x[0], y[0], lPos, angle );
1258  else
1259  setPositionForPoint( x[0], y[0], lPos, angle );
1260  break;
1261  case GEOS_LINESTRING:
1262  if ( mLF->layer()->arrangement() == P_CURVED )
1263  setPositionForLineCurved( lPos, mapShape );
1264  else
1265  setPositionForLine( lPos, mapShape );
1266  break;
1267 
1268  case GEOS_POLYGON:
1269  switch ( mLF->layer()->arrangement() )
1270  {
1271  case P_POINT:
1272  case P_POINT_OVER:
1273  double cx, cy;
1274  mapShape->getCentroid( cx, cy, mLF->layer()->centroidInside() );
1275  if ( mLF->layer()->arrangement() == P_POINT_OVER )
1276  setPositionOverPoint( cx, cy, lPos, angle, mapShape );
1277  else
1278  setPositionForPoint( cx, cy, lPos, angle, mapShape );
1279  break;
1280  case P_LINE:
1281  setPositionForLine( lPos, mapShape );
1282  break;
1283  default:
1284  setPositionForPolygon( lPos, mapShape );
1285  break;
1286  }
1287  }
1288  }
1289 
1290  // purge candidates that are outside the bbox
1291 
1293  while ( i.hasNext() )
1294  {
1295  LabelPosition* pos = i.next();
1296  bool outside = false;
1297  if ( mLF->layer()->pal->getShowPartial() )
1298  outside = !pos->isIntersect( bbox );
1299  else
1300  outside = !pos->isInside( bbox );
1301  if ( outside )
1302  {
1303  i.remove();
1304  delete pos;
1305  }
1306  else // this one is OK
1307  {
1308  pos->insertIntoIndex( candidates );
1309  }
1310  }
1311 
1312  qSort( lPos.begin(), lPos.end(), CostCalculator::candidateSortGrow );
1313  return lPos.count();
1314  }
1315 
1316  void FeaturePart::addSizePenalty( int nbp, QList< LabelPosition* >& lPos, double bbx[4], double bby[4] )
1317  {
1318  if ( !mGeos )
1319  createGeosGeom();
1320 
1321  GEOSContextHandle_t ctxt = geosContext();
1322  int geomType = GEOSGeomTypeId_r( ctxt, mGeos );
1323 
1324  double sizeCost = 0;
1325  if ( geomType == GEOS_LINESTRING )
1326  {
1327  double length;
1328  try
1329  {
1330  if ( GEOSLength_r( ctxt, mGeos, &length ) != 1 )
1331  return; // failed to calculate length
1332  }
1333  catch ( GEOSException &e )
1334  {
1335  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
1336  return;
1337  }
1338  double bbox_length = qMax( bbx[2] - bbx[0], bby[2] - bby[0] );
1339  if ( length >= bbox_length / 4 )
1340  return; // the line is longer than quarter of height or width - don't penalize it
1341 
1342  sizeCost = 1 - ( length / ( bbox_length / 4 ) ); // < 0,1 >
1343  }
1344  else if ( geomType == GEOS_POLYGON )
1345  {
1346  double area;
1347  try
1348  {
1349  if ( GEOSArea_r( ctxt, mGeos, &area ) != 1 )
1350  return;
1351  }
1352  catch ( GEOSException &e )
1353  {
1354  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
1355  return;
1356  }
1357  double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
1358  if ( area >= bbox_area / 16 )
1359  return; // covers more than 1/16 of our view - don't penalize it
1360 
1361  sizeCost = 1 - ( area / ( bbox_area / 16 ) ); // < 0, 1 >
1362  }
1363  else
1364  return; // no size penalty for points
1365 
1366  //std::cout << "size cost " << sizeCost << std::endl;
1367 
1368  // apply the penalty
1369  for ( int i = 0; i < nbp; i++ )
1370  {
1371  lPos.at( i )->setCost( lPos.at( i )->cost() + sizeCost / 100 );
1372  }
1373  }
1374 
1376  {
1377  if ( !p2->mGeos )
1378  p2->createGeosGeom();
1379 
1380  try
1381  {
1382  return ( GEOSPreparedTouches_r( geosContext(), preparedGeom(), p2->mGeos ) == 1 );
1383  }
1384  catch ( GEOSException &e )
1385  {
1386  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
1387  return false;
1388  }
1389  }
1390 
1392  {
1393  if ( !mGeos )
1394  createGeosGeom();
1395  if ( !other->mGeos )
1396  other->createGeosGeom();
1397 
1398  GEOSContextHandle_t ctxt = geosContext();
1399  try
1400  {
1401  GEOSGeometry* g1 = GEOSGeom_clone_r( ctxt, mGeos );
1402  GEOSGeometry* g2 = GEOSGeom_clone_r( ctxt, other->mGeos );
1403  GEOSGeometry* geoms[2] = { g1, g2 };
1404  GEOSGeometry* g = GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 );
1405  GEOSGeometry* gTmp = GEOSLineMerge_r( ctxt, g );
1406  GEOSGeom_destroy_r( ctxt, g );
1407 
1408  if ( GEOSGeomTypeId_r( ctxt, gTmp ) != GEOS_LINESTRING )
1409  {
1410  // sometimes it's not possible to merge lines (e.g. they don't touch at endpoints)
1411  GEOSGeom_destroy_r( ctxt, gTmp );
1412  return false;
1413  }
1414  invalidateGeos();
1415 
1416  // set up new geometry
1417  mGeos = gTmp;
1418  mOwnsGeom = true;
1419 
1420  deleteCoords();
1421  qDeleteAll( mHoles );
1422  mHoles.clear();
1423  extractCoords( mGeos );
1424  return true;
1425  }
1426  catch ( GEOSException &e )
1427  {
1428  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
1429  return false;
1430  }
1431  }
1432 
1434  {
1435  if ( mLF->alwaysShow() )
1436  {
1437  //if feature is set to always show, bump the priority up by orders of magnitude
1438  //so that other feature's labels are unlikely to be placed over the label for this feature
1439  //(negative numbers due to how pal::extract calculates inactive cost)
1440  return -0.2;
1441  }
1442 
1443  return mLF->priority() >= 0 ? mLF->priority() : mLF->layer()->priority();
1444  }
1445 
1446 
1447 } // end namespace pal
pal::Layer * layer() const
Get PAL layer of the label feature. Should be only used internally in PAL.
int reorderPolygon(int nbPoints, double *x, double *y)
static bool candidateSortGrow(const LabelPosition *c1, const LabelPosition *c2)
Sorts label candidates in ascending order of cost.
double length
Definition: pointset.h:56
static unsigned index
QList< FeaturePart * > mHoles
Definition: feature.h:207
double max_char_angle_outside
Definition: feature.h:67
arranges candidates around a point (centroid for polygon)
Definition: pal.h:79
bool alwaysShow() const
Whether label should be always shown (sets very high label priority)
FeaturePart(QgsLabelFeature *lf, const GEOSGeometry *geom)
Creates a new generic feature.
Definition: feature.cpp:60
void getCentroid(double &px, double &py, bool forceInside=false) const
Definition: pointset.cpp:943
double getLabelHeight() const
Definition: feature.h:171
Arrangement arrangement() const
Returns the layer's arrangement policy.
Definition: layer.h:94
double calculatePriority() const
Calculates the priority for the feature.
Definition: feature.cpp:1433
void setCost(double newCost)
Sets the candidate label position's geographical cost.
A layer of spacial entites.
Definition: layer.h:57
const T & at(int i) const
void offsetPosition(double xOffset, double yOffset)
Shift the label by specified offset.
friend class LabelPosition
Definition: pointset.h:63
int setPositionForPolygon(QList< LabelPosition * > &lPos, PointSet *mapShape)
Generate candidates for polygon features.
Definition: feature.cpp:980
static LabelPosition * _createCurvedCandidate(LabelPosition *lp, double angle, double dist)
Definition: feature.cpp:837
QPointF quadOffset() const
Applies to "offset from point" placement strategy and "around point" (in case hasFixedQuadrant() retu...
double getLabelWidth() const
Definition: feature.h:170
QgsFeatureId featureId() const
Returns the unique ID of the feature.
Definition: feature.cpp:152
double priority() const
Returns the layer's priority, between 0 and 1.
Definition: layer.h:167
CHullBox * compute_chull_bbox()
Definition: pointset.cpp:727
bool centroidInside() const
Returns whether labels placed at the centroid of features within the layer are forced to be placed in...
Definition: layer.h:214
double priority() const
Returns the feature's labeling priority.
Only for polygon, arranges candidates with respect of polygon orientation.
Definition: pal.h:84
QString tr(const char *sourceText, const char *disambiguation, int n)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:268
void createGeosGeom() const
Definition: pointset.cpp:158
double x() const
Get the x value of the point.
Definition: qgspoint.h:126
CharacterInfo * char_info
Definition: feature.h:70
pal::LabelInfo * curvedLabelInfo() const
Get additional infor required for curved label placement. Returns null if not set.
void deleteCoords()
Definition: pointset.cpp:240
double width
Definition: pointset.h:55
bool hasFixedPosition() const
Whether the label should use a fixed position instead of being automatically placed.
int setPositionForLine(QList< LabelPosition * > &lPos, PointSet *mapShape)
Generate candidates for line feature.
Definition: feature.cpp:454
bool isIntersect(double *bbox)
Is the labelposition intersect the bounding-box ?
int count(const T &value) const
bool containsLabelCandidate(double x, double y, double width, double height, double alpha=0) const
Tests whether a possible label candidate will fit completely within the shape.
Definition: pointset.cpp:315
qreal x() const
qreal y() const
int setPosition(QList< LabelPosition * > &lPos, double bbox_min[2], double bbox_max[2], PointSet *mapShape, RTree< LabelPosition *, double, 2, double > *candidates)
Generic method to generate candidates.
Definition: feature.cpp:1234
void addSizePenalty(int nbp, QList< LabelPosition * > &lPos, double bbx[4], double bby[4])
Definition: feature.cpp:1316
virtual ~FeaturePart()
Delete the feature.
Definition: feature.cpp:78
bool mergeWithFeaturePart(FeaturePart *other)
Merge other (connected) part with this one and save the result in this part (other is unchanged)...
Definition: feature.cpp:1391
double getAlpha() const
get alpha
PointSet * parent
Definition: pointset.h:156
double getLabelDistance() const
Definition: feature.h:172
double * x
Definition: pointset.h:147
double ymax
Definition: pointset.h:170
double xmin
Definition: pointset.h:167
Layer * layer()
Returns the layer that feature belongs to.
Definition: feature.cpp:147
bool isEmpty() const
PointSet * holeOf
Definition: pointset.h:155
QgsPoint fixedPosition() const
Coordinates of the fixed position (relevant only if hasFixedPosition() returns true) ...
double ymin
Definition: pointset.h:169
bool hasFixedQuadrant() const
Returns whether the quadrant for the label is fixed.
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
Optional additional info about label (for curved labels)
Definition: feature.h:47
double dist_euc2d(double x1, double y1, double x2, double y2)
Definition: geomfunction.h:52
bool isConnected(FeaturePart *p2)
Check whether this part is connected with some other part.
Definition: feature.cpp:1375
double label_height
Definition: feature.h:68
LabelPosition * getNextPart() const
GEOSContextHandle_t geosContext()
Get GEOS context handle to be used in all GEOS library calls with reentrant API.
Definition: pal.cpp:57
void insertIntoIndex(RTree< LabelPosition *, double, 2, double > *index)
Main class to handle feature.
Definition: feature.h:79
bool containsPoint(double x, double y) const
Tests whether point set contains a specified point.
Definition: pointset.cpp:293
bool fitInPolygonOnly() const
Returns whether labels which do not fit completely within a polygon feature are discarded.
Definition: layer.h:229
void setPartId(int id)
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
iterator end()
void extractCoords(const GEOSGeometry *geom)
read coordinates from a GEOS geom
Definition: feature.cpp:86
double * y
Definition: pointset.h:148
LabelPosition * curvedPlacementAtOffset(PointSet *path_positions, double *path_distances, int orientation, int index, double distance)
Definition: feature.cpp:638
bool hasNext() const
Only for lines, labels along the line.
Definition: pal.h:83
bool getShowPartial()
Get flag show partial label.
Definition: pal.cpp:690
Pal * pal
Definition: layer.h:254
void setNextPart(LabelPosition *next)
int setPositionForLineCurved(QList< LabelPosition * > &lPos, PointSet *mapShape)
Generate curved candidates for line features.
Definition: feature.cpp:844
QgsPoint positionOffset() const
Applies only to "offset from point" placement strategy.
double x[4]
Definition: pointset.h:50
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
int setPositionForPoint(double x, double y, QList< LabelPosition * > &lPos, double angle, PointSet *mapShape=0)
Generate candidates for point feature, located around a specified point.
Definition: feature.cpp:288
int setPositionOverPoint(double x, double y, QList< LabelPosition * > &lPos, double angle, PointSet *mapShape=0)
Generate one candidate over or offset the specified point.
Definition: feature.cpp:209
double y[4]
Definition: pointset.h:51
double max_char_angle_inside
Definition: feature.h:66
double distLabel() const
Applies to "around point" placement strategy or linestring features.
void findLineCircleIntersection(double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes)
GEOSGeometry * mGeos
Definition: pointset.h:143
LabelPosition is a candidate feature label position.
Definition: labelposition.h:48
QString name() const
Returns the layer's name.
Definition: layer.h:89
LineArrangementFlags arrangementFlags() const
Returns the layer's arrangement flags.
Definition: layer.h:105
QgsLabelFeature * mLF
Definition: feature.h:206
qint64 QgsFeatureId
Definition: qgsfeature.h:31
double y() const
Get the y value of the point.
Definition: qgspoint.h:134
Quadrant
Position of label candidate relative to feature.
Definition: labelposition.h:58
void invalidateGeos()
Definition: pointset.cpp:212
double alpha
Definition: pointset.h:53
bool hasFixedAngle() const
Whether the label should use a fixed angle instead of using angle from automatic placement.
const GEOSPreparedGeometry * preparedGeom() const
Definition: pointset.cpp:200
#define M_PI
Definition: feature.cpp:54
double length() const
Returns length of line geometry.
Definition: pointset.cpp:1032
void getPointByDistance(double *d, double *ad, double dl, double *px, double *py)
Get a point a set distance along a line geometry.
Definition: pointset.cpp:985
double xmax
Definition: pointset.h:168
int char_num
Definition: feature.h:69
iterator begin()
double fixedAngle() const
Angle in degrees of the fixed angle (relevant only if hasFixedAngle() returns true) ...
arranges candidates over a point (centroid for polygon)
Definition: pal.h:81
int size() const
bool mOwnsGeom
Definition: pointset.h:144
static void splitPolygons(QLinkedList< PointSet * > &shapes_toProcess, QLinkedList< PointSet * > &shapes_final, double xrm, double yrm, const QgsFeatureId &uid)
Split a concave shape into several convex shapes.
Definition: pointset.cpp:363
void append(const T &value)
bool isInside(double *bbox)
Is the labelposition inside the bounding-box ?
QgsFeatureId id() const
Identifier of the label (unique within the parent label provider)