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