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