mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-17 00:04:02 -04:00
[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
This commit is contained in:
parent
47e6d3031b
commit
9cc10e2a21
@ -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()
|
FeaturePart::~FeaturePart()
|
||||||
{
|
{
|
||||||
|
@ -94,6 +94,8 @@ namespace pal
|
|||||||
*/
|
*/
|
||||||
FeaturePart( QgsLabelFeature* lf, const GEOSGeometry* geom );
|
FeaturePart( QgsLabelFeature* lf, const GEOSGeometry* geom );
|
||||||
|
|
||||||
|
FeaturePart( const FeaturePart& other );
|
||||||
|
|
||||||
/** Delete the feature
|
/** Delete the feature
|
||||||
*/
|
*/
|
||||||
virtual ~FeaturePart();
|
virtual ~FeaturePart();
|
||||||
|
@ -236,7 +236,7 @@ namespace pal
|
|||||||
} PruneCtx;
|
} PruneCtx;
|
||||||
|
|
||||||
/** Check whether the candidate in ctx overlap with obstacle feat */
|
/** 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
|
// for sorting
|
||||||
static bool costShrink( void *l, void *r );
|
static bool costShrink( void *l, void *r );
|
||||||
|
@ -61,7 +61,8 @@ namespace pal
|
|||||||
, mMergeLines( false )
|
, mMergeLines( false )
|
||||||
, mUpsidedownLabels( Upright )
|
, 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 )
|
if ( defaultPriority < 0.0001 )
|
||||||
mDefaultPriority = 0.0001;
|
mDefaultPriority = 0.0001;
|
||||||
@ -76,13 +77,13 @@ namespace pal
|
|||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
|
|
||||||
qDeleteAll( mFeatureParts );
|
qDeleteAll( mFeatureParts );
|
||||||
mFeatureParts.clear();
|
qDeleteAll( mObstacleParts );
|
||||||
|
|
||||||
//should already be empty
|
//should already be empty
|
||||||
qDeleteAll( mConnectedHashtable );
|
qDeleteAll( mConnectedHashtable );
|
||||||
mConnectedHashtable.clear();
|
|
||||||
|
|
||||||
delete rtree;
|
delete mFeatureIndex;
|
||||||
|
delete mObstacleIndex;
|
||||||
|
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
}
|
}
|
||||||
@ -132,6 +133,8 @@ namespace pal
|
|||||||
|
|
||||||
GEOSContextHandle_t geosctxt = geosContext();
|
GEOSContextHandle_t geosctxt = geosContext();
|
||||||
|
|
||||||
|
bool featureGeomIsObstacleGeom = !lf->obstacleGeometry();
|
||||||
|
|
||||||
while ( simpleGeometries->size() > 0 )
|
while ( simpleGeometries->size() > 0 )
|
||||||
{
|
{
|
||||||
const GEOSGeometry* geom = simpleGeometries->takeFirst();
|
const GEOSGeometry* geom = simpleGeometries->takeFirst();
|
||||||
@ -168,6 +171,32 @@ namespace pal
|
|||||||
continue;
|
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 ( mMode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
|
||||||
{
|
{
|
||||||
if ( type == GEOS_LINESTRING )
|
if ( type == GEOS_LINESTRING )
|
||||||
@ -186,7 +215,6 @@ namespace pal
|
|||||||
delete fpart;
|
delete fpart;
|
||||||
}
|
}
|
||||||
continue; // don't add the feature part now, do it later
|
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!
|
// feature part is ready!
|
||||||
@ -195,6 +223,57 @@ namespace pal
|
|||||||
}
|
}
|
||||||
delete simpleGeometries;
|
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();
|
mMutex.unlock();
|
||||||
|
|
||||||
// if using only biggest parts...
|
// if using only biggest parts...
|
||||||
@ -224,7 +303,7 @@ namespace pal
|
|||||||
mFeatureParts << fpart;
|
mFeatureParts << fpart;
|
||||||
|
|
||||||
// add to r-tree for fast spatial access
|
// 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
|
// add to hashtable with equally named feature parts
|
||||||
if ( mMergeLines && !labelText.isEmpty() )
|
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 )
|
static FeaturePart* _findConnectedPart( FeaturePart* partCheck, QLinkedList<FeaturePart*>* otherParts )
|
||||||
{
|
{
|
||||||
// iterate in the rest of the parts with the same label
|
// iterate in the rest of the parts with the same label
|
||||||
@ -286,7 +378,7 @@ namespace pal
|
|||||||
// remove partCheck from r-tree
|
// remove partCheck from r-tree
|
||||||
double bmin[2], bmax[2];
|
double bmin[2], bmax[2];
|
||||||
partCheck->getBoundingBox( bmin, bmax );
|
partCheck->getBoundingBox( bmin, bmax );
|
||||||
rtree->Remove( bmin, bmax, partCheck );
|
mFeatureIndex->Remove( bmin, bmax, partCheck );
|
||||||
mFeatureParts.removeOne( partCheck );
|
mFeatureParts.removeOne( partCheck );
|
||||||
|
|
||||||
otherPart->getBoundingBox( bmin, bmax );
|
otherPart->getBoundingBox( bmin, bmax );
|
||||||
@ -295,9 +387,9 @@ namespace pal
|
|||||||
if ( otherPart->mergeWithFeaturePart( partCheck ) )
|
if ( otherPart->mergeWithFeaturePart( partCheck ) )
|
||||||
{
|
{
|
||||||
// reinsert p->item to r-tree (probably not needed)
|
// reinsert p->item to r-tree (probably not needed)
|
||||||
rtree->Remove( bmin, bmax, otherPart );
|
mFeatureIndex->Remove( bmin, bmax, otherPart );
|
||||||
otherPart->getBoundingBox( bmin, bmax );
|
otherPart->getBoundingBox( bmin, bmax );
|
||||||
rtree->Insert( bmin, bmax, otherPart );
|
mFeatureIndex->Insert( bmin, bmax, otherPart );
|
||||||
}
|
}
|
||||||
delete partCheck;
|
delete partCheck;
|
||||||
}
|
}
|
||||||
@ -331,7 +423,7 @@ namespace pal
|
|||||||
|
|
||||||
double bmin[2], bmax[2];
|
double bmin[2], bmax[2];
|
||||||
fpart->getBoundingBox( bmin, bmax );
|
fpart->getBoundingBox( bmin, bmax );
|
||||||
rtree->Remove( bmin, bmax, fpart );
|
mFeatureIndex->Remove( bmin, bmax, fpart );
|
||||||
|
|
||||||
const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosctxt, geom );
|
const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosctxt, geom );
|
||||||
|
|
||||||
@ -387,7 +479,7 @@ namespace pal
|
|||||||
FeaturePart* newfpart = new FeaturePart( fpart->feature(), newgeom );
|
FeaturePart* newfpart = new FeaturePart( fpart->feature(), newgeom );
|
||||||
newFeatureParts.append( newfpart );
|
newFeatureParts.append( newfpart );
|
||||||
newfpart->getBoundingBox( bmin, bmax );
|
newfpart->getBoundingBox( bmin, bmax );
|
||||||
rtree->Insert( bmin, bmax, newfpart );
|
mFeatureIndex->Insert( bmin, bmax, newfpart );
|
||||||
part.clear();
|
part.clear();
|
||||||
part.push_back( p );
|
part.push_back( p );
|
||||||
}
|
}
|
||||||
@ -404,7 +496,7 @@ namespace pal
|
|||||||
FeaturePart* newfpart = new FeaturePart( fpart->feature(), newgeom );
|
FeaturePart* newfpart = new FeaturePart( fpart->feature(), newgeom );
|
||||||
newFeatureParts.append( newfpart );
|
newFeatureParts.append( newfpart );
|
||||||
newfpart->getBoundingBox( bmin, bmax );
|
newfpart->getBoundingBox( bmin, bmax );
|
||||||
rtree->Insert( bmin, bmax, newfpart );
|
mFeatureIndex->Insert( bmin, bmax, newfpart );
|
||||||
delete fpart;
|
delete fpart;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -249,6 +249,9 @@ namespace pal
|
|||||||
/** List of feature parts */
|
/** List of feature parts */
|
||||||
QLinkedList<FeaturePart*> mFeatureParts;
|
QLinkedList<FeaturePart*> mFeatureParts;
|
||||||
|
|
||||||
|
/** List of obstacle parts */
|
||||||
|
QList<FeaturePart*> mObstacleParts;
|
||||||
|
|
||||||
Pal *pal;
|
Pal *pal;
|
||||||
|
|
||||||
double mDefaultPriority;
|
double mDefaultPriority;
|
||||||
@ -269,10 +272,13 @@ namespace pal
|
|||||||
UpsideDownLabels mUpsidedownLabels;
|
UpsideDownLabels mUpsidedownLabels;
|
||||||
|
|
||||||
// indexes (spatial and id)
|
// 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)
|
//! Lookup table of label features (owned by the label feature provider that created them)
|
||||||
QHash< QgsFeatureId, QgsLabelFeature*> mHashtable;
|
QHash< QgsFeatureId, QgsLabelFeature*> mHashtable;
|
||||||
|
|
||||||
|
//obstacle r-tree
|
||||||
|
RTree<FeaturePart*, double, 2, double, 8, 4> *mObstacleIndex;
|
||||||
|
|
||||||
QHash< QString, QLinkedList<FeaturePart*>* > mConnectedHashtable;
|
QHash< QString, QLinkedList<FeaturePart*>* > mConnectedHashtable;
|
||||||
QStringList mConnectedTexts;
|
QStringList mConnectedTexts;
|
||||||
|
|
||||||
@ -296,6 +302,9 @@ namespace pal
|
|||||||
/** Add newly created feature part into r tree and to the list */
|
/** Add newly created feature part into r tree and to the list */
|
||||||
void addFeaturePart( FeaturePart* fpart, const QString &labelText = QString() );
|
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
|
} // end namespace pal
|
||||||
|
@ -151,32 +151,8 @@ namespace pal
|
|||||||
bool extractFeatCallback( FeaturePart *ft_ptr, void *ctx )
|
bool extractFeatCallback( FeaturePart *ft_ptr, void *ctx )
|
||||||
{
|
{
|
||||||
double amin[2], amax[2];
|
double amin[2], amax[2];
|
||||||
|
|
||||||
FeatCallBackCtx *context = ( FeatCallBackCtx* ) ctx;
|
FeatCallBackCtx *context = ( FeatCallBackCtx* ) ctx;
|
||||||
|
|
||||||
#ifdef _DEBUG_FULL_
|
|
||||||
std::cout << "extract feat : " << ft_ptr->getLayer()->getName() << "/" << ft_ptr->getUID() << std::endl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// all feature which are obstacle will be inserted into obstacles
|
|
||||||
if ( ft_ptr->isObstacle() )
|
|
||||||
{
|
|
||||||
ft_ptr->getBoundingBox( amin, amax );
|
|
||||||
context->obstacles->Insert( amin, amax, ft_ptr );
|
|
||||||
}
|
|
||||||
|
|
||||||
// first do some checks whether to extract candidates or not
|
|
||||||
|
|
||||||
// feature has to be labeled?
|
|
||||||
if ( !context->layer->labelLayer() )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// is the feature well defined? TODO Check epsilon
|
|
||||||
if ( ft_ptr->getLabelWidth() < 0.0000001 || ft_ptr->getLabelHeight() < 0.0000001 )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// OK, everything's fine, let's process the feature part
|
|
||||||
|
|
||||||
// Holes of the feature are obstacles
|
// Holes of the feature are obstacles
|
||||||
for ( int i = 0; i < ft_ptr->getNumSelfObstacles(); i++ )
|
for ( int i = 0; i < ft_ptr->getNumSelfObstacles(); i++ )
|
||||||
{
|
{
|
||||||
@ -210,8 +186,28 @@ namespace pal
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct _obstaclebackCtx
|
||||||
|
{
|
||||||
|
RTree<FeaturePart*, double, 2, double> *obstacles;
|
||||||
|
int obstacleCount;
|
||||||
|
} ObstacleCallBackCtx;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback function
|
||||||
|
*
|
||||||
|
* Extract obstacles from indexes
|
||||||
|
*/
|
||||||
|
bool extractObstaclesCallback( FeaturePart *ft_ptr, void *ctx )
|
||||||
|
{
|
||||||
|
double amin[2], amax[2];
|
||||||
|
ObstacleCallBackCtx *context = ( ObstacleCallBackCtx* ) ctx;
|
||||||
|
|
||||||
|
// insert into obstacles
|
||||||
|
ft_ptr->getBoundingBox( amin, amax );
|
||||||
|
context->obstacles->Insert( amin, amax, ft_ptr );
|
||||||
|
context->obstacleCount++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct _filterContext
|
typedef struct _filterContext
|
||||||
{
|
{
|
||||||
@ -267,20 +263,23 @@ namespace pal
|
|||||||
|
|
||||||
QLinkedList<Feats*> *fFeats = new QLinkedList<Feats*>;
|
QLinkedList<Feats*> *fFeats = new QLinkedList<Feats*>;
|
||||||
|
|
||||||
FeatCallBackCtx *context = new FeatCallBackCtx();
|
FeatCallBackCtx context;
|
||||||
context->fFeats = fFeats;
|
context.fFeats = fFeats;
|
||||||
context->obstacles = obstacles;
|
context.obstacles = obstacles;
|
||||||
context->candidates = prob->candidates;
|
context.candidates = prob->candidates;
|
||||||
|
context.bbox_min[0] = amin[0];
|
||||||
|
context.bbox_min[1] = amin[1];
|
||||||
|
context.bbox_max[0] = amax[0];
|
||||||
|
context.bbox_max[1] = amax[1];
|
||||||
|
|
||||||
context->bbox_min[0] = amin[0];
|
ObstacleCallBackCtx obstacleContext;
|
||||||
context->bbox_min[1] = amin[1];
|
obstacleContext.obstacles = obstacles;
|
||||||
|
obstacleContext.obstacleCount = 0;
|
||||||
context->bbox_max[0] = amax[0];
|
|
||||||
context->bbox_max[1] = amax[1];
|
|
||||||
|
|
||||||
// first step : extract features from layers
|
// first step : extract features from layers
|
||||||
|
|
||||||
int previousFeatureCount = 0;
|
int previousFeatureCount = 0;
|
||||||
|
int previousObstacleCount = 0;
|
||||||
|
|
||||||
QStringList layersWithFeaturesInBBox;
|
QStringList layersWithFeaturesInBBox;
|
||||||
|
|
||||||
@ -303,19 +302,23 @@ namespace pal
|
|||||||
|
|
||||||
layer->chopFeaturesAtRepeatDistance();
|
layer->chopFeaturesAtRepeatDistance();
|
||||||
|
|
||||||
// find features within bounding box and generate candidates list
|
layer->mMutex.lock();
|
||||||
context->layer = layer;
|
|
||||||
context->layer->mMutex.lock();
|
|
||||||
context->layer->rtree->Search( amin, amax, extractFeatCallback, ( void* ) context );
|
|
||||||
context->layer->mMutex.unlock();
|
|
||||||
|
|
||||||
if ( context->fFeats->size() - previousFeatureCount > 0 )
|
// find features within bounding box and generate candidates list
|
||||||
|
context.layer = layer;
|
||||||
|
layer->mFeatureIndex->Search( amin, amax, extractFeatCallback, ( void* ) &context );
|
||||||
|
// find obstacles within bounding box
|
||||||
|
layer->mObstacleIndex->Search( amin, amax, extractObstaclesCallback, ( void* ) &obstacleContext );
|
||||||
|
|
||||||
|
layer->mMutex.unlock();
|
||||||
|
|
||||||
|
if ( context.fFeats->size() - previousFeatureCount > 0 || obstacleContext.obstacleCount > previousObstacleCount )
|
||||||
{
|
{
|
||||||
layersWithFeaturesInBBox << layer->name();
|
layersWithFeaturesInBBox << layer->name();
|
||||||
}
|
}
|
||||||
previousFeatureCount = context->fFeats->size();
|
previousFeatureCount = context.fFeats->size();
|
||||||
|
previousObstacleCount = obstacleContext.obstacleCount;
|
||||||
}
|
}
|
||||||
delete context;
|
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
|
|
||||||
prob->nbLabelledLayers = layersWithFeaturesInBBox.size();
|
prob->nbLabelledLayers = layersWithFeaturesInBBox.size();
|
||||||
|
@ -349,6 +349,7 @@ QgsLabelFeature::QgsLabelFeature( QgsFeatureId id, GEOSGeometry* geometry, const
|
|||||||
: mLayer( 0 )
|
: mLayer( 0 )
|
||||||
, mId( id )
|
, mId( id )
|
||||||
, mGeometry( geometry )
|
, mGeometry( geometry )
|
||||||
|
, mObstacleGeometry( 0 )
|
||||||
, mSize( size )
|
, mSize( size )
|
||||||
, mPriority( -1 )
|
, mPriority( -1 )
|
||||||
, mHasFixedPosition( false )
|
, mHasFixedPosition( false )
|
||||||
@ -369,9 +370,20 @@ QgsLabelFeature::~QgsLabelFeature()
|
|||||||
if ( mGeometry )
|
if ( mGeometry )
|
||||||
GEOSGeom_destroy_r( QgsGeometry::getGEOSHandler(), mGeometry );
|
GEOSGeom_destroy_r( QgsGeometry::getGEOSHandler(), mGeometry );
|
||||||
|
|
||||||
|
if ( mObstacleGeometry )
|
||||||
|
GEOSGeom_destroy_r( QgsGeometry::getGEOSHandler(), mObstacleGeometry );
|
||||||
|
|
||||||
delete mInfo;
|
delete mInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QgsLabelFeature::setObstacleGeometry( GEOSGeometry* obstacleGeom )
|
||||||
|
{
|
||||||
|
if ( mObstacleGeometry )
|
||||||
|
GEOSGeom_destroy_r( QgsGeometry::getGEOSHandler(), mObstacleGeometry );
|
||||||
|
|
||||||
|
mObstacleGeometry = obstacleGeom;
|
||||||
|
}
|
||||||
|
|
||||||
QgsAbstractLabelProvider*QgsLabelFeature::provider() const
|
QgsAbstractLabelProvider*QgsLabelFeature::provider() const
|
||||||
{
|
{
|
||||||
return mLayer ? mLayer->provider() : 0;
|
return mLayer ? mLayer->provider() : 0;
|
||||||
|
@ -62,6 +62,22 @@ class CORE_EXPORT QgsLabelFeature
|
|||||||
//! Get access to the associated geometry
|
//! Get access to the associated geometry
|
||||||
GEOSGeometry* geometry() const { return mGeometry; }
|
GEOSGeometry* geometry() const { return mGeometry; }
|
||||||
|
|
||||||
|
/** Sets the label's obstacle geometry, if different to the feature geometry.
|
||||||
|
* This can be used to override the shape of the feature for obstacle detection, eg to
|
||||||
|
* buffer around a point geometry to prevent labels being placed too close to the
|
||||||
|
* point itself. It not set, the feature's geometry is used for obstacle detection.
|
||||||
|
* Ownership of obstacle geometry is transferred.
|
||||||
|
* @note added in QGIS 2.14
|
||||||
|
* @see obstacleGeometry()
|
||||||
|
*/
|
||||||
|
void setObstacleGeometry( GEOSGeometry* obstacleGeom );
|
||||||
|
|
||||||
|
/** Returns the label's obstacle geometry, if different to the feature geometry.
|
||||||
|
* @note added in QGIS 2.14
|
||||||
|
* @see setObstacleGeometry()
|
||||||
|
*/
|
||||||
|
GEOSGeometry* obstacleGeometry() const { return mObstacleGeometry; }
|
||||||
|
|
||||||
//! Size of the label (in map units)
|
//! Size of the label (in map units)
|
||||||
QSizeF size() const { return mSize; }
|
QSizeF size() const { return mSize; }
|
||||||
|
|
||||||
@ -196,6 +212,8 @@ class CORE_EXPORT QgsLabelFeature
|
|||||||
QgsFeatureId mId;
|
QgsFeatureId mId;
|
||||||
//! Geometry of the feature to be labelled
|
//! Geometry of the feature to be labelled
|
||||||
GEOSGeometry* mGeometry;
|
GEOSGeometry* mGeometry;
|
||||||
|
//! Optional geometry to use for label obstacles, if different to mGeometry
|
||||||
|
GEOSGeometry* mObstacleGeometry;
|
||||||
//! Width and height of the label
|
//! Width and height of the label
|
||||||
QSizeF mSize;
|
QSizeF mSize;
|
||||||
//! Priority of the label
|
//! Priority of the label
|
||||||
|
@ -176,6 +176,16 @@ class TestPointPlacement(TestPlacementBase):
|
|||||||
self.removeMapLayer(self.layer)
|
self.removeMapLayer(self.layer)
|
||||||
self.layer = None
|
self.layer = None
|
||||||
|
|
||||||
|
def test_multipolygon_obstacle(self):
|
||||||
|
# Test that all parts of multipolygon are used as an obstacle
|
||||||
|
self.layer = TestQgsPalLabeling.loadFeatureLayer('point')
|
||||||
|
polyLayer = TestQgsPalLabeling.loadFeatureLayer('multi_polygon')
|
||||||
|
self._TestMapSettings = self.cloneMapSettings(self._MapSettings)
|
||||||
|
self.checkTest()
|
||||||
|
self.removeMapLayer(self.layer)
|
||||||
|
self.removeMapLayer(polyLayer)
|
||||||
|
self.layer = None
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# NOTE: unless PAL_SUITE env var is set all test class methods will be run
|
# NOTE: unless PAL_SUITE env var is set all test class methods will be run
|
||||||
# SEE: test_qgspallabeling_tests.suiteTests() to define suite
|
# SEE: test_qgspallabeling_tests.suiteTests() to define suite
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
245
tests/testdata/labeling/multi_polygon.qml
vendored
Normal file
245
tests/testdata/labeling/multi_polygon.qml
vendored
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
|
||||||
|
<qgis version="2.13.0-Master" minimumScale="-4.65661e-10" maximumScale="1e+08" simplifyDrawingHints="1" minLabelScale="0" maxLabelScale="1e+08" simplifyDrawingTol="1" simplifyMaxScale="1" hasScaleBasedVisibilityFlag="0" simplifyLocal="1" scaleBasedLabelVisibilityFlag="0">
|
||||||
|
<edittypes>
|
||||||
|
<edittype widgetv2type="TextEdit" name="pkuid">
|
||||||
|
<widgetv2config IsMultiline="0" fieldEditable="1" UseHtml="0" labelOnTop="0"/>
|
||||||
|
</edittype>
|
||||||
|
<edittype widgetv2type="TextEdit" name="ftype">
|
||||||
|
<widgetv2config IsMultiline="0" fieldEditable="1" UseHtml="0" labelOnTop="0"/>
|
||||||
|
</edittype>
|
||||||
|
<edittype widgetv2type="TextEdit" name="text">
|
||||||
|
<widgetv2config IsMultiline="0" fieldEditable="1" UseHtml="0" labelOnTop="0"/>
|
||||||
|
</edittype>
|
||||||
|
</edittypes>
|
||||||
|
<renderer-v2 forceraster="0" symbollevels="0" type="singleSymbol">
|
||||||
|
<symbols>
|
||||||
|
<symbol alpha="1" clip_to_extent="1" type="fill" name="0">
|
||||||
|
<layer pass="0" class="SimpleFill" locked="0">
|
||||||
|
<prop k="border_width_map_unit_scale" v="0,0,0,0,0,0"/>
|
||||||
|
<prop k="color" v="133,149,161,255"/>
|
||||||
|
<prop k="joinstyle" v="bevel"/>
|
||||||
|
<prop k="offset" v="0,0"/>
|
||||||
|
<prop k="offset_map_unit_scale" v="0,0,0,0,0,0"/>
|
||||||
|
<prop k="offset_unit" v="MM"/>
|
||||||
|
<prop k="outline_color" v="0,0,0,255"/>
|
||||||
|
<prop k="outline_style" v="no"/>
|
||||||
|
<prop k="outline_width" v="0.26"/>
|
||||||
|
<prop k="outline_width_unit" v="MM"/>
|
||||||
|
<prop k="style" v="solid"/>
|
||||||
|
</layer>
|
||||||
|
</symbol>
|
||||||
|
</symbols>
|
||||||
|
<rotation/>
|
||||||
|
<sizescale scalemethod="diameter"/>
|
||||||
|
</renderer-v2>
|
||||||
|
<labeling type="simple"/>
|
||||||
|
<customproperties>
|
||||||
|
<property key="labeling" value="pal"/>
|
||||||
|
<property key="labeling/addDirectionSymbol" value="false"/>
|
||||||
|
<property key="labeling/angleOffset" value="0"/>
|
||||||
|
<property key="labeling/blendMode" value="0"/>
|
||||||
|
<property key="labeling/bufferBlendMode" value="0"/>
|
||||||
|
<property key="labeling/bufferColorA" value="255"/>
|
||||||
|
<property key="labeling/bufferColorB" value="255"/>
|
||||||
|
<property key="labeling/bufferColorG" value="255"/>
|
||||||
|
<property key="labeling/bufferColorR" value="255"/>
|
||||||
|
<property key="labeling/bufferDraw" value="false"/>
|
||||||
|
<property key="labeling/bufferJoinStyle" value="64"/>
|
||||||
|
<property key="labeling/bufferNoFill" value="false"/>
|
||||||
|
<property key="labeling/bufferSize" value="1"/>
|
||||||
|
<property key="labeling/bufferSizeInMapUnits" value="false"/>
|
||||||
|
<property key="labeling/bufferSizeMapUnitMaxScale" value="0"/>
|
||||||
|
<property key="labeling/bufferSizeMapUnitMinScale" value="0"/>
|
||||||
|
<property key="labeling/bufferTransp" value="0"/>
|
||||||
|
<property key="labeling/centroidInside" value="false"/>
|
||||||
|
<property key="labeling/centroidWhole" value="false"/>
|
||||||
|
<property key="labeling/decimals" value="3"/>
|
||||||
|
<property key="labeling/displayAll" value="false"/>
|
||||||
|
<property key="labeling/dist" value="0"/>
|
||||||
|
<property key="labeling/distInMapUnits" value="false"/>
|
||||||
|
<property key="labeling/distMapUnitMaxScale" value="0"/>
|
||||||
|
<property key="labeling/distMapUnitMinScale" value="0"/>
|
||||||
|
<property key="labeling/drawLabels" value="true"/>
|
||||||
|
<property key="labeling/enabled" value="true"/>
|
||||||
|
<property key="labeling/fieldName" value="text"/>
|
||||||
|
<property key="labeling/fitInPolygonOnly" value="false"/>
|
||||||
|
<property key="labeling/fontCapitals" value="0"/>
|
||||||
|
<property key="labeling/fontFamily" value="Ubuntu"/>
|
||||||
|
<property key="labeling/fontItalic" value="true"/>
|
||||||
|
<property key="labeling/fontLetterSpacing" value="0"/>
|
||||||
|
<property key="labeling/fontLimitPixelSize" value="false"/>
|
||||||
|
<property key="labeling/fontMaxPixelSize" value="10000"/>
|
||||||
|
<property key="labeling/fontMinPixelSize" value="3"/>
|
||||||
|
<property key="labeling/fontSize" value="11"/>
|
||||||
|
<property key="labeling/fontSizeInMapUnits" value="false"/>
|
||||||
|
<property key="labeling/fontSizeMapUnitMaxScale" value="0"/>
|
||||||
|
<property key="labeling/fontSizeMapUnitMinScale" value="0"/>
|
||||||
|
<property key="labeling/fontStrikeout" value="false"/>
|
||||||
|
<property key="labeling/fontUnderline" value="false"/>
|
||||||
|
<property key="labeling/fontWeight" value="63"/>
|
||||||
|
<property key="labeling/fontWordSpacing" value="0"/>
|
||||||
|
<property key="labeling/formatNumbers" value="false"/>
|
||||||
|
<property key="labeling/isExpression" value="false"/>
|
||||||
|
<property key="labeling/labelOffsetInMapUnits" value="true"/>
|
||||||
|
<property key="labeling/labelOffsetMapUnitMaxScale" value="0"/>
|
||||||
|
<property key="labeling/labelOffsetMapUnitMinScale" value="0"/>
|
||||||
|
<property key="labeling/labelPerPart" value="false"/>
|
||||||
|
<property key="labeling/leftDirectionSymbol" value="<"/>
|
||||||
|
<property key="labeling/limitNumLabels" value="false"/>
|
||||||
|
<property key="labeling/maxCurvedCharAngleIn" value="20"/>
|
||||||
|
<property key="labeling/maxCurvedCharAngleOut" value="-20"/>
|
||||||
|
<property key="labeling/maxNumLabels" value="2000"/>
|
||||||
|
<property key="labeling/mergeLines" value="false"/>
|
||||||
|
<property key="labeling/minFeatureSize" value="0"/>
|
||||||
|
<property key="labeling/multilineAlign" value="0"/>
|
||||||
|
<property key="labeling/multilineHeight" value="1"/>
|
||||||
|
<property key="labeling/namedStyle" value="Medium Italic"/>
|
||||||
|
<property key="labeling/obstacle" value="true"/>
|
||||||
|
<property key="labeling/obstacleFactor" value="1"/>
|
||||||
|
<property key="labeling/obstacleType" value="0"/>
|
||||||
|
<property key="labeling/placeDirectionSymbol" value="0"/>
|
||||||
|
<property key="labeling/placement" value="1"/>
|
||||||
|
<property key="labeling/placementFlags" value="10"/>
|
||||||
|
<property key="labeling/plussign" value="false"/>
|
||||||
|
<property key="labeling/preserveRotation" value="true"/>
|
||||||
|
<property key="labeling/previewBkgrdColor" value="#ffffff"/>
|
||||||
|
<property key="labeling/priority" value="5"/>
|
||||||
|
<property key="labeling/quadOffset" value="4"/>
|
||||||
|
<property key="labeling/repeatDistance" value="0"/>
|
||||||
|
<property key="labeling/repeatDistanceMapUnitMaxScale" value="0"/>
|
||||||
|
<property key="labeling/repeatDistanceMapUnitMinScale" value="0"/>
|
||||||
|
<property key="labeling/repeatDistanceUnit" value="1"/>
|
||||||
|
<property key="labeling/reverseDirectionSymbol" value="false"/>
|
||||||
|
<property key="labeling/rightDirectionSymbol" value=">"/>
|
||||||
|
<property key="labeling/scaleMax" value="10000000"/>
|
||||||
|
<property key="labeling/scaleMin" value="1"/>
|
||||||
|
<property key="labeling/scaleVisibility" value="false"/>
|
||||||
|
<property key="labeling/shadowBlendMode" value="6"/>
|
||||||
|
<property key="labeling/shadowColorB" value="0"/>
|
||||||
|
<property key="labeling/shadowColorG" value="0"/>
|
||||||
|
<property key="labeling/shadowColorR" value="0"/>
|
||||||
|
<property key="labeling/shadowDraw" value="false"/>
|
||||||
|
<property key="labeling/shadowOffsetAngle" value="135"/>
|
||||||
|
<property key="labeling/shadowOffsetDist" value="1"/>
|
||||||
|
<property key="labeling/shadowOffsetGlobal" value="true"/>
|
||||||
|
<property key="labeling/shadowOffsetMapUnitMaxScale" value="0"/>
|
||||||
|
<property key="labeling/shadowOffsetMapUnitMinScale" value="0"/>
|
||||||
|
<property key="labeling/shadowOffsetUnits" value="1"/>
|
||||||
|
<property key="labeling/shadowRadius" value="1.5"/>
|
||||||
|
<property key="labeling/shadowRadiusAlphaOnly" value="false"/>
|
||||||
|
<property key="labeling/shadowRadiusMapUnitMaxScale" value="0"/>
|
||||||
|
<property key="labeling/shadowRadiusMapUnitMinScale" value="0"/>
|
||||||
|
<property key="labeling/shadowRadiusUnits" value="1"/>
|
||||||
|
<property key="labeling/shadowScale" value="100"/>
|
||||||
|
<property key="labeling/shadowTransparency" value="30"/>
|
||||||
|
<property key="labeling/shadowUnder" value="0"/>
|
||||||
|
<property key="labeling/shapeBlendMode" value="0"/>
|
||||||
|
<property key="labeling/shapeBorderColorA" value="255"/>
|
||||||
|
<property key="labeling/shapeBorderColorB" value="128"/>
|
||||||
|
<property key="labeling/shapeBorderColorG" value="128"/>
|
||||||
|
<property key="labeling/shapeBorderColorR" value="128"/>
|
||||||
|
<property key="labeling/shapeBorderWidth" value="0"/>
|
||||||
|
<property key="labeling/shapeBorderWidthMapUnitMaxScale" value="0"/>
|
||||||
|
<property key="labeling/shapeBorderWidthMapUnitMinScale" value="0"/>
|
||||||
|
<property key="labeling/shapeBorderWidthUnits" value="1"/>
|
||||||
|
<property key="labeling/shapeDraw" value="false"/>
|
||||||
|
<property key="labeling/shapeFillColorA" value="255"/>
|
||||||
|
<property key="labeling/shapeFillColorB" value="255"/>
|
||||||
|
<property key="labeling/shapeFillColorG" value="255"/>
|
||||||
|
<property key="labeling/shapeFillColorR" value="255"/>
|
||||||
|
<property key="labeling/shapeJoinStyle" value="64"/>
|
||||||
|
<property key="labeling/shapeOffsetMapUnitMaxScale" value="0"/>
|
||||||
|
<property key="labeling/shapeOffsetMapUnitMinScale" value="0"/>
|
||||||
|
<property key="labeling/shapeOffsetUnits" value="1"/>
|
||||||
|
<property key="labeling/shapeOffsetX" value="0"/>
|
||||||
|
<property key="labeling/shapeOffsetY" value="0"/>
|
||||||
|
<property key="labeling/shapeRadiiMapUnitMaxScale" value="0"/>
|
||||||
|
<property key="labeling/shapeRadiiMapUnitMinScale" value="0"/>
|
||||||
|
<property key="labeling/shapeRadiiUnits" value="1"/>
|
||||||
|
<property key="labeling/shapeRadiiX" value="0"/>
|
||||||
|
<property key="labeling/shapeRadiiY" value="0"/>
|
||||||
|
<property key="labeling/shapeRotation" value="0"/>
|
||||||
|
<property key="labeling/shapeRotationType" value="0"/>
|
||||||
|
<property key="labeling/shapeSVGFile" value=""/>
|
||||||
|
<property key="labeling/shapeSizeMapUnitMaxScale" value="0"/>
|
||||||
|
<property key="labeling/shapeSizeMapUnitMinScale" value="0"/>
|
||||||
|
<property key="labeling/shapeSizeType" value="0"/>
|
||||||
|
<property key="labeling/shapeSizeUnits" value="1"/>
|
||||||
|
<property key="labeling/shapeSizeX" value="0"/>
|
||||||
|
<property key="labeling/shapeSizeY" value="0"/>
|
||||||
|
<property key="labeling/shapeTransparency" value="0"/>
|
||||||
|
<property key="labeling/shapeType" value="0"/>
|
||||||
|
<property key="labeling/textColorA" value="255"/>
|
||||||
|
<property key="labeling/textColorB" value="0"/>
|
||||||
|
<property key="labeling/textColorG" value="0"/>
|
||||||
|
<property key="labeling/textColorR" value="0"/>
|
||||||
|
<property key="labeling/textTransp" value="0"/>
|
||||||
|
<property key="labeling/upsidedownLabels" value="0"/>
|
||||||
|
<property key="labeling/wrapChar" value=""/>
|
||||||
|
<property key="labeling/xOffset" value="0"/>
|
||||||
|
<property key="labeling/yOffset" value="0"/>
|
||||||
|
<property key="variableNames" value="_fields_"/>
|
||||||
|
<property key="variableValues" value=""/>
|
||||||
|
</customproperties>
|
||||||
|
<blendMode>0</blendMode>
|
||||||
|
<featureBlendMode>0</featureBlendMode>
|
||||||
|
<layerTransparency>0</layerTransparency>
|
||||||
|
<displayfield>pkuid</displayfield>
|
||||||
|
<label>0</label>
|
||||||
|
<labelattributes>
|
||||||
|
<label fieldname="" text="Label"/>
|
||||||
|
<family fieldname="" name="Ubuntu"/>
|
||||||
|
<size fieldname="" units="pt" value="12"/>
|
||||||
|
<bold fieldname="" on="0"/>
|
||||||
|
<italic fieldname="" on="0"/>
|
||||||
|
<underline fieldname="" on="0"/>
|
||||||
|
<strikeout fieldname="" on="0"/>
|
||||||
|
<color fieldname="" red="0" blue="0" green="0"/>
|
||||||
|
<x fieldname=""/>
|
||||||
|
<y fieldname=""/>
|
||||||
|
<offset x="0" y="0" units="pt" yfieldname="" xfieldname=""/>
|
||||||
|
<angle fieldname="" value="0" auto="0"/>
|
||||||
|
<alignment fieldname="" value="center"/>
|
||||||
|
<buffercolor fieldname="" red="255" blue="255" green="255"/>
|
||||||
|
<buffersize fieldname="" units="pt" value="1"/>
|
||||||
|
<bufferenabled fieldname="" on=""/>
|
||||||
|
<multilineenabled fieldname="" on=""/>
|
||||||
|
<selectedonly on=""/>
|
||||||
|
</labelattributes>
|
||||||
|
<SingleCategoryDiagramRenderer diagramType="Pie">
|
||||||
|
<DiagramCategory penColor="#000000" labelPlacementMethod="XHeight" penWidth="0" diagramOrientation="Up" minimumSize="0" barWidth="5" penAlpha="255" maxScaleDenominator="1e+08" backgroundColor="#ffffff" transparency="0" width="15" scaleDependency="Area" backgroundAlpha="255" angleOffset="1440" scaleBasedVisibility="0" enabled="0" height="15" sizeType="MM" minScaleDenominator="-4.65661e-10">
|
||||||
|
<fontProperties description="Ubuntu,11,-1,5,50,0,0,0,0,0" style=""/>
|
||||||
|
</DiagramCategory>
|
||||||
|
</SingleCategoryDiagramRenderer>
|
||||||
|
<DiagramLayerSettings yPosColumn="-1" linePlacementFlags="10" placement="0" dist="0" xPosColumn="-1" priority="0" obstacle="0" showAll="1"/>
|
||||||
|
<editform></editform>
|
||||||
|
<editforminit/>
|
||||||
|
<editforminitusecode>0</editforminitusecode>
|
||||||
|
<editforminitcode><![CDATA[# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
QGIS forms can have a Python function that is called when the form is
|
||||||
|
opened.
|
||||||
|
|
||||||
|
Use this function to add extra logic to your forms.
|
||||||
|
|
||||||
|
Enter the name of the function in the "Python Init function"
|
||||||
|
field.
|
||||||
|
An example follows:
|
||||||
|
"""
|
||||||
|
from PyQt4.QtGui import QWidget
|
||||||
|
|
||||||
|
def my_form_open(dialog, layer, feature):
|
||||||
|
geom = feature.geometry()
|
||||||
|
control = dialog.findChild(QWidget, "MyLineEdit")
|
||||||
|
]]></editforminitcode>
|
||||||
|
<featformsuppress>0</featformsuppress>
|
||||||
|
<annotationform></annotationform>
|
||||||
|
<editorlayout>generatedlayout</editorlayout>
|
||||||
|
<excludeAttributesWMS/>
|
||||||
|
<excludeAttributesWFS/>
|
||||||
|
<attributeactions/>
|
||||||
|
<conditionalstyles>
|
||||||
|
<rowstyles/>
|
||||||
|
<fieldstyles/>
|
||||||
|
</conditionalstyles>
|
||||||
|
</qgis>
|
BIN
tests/testdata/labeling/pal_features_v3.sqlite
vendored
BIN
tests/testdata/labeling/pal_features_v3.sqlite
vendored
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user