mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-05 00:09:32 -04:00
fix project migration of old reference to new ones
This commit is contained in:
parent
1bc1dd3de1
commit
388c6e3ee6
@ -978,7 +978,7 @@ Encodes a reference to a parametric SVG into a path with parameters according to
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
static QSet<const QgsSymbolLayer *> toSymbolLayerPointers( QgsFeatureRenderer *renderer, const QSet<QgsSymbolLayerId> &symbolLayerIds );
|
||||
static QSet<const QgsSymbolLayer *> toSymbolLayerPointers( const QgsFeatureRenderer *renderer, const QSet<QgsSymbolLayerId> &symbolLayerIds );
|
||||
%Docstring
|
||||
Converts a set of symbol layer id to a set of pointers to actual symbol layers carried by the feature renderer.
|
||||
|
||||
@ -1030,6 +1030,15 @@ The method makes approximations and can modify ``angle`` in order to generate th
|
||||
|
||||
:return: the size of the tile
|
||||
|
||||
.. versionadded:: 3.30
|
||||
%End
|
||||
|
||||
static void fixOldSymbolLayerReferences( const QMap<QString, QgsMapLayer *> &mapLayers );
|
||||
%Docstring
|
||||
:py:class:`QgsSymbolLayerReference` uses :py:class:`QgsSymbolLayer` unique uuid identifier since QGIS 3.30, instead of the symbol
|
||||
key (rule for :py:class:`QgsRuleBasedRenderer` for instance) and index path, so this method migrates ``mapLayers`` old references
|
||||
to new ones.
|
||||
|
||||
.. versionadded:: 3.30
|
||||
%End
|
||||
|
||||
|
@ -1946,6 +1946,14 @@ bool QgsProject::readProjectFile( const QString &filename, Qgis::ProjectReadFlag
|
||||
profile.switchTask( tr( "Resolving references" ) );
|
||||
mRootGroup->resolveReferences( this );
|
||||
|
||||
// we need to migrate old fashion designed QgsSymbolLayerReference to new ones
|
||||
if ( QgsProjectVersion( 3, 28, 0 ) > mSaveVersion )
|
||||
{
|
||||
Q_NOWARN_DEPRECATED_PUSH
|
||||
QgsSymbolLayerUtils::fixOldSymbolLayerReferences( mapLayers() );
|
||||
Q_NOWARN_DEPRECATED_POP
|
||||
}
|
||||
|
||||
if ( !layerTreeElem.isNull() )
|
||||
{
|
||||
mRootGroup->readLayerOrderFromXml( layerTreeElem );
|
||||
|
@ -40,7 +40,7 @@ QgsSymbolLayerReferenceList stringToSymbolLayerReferenceList( const QString &str
|
||||
// TODO QGIS 4 : remove this if branch, keep only else part
|
||||
Q_NOWARN_DEPRECATED_PUSH
|
||||
|
||||
// old masked symbol layer format (before 3.28), we use unique id now!
|
||||
// old masked symbol layer format (before 3.30), we use unique id now!
|
||||
// we load it the old fashion way and we will update the new one later when
|
||||
// the whole project is loaded
|
||||
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "qgssymbollayerreference.h"
|
||||
#include "qgsmarkersymbollayer.h"
|
||||
#include "qmath.h"
|
||||
#include "qgsmasksymbollayer.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QFont>
|
||||
@ -4911,7 +4912,7 @@ double QgsSymbolLayerUtils::sizeInPixelsFromSldUom( const QString &uom, double s
|
||||
return size * scale;
|
||||
}
|
||||
|
||||
QSet<const QgsSymbolLayer *> QgsSymbolLayerUtils::toSymbolLayerPointers( QgsFeatureRenderer *renderer, const QSet<QgsSymbolLayerId> &symbolLayerIds )
|
||||
QSet<const QgsSymbolLayer *> QgsSymbolLayerUtils::toSymbolLayerPointers( const QgsFeatureRenderer *renderer, const QSet<QgsSymbolLayerId> &symbolLayerIds )
|
||||
{
|
||||
class SymbolLayerVisitor : public QgsStyleEntityVisitorInterface
|
||||
{
|
||||
@ -5355,5 +5356,92 @@ QSize QgsSymbolLayerUtils::tileSize( int width, int height, double &angleRad )
|
||||
}
|
||||
|
||||
return tileSize;
|
||||
|
||||
}
|
||||
|
||||
void QgsSymbolLayerUtils::fixOldSymbolLayerReferences( const QMap<QString, QgsMapLayer *> &mapLayers )
|
||||
{
|
||||
for ( QgsMapLayer *ml : mapLayers )
|
||||
{
|
||||
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
|
||||
if ( !vl )
|
||||
continue;
|
||||
|
||||
auto migrateOldReferences = [&mapLayers]( const QList<QgsSymbolLayerReference> &slRefs )
|
||||
{
|
||||
QList<QgsSymbolLayerReference> newRefs;
|
||||
for ( QgsSymbolLayerReference slRef : slRefs )
|
||||
{
|
||||
const QgsVectorLayer *vlRef = qobject_cast<QgsVectorLayer *>( mapLayers[ slRef.layerId() ] );
|
||||
const QgsFeatureRenderer *renderer = vlRef ? vlRef->renderer() : nullptr;
|
||||
QSet<const QgsSymbolLayer *> symbolLayers = renderer ? QgsSymbolLayerUtils::toSymbolLayerPointers(
|
||||
renderer, QSet<QgsSymbolLayerId>() << slRef.symbolLayerId() ) : QSet<const QgsSymbolLayer *>();
|
||||
const QString slId = symbolLayers.isEmpty() ? QString() : ( *symbolLayers.constBegin() )->id();
|
||||
newRefs << QgsSymbolLayerReference( slRef.layerId(), slId );
|
||||
}
|
||||
|
||||
return newRefs;
|
||||
};
|
||||
|
||||
if ( QgsAbstractVectorLayerLabeling *labeling = vl->labeling() )
|
||||
{
|
||||
for ( QString provider : labeling->subProviders() )
|
||||
{
|
||||
QgsPalLayerSettings settings = labeling->settings( provider );
|
||||
QgsTextFormat format = settings.format();
|
||||
QList<QgsSymbolLayerReference> newMaskedSymbolLayers = migrateOldReferences( format.mask().maskedSymbolLayers() );
|
||||
format.mask().setMaskedSymbolLayers( newMaskedSymbolLayers );
|
||||
settings.setFormat( format );
|
||||
labeling->setSettings( new QgsPalLayerSettings( settings ), provider );
|
||||
}
|
||||
}
|
||||
|
||||
if ( QgsFeatureRenderer *renderer = vl->renderer() )
|
||||
{
|
||||
|
||||
class SymbolLayerVisitor : public QgsStyleEntityVisitorInterface
|
||||
{
|
||||
public:
|
||||
bool visitEnter( const QgsStyleEntityVisitorInterface::Node &node ) override
|
||||
{
|
||||
return ( node.type == QgsStyleEntityVisitorInterface::NodeType::SymbolRule );
|
||||
}
|
||||
|
||||
void visitSymbol( const QgsSymbol *symbol )
|
||||
{
|
||||
for ( int idx = 0; idx < symbol->symbolLayerCount(); idx++ )
|
||||
{
|
||||
const QgsSymbolLayer *sl = symbol->symbolLayer( idx );
|
||||
|
||||
// recurse over sub symbols
|
||||
const QgsSymbol *subSymbol = const_cast<QgsSymbolLayer *>( sl )->subSymbol();
|
||||
if ( subSymbol )
|
||||
visitSymbol( subSymbol );
|
||||
|
||||
if ( const QgsMaskMarkerSymbolLayer *maskLayer = dynamic_cast<const QgsMaskMarkerSymbolLayer *>( sl ) )
|
||||
maskSymbolLayers << maskLayer;
|
||||
}
|
||||
}
|
||||
|
||||
bool visit( const QgsStyleEntityVisitorInterface::StyleLeaf &leaf ) override
|
||||
{
|
||||
if ( leaf.entity && leaf.entity->type() == QgsStyle::SymbolEntity )
|
||||
{
|
||||
auto symbolEntity = static_cast<const QgsStyleSymbolEntity *>( leaf.entity );
|
||||
if ( symbolEntity->symbol() )
|
||||
visitSymbol( symbolEntity->symbol() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<const QgsMaskMarkerSymbolLayer *> maskSymbolLayers;
|
||||
};
|
||||
|
||||
SymbolLayerVisitor visitor;
|
||||
renderer->accept( &visitor );
|
||||
|
||||
for ( const QgsMaskMarkerSymbolLayer *maskSymbolLayer : visitor.maskSymbolLayers )
|
||||
// Ugly but there is no other proper way to get those layer in order to modify them
|
||||
const_cast<QgsMaskMarkerSymbolLayer *>( maskSymbolLayer )->setMasks( migrateOldReferences( maskSymbolLayer->masks() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -884,7 +884,7 @@ class CORE_EXPORT QgsSymbolLayerUtils
|
||||
* Converts a set of symbol layer id to a set of pointers to actual symbol layers carried by the feature renderer.
|
||||
* \since QGIS 3.12
|
||||
*/
|
||||
static QSet<const QgsSymbolLayer *> toSymbolLayerPointers( QgsFeatureRenderer *renderer, const QSet<QgsSymbolLayerId> &symbolLayerIds );
|
||||
static QSet<const QgsSymbolLayer *> toSymbolLayerPointers( const QgsFeatureRenderer *renderer, const QSet<QgsSymbolLayerId> &symbolLayerIds );
|
||||
|
||||
/**
|
||||
* Calculates the frame rate (in frames per second) at which the given \a renderer must be redrawn.
|
||||
@ -928,6 +928,14 @@ class CORE_EXPORT QgsSymbolLayerUtils
|
||||
*/
|
||||
static QSize tileSize( int width, int height, double &angleRad SIP_INOUT );
|
||||
|
||||
/**
|
||||
* QgsSymbolLayerReference uses QgsSymbolLayer unique uuid identifier since QGIS 3.30, instead of the symbol
|
||||
* key (rule for QgsRuleBasedRenderer for instance) and index path, so this method migrates \a mapLayers old references
|
||||
* to new ones.
|
||||
* \since QGIS 3.30
|
||||
*/
|
||||
Q_DECL_DEPRECATED static void fixOldSymbolLayerReferences( const QMap<QString, QgsMapLayer *> &mapLayers );
|
||||
|
||||
///@cond PRIVATE
|
||||
#ifndef SIP_RUN
|
||||
static QgsProperty rotateWholeSymbol( double additionalRotation, const QgsProperty &property )
|
||||
|
@ -145,7 +145,7 @@ class TestSelectiveMasking(unittest.TestCase):
|
||||
cls.report != REPORT_TITLE):
|
||||
QDesktopServices.openUrl(QUrl("file:///{}".format(report_file_path)))
|
||||
|
||||
def get_symbollayer_ref(self, layer, ruleId, symbollayer_ids):
|
||||
def get_symbollayer(self, layer, ruleId, symbollayer_ids):
|
||||
"""
|
||||
Returns the symbol layer according to given layer, ruleId (None if no rule) and the path
|
||||
to symbol layer id (for instance [0, 1])
|
||||
@ -164,6 +164,14 @@ class TestSelectiveMasking(unittest.TestCase):
|
||||
symbol = symbollayer.subSymbol()
|
||||
symbollayer = symbol.symbolLayer(symbollayer_ids[i])
|
||||
|
||||
return symbollayer
|
||||
|
||||
def get_symbollayer_ref(self, layer, ruleId, symbollayer_ids):
|
||||
"""
|
||||
Returns the symbol layer according to given layer, ruleId (None if no rule) and the path
|
||||
to symbol layer id (for instance [0, 1])
|
||||
"""
|
||||
symbollayer = self.get_symbollayer(layer, rule, symbollayer_ids)
|
||||
return QgsSymbolLayerReference(layer.id(), symbollayer.id())
|
||||
|
||||
def check_renderings(self, map_settings, control_name):
|
||||
@ -265,7 +273,7 @@ class TestSelectiveMasking(unittest.TestCase):
|
||||
# simple ids
|
||||
mask_layer = QgsMaskMarkerSymbolLayer()
|
||||
mask_layer.setMasks([
|
||||
self.get_symbollayer_ref(self.lines_layer, "", [0]),
|
||||
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", [0])),
|
||||
QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some_id", [1, 3, 5, 19])),
|
||||
QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some_other_id", [4, 5])),
|
||||
])
|
||||
@ -273,8 +281,14 @@ class TestSelectiveMasking(unittest.TestCase):
|
||||
props = mask_layer.properties()
|
||||
|
||||
mask_layer2 = QgsMaskMarkerSymbolLayer.create(props)
|
||||
print(f"mask2={mask_layer2.masks()}")
|
||||
print("mask={}".format([
|
||||
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", [0])),
|
||||
QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some_id", [1, 3, 5, 19])),
|
||||
QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some_other_id", [4, 5])),
|
||||
]))
|
||||
self.assertEqual(mask_layer2.masks(), [
|
||||
self.get_symbollayer_ref(self.lines_layer, "", [0]),
|
||||
QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", [0])),
|
||||
QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some_id", [1, 3, 5, 19])),
|
||||
QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some_other_id", [4, 5])),
|
||||
])
|
||||
@ -313,6 +327,73 @@ class TestSelectiveMasking(unittest.TestCase):
|
||||
QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some other; id, lik;e, this", [4, 5])),
|
||||
])
|
||||
|
||||
def test_migrate_old_references(self):
|
||||
"""
|
||||
Since QGIS 3.30, QgsSymbolLayerReference has change its definition, so we test we can migrate
|
||||
old reference to new ones
|
||||
"""
|
||||
|
||||
# test label mask
|
||||
label_settings = self.polys_layer.labeling().settings()
|
||||
fmt = label_settings.format()
|
||||
# enable a mask
|
||||
fmt.mask().setEnabled(True)
|
||||
fmt.mask().setSize(4.0)
|
||||
# and mask other symbol layers underneath
|
||||
oldMaskRefs = [
|
||||
# the black part of roads
|
||||
QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("", [1, 0])),
|
||||
# the black jets
|
||||
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", [0])),
|
||||
QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", [0]))]
|
||||
fmt.mask().setMaskedSymbolLayers(oldMaskRefs)
|
||||
|
||||
label_settings.setFormat(fmt)
|
||||
self.polys_layer.labeling().setSettings(label_settings)
|
||||
|
||||
self.assertEqual([slRef.symbolLayerIdV2() for slRef in self.polys_layer.labeling().settings().format().mask().maskedSymbolLayers()],
|
||||
["", "", ""])
|
||||
self.assertEqual([slRef.symbolLayerId() for slRef in self.polys_layer.labeling().settings().format().mask().maskedSymbolLayers()],
|
||||
[slRef.symbolLayerId() for slRef in oldMaskRefs])
|
||||
|
||||
QgsSymbolLayerUtils.fixOldSymbolLayerReferences(QgsProject.instance().mapLayers())
|
||||
|
||||
self.assertEqual([QUuid(slRef.symbolLayerIdV2()).isNull() for slRef in self.polys_layer.labeling().settings().format().mask().maskedSymbolLayers()],
|
||||
[False, False, False])
|
||||
self.assertEqual([slRef.symbolLayerIdV2() for slRef in self.polys_layer.labeling().settings().format().mask().maskedSymbolLayers()],
|
||||
[self.get_symbollayer(self.lines_layer2, "", [1, 0]).id(),
|
||||
self.get_symbollayer(self.points_layer, "B52", [0]).id(),
|
||||
self.get_symbollayer(self.points_layer, "Jet", [0]).id()])
|
||||
self.assertEqual([slRef.symbolLayerId() for slRef in self.polys_layer.labeling().settings().format().mask().maskedSymbolLayers()],
|
||||
[QgsSymbolLayerId(), QgsSymbolLayerId(), QgsSymbolLayerId()])
|
||||
|
||||
# test symbol layer masks
|
||||
p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})
|
||||
self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))
|
||||
|
||||
circle_symbol = QgsMarkerSymbol.createSimple({'size': '10'})
|
||||
mask_layer = QgsMaskMarkerSymbolLayer()
|
||||
mask_layer.setSubSymbol(circle_symbol)
|
||||
oldMaskRefs = [QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("", [1, 0]))]
|
||||
mask_layer.setMasks(oldMaskRefs)
|
||||
|
||||
# add this mask layer to the point layer
|
||||
self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)
|
||||
|
||||
self.assertEqual([slRef.symbolLayerIdV2() for slRef in self.points_layer.renderer().symbol().symbolLayers()[1].masks()],
|
||||
[""])
|
||||
self.assertEqual([slRef.symbolLayerId() for slRef in self.points_layer.renderer().symbol().symbolLayers()[1].masks()],
|
||||
[slRef.symbolLayerId() for slRef in oldMaskRefs])
|
||||
|
||||
QgsSymbolLayerUtils.fixOldSymbolLayerReferences(QgsProject.instance().mapLayers())
|
||||
|
||||
self.assertEqual([QUuid(slRef.symbolLayerIdV2()).isNull() for slRef in self.points_layer.renderer().symbol().symbolLayers()[1].masks()],
|
||||
[False])
|
||||
self.assertEqual([slRef.symbolLayerIdV2() for slRef in self.points_layer.renderer().symbol().symbolLayers()[1].masks()],
|
||||
[self.get_symbollayer(self.lines_layer2, "", [1, 0]).id()])
|
||||
self.assertEqual([slRef.symbolLayerId() for slRef in self.points_layer.renderer().symbol().symbolLayers()[1].masks()],
|
||||
[QgsSymbolLayerId()])
|
||||
|
||||
def test_label_mask(self):
|
||||
# modify labeling settings
|
||||
label_settings = self.polys_layer.labeling().settings()
|
||||
|
Loading…
x
Reference in New Issue
Block a user