L’idée de créer ce blog est parti du déficit de valorisation des contributions que j’ai pû faire ou financer autour de QGIS.
Commençons donc par l’histoire d’un plugin, simple au départ, qui nous a amené à modifier le moteur de rendu de QGIS et étendre beaucoup les fonctionnalités initiales.
Une question récurrente sur les forum d’aide géomatique est donc:
» Comment puis-je détourer un territoire pour masquer les contours en dehors de cette zone, et n’afficher les étiquettes que pour les objets dans ce territoire? »
ça, par exemple:
Le monde de Mapinfo nous avait habitué au bon vieux et frustrant « Pochoir », lent, qui coupait les objets, et masquait absolument les objets en dehors, y compris les étiquettes qui débordent.
La première chose qui m’a toujours choqués sur une carte, c’est de cacher le territoire auquel appartient notre zone d’intérêt. La beauté de la lecture de carte, c’est de comprendre le territoire dans lequel on vit. Et la réalité, comme le nuage de Tchernobyl, ne s’arrête pas à nos frontières de papier.
Donc, exit le pochoir, bienvenue la couche de masquage avec transparence, modes de fusions, lissages et ombrages. Pouvoir mettre en lumière le contexte géographique d’un territoire, mais en le mettant mieux en valeur, sans complètement masquer le reste.
Depuis QGIS 2.4, il est possible de faire énormément de choses avec des expressions et des variables, mais ces solutions sont longues à configurer, et pas forcément à la portée du premier venu. Et notre objectif à l’agence de l’eau était de rendre ça accessible à tous.
Nous avons donc réalisé une première version d’extension QGIS, le plugin Mask, surtout pour se faire la main en python. Une sélection polygonale, on fusionne les objets, on fait un très grand carré autour, et hop on fait un trou… Basique, efficace, pas vraiment beau.
Mais zoomer sur la couche.. nous envoie à l’échelle européenne. Et les objets tombent parfois en dehors des zones de validité des projections de la carte..
On passe à la V2.
Idée brillante de Xavier, mon collègue: « Pourquoi ne pas faire un mode de rendu qui représente les objets inversés, en représentant le polygone univers, sans bricoler les géométries? »
Oui, mais c'est une évolution du core de QGIS en C++...
Et la magie de l’Open Source opère. Un cahier des charges de deux pages, un marché avec société spécialisée qui va bien, et c’est parti.
Faire un masque ne nécessite ainsi aucun plugin. On sauvegarde dans sa bibliothèque de styles un symbole inversé joli, on filtre la couche, et c’est fait!
Coup de chance, ou plutôt inspiration réciproque, Nyall Dawson ajoute en même temps le mode de rendu de bordure de polygone dégradés:
et ça donne vraiment bien avec notre rendu par polygone inversé:
C'est bien, mais les étiquettes en dehors du masque, c'est vraiment illisible
Comme on a des expressions un peu partout, on peut alors imaginer filtrer les étiquettes sur la base d’une géométrie en mémoire.
Là on a eu un peu peur que des intersections spatiales à la volée sur des objets géographiques soient vraiment très peu performantes. Donc, on a décidé de tester la preuve du concept dans un plugin python, quitte à proposer ça dans le core plus tard si c’est validé.
Avant, avec les étiquettes partout:
Après:
Wonderfull, j’en ai rêvé depuis mes débuts en SIG, ça fait du bien de se sentir un peu capable d’influencer les choses.
Comment ça marche ?
Voilà la solution technique trouvée avec Oslandia (merci Hugo et Vincent):
- le plugin Mask ajoute deux choses au moteur d’expression:
- une variable contenant la géométrie de l’objet Masque courant. (on a donc un seul masque actif à la fois)
- une expression qui renvoie 0 ou 1 selon que l’objet cartographique évalué est dans le masque ou non. in_mask(srid). L’expression spatiale étant variable suivant les types d’objets, et la vitesse d’execution étant plus rapide avec des centroides, on donne le choix à l’utilisateur de choisir ses opérateurs
- et aussi une interface qui permet d’activer le masque, choisir et changer styles et paramètres, activer ou non les filtres d’étiquettes sur les couches du projet, sauver en fichier ou en mémoire…
L’expression in_mask se positionne dans les paramètres contrôlés par expression d’étiquetage de chaque couche à filtrer. Au passage, nous avons découvert que l’expression Rendu/Afficher l’étiquette permet d’alléger le moteur d’étiquetage en n’envoyant pas cette étiquette dans le calcul de conflit d’étiquettes, ce qui paradoxalement va accélerer l’étiquetage dans bon nombre de cas. Youpi!
Une petite démonstration valant mieux qu’un long discours:
OK, chouette, mais moi j’ai besoin de générer 250 cartes de communes avec un masque, vous savez faire? (on a des utilisateurs exigeants ici)
Ah, ça tombe bien, notre première contribution était un cofinancement de l’Atlas de QGIS, on imaginait donc bien que c’était faisable.
On ajoute donc quelques évènements (signaux Qt) dans QGIS qui seront captés par le plugin pour modifier à la volée la géométrie de l’objet Atlas, tout en gardant ses propriétés. A partir du moment ou un masque est actif, il n’y a donc rien de plus à faire. Si on ne vaut pas de masquage d’Atlas, on enlève cette couche du projet.
Dans les premières versions du plugin qui ont introduit ces fonctionnalités, nous utilisions une couche temporaire spécifique à l’Atlas… ce fut une MAUVAISE IDEE… Le composeur de carte est en effet généralement utilisé en verrouillant les couches de chaque carte, et donc la couche de masque temporaire n’en fait pas partie, et donc… pas de masque dans le composeur. Mais c’est corrigé en version 1.5.
Il reste encore des cas mal maitrisés, comme le renommage de couche qui désactive le mask à la réouverture du projet, ou encore l’obligation d’utiliser le plugin Memory Layer Saver pour sauver les couches mémoire avec le projet.
N’hésitez pas à signaler les soucis sur le dépôt github:
https://github.com/aeag/mask/issues
La documentation est là aussi, dans le wiki
https://github.com/aeag/mask/wiki