QGIS API Documentation  2.5.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
layer.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 #include <stddef.h>
37 #include <geos_c.h>
38 
39 #include <iostream>
40 #include <cstring>
41 #include <cmath>
42 #include <vector>
43 
44 
45 #include <pal/pal.h>
46 #include <pal/layer.h>
47 #include <pal/palexception.h>
48 #include <pal/internalexception.h>
49 
50 #include "linkedlist.hpp"
51 #include "hashtable.hpp"
52 
53 #include "feature.h"
54 #include "geomfunction.h"
55 #include "util.h"
56 
57 #include "simplemutex.h"
58 
59 namespace pal
60 {
61 
62  Layer::Layer( const char *lyrName, double min_scale, double max_scale, Arrangement arrangement, Units label_unit, double defaultPriority, bool obstacle, bool active, bool toLabel, Pal *pal, bool displayAll )
63  : pal( pal ), obstacle( obstacle ), active( active ),
64  toLabel( toLabel ), displayAll( displayAll ), centroidInside( false ), label_unit( label_unit ),
65  min_scale( min_scale ), max_scale( max_scale ),
66  arrangement( arrangement ), arrangementFlags( 0 ), mode( LabelPerFeature ), mergeLines( false )
67  {
68 
69  this->name = new char[strlen( lyrName ) +1];
70  strcpy( this->name, lyrName );
71 
72  modMutex = new SimpleMutex();
73 
75  hashtable = new HashTable<Feature*> ( 5281 );
76 
79 
80  if ( defaultPriority < 0.0001 )
81  this->defaultPriority = 0.0001;
82  else if ( defaultPriority > 1.0 )
83  this->defaultPriority = 1.0;
84  else
85  this->defaultPriority = defaultPriority;
86 
89  }
90 
92  {
93  modMutex->lock();
94 
95  if ( featureParts )
96  {
97  while ( featureParts->size() )
98  {
99  delete featureParts->pop_front();
100  }
101  delete featureParts;
102 
103  }
104 
105  // this hashtable and list should be empty if they still exist
106  delete connectedHashtable;
107 
108  // features in the hashtable
109  if ( features )
110  {
111  while ( features->size() )
112  {
113  delete features->pop_front();
114  }
115  delete features;
116  }
117 
118  if ( name )
119  delete[] name;
120 
121  delete rtree;
122 
123  delete hashtable;
124  delete modMutex;
125  }
126 
127  Feature* Layer::getFeature( const char* geom_id )
128  {
129  Feature** fptr = hashtable->find( geom_id );
130  return ( fptr ? *fptr : NULL );
131  }
132 
133 
134  bool Layer::isScaleValid( double scale )
135  {
136  return ( scale >= min_scale || min_scale == -1 )
137  && ( scale <= max_scale || max_scale == -1 );
138  }
139 
140 
142  {
143  return features->size();
144  }
145 
146  const char *Layer::getName()
147  {
148  return name;
149  }
150 
152  {
153  return arrangement;
154  }
155 
156  void Layer::setArrangement( Arrangement arrangement )
157  {
158  this->arrangement = arrangement;
159  }
160 
161 
163  {
164  return obstacle;
165  }
166 
168  {
169  return toLabel;
170  }
171 
173  {
174  return active;
175  }
176 
177 
179  {
180  return min_scale;
181  }
182 
184  {
185  return max_scale;
186  }
187 
189  {
190  return defaultPriority;
191  }
192 
193  void Layer::setObstacle( bool obstacle )
194  {
195  this->obstacle = obstacle;
196  }
197 
198  void Layer::setActive( bool active )
199  {
200  this->active = active;
201  }
202 
203  void Layer::setToLabel( bool toLabel )
204  {
205  this->toLabel = toLabel;
206  }
207 
208  void Layer::setMinScale( double min_scale )
209  {
210  this->min_scale = min_scale;
211  }
212 
213  void Layer::setMaxScale( double max_scale )
214  {
215  this->max_scale = max_scale;
216  }
217 
218  void Layer::setPriority( double priority )
219  {
220  if ( priority >= 1.0 ) // low priority
221  defaultPriority = 1.0;
222  else if ( priority <= 0.0001 )
223  defaultPriority = 0.0001; // high priority
224  else
225  defaultPriority = priority;
226  }
227 
228 
229 
230  bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x, double label_y, const char* labelText,
231  double labelPosX, double labelPosY, bool fixedPos, double angle, bool fixedAngle,
232  int xQuadOffset, int yQuadOffset, double xOffset, double yOffset, bool alwaysShow, double repeatDistance )
233  {
234  if ( !geom_id || label_x < 0 || label_y < 0 )
235  return false;
236 
237  modMutex->lock();
238 
239  if ( hashtable->find( geom_id ) )
240  {
241  modMutex->unlock();
242  //A feature with this id already exists. Don't throw an exception as sometimes,
243  //the same feature is added twice (dateline split with otf-reprojection)
244  return false;
245  }
246 
247  // Split MULTI GEOM and Collection in simple geometries
248  const GEOSGeometry *the_geom = userGeom->getGeosGeometry();
249 
250  Feature* f = new Feature( this, geom_id, userGeom, label_x, label_y );
251  if ( fixedPos )
252  {
253  f->setFixedPosition( labelPosX, labelPosY );
254  }
255  if ( xQuadOffset != 0 || yQuadOffset != 0 )
256  {
257  f->setQuadOffset( xQuadOffset, yQuadOffset );
258  }
259  if ( xOffset != 0.0 || yOffset != 0.0 )
260  {
261  f->setPosOffset( xOffset, yOffset );
262  }
263  if ( fixedAngle )
264  {
265  f->setFixedAngle( angle );
266  }
267  // use layer-level defined rotation, but not if position fixed
268  if ( !fixedPos && angle != 0.0 )
269  {
270  f->setFixedAngle( angle );
271  }
272  f->setRepeatDistance( repeatDistance );
273 
274  f->setAlwaysShow( alwaysShow );
275 
276  bool first_feat = true;
277 
278  double geom_size = -1, biggest_size = -1;
279  FeaturePart* biggest_part = NULL;
280 
281  // break the (possibly multi-part) geometry into simple geometries
282  LinkedList <const GEOSGeometry*> *simpleGeometries = unmulti( the_geom );
283  if ( simpleGeometries == NULL ) // unmulti() failed?
284  {
285  modMutex->unlock();
287  }
288 
289  while ( simpleGeometries->size() > 0 )
290  {
291  const GEOSGeometry* geom = simpleGeometries->pop_front();
292 
293  // ignore invalid geometries (e.g. polygons with self-intersecting rings)
294  if ( GEOSisValid( geom ) != 1 ) // 0=invalid, 1=valid, 2=exception
295  {
296  std::cerr << "ignoring invalid feature " << geom_id << std::endl;
297  continue;
298  }
299 
300  int type = GEOSGeomTypeId( geom );
301 
302  if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON )
303  {
304  modMutex->unlock();
306  }
307 
308  FeaturePart* fpart = new FeaturePart( f, geom );
309 
310  // ignore invalid geometries
311  if (( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) ||
312  ( type == GEOS_POLYGON && fpart->nbPoints < 3 ) )
313  {
314  delete fpart;
315  continue;
316  }
317 
318  // polygons: reorder coordinates
319  if ( type == GEOS_POLYGON && reorderPolygon( fpart->nbPoints, fpart->x, fpart->y ) != 0 )
320  {
321  delete fpart;
322  continue;
323  }
324 
325  if ( mode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
326  {
327  if ( type == GEOS_LINESTRING )
328  GEOSLength( geom, &geom_size );
329  else if ( type == GEOS_POLYGON )
330  GEOSArea( geom, &geom_size );
331 
332  if ( geom_size > biggest_size )
333  {
334  biggest_size = geom_size;
335  delete biggest_part; // safe with NULL part
336  biggest_part = fpart;
337  }
338  continue; // don't add the feature part now, do it later
339  // TODO: we should probably add also other parts to act just as obstacles
340  }
341 
342  // feature part is ready!
343  addFeaturePart( fpart, labelText );
344 
345  first_feat = false;
346  }
347  delete simpleGeometries;
348 
349  userGeom->releaseGeosGeometry( the_geom );
350 
351  modMutex->unlock();
352 
353  // if using only biggest parts...
354  if (( mode == LabelPerFeature || f->fixedPosition() ) && biggest_part != NULL )
355  {
356  addFeaturePart( biggest_part, labelText );
357  first_feat = false;
358  }
359 
360  // add feature to layer if we have added something
361  if ( !first_feat )
362  {
363  features->push_back( f );
364  hashtable->insertItem( geom_id, f );
365  }
366  else
367  {
368  delete f;
369  }
370 
371  return !first_feat; // true if we've added something
372  }
373 
374  void Layer::addFeaturePart( FeaturePart* fpart, const char* labelText )
375  {
376  double bmin[2];
377  double bmax[2];
378  fpart->getBoundingBox( bmin, bmax );
379 
380  // add to list of layer's feature parts
381  featureParts->push_back( fpart );
382 
383  // add to r-tree for fast spatial access
384  rtree->Insert( bmin, bmax, fpart );
385 
386  // add to hashtable with equally named feature parts
387  if ( mergeLines && labelText )
388  {
389  LinkedList< FeaturePart*>** lstPtr = connectedHashtable->find( labelText );
391  if ( lstPtr == NULL )
392  {
393  // entry doesn't exist yet
395  connectedHashtable->insertItem( labelText, lst );
396 
397  char* txt = new char[strlen( labelText ) +1];
398  strcpy( txt, labelText );
399  connectedTexts->push_back( txt );
400  }
401  else
402  {
403  lst = *lstPtr;
404  }
405  lst->push_back( fpart ); // add to the list
406  }
407  }
408 
409 
410  void Layer::setLabelUnit( Units label_unit )
411  {
412  if ( label_unit == PIXEL || label_unit == METER )
413  this->label_unit = label_unit;
414  }
415 
417  {
418  return label_unit;
419  }
420 
421 
423  {
424  // iterate in the rest of the parts with the same label
425  Cell<FeaturePart*>* p = otherParts->getFirst();
426  while ( p )
427  {
428  if ( partCheck->isConnected( p->item ) )
429  {
430  // stop checking for other connected parts
431  return p->item;
432  }
433  p = p->next;
434  }
435 
436  return NULL; // no connected part found...
437  }
438 
440  {
441  // go through all label texts
442  char* labelText;
443  while (( labelText = connectedTexts->pop_front() ) )
444  {
445  //std::cerr << "JOIN: " << labelText << std::endl;
446  LinkedList<FeaturePart*>** partsPtr = connectedHashtable->find( labelText );
447  if ( !partsPtr )
448  continue; // shouldn't happen
449  LinkedList<FeaturePart*>* parts = *partsPtr;
450 
451  // go one-by-one part, try to merge
452  while ( parts->size() )
453  {
454  // part we'll be checking against other in this round
455  FeaturePart* partCheck = parts->pop_front();
456 
457  FeaturePart* otherPart = _findConnectedPart( partCheck, parts );
458  if ( otherPart )
459  {
460  //std::cerr << "- connected " << partCheck << " with " << otherPart << std::endl;
461 
462  // remove partCheck from r-tree
463  double bmin[2], bmax[2];
464  partCheck->getBoundingBox( bmin, bmax );
465  rtree->Remove( bmin, bmax, partCheck );
466  featureParts->remove( partCheck );
467 
468  otherPart->getBoundingBox( bmin, bmax );
469 
470  // merge points from partCheck to p->item
471  if ( otherPart->mergeWithFeaturePart( partCheck ) )
472  {
473  // reinsert p->item to r-tree (probably not needed)
474  rtree->Remove( bmin, bmax, otherPart );
475  otherPart->getBoundingBox( bmin, bmax );
476  rtree->Insert( bmin, bmax, otherPart );
477  }
478  }
479  }
480 
481  // we're done processing feature parts with this particular label text
482  delete parts;
483  *partsPtr = NULL;
484  delete labelText;
485  }
486 
487  // we're done processing connected fetures
488  delete connectedHashtable;
489  connectedHashtable = NULL;
490  delete connectedTexts;
491  connectedTexts = NULL;
492  }
493 
495  {
497  while ( FeaturePart* fpart = featureParts->pop_front() )
498  {
499  const GEOSGeometry* geom = fpart->getGeometry();
500  double chopInterval = fpart->getFeature()->repeatDistance();
501  if ( chopInterval != 0. && GEOSGeomTypeId( geom ) == GEOS_LINESTRING )
502  {
503 
504  double bmin[2], bmax[2];
505  fpart->getBoundingBox( bmin, bmax );
506  rtree->Remove( bmin, bmax, fpart );
507 
508  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq( geom );
509 
510  // get number of points
511  unsigned int n;
512  GEOSCoordSeq_getSize( cs, &n );
513 
514  // Read points
515  std::vector<Point> points( n );
516  for ( unsigned int i = 0; i < n; ++i )
517  {
518  GEOSCoordSeq_getX( cs, i, &points[i].x );
519  GEOSCoordSeq_getY( cs, i, &points[i].y );
520  }
521 
522  // Cumulative length vector
523  std::vector<double> len( n, 0 );
524  for ( unsigned int i = 1; i < n; ++i )
525  {
526  double dx = points[i].x - points[i - 1].x;
527  double dy = points[i].y - points[i - 1].y;
528  len[i] = len[i - 1] + std::sqrt( dx * dx + dy * dy );
529  }
530 
531  // Walk along line
532  unsigned int cur = 0;
533  double lambda = 0;
534  std::vector<Point> part;
535  for ( ;; )
536  {
537  lambda += chopInterval;
538  for ( ; cur < n && lambda > len[cur]; ++cur )
539  {
540  part.push_back( points[cur] );
541  }
542  if ( cur >= n )
543  {
544  break;
545  }
546  double c = ( lambda - len[cur - 1] ) / ( len[cur] - len[cur - 1] );
547  Point p;
548  p.x = points[cur - 1].x + c * ( points[cur].x - points[cur - 1].x );
549  p.y = points[cur - 1].y + c * ( points[cur].y - points[cur - 1].y );
550  part.push_back( p );
551  GEOSCoordSequence* cooSeq = GEOSCoordSeq_create( part.size(), 2 );
552  for ( std::size_t i = 0; i < part.size(); ++i )
553  {
554  GEOSCoordSeq_setX( cooSeq, i, part[i].x );
555  GEOSCoordSeq_setY( cooSeq, i, part[i].y );
556  }
557 
558  GEOSGeometry* newgeom = GEOSGeom_createLineString( cooSeq );
559  FeaturePart* newfpart = new FeaturePart( fpart->getFeature(), newgeom );
560  newFeatureParts->push_back( newfpart );
561  newfpart->getBoundingBox( bmin, bmax );
562  rtree->Insert( bmin, bmax, newfpart );
563  part.clear();
564  part.push_back( p );
565  }
566  // Create final part
567  part.push_back( points[n - 1] );
568  GEOSCoordSequence* cooSeq = GEOSCoordSeq_create( part.size(), 2 );
569  for ( std::size_t i = 0; i < part.size(); ++i )
570  {
571  GEOSCoordSeq_setX( cooSeq, i, part[i].x );
572  GEOSCoordSeq_setY( cooSeq, i, part[i].y );
573  }
574 
575  GEOSGeometry* newgeom = GEOSGeom_createLineString( cooSeq );
576  FeaturePart* newfpart = new FeaturePart( fpart->getFeature(), newgeom );
577  newFeatureParts->push_back( newfpart );
578  newfpart->getBoundingBox( bmin, bmax );
579  rtree->Insert( bmin, bmax, newfpart );
580  }
581  else
582  {
583  newFeatureParts->push_back( fpart );
584  }
585  }
586 
587  delete featureParts;
588  featureParts = newFeatureParts;
589  }
590 
591 
592 
593 } // end namespace
594 
Arrangement arrangement
optional flags used for some placement methods
Definition: layer.h:112
double max_scale
Definition: layer.h:109
void setObstacle(bool obstacle)
mark layer's features as obstacles
Definition: layer.cpp:193
int reorderPolygon(int nbPoints, double *x, double *y)
bool isObstacle()
return the obstacle status
Definition: layer.cpp:162
void setMaxScale(double max_scale)
set the maximum valid scale, upon this scale the layer will not be labelled
Definition: layer.cpp:213
void addFeaturePart(FeaturePart *fpart, const char *labelText=NULL)
add newly creted feature part into r tree and to the list
Definition: layer.cpp:374
LabelMode mode
Definition: layer.h:114
LinkedList< const GEOSGeometry * > * unmulti(const GEOSGeometry *the_geom)
Definition: util.cpp:196
bool toLabel
Definition: layer.h:102
bool isScaleValid(double scale)
check if the scal is in the scale range min_scale -> max_scale
Definition: layer.cpp:134
bool obstacle
Definition: layer.h:100
double getPriority()
return the layer's priority
Definition: layer.cpp:188
LinkedList< FeaturePart * > * featureParts
list of feature parts
Definition: layer.h:91
void setFixedAngle(double a)
Definition: feature.h:98
bool isActive()
return the layer's activity status
Definition: layer.cpp:172
void setPriority(double priority)
\ brief set the layer priority
Definition: layer.cpp:218
Pal main class.
Definition: pal.h:123
double defaultPriority
Definition: layer.h:98
static FeaturePart * _findConnectedPart(FeaturePart *partCheck, LinkedList< FeaturePart * > *otherParts)
Definition: layer.cpp:422
Units getLabelUnit()
get units for label size
Definition: layer.cpp:416
char * name
Definition: layer.h:88
LinkedList< char * > * connectedTexts
Definition: layer.h:125
Feature * getFeature(const char *geom_id)
return pointer to feature or NULL if doesn't exist
Definition: layer.cpp:127
int getNbFeatures()
get the number of features into layer
Definition: layer.cpp:141
double getMaxScale()
return the maximum valid scale
Definition: layer.cpp:183
void setArrangement(Arrangement arrangement)
set arrangement policy
Definition: layer.cpp:156
void setPosOffset(double x, double y)
Definition: feature.h:95
double getMinScale()
return the minimum valid scale
Definition: layer.cpp:178
bool ptrFeaturePartCompare(FeaturePart *a, FeaturePart *b)
Definition: util.h:249
void setQuadOffset(double x, double y)
Definition: feature.h:94
Units label_unit
Definition: layer.h:106
virtual const GEOSGeometry * getGeosGeometry()=0
get the GEOSGeometry of the feature This method is called by Pal each time it needs a geom's coordina...
bool mergeWithFeaturePart(FeaturePart *other)
merge other (connected) part with this one and save the result in this part (other is unchanged)...
Definition: feature.cpp:1442
double * x
Definition: pointset.h:102
void setMinScale(double min_scale)
set the minimum valid scale, below this scale the layer will not be labelled
Definition: layer.cpp:208
void setAlwaysShow(bool bl)
Definition: feature.h:101
void setRepeatDistance(double dist)
Definition: feature.h:99
double x
Definition: util.h:79
bool isConnected(FeaturePart *p2)
check whether this part is connected with some other part
Definition: feature.cpp:1437
void setLabelUnit(Units label_unit)
set unit for label size
Definition: layer.cpp:410
HashTable< Feature * > * hashtable
Definition: layer.h:122
Main class to handle feature.
Definition: feature.h:138
pixel [px]
Definition: pal.h:67
Thrown when a geometry type is not like expected.
Layer(const char *lyrName, double min_scale, double max_scale, Arrangement arrangement, Units label_unit, double defaultPriority, bool obstacle, bool active, bool toLabel, Pal *pal, bool displayAll=false)
Create a new layer.
Definition: layer.cpp:62
bool isToLabel()
return if the layer will be labelled or not
Definition: layer.cpp:167
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)
bool strCompare(char *a, char *b)
Definition: util.h:230
double * y
Definition: pointset.h:103
bool registerFeature(const char *geom_id, PalGeometry *userGeom, double label_x=-1, double label_y=-1, const char *labelText=NULL, double labelPosX=0.0, double labelPosY=0.0, bool fixedPos=false, double angle=0.0, bool fixedAngle=false, int xQuadOffset=0, int yQuadOffset=0, double xOffset=0.0, double yOffset=0.0, bool alwaysShow=false, double repeatDistance=0)
register a feature in the layer
Definition: layer.cpp:230
void getBoundingBox(double min[2], double max[2]) const
Definition: pointset.h:168
void joinConnectedFeatures()
join connected features with the same label text
Definition: layer.cpp:439
const char * getName()
get layer's name
Definition: layer.cpp:146
void setFixedPosition(double x, double y)
Definition: feature.h:93
virtual ~Layer()
Delete the layer.
Definition: layer.cpp:91
enum _arrangement Arrangement
typedef for _arrangement enumeration
Definition: pal.h:104
double min_scale
Definition: layer.h:108
double y
Definition: util.h:79
meter [m]
Definition: pal.h:68
void setActive(bool active)
activate or desactivate the layer
Definition: layer.cpp:198
LinkedList< Feature * > * features
list of features - for deletion
Definition: layer.h:94
void chopFeaturesAtRepeatDistance()
chop layer features at the repeat distance
Definition: layer.cpp:494
HashTable< LinkedList< FeaturePart * > * > * connectedHashtable
Definition: layer.h:124
Interface that allows Pal to access user's geometries.
Definition: palgeometry.h:42
RTree< FeaturePart *, double, 2, double, 8, 4 > * rtree
Definition: layer.h:121
virtual void releaseGeosGeometry(const GEOSGeometry *the_geom)=0
Called by Pal when it doesn't need the coordinates anymore.
void setToLabel(bool toLabel)
tell pal whether the layer has to be labelled.
Definition: layer.cpp:203
enum _Units Units
Typedef for _Units enumeration.
Definition: pal.h:74
bool mergeLines
Definition: layer.h:115
bool ptrFeatureCompare(Feature *a, Feature *b)
Definition: util.h:245
SimpleMutex * modMutex
Definition: layer.h:127
friend class FeaturePart
Definition: layer.h:68
bool fixedPosition() const
Definition: feature.h:96
bool active
Definition: layer.h:101