Utilisation de WebGL pour le rendu de cartes dans OpenLayers, partie 1
Olivier Guyot
Aujourd'hui, nous allons parler de cartes interactives sur le web. Il existe de nombreuses options pour créer de belles cartes avec des fonctionnalités intéressantes et un contenu attrayant. Dans cet article, nous allons nous concentrer sur les cartes basées sur des données vectorielles, créées à l'aide d'OpenLayers.
Plus précisément, nous allons examiner comment OpenLayers évolue actuellement pour utiliser l'API WebGL afin d'offrir des performances de rendu bien meilleures, et ce qui se passe exactement dans le code de rendu. C'est parti ! More specifically, we are going to look into how OpenLayers is currently evolving to use the in order to offer much better rendering performance, and what exactly is going on inside the rendering code. Let’s dive in!
Un peu de contexte
OpenLayers est une bibliothèque populaire pour la création de cartes interactives. Elle existe depuis un certain temps maintenant, et a subi de nombreux remaniements et réécritures. Outre la création de cartes, OpenLayers fournit également de nombreux utilitaires pour transformer les données depuis et vers de nombreux formats tels que GeoJSON, GPX et Mapbox Vector Tiles, ainsi que pour interagir avec des protocoles standards tels que WMS, WFS, etc.
L'augmentation récente de l'utilisation des données vectorielles suite à l'apparition de moteurs de rendu performants tels que Mapbox a constitué un véritable défi pour OpenLayers : traditionnellement, on partait du principe que les grands ensembles de données vectorielles étaient convertis en images et servis sous forme de tuiles (par exemple via WMTS) ou d'images de taille personnalisée (via WMS). En tant que tel, le rendu de formes réelles au lieu d'images contenant lesdites formes ne se produisait pas très souvent, et pas dans des volumes trop élevés.
Le moteur de rendu vectoriel disponible dans OpenLayers n’est pas pris de court par cette tendance : il est hautement optimisé et peut gérer des milliers de formes complexes, simplifiant les lignes et les polygones en temps réel et utilisant un index spatial pour un filtrage rapide des géométries en dehors de la vue actuelle. Pourtant, lorsqu'il est mis à l'épreuve sur des ensembles de données très denses (typiquement, vector tiles), le moteur de rendu a du mal à suivre et les performances deviennent un facteur limitant dans de nombreuses situations.
Des solutions de contournement ont été trouvées, des optimisations ont été effectuées, et les gens peuvent toujours se débrouiller pour avoir des cartes interactives avec beaucoup de données vectorielles et continuer à compter sur OpenLayers comme ils le faisaient auparavant. Mais au bout du compte, le problème de fond est toujours présent : actuellement, le rendu des formes n'est tout simplement pas assez rapide.
Un voyage vers WebGL
Où nous en sommes
Comme c'est le cas actuellement et depuis un certain temps, il existe deux alternatives principales pour le rendu des graphiques sur une page Web : l'API Canvas et l'API WebGL.
OpenLayers a toujours été capable de s'appuyer sur différentes méthodes de rendu : dans le passé, il était capable de rendre des choses en utilisant SVG ou même le DOM, mais aujourd'hui, ses principales capacités de rendu reposent sur l'API Canvas. Cette API offre une excellente qualité visuelle, des résultats cohérents et un rendu de texte puissant. Son principal inconvénient est probablement le fait qu'elle est tout simplement trop lente pour les opérations complexes.
D'autre part, l'API WebGL a le potentiel d'être extrêmement rapide, mais elle est coûteuse à mettre en œuvre, très sujette aux erreurs et très, très mauvaise pour dessiner du texte. En fait, elle ne vous aidera absolument pas à dessiner du texte, et vous offrira le plaisir de devenir un expert en crénage, ligatures et autres joies de la vie.
L'implémentation de moteurs de rendu basés sur WebGL dans OpenLayers est un travail en cours depuis plusieurs années maintenant, et a montré quelques progrès impressionnants récemment. Pourtant, les moteurs de rendu classiques basés sur Canvas restent souvent l'option par défaut, offrant toutes les fonctionnalités et répondant à la majorité des cas d'utilisation.
Prochaine étape : pas (si) loin devant !
Actuellement, OpenLayers offre uniquement un moteur de rendu basé sur WebGL pour les points. Tout autre type de données vectorielles (lignes, polygones) doit passer par le moteur de rendu Canvas. Cela peut toujours être utile dans des scénarios tels que la visualisation de données, mais pas lorsqu'il s'agit de tuiles vectorielles complètes (par exemple les données OpenStreetMap).
La prochaine étape évidente est d'être capable d'effectuer le rendu de lignes et de polygones également ! Ces deux types de géométrie sont beaucoup plus difficiles à gérer que les points : WebGL ne propose pas de primitives de lignes ou de polygones, qui doivent donc être décomposées en triangles pour le rendu.
La bonne nouvelle est qu'il existe de nombreuses sources d'inspiration en ligne car il s'agit essentiellement d'un problème résolu (en quelque sorte) dans WebGL. D'autre part, le rendu de géométries sur une carte présente des défis différents de ceux d'un objet 3D classique, par exemple. Cela signifie que nous devons faire tout le travail nous-mêmes et ne pas dépendre d’outils existants! Mais où serait le plaisir sinon ?
Se mettre au travail
C'est la partie où cet article devient technique. Nous allons examiner la logique de rendu actuelle de WebGL et comment elle va évoluer.
Un point est un carré et vice versa
Comme d'autres types de géométries, les points doivent être décomposés en triangles pour être rendus.
L'approche habituelle consiste à construire un "quad" :
Vous voyez ? quatre sommets, deux triangles, un quad.
Cette configuration très simple nous donne une surface de travail pour représenter le point en utilisant n'importe quoi, des couleurs simples aux images réelles :
Cette approche présente plusieurs avantages : chaque point nécessite un nombre fixe de triangles, et la logique de génération des positions des sommets est triviale (ajout d'un décalage par rapport au centre du point).
Des données aux triangles
Les points doivent être lus à partir d'une source de données vectorielles ; ce type de source peut prendre ses données n'importe où : fichiers distants, services WFS, objets générés dynamiquement, etc.
En tant que telle, elle fournit l'abstraction parfaite pour le rendu d'objets d'origines diverses.
La classe responsable du rendu des points avec WebGL s'appelle WebGLPointsLayerRenderer et fonctionne comme suit :
Bien sûr, la réalité est un peu plus complexe, mais il s'agit d'une représentation précise du flux de travail général. Il est également intéressant de noter que l'étape de génération des triangles est effectuée dans un autre fil d'exécution (thread) afin de ne pas trop affecter la fluidité de l'expérience utilisateur.
Cela fonctionne pour les points et, pour être honnête, il n'y a aucune raison pour que cela ne fonctionne pas non plus pour les lignes ou les polygones ! Quelque chose comme ça, par exemple :
Il y a une étape supplémentaire ici, mais elle n'est pas trop complexe : discriminer les géométries par type et les stocker dans des listes séparées pour être traitées plus tard. C'est essentiel car chaque type de géométrie a ses propres spécificités.
Bon, c'est facile, non ?
Sauf qu'il y a aussi deux nouveaux processus dont nous n'avons pas parlé :
Ces deux-là, par contre, seront beaucoup plus impliqués. Voyons cela de plus près.
Finalement, un polygone ne serait-il pas un carré avec beaucoup de côtés ?
Disons-le franchement : pas vraiment. Les géométries polygonales peuvent compter des centaines ou des milliers de sommets et posséder des formes concaves complexes. Ils peuvent même avoir des trous !
La décomposition d'un polygone en triangles (également appelée triangulation) est un sujet complexe qui fait l'objet de recherches depuis des décennies. Plusieurs algorithmes ont été inventés avec des résultats et des forces variables. Dans notre cas, nous avons besoin d'un algorithme qui privilégie la vitesse à la précision et qui puisse traiter les polygones complexes avec des trous. Heureusement, il existe une bibliothèque Open Source qui fait exactement cela !
Cette bibliothèque fait en fait partie du moteur de rendu Mapbox-gl-js dont j'ai parlé précédemment, et c'est l'une des raisons pour lesquelles il est si performant. Réimplémenter un algorithme complexe comme celui-ci n'apporterait probablement aucun avantage, et la bibliothèque dispose d'une licence permissive, il n'y a donc pas grand-chose à craindre.
Voyons donc à quoi ressemble la triangulation d'un polygone :
Ce que nous appelons un polygone est généralement défini par un seul anneau extérieur et de zéro à plusieurs anneaux intérieurs. L'anneau extérieur est la limite du polygone et les anneaux intérieurs définissent les trous dans le polygone.
Comme nous l'avons vu précédemment, une forme d'anneau (qui est essentiellement une ligne fermée) ne peut pas être utilisée immédiatement pour dessiner une zone délimitée en utilisant WebGL : nous devons trianguler. Le schéma présenté ci-dessus montre comment un polygone très simple est dessiné, mais en réalité les choses peuvent devenir beaucoup plus corsées (cette image est un exemple tiré de la bibliothèque mapbox/earcut).
Tout compte fait, ce n'était pas trop difficile, me direz-vous ! Je suis d'accord, c'était en fait la partie la plus facile. Pour les lignes, nous devons nous débrouiller seuls. C'est pourquoi nous ne les aborderons pas dans cet article mais dans la partie 2 !
A venir
Dans cet article, nous avons jeté un coup d'œil à ce qui se passe dans le moteur de rendu d'une bibliothèque comme OpenLayers, au moins pour les points et les polygones. Dans la partie 2, nous essaierons de nous familiariser avec les lignes et leurs nombreux pièges, et nous jetterons un coup d'œil à l'implémentation réelle avec une démo en direct. Bon codage !
Pour plus d'informations,
n'hésitez pas à prendre contact avec nous !
Carrière
Vous souhaitez travailler dans un environnement inspirant et rejoindre nos équipes motivées et multiculturelles ?