Message posté par : Feneck91 (schateau(a)laposte.net)
----------------------------------------
Bonjour à tous et à toutes.
Je suis développeur pour une grande société française.
L'entité dans laquelle je travaille réalise des cartes, l'outils QGIS (3.x) est
utilisé.
Nous utilisons une base de données postgresql afin de gérer les donnés vectorielle. Un
besoin a été de réaliser un plugin afin de sélectionner les données intéressantes à
consulter.
Autre point : la capacité de faire un export afin de mettre tout un projet sur un clef ou
disque dur afin de travailler de chez soi.
Ceci fonctionne plutôt bien. On peut même y intégrer les rasters mais ce n'est pas
très rapide et je pense que les pyramides ne sont pas calculées.
La chose que je voudrais faire, c'est que, pour le format GeoPackage, je puis intégrer
toutes les layers mais aussi la symbologie.
Actuellement j'exporte le fichier projet QGS et je le patch pour que ça fonctionne.
Mais, on peut intégrer la symbologie dans le géopackage, hélas rien ne semble fonctionner
et mes multiples recherches sur internet n'ont rien données.
Je vous livre une partie du programme python.
Ça pourrait même vous servir...
Le code "options.symbologyExport = QgsVectorFileWriter.FeatureSymbology" semble
ne rien faire.
Normalement, lorsque la symbologie est présente, un table nommée "layer_styles"
doit être présente.
-----------------
Code :
#===========================================================================================================================
# Sauve le document.
#
# Exemple : self.saveDocument(documentQLR,
"D:\Dev\GeoMaps\QGisPlugins\DMDatabaseVector\Datas\Template\VN2SHP_Template_cpy.qgs")
#===========================================================================================================================
def saveDocument(self, documentQLR, strPath):
fileSave = QFile(strPath)
if fileSave.open(QIODevice.WriteOnly | QIODevice.Text):
ouStream = QTextStream(fileSave)
ouStream.setCodec("UTF-8")
ouStream 0:
try:
QSettings().setValue(f"DMDatabaseVector/Settings/ExportFilePath", strFileName)
bAlwaysCopyRasterFiles =
QSettings().value(f"DMDatabaseVector/Settings/AlwaysCopyRasterFiles", True,
type=bool)
self.CreateSplashScreen(self.CONST_SPLASH_SCREEN_EXPORT,
self.tr('Exporting project file...'))
pInstance = QgsProject.instance()
strCurrentFilePath = pInstance.absoluteFilePath()
isDirty = pInstance.isDirty()
# Suppression si déjà existant
if os.path.isfile(strFileName):
os.remove(strFileName)
pInstance.write(strFileName)
pInstance.setFileName(strCurrentFilePath)
pInstance.setDirty(isDirty)
if os.path.isfile(strFileName):
fileQLR = QFile(strFileName)
self.SetSplashScreeMessageAndValue(9.99, self.tr('Reloading
exported project file, please wait...'))
if fileQLR.open(QIODevice.ReadOnly | QIODevice.Text):
if self.__CancelLoading:
raise UserWarning(self.tr('Operation canceled by
user!'))
documentQLR = QDomDocument()
bLoadOK = documentQLR.setContent(fileQLR)
fileQLR.close()
if bLoadOK:
layers = QgsProject.instance().mapLayers() # dictionary
strFolderPath = os.path.dirname(strFileName)
strFileNameWithoutExtension =
os.path.splitext(os.path.basename(strFileName))[0]
iNumLayerSaved = 0
iNbLayers = len(layers.values())
iNumLayerSaved, isUniqueFileCreated =
self.RecursiveExportQGS(documentQLR, bAlwaysCopyRasterFiles, lambdaFormatLayerName,
strFolderPath, strFileNameWithoutExtension, strFormat, strExtension, bOnlyOneFile, [],
iNbLayers, 0, False, {}, None)
self.SetSplashScreeMessageAndValue(94.0, self.tr('Saving
exported project file...'))
self.saveDocument(documentQLR, strFileName)
self.SetSplashScreeMessageAndValue(100.0,
self.tr('Done.'))
self.ManagesplashEvents(1.5)
bExportOK = True
else:
raise Exception(self.tr("Bad format for the exported
project file '{}'!").format(strFileName))
else:
raise Exception(self.tr("The exported project file
'{}' cannot be read!").format(strFileName))
else:
raise Exception(self.tr("The exported project file '{}'
cannot be found!").format(strFileName))
except UserWarning as ex:
self.CloseSplashScreen()
QMessageBox.warning(self, self.tr("Plugin"),
"{}".format(ex), QMessageBox.Ok)
except Exception as ex:
self.CloseSplashScreen()
QMessageBox.critical(self, self.tr("Exception"),
self.tr("Exception raised: {}").format(ex), QMessageBox.Ok)
finally:
self.CloseSplashScreen()
if bExportOK:
if QMessageBox.Yes == QMessageBox.question(self, self.tr("Loading
exported project ?"), self.tr("Would you like to load the exported project now
?nnBe careful, you will lose all actual project content!"), QMessageBox.Yes,
QMessageBox.No):
QgsProject.instance().clear()
QgsProject.instance().read(strFileName)
self.close()
#===========================================================================================================================
# Export chaque layer dans un fichier.
#
# documentQLR : document contenant le fichier qgs, permet de patcher les sources
(fichiers au lieu de BDD)
# bAlwaysCopyRasterFiles : indique si les rasters doivent toujours être copiés ou pas
# lambdaFormatLayerName : permet de formatter le nom de la layer à mettre dans
"source" et "datasource"
# strFolderPath : chemin racine où sauver la donnée
# strFileName :nom du fichier sans l'extension, non utilisé si sauvegarde en multi
fichier
# strFormat : format d'exportation
# strExtension : extension (sans le '.')
# bOnlyOneFile : sauvegarde dans un seul fichier ou en multi-fichier (un par layer)
# lstArboFolders : liste de l'arborescence des répertoires
# iNbLayersToSave : Nombre de layers à sauver (pour calculer le % d'avancement)
# iNumLayerSaved : Nombre de layers déjà sauvées (pour calculer le %
d'avancement)
# isUniqueFileCreated : Ne vaut true QUE lorsque le fichier unique a été créé. En
effet si la première couche a créer est un raster que l'option bAlwaysCopyRasterFiles
est true, à la deuxième layer à créer le iNumLayerSaved sera = 1 et les
# options de création ne seront pas corrects.
# dicLayersName : Nom des layers déjà sauvées, utilisé lorsque bOnlyOneFile est True
pour ne pas réutiliser le même nom deux fois dans un fichier unique
# layerRoot : Noeud racine (appel récursif)
#===========================================================================================================================
def RecursiveExportQGS(self, documentQLR, bAlwaysCopyRasterFiles,
lambdaFormatLayerName, strFolderPath, strFileName, strFormat, strExtension, bOnlyOneFile,
lstArboFolders, iNbLayersToSave, iNumLayerSaved, isUniqueFileCreated, dicLayersName,
layerRoot = None):
layerRoot = layerRoot if not (layerRoot is None) else
QgsProject.instance().layerTreeRoot()
for layerTreeNode in layerRoot.children():
if self.__CancelLoading:
raise UserWarning(self.tr('Operation canceled by user!'))
if QgsLayerTree.isLayer(layerTreeNode):
layer = None
try:
layer = layerTreeNode.layer()
except Exception as ex:
try:
for layerNodeBlc in
QgsProject.instance().layerTreeRoot().findLayers():
if layerNodeBlc.name() == layerTreeNode.name() and
layerNodeBlc.parent().name() == layerTreeNode.parent().name():
layer = layerNodeBlc.layer()
break
except Exception as ex:
# La layer est vide, n'existe pas, il faut l'ignorer
pass
# Initialiser les options
options = QgsVectorFileWriter.SaveVectorOptions()
options.driverName = strFormat
options.fileEncoding = "utf-8"
if not(layer is None) and layer.isValid() and (isinstance(layer,
QgsVectorLayer) or isinstance(layer, QgsRasterLayer)):
# Mettre à jour la splash screen
self.SetSplashScreeMessageAndValue(10.0 + iNumLayerSaved * (80.0 /
iNbLayersToSave), self.tr('Exporting layer {}...').format(layer.name()))
# Calcul layer name
strLayerNameIntoDic = ""
if bOnlyOneFile :
# Calculer le nom de la layer
options.layerName = ".".join(lstArboFolders) +
("" if len(lstArboFolders) == 0 else ".") +
"_".join(layer.name().split(' '))
iIndexLayerNameSame = 2
# Faire en sorte qu'elle soit unique
while options.layerName in dicLayersName:
strLayerName = (".".join(lstArboFolders) +
("" if len(lstArboFolders) == 0 else ".") +
"_".join(layer.name().split(' '))) +
f"~{iIndexLayerNameSame}"
iIndexLayerNameSame += 1
strLayerNameIntoDic = options.layerName
else:
options.layerName = "_".join(layer.name().split('
'))
iIndexLayerNameSame = 2
# Faire en sorte qu'elle soit unique
while os.path.join(*lstArboFolders, options.layerName) in
dicLayersName:
strLayerName = "_".join(layer.name().split('
')) + f"~{iIndexLayerNameSame}"
iIndexLayerNameSame += 1
strLayerNameIntoDic = os.path.join(*lstArboFolders,
options.layerName)
# Récupération des informations dans le fichier qgs
strInfoLayerDatas = None
qDomNodeCurrent =
documentQLR.documentElement().firstChildElement("projectlayers")
if not qDomNodeCurrent.isNull():
qDomNodeCurrent =
qDomNodeCurrent.firstChildElement("maplayer")
if not qDomNodeCurrent.isNull() :
qDomNodeCurrent =
self.findChildNodeFromAttributeOrNode(qDomNodeCurrent, "id", layer.id(), False)
if not qDomNodeCurrent.isNull():
qDomElementDataSource =
qDomNodeCurrent.firstChildElement("datasource")
qDomElementProvider =
qDomNodeCurrent.firstChildElement("provider")
if not (qDomElementDataSource.isNull()) :
if not (qDomElementProvider.isNull()):
strInfoLayerDatas =
qDomElementProvider.firstChild().nodeValue() + "¤" +
qDomElementDataSource.firstChild().nodeValue()
else:
raise Exception(self.tr("Dom node
'{}' not found for layer = '{}' / id =
'{}'!").format("provider", layer.name(), layer.id()))
else:
raise Exception(self.tr("Dom node '{}'
not found for layer = '{}' / id =
'{}'!").format("datasource", layer.name(), layer.id()))
else:
raise Exception(self.tr("Dom node '{}' not
found for layer = '{}' / id = '{}'!").format("id",
layer.name(), layer.id()))
else:
raise Exception(self.tr("Dom node '{}' not found
for layer = '{}' / id = '{}'!").format("maplayer",
layer.name(), layer.id()))
else:
raise Exception(self.tr("Dom node '{}' not found for
layer = '{}' / id = '{}'!").format("projectlayers",
layer.name(), layer.id()))
for key, val in dicLayersName.items():
if val[0] == strInfoLayerDatas:
strLayerNameIntoDic = None # On réutilise la même layer que
celle qui existe déjà avec les mêmes attributs
strDomElementSource = val[1]
break
if not(strLayerNameIntoDic is None):
# Vérifier l'arborescence et créer les répertoires
if not(bOnlyOneFile):
for iIndex, folderName in enumerate(lstArboFolders):
path = os.path.join(strFolderPath, *[lstArboFolders for i
in range(0, iIndex)])
if os.path.isfile(path):
os.remove(path)
path = os.path.join(strFolderPath, *lstArboFolders)
if not(os.path.isdir(path)):
os.makedirs(path)
if isinstance(layer, QgsVectorLayer):
# Calcul des fichiers / répertoires relatifs
strFileNameOnly = strFileName + "." + strExtension
if bOnlyOneFile else options.layerName + "." + strExtension
strFileNameRelativePathOnly = strFileNameOnly if bOnlyOneFile
else os.path.join(*lstArboFolders, strFileNameOnly)
strFilePath = os.path.join(strFolderPath,
strFileNameRelativePathOnly)
elif isinstance(layer, QgsRasterLayer):
if bOnlyOneFile and not bAlwaysCopyRasterFiles:
# Copier dans le fichier unique
strFileNameOnly = strFileName + "." +
strExtension if bOnlyOneFile else options.layerName + "." + strExtension
strFileNameRelativePathOnly = strFileNameOnly if
bOnlyOneFile else os.path.join(*lstArboFolders, strFileNameOnly)
strFilePath = os.path.join(strFolderPath,
strFileNameRelativePathOnly)
else:
# Copier le fichier source vers la destination
strFileNameOnly = os.path.basename(layer.source())
strFileNameRelativePathOnly =
os.path.join(*lstArboFolders, strFileNameOnly)
strFilePath = os.path.join(strFolderPath,
strFileNameRelativePathOnly)
path = os.path.join(strFolderPath, *lstArboFolders)
if not(os.path.isdir(path)):
os.makedirs(path)
if not(bOnlyOneFile) or iNumLayerSaved == 0:
if os.path.isfile(strFilePath):
os.remove(strFilePath)
if bOnlyOneFile and isUniqueFileCreated:
options.actionOnExistingFile =
QgsVectorFileWriter.CreateOrOverwriteLayer
# Exportation
if isinstance(layer, QgsVectorLayer):
strDomElementSource = "./" +
strFileNameRelativePathOnly + lambdaFormatLayerName(layer, options.layerName)
# QgsVectorFileWriter.SymbologyExport =
QgsVectorFileWriter.NoSymbology ---------------->
https://qgis.org/pyqgis/master/core/QgsVectorFileWriter.html#qgis.core.QgsV…
options.symbologyExport = QgsVectorFileWriter.NoSymbology
writeResult, errorMessage =
QgsVectorFileWriter.writeAsVectorFormat(layer, strFilePath, options)
isUniqueFileCreated = True # Une fois passé ici, vaut toujours
TRUE
if writeResult != 0:
raise Exception(self.tr("Error while writing vector
layer '{}' / id = '{}': '{}'").format(layer.name(),
layer.id(), errorMessage))
strProvider = "ogr"
elif isinstance(layer, QgsRasterLayer):
strProvider = "gdal"
if bOnlyOneFile and not bAlwaysCopyRasterFiles:
strDomElementSource = strFormat + ":./" +
strFileNameRelativePathOnly + lambdaFormatLayerName(layer, options.layerName)
pipe = QgsRasterPipe()
provider = layer.dataProvider()
projector = QgsRasterProjector()
projector.setCrs(provider.crs(), provider.crs())
if pipe.set(provider.clone()) and pipe.insert(2,
projector):
fw = QgsRasterFileWriter(strFilePath)
fw.setOutputFormat(strFormat)
fw.setCreateOptions(["RASTER_TABLE=" +
options.layerName, 'APPEND_SUBDATASET=' + ('YES' if OnlyOneFile and
isUniqueFileCreated else 'NO'), 'TILE_FORMAT=PNG']) #
'TILING_SCHEME=GoogleMapsCompatible'
if fw.writeRaster(pipe,
provider.xSize(),provider.ySize(),provider.extent(),provider.crs()) !=
QgsRasterFileWriter.NoError:
raise Exception(self.tr("Error while writing
raster layer '{}' / id = '{}'!").format(layer.name(), layer.id()))
isUniqueFileCreated = True # Une fois passé ici, vaut
toujours TRUE
else:
raise Exception(self.tr("Error while preparing
writing raster layer '{}' / id = '{}'!").format(layer.name(),
layer.id()))
else:
# Copie du raster source au même endroit que la
destination
source = QFileInfo(layer.source())
strDomElementSource = "./" +
strFileNameRelativePathOnly
if source.exists():
QFile.copy(source.absoluteFilePath(),strFilePath)
else:
raise Exception(self.tr("Error while writing
raster layer '{}' / id = '{}': file '{}' doesn't
exists!").format(layer.name(), layer.id(), source.absoluteFilePath()))
dicLayersName[strLayerNameIntoDic] = [strInfoLayerDatas,
strDomElementSource]
# Patch du fichier qgs
# Patch 1 : projectlayers
qDomElementDataSource.firstChild().setNodeValue(strDomElementSource)
qDomElementProvider.firstChild().setNodeValue(strProvider)
# Patch 2 : layer-tree-group
qDomNodeCurrent =
documentQLR.documentElement().firstChildElement("layer-tree-group")
if not qDomNodeCurrent.isNull():
qDomNodeCurrent = self.findInLayerTreeGroup(qDomNodeCurrent,
"id", layer.id())
if not qDomNodeCurrent.isNull():
qDomAttributeSource =
self.getNodeAttribute(qDomNodeCurrent, "source")
qDomAttributeProviderKey =
self.getNodeAttribute(qDomNodeCurrent, "providerKey")
if not (qDomAttributeSource.isNull()) :
if not (qDomAttributeProviderKey.isNull()):
qDomAttributeSource.setNodeValue(strDomElementSource)
qDomAttributeProviderKey.setNodeValue(strProvider)
else:
raise Exception(self.tr("Dom attribute
'{}' not found for layer = '{}' / id =
'{}'!").format("providerKey", layer.name(), layer.id()))
else:
raise Exception(self.tr("Dom attribute '{}'
not found for layer = '{}' / id = '{}'!").format("source",
layer.name(), layer.id()))
else:
raise Exception(self.tr("Dom node '{}' not found
for layer = '{}' / id = '{}'!").format("layer-tree-group",
layer.name(), layer.id()))
else:
raise Exception(self.tr("Dom node '{}' not found for
layer = '{}' / id = '{}'!").format("layer-tree-group",
layer.name(), layer.id()))
iNumLayerSaved += 1
elif QgsLayerTree.isGroup(layerTreeNode):
lstArboFoldersCopy = copy.deepcopy(lstArboFolders)
lstArboFoldersCopy.append(layerTreeNode.name())
iNumLayerSaved, isUniqueFileCreated = self.RecursiveExportQGS(documentQLR,
bAlwaysCopyRasterFiles, lambdaFormatLayerName, strFolderPath, strFileName, strFormat,
strExtension, bOnlyOneFile, lstArboFoldersCopy, iNbLayersToSave, iNumLayerSaved,
isUniqueFileCreated, dicLayersName, layerTreeNode)
return iNumLayerSaved, isUniqueFileCreated
#===========================================================================================================================
# Retrouver le noeud dont sont attribue dont le nom est passé en paramètre est égla à
la valeur recherchée.
#
# qDomNode : de type QDomNode, est le noeud parent contenant les sous-noeuds.
# strAttributeName : nom du sous-noeud a rechercher.
# Retourne la valeur du sous-noeud, None si non trouvé.
#===========================================================================================================================
def findInLayerTreeGroup(self, qDomNode, strAttributeName, strAttributeValue):
while not qDomNode.isNull():
nodeAttribute =
self.findChildNodeFromAttributeOrNode(qDomNode.firstChildElement("layer-tree-layer"),
strAttributeName, strAttributeValue, True)
if nodeAttribute.isNull():
# Look in children
nodeAttribute =
self.findInLayerTreeGroup(qDomNode.firstChildElement(qDomNode.nodeName()),
strAttributeName, strAttributeValue)
if not(nodeAttribute.isNull()):
qDomNode = nodeAttribute
break
qDomNode = qDomNode.nextSiblingElement(qDomNode.nodeName())
return qDomNode
-----------------
----------------------------------------
Le message est situé
https://georezo.net/forum/viewtopic.php?pid=322640#p322640
Pour y répondre : qgis_fr(a)ml.georezo.net ou reply de votre messagerie
Pour vous désabonner connectez-vous sur le forum puis Profil / Abonnement
--
Association GeoRezo - le portail géomatique
https://georezo.net