Skip to content

Commit 9cc10e2

Browse files
committed
[labeling] Allow different obstacle geometry to feature geometry
This change makes it possible to have a different geometry used for labeling obstacle detection to the geometry used for generating label position candidates. Also fixes parts of multipolygon features were not treated as obstacles when "label only largest part" of polygon was checked. Some inefficiencies in pal were also fixed (eg avoiding adding features/obstacles to pal rtree indexes when they will never be used). Sponsored by City of Uster
1 parent 47e6d30 commit 9cc10e2

File tree

13 files changed

+455
-54
lines changed

13 files changed

+455
-54
lines changed

src/core/pal/feature.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,16 @@ namespace pal
7474

7575
}
7676

77+
FeaturePart::FeaturePart( const FeaturePart& other )
78+
: PointSet( other )
79+
, mLF( other.mLF )
80+
{
81+
Q_FOREACH ( FeaturePart* part, other.mHoles )
82+
{
83+
mHoles << new FeaturePart( *part );
84+
}
85+
}
86+
7787

7888
FeaturePart::~FeaturePart()
7989
{

src/core/pal/feature.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ namespace pal
9494
*/
9595
FeaturePart( QgsLabelFeature* lf, const GEOSGeometry* geom );
9696

97+
FeaturePart( const FeaturePart& other );
98+
9799
/** Delete the feature
98100
*/
99101
virtual ~FeaturePart();

src/core/pal/labelposition.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ namespace pal
236236
} PruneCtx;
237237

238238
/** Check whether the candidate in ctx overlap with obstacle feat */
239-
static bool pruneCallback( LabelPosition *lp, void *ctx );
239+
static bool pruneCallback( LabelPosition *candidatePosition, void *ctx );
240240

241241
// for sorting
242242
static bool costShrink( void *l, void *r );

src/core/pal/layer.cpp

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ namespace pal
6161
, mMergeLines( false )
6262
, mUpsidedownLabels( Upright )
6363
{
64-
rtree = new RTree<FeaturePart*, double, 2, double>();
64+
mFeatureIndex = new RTree<FeaturePart*, double, 2, double>();
65+
mObstacleIndex = new RTree<FeaturePart*, double, 2, double>();
6566

6667
if ( defaultPriority < 0.0001 )
6768
mDefaultPriority = 0.0001;
@@ -76,13 +77,13 @@ namespace pal
7677
mMutex.lock();
7778

7879
qDeleteAll( mFeatureParts );
79-
mFeatureParts.clear();
80+
qDeleteAll( mObstacleParts );
8081

8182
//should already be empty
8283
qDeleteAll( mConnectedHashtable );
83-
mConnectedHashtable.clear();
8484

85-
delete rtree;
85+
delete mFeatureIndex;
86+
delete mObstacleIndex;
8687

8788
mMutex.unlock();
8889
}
@@ -132,6 +133,8 @@ namespace pal
132133

133134
GEOSContextHandle_t geosctxt = geosContext();
134135

136+
bool featureGeomIsObstacleGeom = !lf->obstacleGeometry();
137+
135138
while ( simpleGeometries->size() > 0 )
136139
{
137140
const GEOSGeometry* geom = simpleGeometries->takeFirst();
@@ -168,6 +171,32 @@ namespace pal
168171
continue;
169172
}
170173

174+
// is the feature well defined? TODO Check epsilon
175+
bool labelWellDefined = ( lf->size().width() > 0.0000001 && lf->size().height() > 0.0000001 );
176+
177+
if ( lf->isObstacle() && featureGeomIsObstacleGeom )
178+
{
179+
//if we are not labelling the layer, only insert it into the obstacle list and avoid an
180+
//unnecessary copy
181+
if ( mLabelLayer && labelWellDefined )
182+
{
183+
addObstaclePart( new FeaturePart( *fpart ) );
184+
}
185+
else
186+
{
187+
addObstaclePart( fpart );
188+
fpart = 0;
189+
}
190+
}
191+
192+
// feature has to be labeled?
193+
if ( !mLabelLayer || !labelWellDefined )
194+
{
195+
//nothing more to do for this part
196+
delete fpart;
197+
continue;
198+
}
199+
171200
if ( mMode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
172201
{
173202
if ( type == GEOS_LINESTRING )
@@ -186,7 +215,6 @@ namespace pal
186215
delete fpart;
187216
}
188217
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
190218
}
191219

192220
// feature part is ready!
@@ -195,6 +223,57 @@ namespace pal
195223
}
196224
delete simpleGeometries;
197225

226+
if ( !featureGeomIsObstacleGeom )
227+
{
228+
//do the same for the obstacle geometry
229+
simpleGeometries = unmulti( lf->obstacleGeometry() );
230+
if ( simpleGeometries == NULL ) // unmulti() failed?
231+
{
232+
mMutex.unlock();
233+
throw InternalException::UnknownGeometry();
234+
}
235+
236+
while ( simpleGeometries->size() > 0 )
237+
{
238+
const GEOSGeometry* geom = simpleGeometries->takeFirst();
239+
240+
// ignore invalid geometries (e.g. polygons with self-intersecting rings)
241+
if ( GEOSisValid_r( geosctxt, geom ) != 1 ) // 0=invalid, 1=valid, 2=exception
242+
{
243+
continue;
244+
}
245+
246+
int type = GEOSGeomTypeId_r( geosctxt, geom );
247+
248+
if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON )
249+
{
250+
mMutex.unlock();
251+
throw InternalException::UnknownGeometry();
252+
}
253+
254+
FeaturePart* fpart = new FeaturePart( lf, geom );
255+
256+
// ignore invalid geometries
257+
if (( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) ||
258+
( type == GEOS_POLYGON && fpart->nbPoints < 3 ) )
259+
{
260+
delete fpart;
261+
continue;
262+
}
263+
264+
// polygons: reorder coordinates
265+
if ( type == GEOS_POLYGON && reorderPolygon( fpart->nbPoints, fpart->x, fpart->y ) != 0 )
266+
{
267+
delete fpart;
268+
continue;
269+
}
270+
271+
// feature part is ready!
272+
addObstaclePart( fpart );
273+
}
274+
delete simpleGeometries;
275+
}
276+
198277
mMutex.unlock();
199278

