QGIS API Documentation  2.12.0-Lyon
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 #include "qgslabelingenginev2.h"
44 
45 namespace pal
46 {
47 
48  Layer::Layer( QgsAbstractLabelProvider* provider, const QString& name, Arrangement arrangement, double defaultPriority, bool active, bool toLabel, Pal *pal, bool displayAll )
49  : mProvider( provider )
50  , mName( name )
51  , pal( pal )
52  , mObstacleType( PolygonInterior )
53  , mActive( active )
54  , mLabelLayer( toLabel )
55  , mDisplayAll( displayAll )
56  , mCentroidInside( false )
57  , mFitInPolygon( false )
58  , mArrangement( arrangement )
59  , mArrangementFlags( 0 )
60  , mMode( LabelPerFeature )
61  , mMergeLines( false )
62  , mUpsidedownLabels( Upright )
63  {
65 
66  if ( defaultPriority < 0.0001 )
67  mDefaultPriority = 0.0001;
68  else if ( defaultPriority > 1.0 )
69  mDefaultPriority = 1.0;
70  else
71  mDefaultPriority = defaultPriority;
72  }
73 
75  {
76  mMutex.lock();
77 
78  qDeleteAll( mFeatureParts );
79  mFeatureParts.clear();
80 
81  //should already be empty
82  qDeleteAll( mConnectedHashtable );
83  mConnectedHashtable.clear();
84 
85  delete rtree;
86 
87  mMutex.unlock();
88  }
89 
90  void Layer::setPriority( double priority )
91  {
92  if ( priority >= 1.0 ) // low priority
93  mDefaultPriority = 1.0;
94  else if ( priority <= 0.0001 )
95  mDefaultPriority = 0.0001; // high priority
96  else
98  }
99 
101  {
102  if ( lf->size().width() < 0 || lf->size().height() < 0 )
103  return false;
104 
105  mMutex.lock();
106 
107  if ( mHashtable.contains( lf->id() ) )
108  {
109  mMutex.unlock();
110  //A feature with this id already exists. Don't throw an exception as sometimes,
111  //the same feature is added twice (dateline split with otf-reprojection)
112  return false;
113  }
114 
115  // assign label feature to this PAL layer
116  lf->setLayer( this );
117 
118  // Split MULTI GEOM and Collection in simple geometries
119 
120  bool addedFeature = false;
121 
122  double geom_size = -1, biggest_size = -1;
123  FeaturePart* biggest_part = NULL;
124 
125  // break the (possibly multi-part) geometry into simple geometries
126  QLinkedList<const GEOSGeometry*>* simpleGeometries = unmulti( lf->geometry() );
127  if ( simpleGeometries == NULL ) // unmulti() failed?
128  {
129  mMutex.unlock();
131  }
132 
133  GEOSContextHandle_t geosctxt = geosContext();
134 
135  while ( simpleGeometries->size() > 0 )
136  {
137  const GEOSGeometry* geom = simpleGeometries->takeFirst();
138 
139  // ignore invalid geometries (e.g. polygons with self-intersecting rings)
140  if ( GEOSisValid_r( geosctxt, geom ) != 1 ) // 0=invalid, 1=valid, 2=exception
141  {
142 // std::cerr << "ignoring invalid feature " << geom_id << std::endl;
143  continue;
144  }
145 
146  int type = GEOSGeomTypeId_r( geosctxt, geom );
147 
148  if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON )
149  {
150  mMutex.unlock();
152  }
153 
154  FeaturePart* fpart = new FeaturePart( lf, geom );
155 
156  // ignore invalid geometries
157  if (( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) ||
158  ( type == GEOS_POLYGON && fpart->nbPoints < 3 ) )
159  {
160  delete fpart;
161  continue;
162  }
163 
164  // polygons: reorder coordinates
165  if ( type == GEOS_POLYGON && reorderPolygon( fpart->nbPoints, fpart->x, fpart->y ) != 0 )
166  {
167  delete fpart;
168  continue;
169  }
170 
171  if ( mMode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
172  {
173  if ( type == GEOS_LINESTRING )
174  GEOSLength_r( geosctxt, geom, &geom_size );
175  else if ( type == GEOS_POLYGON )
176  GEOSArea_r( geosctxt, geom, &geom_size );
177 
178  if ( geom_size > biggest_size )
179  {
180  biggest_size = geom_size;
181  delete biggest_part; // safe with NULL part
182  biggest_part = fpart;
183  }
184  else
185  {
186  delete fpart;
187  }
188  continue; // don't add the feature part now, do it later
189  // TODO: we should probably add also other parts to act just as obstacles
190  }
191 
192  // feature part is ready!
193  addFeaturePart( fpart, lf->labelText() );
194  addedFeature = true;
195  }
196  delete simpleGeometries;
197 
198  mMutex.unlock();
199 
200  // if using only biggest parts...
201  if (( mMode == LabelPerFeature || lf->hasFixedPosition() ) && biggest_part != NULL )
202  {
203  addFeaturePart( biggest_part, lf->labelText() );
204  addedFeature = true;
205  }
206 
207  // add feature to layer if we have added something
208  if ( addedFeature )
209  {
210  mHashtable.insert( lf->id(), lf );
211  }
212 
213  return addedFeature; // true if we've added something
214  }
215 
216 
217  void Layer::addFeaturePart( FeaturePart* fpart, const QString& labelText )
218  {
219  double bmin[2];
220  double bmax[2];
221  fpart->getBoundingBox( bmin, bmax );
222 
223  // add to list of layer's feature parts
224  mFeatureParts << fpart;
225 
226  // add to r-tree for fast spatial access
227  rtree->Insert( bmin, bmax, fpart );
228 
229  // add to hashtable with equally named feature parts
230  if ( mMergeLines && !labelText.isEmpty() )
231  {
233  if ( !mConnectedHashtable.contains( labelText ) )
234  {
235  // entry doesn't exist yet
236  lst = new QLinkedList<FeaturePart*>;
237  mConnectedHashtable.insert( labelText, lst );
238  mConnectedTexts << labelText;
239  }
240  else
241  {
242  lst = mConnectedHashtable.value( labelText );
243  }
244  lst->append( fpart ); // add to the list
245  }
246  }
247 
249  {
250  // iterate in the rest of the parts with the same label
252  while ( p != otherParts->constEnd() )
253  {
254  if ( partCheck->isConnected( *p ) )
255  {
256  // stop checking for other connected parts
257  return *p;
258  }
259  ++p;
260  }
261 
262  return NULL; // no connected part found...
263  }
264 
266  {
267  // go through all label texts
268  Q_FOREACH ( const QString& labelText, mConnectedTexts )
269  {
270  if ( !mConnectedHashtable.contains( labelText ) )
271  continue; // shouldn't happen
272 
273  QLinkedList<FeaturePart*>* parts = mConnectedHashtable.value( labelText );
274 
275  // go one-by-one part, try to merge
276  while ( !parts->isEmpty() )
277  {
278  // part we'll be checking against other in this round
279  FeaturePart* partCheck = parts->takeFirst();
280 
281  FeaturePart* otherPart = _findConnectedPart( partCheck, parts );
282  if ( otherPart )
283  {
284  //std::cerr << "- connected " << partCheck << " with " << otherPart << std::endl;
285 
286  // remove partCheck from r-tree
287  double bmin[2], bmax[2];
288  partCheck->getBoundingBox( bmin, bmax );
289  rtree->Remove( bmin, bmax, partCheck );
290  mFeatureParts.removeOne( partCheck );
291 
292  otherPart->getBoundingBox( bmin, bmax );
293 
294  // merge points from partCheck to p->item
295  if ( otherPart->mergeWithFeaturePart( partCheck ) )
296  {
297  // reinsert p->item to r-tree (probably not needed)
298  rtree->Remove( bmin, bmax, otherPart );
299  otherPart->getBoundingBox( bmin, bmax );
300  rtree->Insert( bmin, bmax, otherPart );
301  }
302  delete partCheck;
303  }
304  }
305 
306  // we're done processing feature parts with this particular label text
307  delete parts;
308  mConnectedHashtable.remove( labelText );
309  }
310 
311  // we're done processing connected features
312 
313  //should be empty, but clear to be safe
314  qDeleteAll( mConnectedHashtable );
315  mConnectedHashtable.clear();
316 
318  }
319 
321  {
322  GEOSContextHandle_t geosctxt = geosContext();
323  QLinkedList<FeaturePart*> newFeatureParts;
324  while ( !mFeatureParts.isEmpty() )
325  {
326  FeaturePart* fpart = mFeatureParts.takeFirst();
327  const GEOSGeometry* geom = fpart->geos();
328  double chopInterval = fpart->repeatDistance();
329  if ( chopInterval != 0. && GEOSGeomTypeId_r( geosctxt, geom ) == GEOS_LINESTRING )
330  {
331 
332  double bmin[2], bmax[2];
333  fpart->getBoundingBox( bmin, bmax );
334  rtree->Remove( bmin, bmax, fpart );
335 
336  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosctxt, geom );
337 
338  // get number of points
339  unsigned int n;
340  GEOSCoordSeq_getSize_r( geosctxt, cs, &n );
341 
342  // Read points
343  std::vector<Point> points( n );
344  for ( unsigned int i = 0; i < n; ++i )
345  {
346  GEOSCoordSeq_getX_r( geosctxt, cs, i, &points[i].x );
347  GEOSCoordSeq_getY_r( geosctxt, cs, i, &points[i].y );
348  }
349 
350  // Cumulative length vector
351  std::vector<double> len( n, 0 );
352  for ( unsigned int i = 1; i < n; ++i )
353  {
354  double dx = points[i].x - points[i - 1].x;
355  double dy = points[i].y - points[i - 1].y;
356  len[i] = len[i - 1] + std::sqrt( dx * dx + dy * dy );
357  }
358 
359  // Walk along line
360  unsigned int cur = 0;
361  double lambda = 0;
362  std::vector<Point> part;
363  for ( ;; )
364  {
365  lambda += chopInterval;
366  for ( ; cur < n && lambda > len[cur]; ++cur )
367  {
368  part.push_back( points[cur] );
369  }
370  if ( cur >= n )
371  {
372  break;
373  }
374  double c = ( lambda - len[cur - 1] ) / ( len[cur] - len[cur - 1] );
375  Point p;
376  p.x = points[cur - 1].x + c * ( points[cur].x - points[cur - 1].x );
377  p.y = points[cur - 1].y + c * ( points[cur].y - points[cur - 1].y );
378  part.push_back( p );
379  GEOSCoordSequence* cooSeq = GEOSCoordSeq_create_r( geosctxt, part.size(), 2 );
380  for ( std::size_t i = 0; i < part.size(); ++i )
381  {
382  GEOSCoordSeq_setX_r( geosctxt, cooSeq, i, part[i].x );
383  GEOSCoordSeq_setY_r( geosctxt, cooSeq, i, part[i].y );
384  }
385 
386  GEOSGeometry* newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
387  FeaturePart* newfpart = new FeaturePart( fpart->feature(), newgeom );
388  newFeatureParts.append( newfpart );
389  newfpart->getBoundingBox( bmin, bmax );
390  rtree->Insert( bmin, bmax, newfpart );
391  part.clear();
392  part.push_back( p );
393  }
394  // Create final part
395  part.push_back( points[n - 1] );
396  GEOSCoordSequence* cooSeq = GEOSCoordSeq_create_r( geosctxt, part.size(), 2 );
397  for ( std::size_t i = 0; i < part.size(); ++i )
398  {
399  GEOSCoordSeq_setX_r( geosctxt, cooSeq, i, part[i].x );
400  GEOSCoordSeq_setY_r( geosctxt, cooSeq, i, part[i].y );
401  }
402 
403  GEOSGeometry* newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
404  FeaturePart* newfpart = new FeaturePart( fpart->feature(), newgeom );
405  newFeatureParts.append( newfpart );
406  newfpart->getBoundingBox( bmin, bmax );
407  rtree->Insert( bmin, bmax, newfpart );
408  delete fpart;
409  }
410  else
411  {
412  newFeatureParts.append( fpart );
413  }
414  }
415 
416  mFeatureParts = newFeatureParts;
417  }
418 
419 
420 
421 } // end namespace
422 
void clear()
int reorderPolygon(int nbPoints, double *x, double *y)
bool mMergeLines
Definition: layer.h:269
iterator insert(const Key &key, const T &value)
const GEOSGeometry * geos() const
Returns the point set's GEOS geometry.
Definition: pointset.cpp:1024
QgsLabelFeature * feature()
Returns the parent feature.
Definition: feature.h:140
QLinkedList< FeaturePart * > mFeatureParts
List of feature parts.
Definition: layer.h:252
QStringList mConnectedTexts
Definition: layer.h:279
Layer(QgsAbstractLabelProvider *provider, const QString &name, Arrangement arrangement, double defaultPriority, bool active, bool toLabel, Pal *pal, bool displayAll=false)
Create a new layer.
Definition: layer.cpp:48
static FeaturePart * _findConnectedPart(FeaturePart *partCheck, QLinkedList< FeaturePart * > *otherParts)
Definition: layer.cpp:248
QHash< QgsFeatureId, QgsLabelFeature * > mHashtable
Lookup table of label features (owned by the label feature provider that created them) ...
Definition: layer.h:276
double mDefaultPriority
Definition: layer.h:256
void setPriority(double priority)
Sets the layer's priority.
Definition: layer.cpp:90
Pal main class.
Definition: pal.h:111
double priority() const
Returns the layer's priority, between 0 and 1.
Definition: layer.h:167
void unlock()
double repeatDistance()
Definition: feature.h:180
GEOSGeometry * geometry() const
Get access to the associated geometry.
QSizeF size() const
Size of the label (in map units)
bool hasFixedPosition() const
Whether the label should use a fixed position instead of being automatically placed.
bool isEmpty() const
bool mergeWithFeaturePart(FeaturePart *other)
Merge other (connected) part with this one and save the result in this part (other is unchanged)...
Definition: feature.cpp:1391
double * x
Definition: pointset.h:147
QLinkedList< const GEOSGeometry * > * unmulti(const GEOSGeometry *the_geom)
Definition: util.cpp:113
void setLayer(pal::Layer *layer)
Assign PAL layer to the label feature. Should be only used internally in PAL.
bool isEmpty() const
double x
Definition: util.h:75
bool isConnected(FeaturePart *p2)
Check whether this part is connected with some other part.
Definition: feature.cpp:1375
GEOSContextHandle_t geosContext()
Get GEOS context handle to be used in all GEOS library calls with reentrant API.
Definition: pal.cpp:57
Main class to handle feature.
Definition: feature.h:79
The QgsAbstractLabelProvider class is an interface class.
Thrown when a geometry type is not like expected.
void lock()
double * y
Definition: pointset.h:148
void getBoundingBox(double min[2], double max[2]) const
Definition: pointset.h:114
const_iterator constBegin() const
void joinConnectedFeatures()
Join connected features with the same label text.
Definition: layer.cpp:265
virtual ~Layer()
Definition: layer.cpp:74
const_iterator constEnd() const
double y
Definition: util.h:75
QHash< QString, QLinkedList< FeaturePart * > * > mConnectedHashtable
Definition: layer.h:278
Arrangement
The way to arrange labels against spatial entities.
Definition: pal.h:77
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
void chopFeaturesAtRepeatDistance()
Chop layer features at the repeat distance.
Definition: layer.cpp:320
bool registerFeature(QgsLabelFeature *label)
Register a feature in the layer.
Definition: layer.cpp:100
QMutex mMutex
Definition: layer.h:281
void addFeaturePart(FeaturePart *fpart, const QString &labelText=QString())
Add newly created feature part into r tree and to the list.
Definition: layer.cpp:217
LabelMode mMode
Definition: layer.h:268
RTree< FeaturePart *, double, 2, double, 8, 4 > * rtree
Definition: layer.h:274
bool contains(const Key &key) const
qreal height() const
friend class FeaturePart
Definition: layer.h:60
QString labelText() const
Text of the label.
int size() const
qreal width() const
void append(const T &value)
QgsFeatureId id() const
Identifier of the label (unique within the parent label provider)