QGIS API Documentation  2.11.0-Master
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 #define _CRT_SECURE_NO_DEPRECATE
31 
32 #include <stddef.h>
33 #include <geos_c.h>
34 
35 #include <iostream>
36 #include <cstring>
37 #include <cmath>
38 #include <vector>
39 
40 #include "pal.h"
41 #include "layer.h"
42 #include "palexception.h"
43 #include "internalexception.h"
44 #include "linkedlist.hpp"
45 #include "hashtable.hpp"
46 #include "feature.h"
47 #include "geomfunction.h"
48 #include "util.h"
49 
50 namespace pal
51 {
52 
53  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 )
54  : pal( pal )
55  , obstacle( obstacle )
56  , active( active )
57  , toLabel( toLabel )
58  , displayAll( displayAll )
59  , centroidInside( false )
60  , label_unit( label_unit )
61  , min_scale( min_scale )
62  , max_scale( max_scale )
63  , arrangement( arrangement )
64  , arrangementFlags( 0 )
65  , mode( LabelPerFeature )
66  , mergeLines( false )
67  , upsidedownLabels( Upright )
68  {
69 
70  this->name = new char[strlen( lyrName ) +1];
71  strcpy( this->name, lyrName );
72 
74  hashtable = new HashTable<Feature*> ( 5281 );
75 
78 
79  if ( defaultPriority < 0.0001 )
80  this->defaultPriority = 0.0001;
81  else if ( defaultPriority > 1.0 )
82  this->defaultPriority = 1.0;
83  else
84  this->defaultPriority = defaultPriority;
85 
88  }
89 
91  {
92  mMutex.lock();
93 
94  if ( featureParts )
95  {
96  while ( featureParts->size() )
97  {
98  delete featureParts->pop_front();
99  }
100  delete featureParts;
101 
102  }
103 
104  // this hashtable and list should be empty if they still exist
105  delete connectedHashtable;
106 
107  // features in the hashtable
108  if ( features )
109  {
110  while ( features->size() )
111  {
112  delete features->pop_front();
113  }
114  delete features;
115  }
116 
117  if ( name )
118  delete[] name;
119 
120  delete rtree;
121 
122  delete hashtable;
123  mMutex.unlock();
124  delete connectedTexts;
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  mMutex.lock();
238 
239  if ( hashtable->find( geom_id ) )
240  {
241  mMutex.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  mMutex.unlock();
287  }
288 
289  GEOSContextHandle_t geosctxt = geosContext();
290 
291  while ( simpleGeometries->size() > 0 )
292  {
293  const GEOSGeometry* geom = simpleGeometries->pop_front();
294 
295  // ignore invalid geometries (e.g. polygons with self-intersecting rings)
296  if ( GEOSisValid_r( geosctxt, geom ) != 1 ) // 0=invalid, 1=valid, 2=exception
297  {
298  std::cerr << "ignoring invalid feature " << geom_id << std::endl;
299  continue;
300  }
301 
302  int type = GEOSGeomTypeId_r( geosctxt, geom );
303 
304  if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON )
305  {
306  mMutex.unlock();
308  }
309 
310  FeaturePart* fpart = new FeaturePart( f, geom );
311 
312  // ignore invalid geometries
313  if (( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) ||
314  ( type == GEOS_POLYGON && fpart->nbPoints < 3 ) )
315  {
316  delete fpart;
317  continue;
318  }
319 
320  // polygons: reorder coordinates
321  if ( type == GEOS_POLYGON && reorderPolygon( fpart->nbPoints, fpart->x, fpart->y ) != 0 )
322  {
323  delete fpart;
324  continue;
325  }
326 
327  if ( mode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
328  {
329  if ( type == GEOS_LINESTRING )
330  GEOSLength_r( geosctxt, geom, &geom_size );
331  else if ( type == GEOS_POLYGON )
332  GEOSArea_r( geosctxt, geom, &geom_size );
333 
334  if ( geom_size > biggest_size )
335  {
336  biggest_size = geom_size;
337  delete biggest_part; // safe with NULL part
338  biggest_part = fpart;
339  }
340  continue; // don't add the feature part now, do it later
341  // TODO: we should probably add also other parts to act just as obstacles
342  }
343 
344  // feature part is ready!
345  addFeaturePart( fpart, labelText );
346 
347  first_feat = false;
348  }
349  delete simpleGeometries;
350 
351  userGeom->releaseGeosGeometry( the_geom );
352 
353  mMutex.unlock();
354 
355  // if using only biggest parts...
356  if (( mode == LabelPerFeature || f->fixedPosition() ) && biggest_part != NULL )
357  {
358  addFeaturePart( biggest_part, labelText );
359  first_feat = false;
360  }
361 
362  // add feature to layer if we have added something
363  if ( !first_feat )
364  {
365  features->push_back( f );
366  hashtable->insertItem( geom_id, f );
367  }
368  else
369  {
370  delete f;
371  }
372 
373  return !first_feat; // true if we've added something
374  }
375 
376  void Layer::addFeaturePart( FeaturePart* fpart, const char* labelText )
377  {
378  double bmin[2];
379  double bmax[2];
380  fpart->getBoundingBox( bmin, bmax );
381 
382  // add to list of layer's feature parts
383  featureParts->push_back( fpart );
384 
385  // add to r-tree for fast spatial access
386  rtree->Insert( bmin, bmax, fpart );
387 
388  // add to hashtable with equally named feature parts
389  if ( mergeLines && labelText )
390  {
391  LinkedList< FeaturePart*>** lstPtr = connectedHashtable->find( labelText );
393  if ( lstPtr == NULL )
394  {
395  // entry doesn't exist yet
397  connectedHashtable->insertItem( labelText, lst );
398 
399  char* txt = new char[strlen( labelText ) +1];
400  strcpy( txt, labelText );
401  connectedTexts->push_back( txt );
402  }
403  else
404  {
405  lst = *lstPtr;
406  }
407  lst->push_back( fpart ); // add to the list
408  }
409  }
410 
411 
412  void Layer::setLabelUnit( Units label_unit )
413  {
414  if ( label_unit == PIXEL || label_unit == METER )
415  this->label_unit = label_unit;
416  }
417 
419  {
420  return label_unit;
421  }
422 
423 
425  {
426  // iterate in the rest of the parts with the same label
427  Cell<FeaturePart*>* p = otherParts->getFirst();
428  while ( p )
429  {
430  if ( partCheck->isConnected( p->item ) )
431  {
432  // stop checking for other connected parts
433  return p->item;
434  }
435  p = p->next;
436  }
437 
438  return NULL; // no connected part found...
439  }
440 
442  {
443  // go through all label texts
444  char* labelText;
445  while (( labelText = connectedTexts->pop_front() ) )
446  {
447  //std::cerr << "JOIN: " << labelText << std::endl;
448  LinkedList<FeaturePart*>** partsPtr = connectedHashtable->find( labelText );
449  if ( !partsPtr )
450  continue; // shouldn't happen
451  LinkedList<FeaturePart*>* parts = *partsPtr;
452 
453  // go one-by-one part, try to merge
454  while ( parts->size() )
455  {
456  // part we'll be checking against other in this round
457  FeaturePart* partCheck = parts->pop_front();
458 
459  FeaturePart* otherPart = _findConnectedPart( partCheck, parts );
460  if ( otherPart )
461  {
462  //std::cerr << "- connected " << partCheck << " with " << otherPart << std::endl;
463 
464  // remove partCheck from r-tree
465  double bmin[2], bmax[2];
466  partCheck->getBoundingBox( bmin, bmax );
467  rtree->Remove( bmin, bmax, partCheck );
468  featureParts->remove( partCheck );
469 
470  otherPart->getBoundingBox( bmin, bmax );
471 
472  // merge points from partCheck to p->item
473  if ( otherPart->mergeWithFeaturePart( partCheck ) )
474  {
475  // reinsert p->item to r-tree (probably not needed)
476  rtree->Remove( bmin, bmax, otherPart );
477  otherPart->getBoundingBox( bmin, bmax );
478  rtree->Insert( bmin, bmax, otherPart );
479  }
480  }
481  }
482 
483  // we're done processing feature parts with this particular label text
484  delete parts;
485  *partsPtr = NULL;
486  delete labelText;
487  }
488 
489  // we're done processing connected fetures
490  delete connectedHashtable;
491  connectedHashtable = NULL;
492  delete connectedTexts;
493  connectedTexts = NULL;
494  }
495 
497  {
498  GEOSContextHandle_t geosctxt = geosContext();
500  while ( FeaturePart* fpart = featureParts->pop_front() )
501  {
502  const GEOSGeometry* geom = fpart->getGeometry();
503  double chopInterval = fpart->getFeature()->repeatDistance();
504  if ( chopInterval != 0. && GEOSGeomTypeId_r( geosctxt, geom ) == GEOS_LINESTRING )
505  {
506 
507  double bmin[2], bmax[2];
508  fpart->getBoundingBox( bmin, bmax );
509  rtree->Remove( bmin, bmax, fpart );
510 
511  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosctxt, geom );
512 
513  // get number of points
514  unsigned int n;
515  GEOSCoordSeq_getSize_r( geosctxt, cs, &n );
516 
517  // Read points
518  std::vector<Point> points( n );
519  for ( unsigned int i = 0; i < n; ++i )
520  {
521  GEOSCoordSeq_getX_r( geosctxt, cs, i, &points[i].x );
522  GEOSCoordSeq_getY_r( geosctxt, cs, i, &points[i].y );
523  }
524 
525  // Cumulative length vector
526  std::vector<double> len( n, 0 );
527  for ( unsigned int i = 1; i < n; ++i )
528  {
529  double dx = points[i].x - points[i - 1].x;
530  double dy = points[i].y - points[i - 1].y;
531  len[i] = len[i - 1] + std::sqrt( dx * dx + dy * dy );
532  }
533 
534  // Walk along line
535  unsigned int cur = 0;
536  double lambda = 0;
537  std::vector<Point> part;
538  for ( ;; )
539  {
540  lambda += chopInterval;
541  for ( ; cur < n && lambda > len[cur]; ++cur )
542  {
543  part.push_back( points[cur] );
544  }
545  if ( cur >= n )
546  {
547  break;
548  }
549  double c = ( lambda - len[cur - 1] ) / ( len[cur] - len[cur - 1] );
550  Point p;
551  p.x = points[cur - 1].x + c * ( points[cur].x - points[cur - 1].x );
552  p.y = points[cur - 1].y + c * ( points[cur].y - points[cur - 1].y );
553  part.push_back( p );
554  GEOSCoordSequence* cooSeq = GEOSCoordSeq_create_r( geosctxt, part.size(), 2 );
555  for ( std::size_t i = 0; i < part.size(); ++i )
556  {
557  GEOSCoordSeq_setX_r( geosctxt, cooSeq, i, part[i].x );
558  GEOSCoordSeq_setY_r( geosctxt, cooSeq, i, part[i].y );
559  }
560 
561  GEOSGeometry* newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
562  FeaturePart* newfpart = new FeaturePart( fpart->getFeature(), newgeom );
563  newFeatureParts->push_back( newfpart );
564  newfpart->getBoundingBox( bmin, bmax );
565  rtree->Insert( bmin, bmax, newfpart );
566  part.clear();
567  part.push_back( p );
568  }
569  // Create final part
570  part.push_back( points[n - 1] );
571  GEOSCoordSequence* cooSeq = GEOSCoordSeq_create_r( geosctxt, part.size(), 2 );
572  for ( std::size_t i = 0; i < part.size(); ++i )
573  {
574  GEOSCoordSeq_setX_r( geosctxt, cooSeq, i, part[i].x );
575  GEOSCoordSeq_setY_r( geosctxt, cooSeq, i, part[i].y );
576  }
577 
578  GEOSGeometry* newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
579  FeaturePart* newfpart = new FeaturePart( fpart->getFeature(), newgeom );
580  newFeatureParts->push_back( newfpart );
581  newfpart->getBoundingBox( bmin, bmax );
582  rtree->Insert( bmin, bmax, newfpart );
583  }
584  else
585  {
586  newFeatureParts->push_back( fpart );
587  }
588  }
589 
590  delete featureParts;
591  featureParts = newFeatureParts;
592  }
593 
594 
595 
596 } // end namespace
597 
Arrangement arrangement
Optional flags used for some placement methods.
Definition: layer.h:286
double max_scale
Definition: layer.h:283
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:376
LabelMode mode
Definition: layer.h:288
LinkedList< const GEOSGeometry * > * unmulti(const GEOSGeometry *the_geom)
Definition: util.cpp:186
bool toLabel
Definition: layer.h:276
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:274
double getPriority()
return the layer's priority
Definition: layer.cpp:188
LinkedList< FeaturePart * > * featureParts
List of feature parts.
Definition: layer.h:265
void setFixedAngle(double a)
Definition: feature.h:93
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:121
double defaultPriority
Definition: layer.h:272
static FeaturePart * _findConnectedPart(FeaturePart *partCheck, LinkedList< FeaturePart * > *otherParts)
Definition: layer.cpp:424
Units getLabelUnit()
get units for label size
Definition: layer.cpp:418
void unlock()
char * name
Definition: layer.h:262
LinkedList< char * > * connectedTexts
Definition: layer.h:298
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:90
double getMinScale()
return the minimum valid scale
Definition: layer.cpp:178
bool ptrFeaturePartCompare(FeaturePart *a, FeaturePart *b)
Definition: util.h:244
void setQuadOffset(double x, double y)
Definition: feature.h:89
Units label_unit
Definition: layer.h:280
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:1456
double * x
Definition: pointset.h:204
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:96
void setRepeatDistance(double dist)
Definition: feature.h:94
double x
Definition: util.h:74
bool isConnected(FeaturePart *p2)
Check whether this part is connected with some other part.
Definition: feature.cpp:1451
void setLabelUnit(Units label_unit)
set unit for label size
Definition: layer.cpp:412
GEOSContextHandle_t geosContext()
Get GEOS context handle to be used in all GEOS library calls with reentrant API.
Definition: pal.cpp:79
HashTable< Feature * > * hashtable
Definition: layer.h:295
Main class to handle feature.
Definition: feature.h:133
pixel [px]
Definition: pal.h:65
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:53
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:225
void lock()
double * y
Definition: pointset.h:205
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:140
void joinConnectedFeatures()
Join connected features with the same label text.
Definition: layer.cpp:441
const char * getName()
get layer's name
Definition: layer.cpp:146
void setFixedPosition(double x, double y)
Definition: feature.h:88
virtual ~Layer()
Delete the layer.
Definition: layer.cpp:90
enum _arrangement Arrangement
Typedef for _arrangement enumeration.
Definition: pal.h:102
double min_scale
Definition: layer.h:282
double y
Definition: util.h:74
meter [m]
Definition: pal.h:66
void setActive(bool active)
activate or desactivate the layer
Definition: layer.cpp:198
LinkedList< Feature * > * features
List of features - for deletion.
Definition: layer.h:268
void chopFeaturesAtRepeatDistance()
Chop layer features at the repeat distance.
Definition: layer.cpp:496
QMutex mMutex
Definition: layer.h:300
HashTable< LinkedList< FeaturePart * > * > * connectedHashtable
Definition: layer.h:297
Interface that allows Pal to access user's geometries.
Definition: palgeometry.h:42
RTree< FeaturePart *, double, 2, double, 8, 4 > * rtree
Definition: layer.h:294
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:72
bool mergeLines
Definition: layer.h:289
bool ptrFeatureCompare(Feature *a, Feature *b)
Definition: util.h:240
friend class FeaturePart
Definition: layer.h:63
bool fixedPosition() const
Definition: feature.h:91
bool active
Definition: layer.h:275