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