QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties 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 #define _CRT_SECURE_NO_DEPRECATE
31 
32 #include "pal.h"
33 #include "layer.h"
34 #include "palexception.h"
35 #include "internalexception.h"
36 #include "feature.h"
37 #include "geomfunction.h"
38 #include "util.h"
39 #include <iostream>
40 #include <cmath>
41 #include <vector>
42 
43 namespace pal
44 {
45 
46  Layer::Layer( const QString &lyrName, Arrangement arrangement, double defaultPriority, bool obstacle, bool active, bool toLabel, Pal *pal, bool displayAll )
47  : mName( lyrName )
48  , pal( pal )
49  , mObstacle( obstacle )
50  , mObstacleType( PolygonInterior )
51  , mActive( active )
52  , mLabelLayer( toLabel )
53  , mDisplayAll( displayAll )
54  , mCentroidInside( false )
55  , mFitInPolygon( false )
56  , mArrangement( arrangement )
57  , mArrangementFlags( 0 )
58  , mMode( LabelPerFeature )
59  , mMergeLines( false )
60  , mUpsidedownLabels( Upright )
61  {
64 
67 
68  if ( defaultPriority < 0.0001 )
69  mDefaultPriority = 0.0001;
70  else if ( defaultPriority > 1.0 )
71  mDefaultPriority = 1.0;
72  else
73  mDefaultPriority = defaultPriority;
74 
77  }
78 
80  {
81  mMutex.lock();
82 
83  if ( featureParts )
84  {
85  qDeleteAll( *featureParts );
86  delete featureParts;
87  }
88 
89  // this hashtable and list should be empty if they still exist
90  delete connectedHashtable;
91 
92  // features in the hashtable
93  if ( features )
94  {
95  qDeleteAll( *features );
96  delete features;
97  }
98 
99  delete rtree;
100 
101  delete hashtable;
102  mMutex.unlock();
103  delete connectedTexts;
104  }
105 
106  Feature* Layer::getFeature( const QString& geom_id )
107  {
109  if ( i != hashtable->constEnd() )
110  return *i;
111  else
112  return 0;
113  }
114 
115  void Layer::setPriority( double priority )
116  {
117  if ( priority >= 1.0 ) // low priority
118  mDefaultPriority = 1.0;
119  else if ( priority <= 0.0001 )
120  mDefaultPriority = 0.0001; // high priority
121  else
123  }
124 
125  bool Layer::registerFeature( const QString& geom_id, PalGeometry *userGeom, double label_x, double label_y, const QString &labelText,
126  double labelPosX, double labelPosY, bool fixedPos, double angle, bool fixedAngle,
127  int xQuadOffset, int yQuadOffset, double xOffset, double yOffset, bool alwaysShow, double repeatDistance )
128  {
129  if ( geom_id.isEmpty() || label_x < 0 || label_y < 0 )
130  return false;
131 
132  mMutex.lock();
133 
134  if ( hashtable->contains( geom_id ) )
135  {
136  mMutex.unlock();
137  //A feature with this id already exists. Don't throw an exception as sometimes,
138  //the same feature is added twice (dateline split with otf-reprojection)
139  return false;
140  }
141 
142  // Split MULTI GEOM and Collection in simple geometries
143  const GEOSGeometry *the_geom = userGeom->getGeosGeometry();
144 
145  Feature* f = new Feature( this, geom_id, userGeom, label_x, label_y );
146  if ( fixedPos )
147  {
148  f->setFixedPosition( labelPosX, labelPosY );
149  }
150  if ( xQuadOffset != 0 || yQuadOffset != 0 )
151  {
152  f->setQuadOffset( xQuadOffset, yQuadOffset );
153  }
154  if ( xOffset != 0.0 || yOffset != 0.0 )
155  {
156  f->setPosOffset( xOffset, yOffset );
157  }
158  if ( fixedAngle )
159  {
160  f->setFixedAngle( angle );
161  }
162  // use layer-level defined rotation, but not if position fixed
163  if ( !fixedPos && angle != 0.0 )
164  {
165  f->setFixedAngle( angle );
166  }
167  f->setRepeatDistance( repeatDistance );
168 
169  f->setAlwaysShow( alwaysShow );
170 
171  // feature inherits layer setting for acting as an obstacle
172  f->setIsObstacle( mObstacle );
173 
174  bool first_feat = true;
175 
176  double geom_size = -1, biggest_size = -1;
177  FeaturePart* biggest_part = NULL;
178 
179  // break the (possibly multi-part) geometry into simple geometries
180  QLinkedList<const GEOSGeometry*>* simpleGeometries = unmulti( the_geom );
181  if ( simpleGeometries == NULL ) // unmulti() failed?
182  {
183  mMutex.unlock();
185  }
186 
187  GEOSContextHandle_t geosctxt = geosContext();
188 
189  while ( simpleGeometries->size() > 0 )
190  {
191  const GEOSGeometry* geom = simpleGeometries->takeFirst();
192 
193  // ignore invalid geometries (e.g. polygons with self-intersecting rings)
194  if ( GEOSisValid_r( geosctxt, geom ) != 1 ) // 0=invalid, 1=valid, 2=exception
195  {
196 // std::cerr << "ignoring invalid feature " << geom_id << std::endl;
197  continue;
198  }
199 
200  int type = GEOSGeomTypeId_r( geosctxt, geom );
201 
202  if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON )
203  {
204  mMutex.unlock();
206  }
207 
208  FeaturePart* fpart = new FeaturePart( f, geom );
209 
210  // ignore invalid geometries
211  if (( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) ||
212  ( type == GEOS_POLYGON && fpart->nbPoints < 3 ) )
213  {
214  delete fpart;
215  continue;
216  }
217 
218  // polygons: reorder coordinates
219  if ( type == GEOS_POLYGON && reorderPolygon( fpart->nbPoints, fpart->x, fpart->y ) != 0 )
220  {
221  delete fpart;
222  continue;
223  }
224 
225  if ( mMode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
226  {
227  if ( type == GEOS_LINESTRING )
228  GEOSLength_r( geosctxt, geom, &geom_size );
229  else if ( type == GEOS_POLYGON )
230  GEOSArea_r( geosctxt, geom, &geom_size );
231 
232  if ( geom_size > biggest_size )
233  {
234  biggest_size = geom_size;
235  delete biggest_part; // safe with NULL part
236  biggest_part = fpart;
237  }
238  else
239  {
240  delete fpart;
241  }
242  continue; // don't add the feature part now, do it later
243  // TODO: we should probably add also other parts to act just as obstacles
244  }
245 
246  // feature part is ready!
247  addFeaturePart( fpart, labelText );
248 
249  first_feat = false;
250  }
251  delete simpleGeometries;
252 
253  userGeom->releaseGeosGeometry( the_geom );
254 
255  mMutex.unlock();
256 
257  // if using only biggest parts...
258  if (( mMode == LabelPerFeature || f->fixedPosition() ) && biggest_part != NULL )
259  {
260  addFeaturePart( biggest_part, labelText );
261  first_feat = false;
262  }
263 
264  // add feature to layer if we have added something
265  if ( !first_feat )
266  {
267  features->append( f );
268  hashtable->insert( geom_id, f );
269  }
270  else
271  {
272  delete f;
273  }
274 
275  return !first_feat; // true if we've added something
276  }
277 
278  void Layer::addFeaturePart( FeaturePart* fpart, const QString& labelText )
279  {
280  double bmin[2];
281  double bmax[2];
282  fpart->getBoundingBox( bmin, bmax );
283 
284  // add to list of layer's feature parts
285  featureParts->append( fpart );
286 
287  // add to r-tree for fast spatial access
288  rtree->Insert( bmin, bmax, fpart );
289 
290  // add to hashtable with equally named feature parts
291  if ( mMergeLines && !labelText.isEmpty() )
292  {
293  QHash< QString, QLinkedList<FeaturePart*>* >::const_iterator lstPtr = connectedHashtable->find( labelText );
295  if ( lstPtr == connectedHashtable->constEnd() )
296  {
297  // entry doesn't exist yet
298  lst = new QLinkedList<FeaturePart*>;
299  connectedHashtable->insert( labelText, lst );
300  connectedTexts->append( labelText );
301  }
302  else
303  {
304  lst = *lstPtr;
305  }
306  lst->append( fpart ); // add to the list
307  }
308  }
309 
311  {
312  // iterate in the rest of the parts with the same label
314  while ( p != otherParts->constEnd() )
315  {
316  if ( partCheck->isConnected( *p ) )
317  {
318  // stop checking for other connected parts
319  return *p;
320  }
321  p++;
322  }
323 
324  return NULL; // no connected part found...
325  }
326 
328  {
329  // go through all label texts
330  QString labelText;
331  while ( !connectedTexts->isEmpty() )
332  {
333  labelText = connectedTexts->takeFirst();
334 
335  //std::cerr << "JOIN: " << labelText << std::endl;
336  QHash< QString, QLinkedList<FeaturePart*>* >::const_iterator partsPtr = connectedHashtable->find( labelText );
337  if ( partsPtr == connectedHashtable->constEnd() )
338  continue; // shouldn't happen
339  QLinkedList<FeaturePart*>* parts = *partsPtr;
340 
341  // go one-by-one part, try to merge
342  while ( !parts->isEmpty() )
343  {
344  // part we'll be checking against other in this round
345  FeaturePart* partCheck = parts->takeFirst();
346 
347  FeaturePart* otherPart = _findConnectedPart( partCheck, parts );
348  if ( otherPart )
349  {
350  //std::cerr << "- connected " << partCheck << " with " << otherPart << std::endl;
351 
352  // remove partCheck from r-tree
353  double bmin[2], bmax[2];
354  partCheck->getBoundingBox( bmin, bmax );
355  rtree->Remove( bmin, bmax, partCheck );
356  featureParts->removeOne( partCheck );
357 
358  otherPart->getBoundingBox( bmin, bmax );
359 
360  // merge points from partCheck to p->item
361  if ( otherPart->mergeWithFeaturePart( partCheck ) )
362  {
363  // reinsert p->item to r-tree (probably not needed)
364  rtree->Remove( bmin, bmax, otherPart );
365  otherPart->getBoundingBox( bmin, bmax );
366  rtree->Insert( bmin, bmax, otherPart );
367  }
368  }
369  }
370 
371  // we're done processing feature parts with this particular label text
372  delete parts;
373  }
374 
375  // we're done processing connected fetures
376  delete connectedHashtable;
377  connectedHashtable = NULL;
378  delete connectedTexts;
379  connectedTexts = NULL;
380  }
381 
383  {
384  GEOSContextHandle_t geosctxt = geosContext();
386  while ( !featureParts->isEmpty() )
387  {
388  FeaturePart* fpart = featureParts->takeFirst();
389  const GEOSGeometry* geom = fpart->geos();
390  double chopInterval = fpart->getFeature()->repeatDistance();
391  if ( chopInterval != 0. && GEOSGeomTypeId_r( geosctxt, geom ) == GEOS_LINESTRING )
392  {
393 
394  double bmin[2], bmax[2];
395  fpart->getBoundingBox( bmin, bmax );
396  rtree->Remove( bmin, bmax, fpart );
397 
398  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosctxt, geom );
399 
400  // get number of points
401  unsigned int n;
402  GEOSCoordSeq_getSize_r( geosctxt, cs, &n );
403 
404  // Read points
405  std::vector<Point> points( n );
406  for ( unsigned int i = 0; i < n; ++i )
407  {
408  GEOSCoordSeq_getX_r( geosctxt, cs, i, &points[i].x );
409  GEOSCoordSeq_getY_r( geosctxt, cs, i, &points[i].y );
410  }
411 
412  // Cumulative length vector
413  std::vector<double> len( n, 0 );
414  for ( unsigned int i = 1; i < n; ++i )
415  {
416  double dx = points[i].x - points[i - 1].x;
417  double dy = points[i].y - points[i - 1].y;
418  len[i] = len[i - 1] + std::sqrt( dx * dx + dy * dy );
419  }
420 
421  // Walk along line
422  unsigned int cur = 0;
423  double lambda = 0;
424  std::vector<Point> part;
425  for ( ;; )
426  {
427  lambda += chopInterval;
428  for ( ; cur < n && lambda > len[cur]; ++cur )
429  {
430  part.push_back( points[cur] );
431  }
432  if ( cur >= n )
433  {
434  break;
435  }
436  double c = ( lambda - len[cur - 1] ) / ( len[cur] - len[cur - 1] );
437  Point p;
438  p.x = points[cur - 1].x + c * ( points[cur].x - points[cur - 1].x );
439  p.y = points[cur - 1].y + c * ( points[cur].y - points[cur - 1].y );
440  part.push_back( p );
441  GEOSCoordSequence* cooSeq = GEOSCoordSeq_create_r( geosctxt, part.size(), 2 );
442  for ( std::size_t i = 0; i < part.size(); ++i )
443  {
444  GEOSCoordSeq_setX_r( geosctxt, cooSeq, i, part[i].x );
445  GEOSCoordSeq_setY_r( geosctxt, cooSeq, i, part[i].y );
446  }
447 
448  GEOSGeometry* newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
449  FeaturePart* newfpart = new FeaturePart( fpart->getFeature(), newgeom );
450  newFeatureParts->append( newfpart );
451  newfpart->getBoundingBox( bmin, bmax );
452  rtree->Insert( bmin, bmax, newfpart );
453  part.clear();
454  part.push_back( p );
455  }
456  // Create final part
457  part.push_back( points[n - 1] );
458  GEOSCoordSequence* cooSeq = GEOSCoordSeq_create_r( geosctxt, part.size(), 2 );
459  for ( std::size_t i = 0; i < part.size(); ++i )
460  {
461  GEOSCoordSeq_setX_r( geosctxt, cooSeq, i, part[i].x );
462  GEOSCoordSeq_setY_r( geosctxt, cooSeq, i, part[i].y );
463  }
464 
465  GEOSGeometry* newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
466  FeaturePart* newfpart = new FeaturePart( fpart->getFeature(), newgeom );
467  newFeatureParts->append( newfpart );
468  newfpart->getBoundingBox( bmin, bmax );
469  rtree->Insert( bmin, bmax, newfpart );
470  delete fpart;
471  }
472  else
473  {
474  newFeatureParts->append( fpart );
475  }
476  }
477 
478  delete featureParts;
479  featureParts = newFeatureParts;
480  }
481 
482 
483 
484 } // end namespace
485 
int reorderPolygon(int nbPoints, double *x, double *y)
bool mMergeLines
Definition: layer.h:303
void setIsObstacle(bool obstacle)
Sets whether the feature will act as an obstacle for labels.
Definition: feature.h:115
const GEOSGeometry * geos() const
Returns the point set's GEOS geometry.
Definition: pointset.cpp:1025
Feature * getFeature()
Returns the parent feature.
Definition: feature.h:264
static FeaturePart * _findConnectedPart(FeaturePart *partCheck, QLinkedList< FeaturePart * > *otherParts)
Definition: layer.cpp:310
double mDefaultPriority
Definition: layer.h:289
void setFixedAngle(double a)
Set label rotation to fixed value.
Definition: feature.h:106
void setPriority(double priority)
Sets the layer's priority.
Definition: layer.cpp:115
Pal main class.
Definition: pal.h:109
double priority() const
Returns the layer's priority, between 0 and 1.
Definition: layer.h:177
void unlock()
QLinkedList< Feature * > * features
List of features - for deletion.
Definition: layer.h:285
bool registerFeature(const QString &geom_id, PalGeometry *userGeom, double label_x=-1, double label_y=-1, const QString &labelText=QString(), 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:125
bool mObstacle
Definition: layer.h:291
void setPosOffset(double x, double y)
Definition: feature.h:101
void setQuadOffset(double x, double y)
Definition: feature.h:88
QHash< QString, QLinkedList< FeaturePart * > * > * connectedHashtable
Definition: layer.h:311
bool isEmpty() const
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:1425
double * x
Definition: pointset.h:148
Feature * getFeature(const QString &geom_id)
Return pointer to feature or NULL if doesn't exist.
Definition: layer.cpp:106
QLinkedList< const GEOSGeometry * > * unmulti(const GEOSGeometry *the_geom)
Definition: util.cpp:113
bool isEmpty() const
void setAlwaysShow(bool bl)
Definition: feature.h:109
void setRepeatDistance(double dist)
Definition: feature.h:107
QLinkedList< FeaturePart * > * featureParts
List of feature parts.
Definition: layer.h:282
double x
Definition: util.h:70
bool isConnected(FeaturePart *p2)
Check whether this part is connected with some other part.
Definition: feature.cpp:1409
Layer(const QString &lyrName, Arrangement arrangement, double defaultPriority, bool obstacle, bool active, bool toLabel, Pal *pal, bool displayAll=false)
Create a new layer.
Definition: layer.cpp:46
GEOSContextHandle_t geosContext()
Get GEOS context handle to be used in all GEOS library calls with reentrant API.
Definition: pal.cpp:56
Main class to handle feature.
Definition: feature.h:203
Thrown when a geometry type is not like expected.
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)
void lock()
double * y
Definition: pointset.h:149
void getBoundingBox(double min[2], double max[2]) const
Definition: pointset.h:115
const_iterator constBegin() const
void joinConnectedFeatures()
Join connected features with the same label text.
Definition: layer.cpp:327
void setFixedPosition(double x, double y)
Set label position of the feature to fixed x/y values.
Definition: feature.h:87
virtual ~Layer()
Definition: layer.cpp:79
const_iterator constEnd() const
double y
Definition: util.h:70
Arrangement
The way to arrange labels against spatial entities.
Definition: pal.h:75
void chopFeaturesAtRepeatDistance()
Chop layer features at the repeat distance.
Definition: layer.cpp:382
QMutex mMutex
Definition: layer.h:314
QLinkedList< QString > * connectedTexts
Definition: layer.h:312
void addFeaturePart(FeaturePart *fpart, const QString &labelText=QString())
Add newly created feature part into r tree and to the list.
Definition: layer.cpp:278
LabelMode mMode
Definition: layer.h:302
Interface that allows Pal to access user's geometries.
Definition: palgeometry.h:41
RTree< FeaturePart *, double, 2, double, 8, 4 > * rtree
Definition: layer.h:308
virtual void releaseGeosGeometry(const GEOSGeometry *the_geom)=0
Called by Pal when it doesn't need the coordinates anymore.
QHash< QString, Feature * > * hashtable
Definition: layer.h:309
friend class FeaturePart
Definition: layer.h:60
int size() const
double repeatDistance() const
Definition: feature.h:108
void append(const T &value)
bool fixedPosition() const
Definition: feature.h:102