mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -151,32 +151,8 @@ namespace pal
|
||||
bool extractFeatCallback( FeaturePart *ft_ptr, void *ctx )
|
||||
{
|
||||
double amin[2], amax[2];
|
||||
|
||||
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
|
||||
for ( int i = 0; i < ft_ptr->getNumSelfObstacles(); i++ )
|
||||
{
|
||||
@ -210,8 +186,28 @@ namespace pal
|
||||
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
|
||||
{
|
||||
@ -267,20 +263,23 @@ namespace pal
|
||||
|
||||
QLinkedList<Feats*> *fFeats = new QLinkedList<Feats*>;
|
||||
|
||||
FeatCallBackCtx *context = new FeatCallBackCtx();
|
||||
context->fFeats = fFeats;
|
||||
context->obstacles = obstacles;
|
||||
context->candidates = prob->candidates;
|
||||
FeatCallBackCtx context;
|
||||
context.fFeats = fFeats;
|
||||
context.obstacles = obstacles;
|
||||
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];
|
||||
context->bbox_min[1] = amin[1];
|
||||
|
||||
context->bbox_max[0] = amax[0];
|
||||
context->bbox_max[1] = amax[1];
|
||||
ObstacleCallBackCtx obstacleContext;
|
||||
obstacleContext.obstacles = obstacles;
|
||||
obstacleContext.obstacleCount = 0;
|
||||
|
||||
// first step : extract features from layers
|
||||
|
||||
int previousFeatureCount = 0;
|
||||
int previousObstacleCount = 0;
|
||||
|
||||
QStringList layersWithFeaturesInBBox;
|
||||
|
||||
@ -303,19 +302,23 @@ namespace pal
|
||||
|
||||
layer->chopFeaturesAtRepeatDistance();
|
||||
|
||||
// find features within bounding box and generate candidates list
|
||||
context->layer = layer;
|
||||
context->layer->mMutex.lock();
|
||||
context->layer->rtree->Search( amin, amax, extractFeatCallback, ( void* ) context );
|
||||
context->layer->mMutex.unlock();
|
||||
layer->mMutex.lock();
|
||||
|
||||
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();
|
||||
}
|
||||
previousFeatureCount = context->fFeats->size();
|
||||
previousFeatureCount = context.fFeats->size();
|
||||
previousObstacleCount = obstacleContext.obstacleCount;
|
||||
}
|
||||
delete context;
|
||||
mMutex.unlock();
|
||||
|
||||
prob->nbLabelledLayers = layersWithFeaturesInBBox.size();
|
||||
|
@ -349,6 +349,7 @@ QgsLabelFeature::QgsLabelFeature( QgsFeatureId id, GEOSGeometry* geometry, const
|
||||
: mLayer( 0 )
|
||||
, mId( id )
|
||||
, mGeometry( geometry )
|
||||
, mObstacleGeometry( 0 )
|
||||
, mSize( size )
|
||||
, mPriority( -1 )
|
||||
, mHasFixedPosition( false )
|
||||
@ -369,9 +370,20 @@ QgsLabelFeature::~QgsLabelFeature()
|
||||
if ( mGeometry )
|
||||
GEOSGeom_destroy_r( QgsGeometry::getGEOSHandler(), mGeometry );
|
||||
|
||||
if ( mObstacleGeometry )
|
||||
GEOSGeom_destroy_r( QgsGeometry::getGEOSHandler(), mObstacleGeometry );
|
||||
|
||||
delete mInfo;
|
||||
}
|
||||
|
||||
void QgsLabelFeature::setObstacleGeometry( GEOSGeometry* obstacleGeom )
|
||||
{
|
||||
if ( mObstacleGeometry )
|
||||
GEOSGeom_destroy_r( QgsGeometry::getGEOSHandler(), mObstacleGeometry );
|
||||
|
||||
mObstacleGeometry = obstacleGeom;
|
||||
}
|
||||
|
||||
QgsAbstractLabelProvider*QgsLabelFeature::provider() const
|
||||
{
|
||||
return mLayer ? mLayer->provider() : 0;
|
||||
|
@ -62,6 +62,22 @@ class CORE_EXPORT QgsLabelFeature
|
||||
//! Get access to the associated geometry
|
||||
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)
|
||||
QSizeF size() const { return mSize; }
|
||||
|
||||
@ -196,6 +212,8 @@ class CORE_EXPORT QgsLabelFeature
|
||||
QgsFeatureId mId;
|
||||
//! Geometry of the feature to be labelled
|
||||
GEOSGeometry* mGeometry;
|
||||
//! Optional geometry to use for label obstacles, if different to mGeometry
|
||||
GEOSGeometry* mObstacleGeometry;
|
||||
//! Width and height of the label
|
||||
QSizeF mSize;
|
||||
//! Priority of the label
|
||||
|
@ -176,6 +176,16 @@ class TestPointPlacement(TestPlacementBase):
|
||||
self.removeMapLayer(self.layer)
|
||||
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__':
|
||||
# NOTE: unless PAL_SUITE env var is set all test class methods will be run
|
||||
# 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