200279
// if using only biggest parts...
@@ -224,7 +303,7 @@ namespace pal
224303
mFeatureParts << fpart;
225304

226305
// add to r-tree for fast spatial access
227-
rtree->Insert( bmin, bmax, fpart );
306+
mFeatureIndex->Insert( bmin, bmax, fpart );
228307

229308
// add to hashtable with equally named feature parts
230309
if ( mMergeLines && !labelText.isEmpty() )
@@ -245,6 +324,19 @@ namespace pal
245324
}
246325
}
247326

327+
void Layer::addObstaclePart( FeaturePart* fpart )
328+
{
329+
double bmin[2];
330+
double bmax[2];
331+
fpart->getBoundingBox( bmin, bmax );
332+
333+
// add to list of layer's feature parts
334+
mObstacleParts.append( fpart );
335+
336+
// add to obstacle r-tree
337+
mObstacleIndex->Insert( bmin, bmax, fpart );
338+
}
339+
248340
static FeaturePart* _findConnectedPart( FeaturePart* partCheck, QLinkedList<FeaturePart*>* otherParts )
249341
{
250342
// iterate in the rest of the parts with the same label
@@ -286,7 +378,7 @@ namespace pal
286378
// remove partCheck from r-tree
287379
double bmin[2], bmax[2];
288380
partCheck->getBoundingBox( bmin, bmax );
289-
rtree->Remove( bmin, bmax, partCheck );
381+
mFeatureIndex->Remove( bmin, bmax, partCheck );
290382
mFeatureParts.removeOne( partCheck );
291383

292384
otherPart->getBoundingBox( bmin, bmax );
@@ -295,9 +387,9 @@ namespace pal
295387
if ( otherPart->mergeWithFeaturePart( partCheck ) )
296388
{
297389
// reinsert p->item to r-tree (probably not needed)
298-
rtree->Remove( bmin, bmax, otherPart );
390+
mFeatureIndex->Remove( bmin, bmax, otherPart );
299391
otherPart->getBoundingBox( bmin, bmax );
300-
rtree->Insert( bmin, bmax, otherPart );
392+
mFeatureIndex->Insert( bmin, bmax, otherPart );
301393
}
302394
delete partCheck;
303395
}
@@ -331,7 +423,7 @@ namespace pal
331423

332424
double bmin[2], bmax[2];
333425
fpart->getBoundingBox( bmin, bmax );
334-
rtree->Remove( bmin, bmax, fpart );
426+
mFeatureIndex->Remove( bmin, bmax, fpart );
335427

336428
const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosctxt, geom );
337429

@@ -387,7 +479,7 @@ namespace pal
387479
FeaturePart* newfpart = new FeaturePart( fpart->feature(), newgeom );
388480
newFeatureParts.append( newfpart );
389481
newfpart->getBoundingBox( bmin, bmax );
390-
rtree->Insert( bmin, bmax, newfpart );
482+
mFeatureIndex->Insert( bmin, bmax, newfpart );
391483
part.clear();
392484
part.push_back( p );
393485
}
@@ -404,7 +496,7 @@ namespace pal
404496
FeaturePart* newfpart = new FeaturePart( fpart->feature(), newgeom );
405497
newFeatureParts.append( newfpart );
406498
newfpart->getBoundingBox( bmin, bmax );
407-
rtree->Insert( bmin, bmax, newfpart );
499+
mFeatureIndex->Insert( bmin, bmax, newfpart );
408500
delete fpart;
409501
}
410502
else

src/core/pal/layer.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ namespace pal
249249
/** List of feature parts */
250250
QLinkedList<FeaturePart*> mFeatureParts;
251251

252+
/** List of obstacle parts */
253+
QList<FeaturePart*> mObstacleParts;
254+
252255
Pal *pal;
253256

254257
double mDefaultPriority;
@@ -269,10 +272,13 @@ namespace pal
269272
UpsideDownLabels mUpsidedownLabels;
270273

271274
// indexes (spatial and id)
272-
RTree<FeaturePart*, double, 2, double, 8, 4> *rtree;
275+
RTree<FeaturePart*, double, 2, double, 8, 4> *mFeatureIndex;
273276
//! Lookup table of label features (owned by the label feature provider that created them)
274277
QHash< QgsFeatureId, QgsLabelFeature*> mHashtable;
275278

279+
//obstacle r-tree
280+
RTree<FeaturePart*, double, 2, double, 8, 4> *mObstacleIndex;
281+
276282
QHash< QString, QLinkedList<FeaturePart*>* > mConnectedHashtable;
277283
QStringList mConnectedTexts;
278284

@@ -296,6 +302,9 @@ namespace pal
296302
/** Add newly created feature part into r tree and to the list */
297303
void addFeaturePart( FeaturePart* fpart, const QString &labelText = QString() );
298304

305+
/** Add newly created obstacle part into r tree and to the list */
306+
void addObstaclePart( FeaturePart* fpart );
307+
299308
};
300309

301310
} // end namespace pal

0 commit comments

Comments
 (0)