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