Skip to content

Commit

Permalink
[labeling] Allow different obstacle geometry to feature geometry
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nyalldawson committed Nov 21, 2015
1 parent 47e6d30 commit 9cc10e2
Show file tree
Hide file tree
Showing 13 changed files with 455 additions and 54 deletions.
10 changes: 10 additions & 0 deletions src/core/pal/feature.cpp
Expand Up @@ -74,6 +74,16 @@ namespace pal

}

FeaturePart::FeaturePart( const FeaturePart& other )
: PointSet( other )
, mLF( other.mLF )
{
Q_FOREACH ( FeaturePart* part, other.mHoles )
{
mHoles << new FeaturePart( *part );
}
}


FeaturePart::~FeaturePart()
{
Expand Down
2 changes: 2 additions & 0 deletions src/core/pal/feature.h
Expand Up @@ -94,6 +94,8 @@ namespace pal
*/
FeaturePart( QgsLabelFeature* lf, const GEOSGeometry* geom );

FeaturePart( const FeaturePart& other );

/** Delete the feature
*/
virtual ~FeaturePart();
Expand Down
2 changes: 1 addition & 1 deletion src/core/pal/labelposition.h
Expand Up @@ -236,7 +236,7 @@ namespace pal
} PruneCtx;

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

// for sorting
static bool costShrink( void *l, void *r );
Expand Down
116 changes: 104 additions & 12 deletions src/core/pal/layer.cpp
Expand Up @@ -61,7 +61,8 @@ namespace pal
, mMergeLines( false )
, mUpsidedownLabels( Upright )
{
rtree = new RTree<FeaturePart*, double, 2, double>();
mFeatureIndex = new RTree<FeaturePart*, double, 2, double>();
mObstacleIndex = new RTree<FeaturePart*, double, 2, double>();

if ( defaultPriority < 0.0001 )
mDefaultPriority = 0.0001;
Expand All @@ -76,13 +77,13 @@ namespace pal
mMutex.lock();

qDeleteAll( mFeatureParts );
mFeatureParts.clear();
qDeleteAll( mObstacleParts );

//should already be empty
qDeleteAll( mConnectedHashtable );
mConnectedHashtable.clear();

delete rtree;
delete mFeatureIndex;
delete mObstacleIndex;

mMutex.unlock();
}
Expand Down Expand Up @@ -132,6 +133,8 @@ namespace pal

GEOSContextHandle_t geosctxt = geosContext();

bool featureGeomIsObstacleGeom = !lf->obstacleGeometry();

while ( simpleGeometries->size() > 0 )
{
const GEOSGeometry* geom = simpleGeometries->takeFirst();
Expand Down Expand Up @@ -168,6 +171,32 @@ namespace pal
continue;
}

// is the feature well defined? TODO Check epsilon
bool labelWellDefined = ( lf->size().width() > 0.0000001 && lf->size().height() > 0.0000001 );

if ( lf->isObstacle() && featureGeomIsObstacleGeom )
{
//if we are not labelling the layer, only insert it into the obstacle list and avoid an
//unnecessary copy
if ( mLabelLayer && labelWellDefined )
{
addObstaclePart( new FeaturePart( *fpart ) );
}
else
{
addObstaclePart( fpart );
fpart = 0;
}
}

// feature has to be labeled?
if ( !mLabelLayer || !labelWellDefined )
{
//nothing more to do for this part
delete fpart;
continue;
}

if ( mMode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
{
if ( type == GEOS_LINESTRING )
Expand All @@ -186,7 +215,6 @@ namespace pal
delete fpart;
}
continue; // don't add the feature part now, do it later
// TODO: we should probably add also other parts to act just as obstacles
}

// feature part is ready!
Expand All @@ -195,6 +223,57 @@ namespace pal
}
delete simpleGeometries;

