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