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  continue; // don't add the feature part now, do it later
239  // TODO: we should probably add also other parts to act just as obstacles
240  }
241 
242  // feature part is ready!
243  addFeaturePart( fpart, labelText );
244 
245  first_feat = false;
246  }
247  delete simpleGeometries;
248 
249  userGeom->releaseGeosGeometry( the_geom );
250 
251  mMutex.unlock();
252 
253  // if using only biggest parts...
254  if (( mMode == LabelPerFeature || f->fixedPosition() ) && biggest_part != NULL )
255  {
256  addFeaturePart( biggest_part, labelText );
257  first_feat = false;
258  }
259 
260  // add feature to layer if we have added something
261  if ( !first_feat )
262  {
263  features->append( f );
264  hashtable->insert( geom_id, f );
265  }
266  else
267  {
268  delete f;
269  }
270 
271  return !first_feat; // true if we've added something
272  }
273 
274  void Layer::addFeaturePart( FeaturePart* fpart, const QString& labelText )
275  {
276  double bmin[2];
277  double bmax[2];
278  fpart->getBoundingBox( bmin, bmax );
279 
280  // add to list of layer's feature parts
281  featureParts->append( fpart );
282 
283  // add to r-tree for fast spatial access
284  rtree->Insert( bmin, bmax, fpart );
285 
286  // add to hashtable with equally named feature parts
287  if ( mMergeLines && !labelText.isEmpty() )
288  {
289  QHash< QString, QLinkedList<FeaturePart*>* >::const_iterator lstPtr = connectedHashtable->find( labelText );
291  if ( lstPtr == connectedHashtable->constEnd() )
292  {
293  // entry doesn't exist yet
294  lst = new QLinkedList<FeaturePart*>;
295  connectedHashtable->insert( labelText, lst );
296  connectedTexts->append( labelText );
297  }
298  else
299  {
300  lst = *lstPtr;
301  }
302  lst->append( fpart ); // add to the list
303  }
304  }
305 
307  {
308  // iterate in the rest of the parts with the same label
310  while ( p != otherParts->constEnd() )
311  {
312  if ( partCheck->isConnected( *p ) )
313  {
314  // stop checking for other connected parts
315  return *p;
316  }
317  p++;
318  }
319 
320  return NULL; // no connected part found...
321  }
322 
324  {
325  // go through all label texts
326  QString labelText;
327  while ( !connectedTexts->isEmpty() )
328  {
329  labelText = connectedTexts->takeFirst();
330 
331  //std::cerr << "JOIN: " << labelText << std::endl;
332  QHash< QString, QLinkedList<FeaturePart*>* >::const_iterator partsPtr = connectedHashtable->find( labelText );
333  if ( partsPtr == connectedHashtable->constEnd() )
334  continue; // shouldn't happen
335  QLinkedList<FeaturePart*>* parts = *partsPtr;
336 
337  // go one-by-one part, try to merge
338  while ( !parts->isEmpty() )
339  {
340  // part we'll be checking against other in this round
341  FeaturePart* partCheck = parts->takeFirst();
342 
343  FeaturePart* otherPart = _findConnectedPart( partCheck, parts );
344  if ( otherPart )
345  {
346  //std::cerr << "- connected " << partCheck << " with " << otherPart << std::endl;
347 
348  // remove partCheck from r-tree
349  double bmin[2], bmax[2];
350  partCheck->getBoundingBox( bmin, bmax );
351  rtree->Remove( bmin, bmax, partCheck );
352  featureParts->removeOne( partCheck );
353 
354  otherPart->getBoundingBox( bmin, bmax );
355 
356  // merge points from partCheck to p->item
357  if ( otherPart->mergeWithFeaturePart( partCheck ) )
358  {
359  // reinsert p->item to r-tree (probably not needed)
360  rtree->Remove( bmin, bmax, otherPart );
361  otherPart->getBoundingBox( bmin, bmax );
362  rtree->Insert( bmin, bmax, otherPart );
363  }
364  }
365  }
366 
367  // we're done processing feature parts with this particular label text
368  delete parts;
369  }
370 
371  // we're done processing connected fetures
372  delete connectedHashtable;
373  connectedHashtable = NULL;
374  delete connectedTexts;
375  connectedTexts = NULL;
376  }
377 
379  {
380  GEOSContextHandle_t geosctxt = geosContext();
382  while ( !featureParts->isEmpty() )
383  {
384  FeaturePart* fpart = featureParts->takeFirst();
385  const GEOSGeometry* geom = fpart->geos();
386  double chopInterval = fpart->getFeature()->repeatDistance();
387  if ( chopInterval != 0. && GEOSGeomTypeId_r( geosctxt, geom ) == GEOS_LINESTRING )
388  {
389 
390  double bmin[2], bmax[2];
391  fpart->getBoundingBox( bmin, bmax );
392  rtree->Remove( bmin, bmax, fpart );
393 
394  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosctxt, geom );
395 
396  // get number of points
397  unsigned int n;
398  GEOSCoordSeq_getSize_r( geosctxt, cs, &n );
399 
400  // Read points
401  std::vector<Point> points( n );
402  for ( unsigned int i = 0; i < n; ++i )
403  {
404  GEOSCoordSeq_getX_r( geosctxt, cs, i, &points[i].x );
405  GEOSCoordSeq_getY_r( geosctxt, cs, i, &points[i].y );
406  }
407 
408  // Cumulative length vector
409  std::vector<double> len( n, 0 );
410  for ( unsigned int i = 1; i < n; ++i )
411  {
412  double dx = points[i].x - points[i - 1].x;
413  double dy = points[i].y - points[i - 1].y;
414  len[i] = len[i - 1] + std::sqrt( dx * dx + dy * dy );
415  }
416 
417  // Walk along line
418  unsigned int cur = 0;
419  double lambda = 0;
420  std::vector<Point> part;
421  for ( ;; )
422  {
423  lambda += chopInterval;
424  for ( ; cur < n && lambda > len[cur]; ++cur )
425  {
426  part.push_back( points[cur] );
427  }
428  if ( cur >= n )
429  {
430  break;
431  }
432  double c = ( lambda - len[cur - 1] ) / ( len[cur] - len[cur - 1] );
433  Point p;
434  p.x = points[cur - 1].x + c * ( points[cur].x - points[cur - 1].x );
435  p.y = points[cur - 1].y + c * ( points[cur].y - points[cur - 1].y );
436  part.push_back( p );
437  GEOSCoordSequence* cooSeq = GEOSCoordSeq_create_r( geosctxt, part.size(), 2 );
438  for ( std::size_t i = 0; i < part.size(); ++i )
439  {
440  GEOSCoordSeq_setX_r( geosctxt, cooSeq, i, part[i].x );
441  GEOSCoordSeq_setY_r( geosctxt, cooSeq, i, part[i].y );
442  }
443 
444  GEOSGeometry* newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
445  FeaturePart* newfpart = new FeaturePart( fpart->getFeature(), newgeom );
446  newFeatureParts->append( newfpart );
447  newfpart->getBoundingBox( bmin, bmax );
448  rtree->Insert( bmin, bmax, newfpart );
449  part.clear();
450  part.push_back( p );
451  }
452  // Create final part
453  part.push_back( points[n - 1] );
454  GEOSCoordSequence* cooSeq = GEOSCoordSeq_create_r( geosctxt, part.size(), 2 );
455  for ( std::size_t i = 0; i < part.size(); ++i )
456  {
457  GEOSCoordSeq_setX_r( geosctxt, cooSeq, i, part[i].x );
458  GEOSCoordSeq_setY_r( geosctxt, cooSeq, i, part[i].y );
459  }
460 
461  GEOSGeometry* newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
462  FeaturePart* newfpart = new FeaturePart( fpart->getFeature(), newgeom );
463  newFeatureParts->append( newfpart );
464  newfpart->getBoundingBox( bmin, bmax );
465  rtree->Insert( bmin, bmax, newfpart );
466  }
467  else
468  {
469  newFeatureParts->append( fpart );
470  }
471  }
472 
473  delete featureParts;
474  featureParts = newFeatureParts;
475  }
476 
477 
478 
479 } // end namespace
480 
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:975
Feature * getFeature()
Returns the parent feature.
Definition: feature.h:264
static FeaturePart * _findConnectedPart(FeaturePart *partCheck, QLinkedList< FeaturePart * > *otherParts)
Definition: layer.cpp:306
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:1364
double * x
Definition: pointset.h:146
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:1356
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:73
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:147
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:323
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:378
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:274
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