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