if ( !featureGeomIsObstacleGeom )
{
//do the same for the obstacle geometry
simpleGeometries = unmulti( lf->obstacleGeometry() );
if ( simpleGeometries == NULL ) // unmulti() failed?
{
mMutex.unlock();
throw InternalException::UnknownGeometry();
}

while ( simpleGeometries->size() > 0 )
{
const GEOSGeometry* geom = simpleGeometries->takeFirst();

// ignore invalid geometries (e.g. polygons with self-intersecting rings)
if ( GEOSisValid_r( geosctxt, geom ) != 1 ) // 0=invalid, 1=valid, 2=exception
{
continue;
}

int type = GEOSGeomTypeId_r( geosctxt, geom );

if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON )
{
mMutex.unlock();
throw InternalException::UnknownGeometry();
}

FeaturePart* fpart = new FeaturePart( lf, geom );

// ignore invalid geometries
if (( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) ||
( type == GEOS_POLYGON && fpart->nbPoints < 3 ) )
{
delete fpart;
continue;
}

// polygons: reorder coordinates
if ( type == GEOS_POLYGON && reorderPolygon( fpart->nbPoints, fpart->x, fpart->y ) != 0 )
{
delete fpart;
continue;
}

// feature part is ready!
addObstaclePart( fpart );
}
delete simpleGeometries;
}

mMutex.unlock();

// if using only biggest parts...
Expand Down Expand Up @@ -224,7 +303,7 @@ namespace pal
mFeatureParts << fpart;

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

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

void Layer::addObstaclePart( FeaturePart* fpart )
{
double bmin[2];
double bmax[2];
fpart->getBoundingBox( bmin, bmax );

// add to list of layer's feature parts
mObstacleParts.append( fpart );

// add to obstacle r-tree
mObstacleIndex->Insert( bmin, bmax, fpart );
}

static FeaturePart* _findConnectedPart( FeaturePart* partCheck, QLinkedList<FeaturePart*>* otherParts )
{
// iterate in the rest of the parts with the same label
Expand Down Expand Up @@ -286,7 +378,7 @@ namespace pal
// remove partCheck from r-tree
double bmin[2], bmax[2];
partCheck->getBoundingBox( bmin, bmax );
rtree->Remove( bmin, bmax, partCheck );
mFeatureIndex->Remove( bmin, bmax, partCheck );
mFeatureParts.removeOne( partCheck );

otherPart->getBoundingBox( bmin, bmax );
Expand All @@ -295,9 +387,9 @@ namespace pal
if ( otherPart->mergeWithFeaturePart( partCheck ) )
{
// reinsert p->item to r-tree (probably not needed)
rtree->Remove( bmin, bmax, otherPart );
mFeatureIndex->Remove( bmin, bmax, otherPart );
otherPart->getBoundingBox( bmin, bmax );
rtree->Insert( bmin, bmax, otherPart );
mFeatureIndex->Insert( bmin, bmax, otherPart );
}
delete partCheck;
}
Expand Down Expand Up @@ -331,7 +423,7 @@ namespace pal

double bmin[2], bmax[2];
fpart->getBoundingBox( bmin, bmax );
rtree->Remove( bmin, bmax, fpart );
mFeatureIndex->Remove( bmin, bmax, fpart );

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

Expand Down Expand Up @@ -387,7 +479,7 @@ namespace pal
FeaturePart* newfpart = new FeaturePart( fpart->feature(), newgeom );
newFeatureParts.append( newfpart );
newfpart->getBoundingBox( bmin, bmax );
rtree->Insert( bmin, bmax, newfpart );
mFeatureIndex->Insert( bmin, bmax, newfpart );
part.clear();
part.push_back( p );
}
Expand All @@ -404,7 +496,7 @@ namespace pal
FeaturePart* newfpart = new FeaturePart( fpart->feature(), newgeom );
newFeatureParts.append( newfpart );
newfpart->getBoundingBox( bmin, bmax );
rtree->Insert( bmin, bmax, newfpart );
mFeatureIndex->Insert( bmin, bmax, newfpart );
delete fpart;
}
else
Expand Down
11 changes: 10 additions & 1 deletion src/core/pal/layer.h
Expand Up @@ -249,6 +249,9 @@ namespace pal
/** List of feature parts */
QLinkedList<FeaturePart*> mFeatureParts;

/** List of obstacle parts */
QList<FeaturePart*> mObstacleParts;

Pal *pal;

double mDefaultPriority;
Expand All @@ -269,10 +272,13 @@ namespace pal
UpsideDownLabels mUpsidedownLabels;

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

//obstacle r-tree
RTree<FeaturePart*, double, 2, double, 8, 4> *mObstacleIndex;

QHash< QString, QLinkedList<FeaturePart*>* > mConnectedHashtable;
QStringList mConnectedTexts;

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

/** Add newly created obstacle part into r tree and to the list */
void addObstaclePart( FeaturePart* fpart );

};

} // end namespace pal
Expand Down

0 comments on commit 9cc10e2

Please sign in to comment.