Skip to content
Permalink
Browse files

[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
  • Loading branch information
nyalldawson committed Nov 21, 2015
1 parent 47e6d30 commit 9cc10e2a21a6d9fc6ea244b227c3e932951c9717
@@ -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()
{
@@ -94,6 +94,8 @@ namespace pal
*/
FeaturePart( QgsLabelFeature* lf, const GEOSGeometry* geom );

FeaturePart( const FeaturePart& other );

/** Delete the feature
*/
virtual ~FeaturePart();
@@ -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 );
@@ -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;
@@ -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();
}
@@ -132,6 +133,8 @@ namespace pal

GEOSContextHandle_t geosctxt = geosContext();

bool featureGeomIsObstacleGeom = !lf->obstacleGeometry();

while ( simpleGeometries->size() > 0 )
{
const GEOSGeometry* geom = simpleGeometries->takeFirst();
@@ -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 )
@@ -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!
@@ -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...
@@ -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() )
@@ -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
@@ -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 );
@@ -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;
}
@@ -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 );

@@ -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 );
}
@@ -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
@@ -249,6 +249,9 @@ namespace pal
/** List of feature parts */
QLinkedList<FeaturePart*> mFeatureParts;

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

Pal *pal;

double mDefaultPriority;
@@ -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;

@@ -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

0 comments on commit 9cc10e2

Please sign in to comment.
You can’t perform that action at this time.