{"word_count": 1065, "character_count": 8066}
Dans cet article, je vous parlerai d'un petit script QGIS python permettant de présenter sur votre carte l'étendue de vos cartes à imprimer. Ce sera aussi l'occasion de voir comment créer un traitement one-click pour la boîte à outils traitement.J'habite une région magnifique, la Provence, et voilà un certain temps que je prépare mes randos du week end grâce à QGIS.D'ailleurs, si vous ne le savez pas déjà, sachez qu'il est possible de bénéficier des cartes IGN dans QGIS sans clé avec cette adresse-là :&#160;https://wxs.ign.fr/pratique/geoportail/wmts?SERVICE=WMTS&#38;REQUEST=GetCapabilities(cf cet article sur le site de l'IGN)J'ai une petite couche de données appelée tracés qui me permet de tracer mes futures randos, et de suivre celles que j'ai réalisées. J'ai aussi une couche de points qui me permet de marquer des secteurs intéressants.Je pourrais utiliser geoportail.gouv.fr pour imprimer mes cartes, mais l'avantage que je vois avec QGIS est de vraiment pouvoir contrôler l'aspect de mes cartes, leur échelle d'impression. Je peux par exemple superposer une grille de 1 km de côté comme sur les cartes randos de l'IGN que l'on trouve chez le libraire.&#160;Cela s'avère très pratique une fois en balade.Le souci, c'est que comme cela fait un certain temps que je crée mes cartes de randos sous QGIS, je me retrouve avec un nombre incalculable de composeurs d'impression (appelés composers sous QGIS 2 et print layouts sous QGIS 3)Il s'avère que toutes les cartes de rando que j'ai exportées correspondent à des randos que j'ai réalisées. Autrement dit, si je pouvais représenter sur ma carte l'étendue de toutes ces cartes, cela me permettrait de me rappeler des secteurs où j'ai marché dans la région.&#160;Il y a d'autres cas où cela peut être utile, dans un cadre plus professionnel. Imaginez par exemple un agent de votre organisation qui aurait créé des cartes à tout va pour préparer une mission terrain, mais pour lequel il se révèlerait assez dur quelques mois plus tard de vous dire sur une carte les zones sur lesquelles il aurait travaillé.En fait, il suffirait de créer une couche de polygones rectangulaires récupérés depuis l'étendue de mes cartes pour voir ces zones.Le code pythonS'il n'existe pas de fonction toute prête sous QGIS pour reporter dans une couche les polygones rectangulaires correspondant aux étendues des cartes à imprimer, nous avons quand même accès à&#160; toutes les fonctions de l'API QGIS.Si vous voulez tester votre code python, ce que vous pouvez tout simplement faire, c' est d'ouvrir la console python et d'y créer un petit script (en allant dans le menu Extension &#62; Console Python). Voici le code que vous pourrez mettre dans la console ou dans votre script de console, pour récupérer dans une couche mémoire l'étendue de vos cartes (valable pour QGIS3).def getCompositionExtents(crsCode=2154) :        # layer    vl = QgsVectorLayer("Polygon?crs=EPSG:%s"%(crsCode), "Extent Polygons", "memory")    # defining CRS    crs = vl.crs()    crs.createFromId(crsCode)    vl.setCrs(crs)        # data provider    pr = vl.dataProvider()    # start editing    vl.startEditing()        # add attributes    vl.addAttribute(QgsField("name", QVariant.String))    # get compositions    projectInstance= QgsProject.instance()    projectLayoutManager = projectInstance.layoutManager()    comps = projectLayoutManager.printLayouts()    # iterate over compositions    feats = list()    for comp in comps :        nom = comp.name()        print(nom)        for item in comp.items() :            if (isinstance(item, QgsLayoutItemMap)) :                print("toto")                print(item)                e = item.extent()                feat = QgsFeature()                feat.setGeometry(QgsGeometry.fromRect(e))                feat.initAttributes(1)                feat.setAttribute(0, QVariant(nom))                feats.append(feat)    # add features    pr.addFeatures(feats)    # commit changes    vl.commitChanges()        return(vl)    def addLayerToCanvas(vl) :    # add layer to canvas    QgsProject.instance().addMapLayer(vl, False)    layerTree = iface.layerTreeCanvasBridge().rootGroup()    layerTree.insertChildNode(-1, QgsLayerTreeLayer(vl))        vl = getCompositionExtents() # create layer from extentsaddLayerToCanvas(vl) # add layer to canvasPour lancer le script, cliquez juste sur le petit triangle vertEt voilà, cela devrait vous créer une couche avec l'étendue des cartes à imprimer de votre projetDécodageVoici les éléments clés du code :projectInstance= QgsProject.instance()projectLayoutManager = projectInstance.layoutManager()comps = projectLayoutManager.printLayouts()Ce code permet de récupérer les print layouts dans une variable appelée compsfeats = list()for comp in comps :    nom = comp.name()    for item in comp.items() :        if (isinstance(item, QgsLayoutItemMap)) :            e = item.extent()            feat = QgsFeature()            feat.setGeometry(QgsGeometry.fromRect(e))            feat.initAttributes(1)            feat.setAttribute(0, QVariant(nom))            feats.append(feat)Ici, on itère à travers toutes les compositions. Pour chaque composition, on aura un objet, une feature. feat.setGeometry(QgsGeometry.fromRect(e)) permet d'affecter une géométrie à la feature depuis le rectangle formé par l'étenduefeat.initAttributes(1)feat.setAttribute(0, QVariant(nom))permet d'attribuer la variable nom (qui comprend le nom de la composition) à la première colonnedef addLayerToCanvas(vl) :    # add layer to canvas    QgsProject.instance().addMapLayer(vl, False)    layerTree = iface.layerTreeCanvasBridge().rootGroup()    layerTree.insertChildNode(-1, QgsLayerTreeLayer(vl))Cette fonction permet juste d'ajouter la couche créée artificiellement au canevas, soit, à la carteCréation d'un traitement python pour la boîte à outils TraitementExécuter un script depuis la console python n'est pas la chose la plus élégante à faire. Il y a deux externalités possibles à ce bout de code, voire même trois. Les voici dans un ordre de difficulté d'implémentation croissante :Créer une macro à l'ouverture du projet : automatiquement, une couche se crée à l'ouverture du projetCréer un traitement pour la boîte à outils TraitementCréer un pluginDans mon cas, j'ai préféré créer un traitement QGIS. Un plugin serait un peu trop ambitieux pour ce cas d'usage, quoiqu'un bouton dans l'interface aurait pu être utile.Pour créer le traitement, créez un nouveau script, par exemple depuis un modèle :Vous trouverez sur ce lien le code complet pour créer un traitement pour la boîte à outils TraitementCela créera une rubrique My Scripts dans la boîte à outils Traitement.Cela apparaîtra de cette façon-là quand vous l'exécuterez :DécodageDans le script, les fonctions name, displayName, group, groupId permettent de paramétrer la façon dont le script apparaîtra dans la boîte à outils Traitement.(sink, dest_id) = self.parameterAsSink(    parameters,    self.OUTPUT,    context,    flds,    3,   crs)permet de paramétrer les méta-data de la couche. 3 est le type géométrique de la couche. fldset crs sont les champs et système de projection de la couchefeats  = self.fGetExtentFeaturesFromPrintLayouts()for feat in feats :    sink.addFeature(feat, QgsFeatureSink.FastInsert)    # Return the results of the algorithmreturn {self.OUTPUT: dest_id}calcule les features associées aux étendues (voir fonction créée précédemment) et les met dans le sink. Note : pour les fonctions qui sont personnelles, je rajoute le petit préfixe f afin de bien les dissocier des fonctions QGIS du modèle.Il y a vraiment beaucoup à faire et de quoi s'amuser quand on plonge dans l'API et QGIS offre plein de voies possibles afin d'implémenter des traitements originaux.Et après ?Nous pourrions imaginer rapatrier le nom des communes intersectées par ces rectangles, voir quelle surface cela représente en tout et révéler les zones de la région qui resteraient encore à explorer !Petit rappel des gists :Code à coller dans la console PythonCode pour créer le traitement dans la boîte à outils&#160;&#160;