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